From 8540924f8791f095b537463554e4e2f7018803f2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 9 Mar 2024 01:52:35 +0800 Subject: [PATCH 001/601] =?UTF-8?q?Performance=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_manage/Performance.cpp | 115 +++++++++--------- hikyuu_cpp/hikyuu/trade_manage/Performance.h | 8 +- 2 files changed, 61 insertions(+), 62 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp index 82082fb5..db87d0a2 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp @@ -11,60 +11,61 @@ namespace hku { -Performance::Performance() -: m_keys({"帐户初始金额", - "累计投入本金", - "累计投入资产", - "累计借入现金", - "累计借入资产", - "累计红利", - "现金余额", - "未平仓头寸净值", - "当前总资产", - "已平仓交易总成本", - "已平仓净利润总额", - "单笔交易最大占用现金比例%", - "交易平均占用现金比例%", - "已平仓帐户收益率%", - "帐户年复合收益率%", - "帐户平均年收益率%", - "赢利交易赢利总额", - "亏损交易亏损总额", - "已平仓交易总数", - "赢利交易数", - "亏损交易数", - "赢利交易比例%", - "赢利期望值", - "赢利交易平均赢利", - "亏损交易平均亏损", - "平均赢利/平均亏损比例", - "净赢利/亏损比例", - "最大单笔赢利", - "最大单笔盈利百分比%", - "最大单笔亏损", - "最大单笔亏损百分比%", - "赢利交易平均持仓时间", - "赢利交易最大持仓时间", - "亏损交易平均持仓时间", - "亏损交易最大持仓时间", - "空仓总时间", - "空仓时间/总时间%", - "平均空仓时间", - "最长空仓时间", - "最大连续赢利笔数", - "最大连续亏损笔数", - "最大连续赢利金额", - "最大连续亏损金额", - "R乘数期望值", - "交易机会频率/年", - "年度期望R乘数", - "赢利交易平均R乘数", - "亏损交易平均R乘数", - "最大单笔赢利R乘数", - "最大单笔亏损R乘数", - "最大连续赢利R乘数", - "最大连续亏损R乘数"}) { - for (const auto& key : m_keys) { +StringList Performance::ms_keys{"帐户初始金额", + "累计投入本金", + "累计投入资产", + "累计借入现金", + "累计借入资产", + "累计红利", + "现金余额", + "未平仓头寸净值", + "当前总资产", + "已平仓交易总成本", + "已平仓净利润总额", + "单笔交易最大占用现金比例%", + "交易平均占用现金比例%", + "已平仓帐户收益率%", + "帐户年复合收益率%", + "帐户平均年收益率%", + "赢利交易赢利总额", + "亏损交易亏损总额", + "已平仓交易总数", + "赢利交易数", + "亏损交易数", + "赢利交易比例%", + "赢利期望值", + "赢利交易平均赢利", + "亏损交易平均亏损", + "平均赢利/平均亏损比例", + "净赢利/亏损比例", + "最大单笔赢利", + "最大单笔盈利百分比%", + "最大单笔亏损", + "最大单笔亏损百分比%", + "赢利交易平均持仓时间", + "赢利交易最大持仓时间", + "亏损交易平均持仓时间", + "亏损交易最大持仓时间", + "空仓总时间", + "空仓时间/总时间%", + "平均空仓时间", + "最长空仓时间", + "最大连续赢利笔数", + "最大连续亏损笔数", + "最大连续赢利金额", + "最大连续亏损金额", + "R乘数期望值", + "交易机会频率/年", + "年度期望R乘数", + "赢利交易平均R乘数", + "亏损交易平均R乘数", + "最大单笔赢利R乘数", + "最大单笔亏损R乘数", + "最大连续赢利R乘数", + "最大连续亏损R乘数"}; + +Performance::Performance() { + for (const auto& key : ms_keys) { m_result[key] = 0.0; } } @@ -74,14 +75,12 @@ Performance::~Performance() {} Performance& Performance::operator=(const Performance& other) { HKU_IF_RETURN(this == &other, *this); m_result = other.m_result; - m_keys = m_keys; return *this; } Performance& Performance::operator=(Performance&& other) { HKU_IF_RETURN(this == &other, *this); m_result = std::move(other.m_result); - m_keys = std::move(other.m_keys); return *this; } @@ -103,7 +102,7 @@ double Performance::get(const string& name) const { PriceList Performance::values() const { PriceList result(m_result.size()); size_t i = 0; - for (const auto& key : m_keys) { + for (const auto& key : ms_keys) { result[i++] = m_result.at(key); } return result; @@ -120,7 +119,7 @@ string Performance::report(const TradeManagerPtr& tm, const Datetime& datetime) buf.setf(std::ios_base::fixed); buf.precision(tm->precision()); - for (const auto& key : m_keys) { + for (const auto& key : ms_keys) { buf << key << ": " << m_result.at(key) << std::endl; } diff --git a/hikyuu_cpp/hikyuu/trade_manage/Performance.h b/hikyuu_cpp/hikyuu/trade_manage/Performance.h index 071b8bf2..b727b53c 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/Performance.h +++ b/hikyuu_cpp/hikyuu/trade_manage/Performance.h @@ -23,8 +23,8 @@ public: Performance(); virtual ~Performance(); - Performance(const Performance& other) : m_result(other.m_result), m_keys(other.m_keys) {} - Performance(Performance&& other) : m_result(std::move(other.m_result)), m_keys(other.m_keys) {} + Performance(const Performance& other) : m_result(other.m_result) {} + Performance(Performance&& other) : m_result(std::move(other.m_result)) {} Performance& operator=(const Performance& other); Performance& operator=(Performance&& other); @@ -58,7 +58,7 @@ public: /** 获取所有统计项名称,顺序与 values 相同 */ const StringList& names() const { - return m_keys; + return ms_keys; } /** 获取所有统计项值,顺序与 names 相同*/ @@ -70,7 +70,7 @@ public: private: map_type m_result; - StringList m_keys; // 保存统计项顺序, map/unordered_map都不能保持按插入顺序遍历 + static StringList ms_keys; // 保存统计项顺序, map/unordered_map都不能保持按插入顺序遍历 }; } /* namespace hku */ From f844ce7e1cbe3c35ec5f5c95585ac9c8f9cb19ea Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 9 Mar 2024 18:23:23 +0800 Subject: [PATCH 002/601] IC (continue) --- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/IC.h | 31 +++++ hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 100 ++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IIc.h | 45 +++++++ .../unit_test/hikyuu/indicator/test_IC.cpp | 111 ++++++++++++++++++ .../hikyuu/indicator/test_SPEARMAN.cpp | 59 ++++++++-- 6 files changed, 338 insertions(+), 9 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/IC.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IIc.h create mode 100644 hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 76297ac7..15474afb 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -47,6 +47,7 @@ #include "crt/HHV.h" #include "crt/HHVBARS.h" #include "crt/HSL.h" +#include "crt/IC.h" #include "crt/INTPART.h" #include "crt/LAST.h" #include "crt/LIUTONGPAN.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/IC.h b/hikyuu_cpp/hikyuu/indicator/crt/IC.h new file mode 100644 index 00000000..b70fc654 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/IC.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-09 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/Block.h" +#include "../Indicator.h" + +namespace hku { + +Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n = 1, + const Stock& ref_stk = getStock("sh000300")); + +Indicator HKU_API IC(const Block& blk, const KQuery& query, int n = 1, + const Stock& ref_stk = getStock("sh000300")); + +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 Block& blk, const KQuery& query, int n = 1, + const Stock& ref_stk = getStock("sh000300")) { + return IC(blk, query, n, ref_stk)(ind); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp new file mode 100644 index 00000000..0aa965f8 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-09 + * Author: fasiondog + */ + +#include "hikyuu/Block.h" +#include "hikyuu/indicator/crt/ALIGN.h" +#include "hikyuu/indicator/crt/ROCP.h" +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "IIc.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IIc) +#endif + +namespace hku { + +IIc::IIc() : IndicatorImp("IC") { + setParam("n", 1); +} + +IIc::IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) +: IndicatorImp("IC"), m_query(query), m_stks(stks), m_ref_stk(ref_stk) { + setParam("n", n); +} + +IIc::~IIc() {} + +bool IIc::check() { + return getParam("n") >= 1; +} + +IndicatorImpPtr IIc::_clone() { + IIc* p = new IIc(); + p->m_stks = m_stks; + p->m_query = m_query; + p->m_ref_stk = m_ref_stk; + return IndicatorImpPtr(p); +} + +void IIc::_calculate(const Indicator& inputInd) { + HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "No ref_stk was specified!"); + HKU_ERROR_IF_RETURN(m_stks.size() < 2, void(), + "The number of stock is insufficient, need > 2! current: {}", + m_stks.size()); + for (const auto& stk : m_stks) { + HKU_ERROR_IF_RETURN(stk.isNull(), void(), "exist null stock!"); + } + + auto ref_dates = m_ref_stk.getDatetimeList(m_query); + size_t total = ref_dates.size(); + _readyBuffer(total, 1); + HKU_WARN_IF_RETURN(total < 2, void(), "Insufficient data length! current lenght: {}", total); + + int n = getParam("n"); + + vector all_inds; + all_inds.reserve(m_stks.size()); + vector all_returns; + all_returns.reserve(m_stks.size()); + Indicator ind = inputInd; + for (const auto& stk : m_stks) { + auto k = stk.getKData(m_query); + all_inds.push_back(ALIGN(ind(k), ref_dates)); + HKU_INFO("{} {}", stk.market_code(), ALIGN(ind(k), ref_dates)); + all_returns.push_back(ROCP(k.close(), n)); + } + + PriceList tmp(total, Null()); + PriceList tmp_return(total, Null()); + size_t ind_count = all_inds.size(); + auto* dst = this->data(); + for (size_t i = 0; i < total; i++) { + for (size_t j = 0; j < ind_count; j++) { + if (i >= all_inds[j].size() || i >= all_returns[j].size()) { + continue; + } + tmp[i] = all_inds[j][i]; + tmp_return[i] = all_returns[j][i]; + } + auto a = PRICELIST(tmp); + auto b = PRICELIST(tmp_return); + auto ic = hku::SPEARMAN(a, b, ind_count); + dst[i] = ic[ic.size() - 1]; + } +} + +Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) { + IndicatorImpPtr p = make_shared(stks, query, n, ref_stk); + return p; +} + +Indicator HKU_API IC(const Block& blk, const KQuery& query, int n, const Stock& ref_stk) { + StockList stks = blk.getAllStocks(); + return IC(stks, query, n, ref_stk); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.h b/hikyuu_cpp/hikyuu/indicator/imp/IIc.h new file mode 100644 index 00000000..a6316352 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-09 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class IIc : public IndicatorImp { +public: + IIc(); + IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk); + virtual ~IIc(); + + virtual bool check() override; + virtual void _calculate(const Indicator& data) override; + virtual IndicatorImpPtr _clone() override; + +private: + KQuery m_query; + Stock m_ref_stk; + StockList m_stks; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(IndicatorImp); + ar& BOOST_SERIALIZATION_NVP(m_query); + ar& BOOST_SERIALIZATION_NVP(m_ref_stk); + ar& BOOST_SERIALIZATION_NVP(m_stks); + } +#endif +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp new file mode 100644 index 00000000..13c46529 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -0,0 +1,111 @@ +/* + * test_ABS.cpp + * + * Created on: 2019年4月2日 + * Author: fasiondog + */ + +#include "../test_config.h" +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_indicator_IC test_indicator_IC + * @ingroup test_hikyuu_indicator_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_IC") { + StockManager& sm = StockManager::instance(); + StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + Stock ref_stk = sm["sh000001"]; + + KQuery query = KQuery(-200); + Indicator result = IC(stks, query, 1, ref_stk)(MA(CLOSE())); + HKU_INFO("{}", result); + + // PriceList a; + // for (int i = 0; i < 10; ++i) { + // a.push_back(-i); + // } + + // Indicator data = PRICELIST(a); + + // result = ABS(data); + // CHECK_UNARY(result.name() == "ABS"); + // CHECK_UNARY(result.discard() == 0); + // for (int i = 0; i < 10; ++i) { + // CHECK_UNARY(result[i] == -data[i]); + // } + + // result = ABS(-11); + // CHECK_UNARY(result.size() == 1); + // CHECK_UNARY(result.discard() == 0); + // CHECK_UNARY(result[0] == 11); +} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_IC_benchmark") { + // Stock stock = getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(0)); + // Indicator c = kdata.close(); + // int cycle = 1000; // 测试循环次数 + + // { + // BENCHMARK_TIME_MSG(test_ABS_benchmark, cycle, fmt::format("data len: {}", c.size())); + // SPEND_TIME_CONTROL(false); + // for (int i = 0; i < cycle; i++) { + // Indicator ind = ABS(); + // Indicator result = ind(c); + // } + // } +} +#endif + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_IC_export") { + // StockManager& sm = StockManager::instance(); + // string filename(sm.tmpdir()); + // filename += "/ABS.xml"; + + // Stock stock = sm.getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(-20)); + // Indicator x1 = ABS(CLOSE(kdata)); + // { + // std::ofstream ofs(filename); + // boost::archive::xml_oarchive oa(ofs); + // oa << BOOST_SERIALIZATION_NVP(x1); + // } + + // Indicator x2; + // { + // std::ifstream ifs(filename); + // boost::archive::xml_iarchive ia(ifs); + // ia >> BOOST_SERIALIZATION_NVP(x2); + // } + + // CHECK_UNARY(x1.size() == x2.size()); + // CHECK_UNARY(x1.discard() == x2.discard()); + // CHECK_UNARY(x1.getResultNumber() == x2.getResultNumber()); + // for (size_t i = 0; i < x1.size(); ++i) { + // CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); + // } +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index 86c479cc..ffc1c818 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -29,18 +29,34 @@ static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value data_index[i].second = i; } - std::sort( - data_index.begin(), data_index.end(), - std::bind( - std::less(), - std::bind(&std::pair::first, std::placeholders::_1), - std::bind(&std::pair::first, std::placeholders::_2))); + std::sort(data_index.begin(), data_index.end(), + [](const std::pair &a, + const std::pair &b) { + if (std::isnan(a.first)) { + return false; + } + if (std::isnan(b.first) && !std::isnan(a.first)) { + return true; + } + return a.first < b.first; + }); - size_t i = 0; - while (i < total) { + IndicatorImp::value_t null_value = Null(); + int64_t pos = (int64_t)total - 1; + while (pos > 0) { + if (!std::isnan(data_index[pos].first)) { + break; + } + level[data_index[pos].second] = null_value; + pos--; + } + + int64_t len = pos + 1; + int64_t i = 0; + while (i < len) { size_t count = 1; IndicatorImp::value_t score = i + 1.0; - for (size_t j = i + 1; j < total; j++) { + for (int64_t j = i + 1; j < len; j++) { if (data_index[i].first != data_index[j].first) { break; } @@ -57,6 +73,7 @@ static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value /** @par 检测点 */ TEST_CASE("test_spearmanLevel") { + /** @arg 无重复值排序 */ std::vector a{3., 8., 4., 7., 2.}; size_t totala = a.size(); auto levela = std::make_unique(totala); @@ -68,6 +85,7 @@ TEST_CASE("test_spearmanLevel") { CHECK_EQ(ptra[i], doctest::Approx(expecta[i])); } + /** @arg 存在重复值 */ std::vector b{5., 10., 8., 10., 6.}; size_t totalb = b.size(); auto levelb = std::make_unique(totalb); @@ -78,6 +96,29 @@ TEST_CASE("test_spearmanLevel") { for (size_t i = 0; i < totalb; i++) { CHECK_EQ(ptrb[i], doctest::Approx(expectb[i])); } + + /** @arg 存在nan值*/ + IndicatorImp::value_t null_value = Null(); + std::vector c{ + null_value, null_value, 5., 10., null_value, null_value, 8., 10., 6., null_value, null_value}; + size_t totalc = c.size(); + auto levelc = std::make_unique(totalc); + auto *ptrc = levelc.get(); + spearmanLevel(c.data(), levelc.get(), totalc); + + std::vector expectc = {null_value, 1., 4.5, null_value, + 3., 4.5, 2., null_value}; + CHECK_UNARY(std::isnan(ptrc[0])); + CHECK_UNARY(std::isnan(ptrc[1])); + CHECK_EQ(ptrc[2], doctest::Approx(1.)); + CHECK_EQ(ptrc[3], doctest::Approx(4.5)); + CHECK_UNARY(std::isnan(ptrc[4])); + CHECK_UNARY(std::isnan(ptrc[5])); + CHECK_EQ(ptrc[6], doctest::Approx(3.)); + CHECK_EQ(ptrc[7], doctest::Approx(4.5)); + CHECK_EQ(ptrc[8], doctest::Approx(2.)); + CHECK_UNARY(std::isnan(ptrc[9])); + CHECK_UNARY(std::isnan(ptrc[10])); } /** @par 检测点 */ From d63cb48ce74b453c58b904d039ca16ed172e1a49 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 9 Mar 2024 22:39:16 +0800 Subject: [PATCH 003/601] =?UTF-8?q?PRICELIST=20=E5=85=81=E8=AE=B8discard?= =?UTF-8?q?=E5=90=8E=EF=BC=8C=E5=AD=98=E5=9C=A8=20nan=20=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp | 5 ----- 1 file changed, 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp index 2ffa8434..49fd3f6a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp @@ -47,11 +47,6 @@ void IPriceList::_calculate(const Indicator& data) { for (size_t i = m_discard; i < total; ++i) { dst[i] = x[i]; } - - for (size_t i = m_discard; i < total && std::isnan(get(i)); ++i) { - m_discard++; - } - return; } From 3ff9cc4a13c2ad95304dafcf92321f673e8e08ac Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 01:18:19 +0800 Subject: [PATCH 004/601] add IC --- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 0aa965f8..d2264355 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -9,6 +9,7 @@ #include "hikyuu/indicator/crt/ALIGN.h" #include "hikyuu/indicator/crt/ROCP.h" #include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/SPEARMAN.h" #include "IIc.h" #if HKU_SUPPORT_SERIALIZATION From e46c2044df5af595c4356df55042f7bc35a4acc5 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 01:18:32 +0800 Subject: [PATCH 005/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20SPEARMAN=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 18 +-- hikyuu_cpp/hikyuu/indicator/Indicator.h | 2 +- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 27 +++- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h | 20 +++ hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 144 ++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h | 41 +++++ .../hikyuu/indicator/test_SPEARMAN.cpp | 125 ++++++++------- 8 files changed, 311 insertions(+), 67 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 1cb9392d..5e7da4b4 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -315,14 +315,14 @@ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { return p->calculate(); } -Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n) { - HKU_ERROR_IF_RETURN(!ind1.getImp() || !ind2.getImp(), Indicator(), - "ind1 or ind2 is Null Indicator!"); - HKU_ERROR_IF_RETURN(n < 2, Indicator(), "Invalid param n: {} (need >= 2)", n); - IndicatorImpPtr p = make_shared("SPEARMAN"); - p->setParam("n", n); - p->add(IndicatorImp::SPEARMAN, ind1.getImp(), ind2.getImp()); - return p->calculate(); -} +// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n) { +// HKU_ERROR_IF_RETURN(!ind1.getImp() || !ind2.getImp(), Indicator(), +// "ind1 or ind2 is Null Indicator!"); +// HKU_ERROR_IF_RETURN(n < 2, Indicator(), "Invalid param n: {} (need >= 2)", n); +// IndicatorImpPtr p = make_shared("SPEARMAN"); +// p->setParam("n", n); +// p->add(IndicatorImp::SPEARMAN, ind1.getImp(), ind2.getImp()); +// return p->calculate(); +// } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index e23617e5..c43163ed 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -393,7 +393,7 @@ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n); * @param ind2 指标2 * @ingroup Indicator */ -Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n); +// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n); } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index 2e573fa0..d2662a1a 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -1629,7 +1629,11 @@ void IndicatorImp::execute_spearman() { auto *ptrb = levelb.get(); // 不处理 n 不足的情况,防止只需要计算全部序列时,过于耗时 - value_t back = std::pow(value_t(n), 3) - n; + double back = std::pow(n, 3) - n; + vector tmpa; + vector tmpb; + tmpa.reserve(n); + tmpa.reserve(n); for (size_t r = 0; r < result_number; ++r) { auto *dst = this->data(r); auto const *maxdata = maxp->data(r); @@ -1637,13 +1641,26 @@ void IndicatorImp::execute_spearman() { auto const *a = maxdata + discard + 1 - n; auto const *b = mindata + discard + 1 - diff - n; for (size_t i = discard; i < total; ++i) { - spearmanLevel(a, ptra, n); - spearmanLevel(b, ptrb, n); + tmpa.clear(); + tmpb.clear(); + for (int j = 0; j < n; j++) { + if (!std::isnan(a[j]) && !std::isnan(b[j])) { + tmpa.push_back(a[j]); + tmpb.push_back(b[j]); + } + } + int act_count = tmpa.size(); + if (act_count < 2) { + continue; + } + spearmanLevel(tmpa.data(), ptra, act_count); + spearmanLevel(tmpb.data(), ptrb, act_count); value_t sum = 0.0; - for (size_t j = 0; j < n; j++) { + for (int j = 0; j < act_count; j++) { sum += std::pow(ptra[j] - ptrb[j], 2); } - dst[i] = 1 - 6.0 * sum / back; + dst[i] = act_count == n ? 1.0 - 6.0 * sum / back + : 1.0 - 6.0 * sum / (std::pow(act_count, 3) - act_count); a++; b++; } diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 15474afb..6105286c 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -84,6 +84,7 @@ #include "crt/SGN.h" #include "crt/SLOPE.h" #include "crt/SMA.h" +#include "crt/SPEARMAN.h" #include "crt/SQRT.h" #include "crt/STDEV.h" #include "crt/STDP.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h new file mode 100644 index 00000000..af285f9c --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#include "../Indicator.h" + +namespace hku { + +/** + * Spearman 相关系数 + * @param ind1 指标1 + * @param ind2 指标2 + * @ingroup Indicator + */ +Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n = 0); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp new file mode 100644 index 00000000..e34bdce7 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "ISpearman.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::ISpearman) +#endif + +namespace hku { + +ISpearman::ISpearman() : IndicatorImp("SPEARMAN") { + setParam("n", 0); +} + +ISpearman::ISpearman(const Indicator &ref_ind) : IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) { + setParam("n", 0); +} + +ISpearman::~ISpearman() {} + +bool ISpearman::check() { + int n = getParam("n"); + return n == 0 || n >= 2; +} + +IndicatorImpPtr ISpearman::_clone() { + ISpearman *p = new ISpearman(); + p->m_ref_ind = m_ref_ind.clone(); + return IndicatorImpPtr(p); +} + +static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value_t *level, + size_t total) { + std::vector> data_index(total); + for (size_t i = 0; i < total; i++) { + data_index[i].first = data[i]; + data_index[i].second = i; + } + + std::sort( + data_index.begin(), data_index.end(), + std::bind( + std::less(), + std::bind(&std::pair::first, std::placeholders::_1), + std::bind(&std::pair::first, std::placeholders::_2))); + + size_t i = 0; + while (i < total) { + size_t count = 1; + IndicatorImp::value_t score = i + 1.0; + for (size_t j = i + 1; j < total; j++) { + if (data_index[i].first != data_index[j].first) { + break; + } + count++; + score += j + 1; + } + score = score / count; + for (size_t j = 0; j < count; j++) { + level[data_index[i + j].second] = score; + } + i += count; + } +} + +void ISpearman::_calculate(const Indicator &ind) { + auto k = getContext(); + + m_ref_ind.setContext(k); + Indicator ref = m_ref_ind; + if (m_ref_ind.size() != ind.size()) { + ref = ALIGN(m_ref_ind, ind); + } + + size_t total = ind.size(); + _readyBuffer(total, 1); + HKU_IF_RETURN(total == 0, void()); + + int n = getParam("n"); + if (n == 0) { + n = total; + } + + m_discard = std::max(ind.discard(), ref.discard()); + m_discard += n - 1; + if (m_discard > total) { + m_discard = total; + return; + } + + auto levela = std::make_unique(n); + auto levelb = std::make_unique(n); + auto *ptra = levela.get(); + auto *ptrb = levelb.get(); + + // 不处理 n 不足的情况,防止只需要计算全部序列时,过于耗时 + double back = std::pow(n, 3) - n; + vector tmpa; + vector tmpb; + tmpa.reserve(n); + tmpa.reserve(n); + + auto *dst = this->data(); + auto const *a = ind.data() + m_discard + 1 - n; + auto const *b = ref.data() + m_discard + 1 - n; + for (size_t i = m_discard; i < total; ++i) { + tmpa.clear(); + tmpb.clear(); + for (int j = 0; j < n; j++) { + if (!std::isnan(a[j]) && !std::isnan(b[j])) { + tmpa.push_back(a[j]); + tmpb.push_back(b[j]); + } + } + int act_count = tmpa.size(); + if (act_count < 2) { + continue; + } + spearmanLevel(tmpa.data(), ptra, act_count); + spearmanLevel(tmpb.data(), ptrb, act_count); + value_t sum = 0.0; + for (int j = 0; j < act_count; j++) { + sum += std::pow(ptra[j] - ptrb[j], 2); + } + dst[i] = act_count == n ? 1.0 - 6.0 * sum / back + : 1.0 - 6.0 * sum / (std::pow(act_count, 3) - act_count); + a++; + b++; + } +} + +Indicator HKU_API SPEARMAN(const Indicator &ind1, const Indicator &ind2, int n) { + ISpearman *p = new ISpearman(ind2); + p->setParam("n", n); + return IndicatorImpPtr(p); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h new file mode 100644 index 00000000..dc407e0c --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class ISpearman : public IndicatorImp { +public: + ISpearman(); + ISpearman(const Indicator& ref_ind); + virtual ~ISpearman(); + + virtual bool check() override; + virtual void _calculate(const Indicator& data) override; + virtual IndicatorImpPtr _clone() override; + +private: + Indicator m_ref_ind; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(IndicatorImp); + ar& BOOST_SERIALIZATION_NVP(m_ref_ind); + } +#endif +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index ffc1c818..cf3c0511 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -10,6 +10,7 @@ #include "../test_config.h" #include #include +#include #include #include @@ -125,7 +126,7 @@ TEST_CASE("test_spearmanLevel") { TEST_CASE("test_SPEARMAN") { Indicator result; - // 空指标 + /** @arg 空指标 */ result = SPEARMAN(Indicator(), Indicator(), 10); CHECK_UNARY(result.empty()); @@ -135,13 +136,13 @@ TEST_CASE("test_SPEARMAN") { Indicator x = PRICELIST(a); Indicator y = PRICELIST(b); - // 非法参数 n - result = SPEARMAN(x, y, 0); + /** @arg 非法参数 n */ + result = SPEARMAN(x, y, -1); CHECK_UNARY(result.empty()); result = SPEARMAN(x, y, 1); CHECK_UNARY(result.empty()); - // 正常情况 + /** @arg 正常情况 n */ PriceList expect{Null(), 1., 1., 0.95, 0.875}; result = SPEARMAN(x, y, a.size()); CHECK_EQ(result.name(), "SPEARMAN"); @@ -151,14 +152,34 @@ TEST_CASE("test_SPEARMAN") { CHECK_EQ(result[i], doctest::Approx(expect[i])); } - expect = {Null(), Null(), 1., 0.875, 1.}; - result = SPEARMAN(x, y, 3); - CHECK_EQ(result.name(), "SPEARMAN"); - CHECK_EQ(result.discard(), 2); - CHECK_EQ(result.size(), a.size()); - for (size_t i = result.discard(); i < result.size(); i++) { - CHECK_EQ(result[i], doctest::Approx(expect[i])); - } + // expect = {Null(), Null(), 1., 0.875, 1.}; + // result = SPEARMAN(x, y, 3); + // CHECK_EQ(result.name(), "SPEARMAN"); + // CHECK_EQ(result.discard(), 2); + // CHECK_EQ(result.size(), a.size()); + // for (size_t i = result.discard(); i < result.size(); i++) { + // CHECK_EQ(result[i], doctest::Approx(expect[i])); + // } + + // /** @arg 包含 nan 值 */ + // price_t null_value = Null(); + // auto x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value}); + // auto y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value}); + // // expect = {null_value, , 1., 0.875, 1.}; + // // nan, 8, nan, 4, nan, 2, nan + // // nan, 5, nan, 8, nan, 10, nan, + // result = SPEARMAN(x, y, 4); + // HKU_INFO("{}", result); + // for (size_t i = result.discard(); i < result.size(); i++) { + // HKU_INFO("{}: {}", i, result[i]); + // } + + // x = PRICELIST({8., 4., 2.}); + // y = PRICELIST({5., 8., 10.}); + // result = SPEARMAN(x, y, x.size()); + // HKU_INFO("{}", result); + // HKU_INFO("{}", std::pow(null_value, 2)); + // HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size())); } //----------------------------------------------------------------------------- @@ -189,50 +210,50 @@ TEST_CASE("test_SPEARMAN_benchmark") { /** @par 检测点 */ TEST_CASE("test_SPEARMAN_export") { - StockManager &sm = StockManager::instance(); - string filename(sm.tmpdir()); - filename += "/SPEARMAN.xml"; + // StockManager &sm = StockManager::instance(); + // string filename(sm.tmpdir()); + // filename += "/SPEARMAN.xml"; - Stock stock = sm.getStock("sh000001"); - KData kdata = stock.getKData(KQuery(-20)); - Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10); - { - std::ofstream ofs(filename); - boost::archive::xml_oarchive oa(ofs); - oa << BOOST_SERIALIZATION_NVP(x1); - } + // Stock stock = sm.getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(-20)); + // Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10); + // { + // std::ofstream ofs(filename); + // boost::archive::xml_oarchive oa(ofs); + // oa << BOOST_SERIALIZATION_NVP(x1); + // } - Indicator x2; - { - std::ifstream ifs(filename); - boost::archive::xml_iarchive ia(ifs); - ia >> BOOST_SERIALIZATION_NVP(x2); - } + // Indicator x2; + // { + // std::ifstream ifs(filename); + // boost::archive::xml_iarchive ia(ifs); + // ia >> BOOST_SERIALIZATION_NVP(x2); + // } - CHECK_EQ(x2.name(), "SPEARMAN"); - CHECK_EQ(x1.size(), x2.size()); - CHECK_EQ(x1.discard(), x2.discard()); - CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); - for (size_t i = x1.discard(); i < x1.size(); ++i) { - if (std::isnan(x1[i])) { - CHECK_UNARY(std::isnan(x2[i])); - } else { - CHECK_EQ(x1[i], doctest::Approx(x2[i])); - } - } + // CHECK_EQ(x2.name(), "SPEARMAN"); + // CHECK_EQ(x1.size(), x2.size()); + // CHECK_EQ(x1.discard(), x2.discard()); + // CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + // for (size_t i = x1.discard(); i < x1.size(); ++i) { + // if (std::isnan(x1[i])) { + // CHECK_UNARY(std::isnan(x2[i])); + // } else { + // CHECK_EQ(x1[i], doctest::Approx(x2[i])); + // } + // } - Indicator x11 = x1.getResult(1); - Indicator x21 = x2.getResult(1); - CHECK_EQ(x11.size(), x21.size()); - CHECK_EQ(x11.discard(), x21.discard()); - CHECK_EQ(x11.getResultNumber(), x21.getResultNumber()); - for (size_t i = x11.discard(); i < x21.size(); ++i) { - if (std::isnan(x11[i])) { - CHECK_UNARY(std::isnan(x21[i])); - } else { - CHECK_EQ(x11[i], doctest::Approx(x21[i])); - } - } + // Indicator x11 = x1.getResult(1); + // Indicator x21 = x2.getResult(1); + // CHECK_EQ(x11.size(), x21.size()); + // CHECK_EQ(x11.discard(), x21.discard()); + // CHECK_EQ(x11.getResultNumber(), x21.getResultNumber()); + // for (size_t i = x11.discard(); i < x21.size(); ++i) { + // if (std::isnan(x11[i])) { + // CHECK_UNARY(std::isnan(x21[i])); + // } else { + // CHECK_EQ(x11[i], doctest::Approx(x21[i])); + // } + // } } #endif /* #if HKU_SUPPORT_SERIALIZATION */ From 3ba6a0767f0f813060ff40e8c7846b125fb42ba9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 02:03:08 +0800 Subject: [PATCH 006/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20SPEARMAN=20?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 2 +- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 10 -- hikyuu_cpp/hikyuu/indicator/Indicator.h | 8 -- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 122 ---------------- hikyuu_cpp/hikyuu/indicator/IndicatorImp.h | 37 +++-- hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h | 2 + hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 19 ++- hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h | 3 +- .../hikyuu/indicator/test_SPEARMAN.cpp | 132 +++++++++--------- hikyuu_pywrap/indicator/_build_in.cpp | 10 +- 10 files changed, 110 insertions(+), 235 deletions(-) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index ecc9f3b8..af68a302 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -869,7 +869,7 @@ :param Indicator ind1: 输入参数1 :param Indicator ind2: 输入参数2 - :param int n: 指定窗口 + :param int n: 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度 .. py:function:: SQRT([data]) diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 5e7da4b4..fe0093b3 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -315,14 +315,4 @@ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { return p->calculate(); } -// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n) { -// HKU_ERROR_IF_RETURN(!ind1.getImp() || !ind2.getImp(), Indicator(), -// "ind1 or ind2 is Null Indicator!"); -// HKU_ERROR_IF_RETURN(n < 2, Indicator(), "Invalid param n: {} (need >= 2)", n); -// IndicatorImpPtr p = make_shared("SPEARMAN"); -// p->setParam("n", n); -// p->add(IndicatorImp::SPEARMAN, ind1.getImp(), ind2.getImp()); -// return p->calculate(); -// } - } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index c43163ed..984ea1f7 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -387,14 +387,6 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_ */ Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n); -/** - * Spearman 相关系数 - * @param ind1 指标1 - * @param ind2 指标2 - * @ingroup Indicator - */ -// Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n); - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index d2662a1a..51d3c032 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -97,10 +97,6 @@ string HKU_API getOPTypeName(IndicatorImp::OPType op) { name = "CORR"; break; - case IndicatorImp::SPEARMAN: - name = "SPEARMAN"; - break; - default: name = "UNKNOWN"; break; @@ -542,10 +538,6 @@ string IndicatorImp::formula() const { buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")"; break; - case SPEARMAN: - buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")"; - break; - default: HKU_ERROR("Wrong optype! {}", int(m_optype)); break; @@ -809,10 +801,6 @@ Indicator IndicatorImp::calculate() { execute_corr(); break; - case SPEARMAN: - execute_spearman(); - break; - default: HKU_ERROR("Unkown Indicator::OPType! {}", int(m_optype)); break; @@ -1557,116 +1545,6 @@ void IndicatorImp::execute_corr() { setDiscard(discard + 2); } -static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value_t *level, - size_t total) { - std::vector> data_index(total); - for (size_t i = 0; i < total; i++) { - data_index[i].first = data[i]; - data_index[i].second = i; - } - - std::sort( - data_index.begin(), data_index.end(), - std::bind( - std::less(), - std::bind(&std::pair::first, std::placeholders::_1), - std::bind(&std::pair::first, std::placeholders::_2))); - - size_t i = 0; - while (i < total) { - size_t count = 1; - IndicatorImp::value_t score = i + 1.0; - for (size_t j = i + 1; j < total; j++) { - if (data_index[i].first != data_index[j].first) { - break; - } - count++; - score += j + 1; - } - score = score / count; - for (size_t j = 0; j < count; j++) { - level[data_index[i + j].second] = score; - } - i += count; - } -} - -void IndicatorImp::execute_spearman() { - m_right->calculate(); - m_left->calculate(); - - const IndicatorImp *maxp, *minp; - if (m_right->size() > m_left->size()) { - maxp = m_right.get(); - minp = m_left.get(); - } else { - maxp = m_left.get(); - minp = m_right.get(); - } - - size_t total = maxp->size(); - size_t discard = maxp->size() - minp->size() + minp->discard(); - if (discard < maxp->discard()) { - discard = maxp->discard(); - } - - size_t result_number = std::min(minp->getResultNumber(), maxp->getResultNumber()); - size_t diff = maxp->size() - minp->size(); - _readyBuffer(total, result_number); - - int n = getParam("n"); - if (n < 2 || discard + 2 > total) { - setDiscard(total); - return; - } - - discard += n - 1; - setDiscard(discard); - - auto levela = std::make_unique(n); - auto levelb = std::make_unique(n); - auto *ptra = levela.get(); - auto *ptrb = levelb.get(); - - // 不处理 n 不足的情况,防止只需要计算全部序列时,过于耗时 - double back = std::pow(n, 3) - n; - vector tmpa; - vector tmpb; - tmpa.reserve(n); - tmpa.reserve(n); - for (size_t r = 0; r < result_number; ++r) { - auto *dst = this->data(r); - auto const *maxdata = maxp->data(r); - auto const *mindata = minp->data(r); - auto const *a = maxdata + discard + 1 - n; - auto const *b = mindata + discard + 1 - diff - n; - for (size_t i = discard; i < total; ++i) { - tmpa.clear(); - tmpb.clear(); - for (int j = 0; j < n; j++) { - if (!std::isnan(a[j]) && !std::isnan(b[j])) { - tmpa.push_back(a[j]); - tmpb.push_back(b[j]); - } - } - int act_count = tmpa.size(); - if (act_count < 2) { - continue; - } - spearmanLevel(tmpa.data(), ptra, act_count); - spearmanLevel(tmpb.data(), ptrb, act_count); - value_t sum = 0.0; - for (int j = 0; j < act_count; j++) { - sum += std::pow(ptra[j] - ptrb[j], 2); - } - dst[i] = act_count == n ? 1.0 - 6.0 * sum / back - : 1.0 - 6.0 * sum / (std::pow(act_count, 3) - act_count); - a++; - b++; - } - } -} - void IndicatorImp::_dyn_calculate(const Indicator &ind) { // SPEND_TIME(IndicatorImp__dyn_calculate); const auto &ind_param = getIndParamImp("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h index e2100e26..91e93952 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -31,25 +31,24 @@ class HKU_API IndicatorImp : public enable_shared_from_this { public: enum OPType { - LEAF, ///< 叶子节点 - OP, /// OP(OP1,OP2) OP1->calcalue(OP2->calculate(ind)) - ADD, ///< 加 - SUB, ///< 减 - MUL, ///< 乘 - DIV, ///< 除 - MOD, ///< 取模 - EQ, ///< 等于 - GT, ///< 大于 - LT, ///< 小于 - NE, ///< 不等于 - GE, ///< 大于等于 - LE, ///< 小于等于 - AND, ///< 与 - OR, ///< 或 - WEAVE, ///< 特殊的,需要两个指标作为参数的指标 - OP_IF, /// if操作 - CORR, ///< 相关系数,需要两个指标作为参数 - SPEARMAN, ///< spearman 相关系数 + LEAF, ///< 叶子节点 + OP, /// OP(OP1,OP2) OP1->calcalue(OP2->calculate(ind)) + ADD, ///< 加 + SUB, ///< 减 + MUL, ///< 乘 + DIV, ///< 除 + MOD, ///< 取模 + EQ, ///< 等于 + GT, ///< 大于 + LT, ///< 小于 + NE, ///< 不等于 + GE, ///< 大于等于 + LE, ///< 小于等于 + AND, ///< 与 + OR, ///< 或 + WEAVE, ///< 特殊的,需要两个指标作为参数的指标 + OP_IF, /// if操作 + CORR, ///< 相关系数,需要两个指标作为参数 INVALID }; diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h index af285f9c..e559918a 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h @@ -13,8 +13,10 @@ namespace hku { * Spearman 相关系数 * @param ind1 指标1 * @param ind2 指标2 + * @param n 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度 * @ingroup Indicator */ +Indicator HKU_API SPEARMAN(int n = 0); Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n = 0); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index e34bdce7..7de300ec 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -18,8 +18,13 @@ ISpearman::ISpearman() : IndicatorImp("SPEARMAN") { setParam("n", 0); } -ISpearman::ISpearman(const Indicator &ref_ind) : IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) { - setParam("n", 0); +ISpearman::ISpearman(int n) : IndicatorImp("SPEARMAN") { + setParam("n", n); +} + +ISpearman::ISpearman(const Indicator &ref_ind, int n) +: IndicatorImp("SPEARMAN"), m_ref_ind(ref_ind) { + setParam("n", n); } ISpearman::~ISpearman() {} @@ -135,10 +140,14 @@ void ISpearman::_calculate(const Indicator &ind) { } } +Indicator HKU_API SPEARMAN(int n) { + return make_shared(n); +} + Indicator HKU_API SPEARMAN(const Indicator &ind1, const Indicator &ind2, int n) { - ISpearman *p = new ISpearman(ind2); - p->setParam("n", n); - return IndicatorImpPtr(p); + auto p = make_shared(ind2, n); + Indicator result(p); + return result(ind1); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h index dc407e0c..ac313e1d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h @@ -14,7 +14,8 @@ namespace hku { class ISpearman : public IndicatorImp { public: ISpearman(); - ISpearman(const Indicator& ref_ind); + ISpearman(int n); + ISpearman(const Indicator& ref_ind, int n); virtual ~ISpearman(); virtual bool check() override; diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index cf3c0511..03d899a0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -152,34 +152,34 @@ TEST_CASE("test_SPEARMAN") { CHECK_EQ(result[i], doctest::Approx(expect[i])); } - // expect = {Null(), Null(), 1., 0.875, 1.}; - // result = SPEARMAN(x, y, 3); - // CHECK_EQ(result.name(), "SPEARMAN"); - // CHECK_EQ(result.discard(), 2); - // CHECK_EQ(result.size(), a.size()); - // for (size_t i = result.discard(); i < result.size(); i++) { - // CHECK_EQ(result[i], doctest::Approx(expect[i])); - // } + expect = {Null(), Null(), 1., 0.875, 1.}; + result = SPEARMAN(x, y, 3); + CHECK_EQ(result.name(), "SPEARMAN"); + CHECK_EQ(result.discard(), 2); + CHECK_EQ(result.size(), a.size()); + for (size_t i = result.discard(); i < result.size(); i++) { + CHECK_EQ(result[i], doctest::Approx(expect[i])); + } - // /** @arg 包含 nan 值 */ - // price_t null_value = Null(); - // auto x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value}); - // auto y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value}); - // // expect = {null_value, , 1., 0.875, 1.}; - // // nan, 8, nan, 4, nan, 2, nan - // // nan, 5, nan, 8, nan, 10, nan, - // result = SPEARMAN(x, y, 4); - // HKU_INFO("{}", result); - // for (size_t i = result.discard(); i < result.size(); i++) { - // HKU_INFO("{}: {}", i, result[i]); - // } + /** @arg 包含 nan 值 */ + price_t null_value = Null(); + x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value}); + y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value}); + // expect = {null_value, , 1., 0.875, 1.}; + // nan, 8, nan, 4, nan, 2, nan + // nan, 5, nan, 8, nan, 10, nan, + result = SPEARMAN(x, y, 4); + HKU_INFO("{}", result); + for (size_t i = result.discard(); i < result.size(); i++) { + HKU_INFO("{}: {}", i, result[i]); + } - // x = PRICELIST({8., 4., 2.}); - // y = PRICELIST({5., 8., 10.}); - // result = SPEARMAN(x, y, x.size()); - // HKU_INFO("{}", result); - // HKU_INFO("{}", std::pow(null_value, 2)); - // HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size())); + x = PRICELIST({8., 4., 2.}); + y = PRICELIST({5., 8., 10.}); + result = SPEARMAN(x, y, x.size()); + HKU_INFO("{}", result); + HKU_INFO("{}", std::pow(null_value, 2)); + HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size())); } //----------------------------------------------------------------------------- @@ -210,50 +210,50 @@ TEST_CASE("test_SPEARMAN_benchmark") { /** @par 检测点 */ TEST_CASE("test_SPEARMAN_export") { - // StockManager &sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/SPEARMAN.xml"; + StockManager &sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/SPEARMAN.xml"; - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(x1); - // } + Stock stock = sm.getStock("sh000001"); + KData kdata = stock.getKData(KQuery(-20)); + Indicator x1 = SPEARMAN(CLOSE(kdata), OPEN(kdata), 10); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(x1); + } - // Indicator x2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(x2); - // } + Indicator x2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(x2); + } - // CHECK_EQ(x2.name(), "SPEARMAN"); - // CHECK_EQ(x1.size(), x2.size()); - // CHECK_EQ(x1.discard(), x2.discard()); - // CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); - // for (size_t i = x1.discard(); i < x1.size(); ++i) { - // if (std::isnan(x1[i])) { - // CHECK_UNARY(std::isnan(x2[i])); - // } else { - // CHECK_EQ(x1[i], doctest::Approx(x2[i])); - // } - // } + CHECK_EQ(x2.name(), "SPEARMAN"); + CHECK_EQ(x1.size(), x2.size()); + CHECK_EQ(x1.discard(), x2.discard()); + CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + for (size_t i = x1.discard(); i < x1.size(); ++i) { + if (std::isnan(x1[i])) { + CHECK_UNARY(std::isnan(x2[i])); + } else { + CHECK_EQ(x1[i], doctest::Approx(x2[i])); + } + } - // Indicator x11 = x1.getResult(1); - // Indicator x21 = x2.getResult(1); - // CHECK_EQ(x11.size(), x21.size()); - // CHECK_EQ(x11.discard(), x21.discard()); - // CHECK_EQ(x11.getResultNumber(), x21.getResultNumber()); - // for (size_t i = x11.discard(); i < x21.size(); ++i) { - // if (std::isnan(x11[i])) { - // CHECK_UNARY(std::isnan(x21[i])); - // } else { - // CHECK_EQ(x11[i], doctest::Approx(x21[i])); - // } - // } + Indicator x11 = x1.getResult(1); + Indicator x21 = x2.getResult(1); + CHECK_EQ(x11.size(), x21.size()); + CHECK_EQ(x11.discard(), x21.discard()); + CHECK_EQ(x11.getResultNumber(), x21.getResultNumber()); + for (size_t i = x11.discard(); i < x21.size(); ++i) { + if (std::isnan(x11[i])) { + CHECK_UNARY(std::isnan(x21[i])); + } else { + CHECK_EQ(x11[i], doctest::Approx(x21[i])); + } + } } #endif /* #if HKU_SUPPORT_SERIALIZATION */ diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 3be04865..2aa66225 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -466,6 +466,9 @@ Indicator (*ZHBOND10_2)(const DatetimeList&, double) = ZHBOND10; Indicator (*ZHBOND10_3)(const KData& k, double) = ZHBOND10; Indicator (*ZHBOND10_4)(const Indicator&, double) = ZHBOND10; +Indicator (*SPEARMAN_1)(int) = SPEARMAN; +Indicator (*SPEARMAN_2)(const Indicator&, const Indicator&, int) = SPEARMAN; + void export_Indicator_build_in(py::module& m) { m.def("KDATA", KDATA1); m.def("KDATA", KDATA3, R"(KDATA([data]) @@ -1645,12 +1648,13 @@ void export_Indicator_build_in(py::module& m) { :param DatetimeList|KDate|Indicator data: 输入的日期参考,优先使用上下文中的日期 :param float default_val: 如果输入的日期早于已有国债数据的最早记录,则使用此默认值)"); - m.def("SPEARMAN", SPEARMAN, py::arg("ind1"), py::arg("ind2"), py::arg("n"), - R"(SPEARMAN(ind1, ind2, n) + m.def("SPEARMAN", SPEARMAN_1, py::arg("n") = 0); + m.def("SPEARMAN", SPEARMAN_2, py::arg("ind1"), py::arg("ind2"), py::arg("n") = 0, + R"(SPEARMAN(ind1, ind2[, n]) Spearman 相关系数 :param Indicator ind1: 输入参数1 :param Indicator ind2: 输入参数2 - :param int n: 指定窗口)"); + :param int n: 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度)"); } From a9b64b973de9255a874df7ac054f96941ee10d28 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 03:19:37 +0800 Subject: [PATCH 007/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20CORR=20=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 2 +- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 10 -- hikyuu_cpp/hikyuu/indicator/Indicator.h | 8 -- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 94 ------------- hikyuu_cpp/hikyuu/indicator/IndicatorImp.h | 3 - hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/CORR.h | 23 ++++ hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp | 123 ++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/ICorr.h | 42 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 1 - .../unit_test/hikyuu/indicator/test_CORR.cpp | 5 +- .../hikyuu/indicator/test_SPEARMAN.cpp | 21 +-- hikyuu_pywrap/indicator/_build_in.cpp | 9 +- 14 files changed, 208 insertions(+), 135 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/CORR.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/ICorr.h diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index af68a302..5aad9e1e 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -207,7 +207,7 @@ :param Indicator ind1: 指标1 :param Indicator ind2: 指标2 - :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数 + :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数。如果为0,使用输入的ind长度。 :rtype: Indicator diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index fe0093b3..323c5e84 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -305,14 +305,4 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_ return IF(x, CVAL(x, a), CVAL(x, b)); } -Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { - HKU_ERROR_IF_RETURN(!ind1.getImp() || !ind2.getImp(), Indicator(), - "ind1 or ind2 is Null Indicator!"); - HKU_ERROR_IF_RETURN(n < 2, Indicator(), "Invalid param n: {} (need >= 2)", n); - IndicatorImpPtr p = make_shared("CORR"); - p->setParam("n", n); - p->add(IndicatorImp::CORR, ind1.getImp(), ind2.getImp()); - return p->calculate(); -} - } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index 984ea1f7..677474e8 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -379,14 +379,6 @@ Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, const Indicator& Indicator HKU_API IF(const Indicator& x, const Indicator& a, Indicator::value_t b); Indicator HKU_API IF(const Indicator& x, Indicator::value_t a, Indicator::value_t b); -/** - * 计算样本相关系数与协方差。返回的结果集中,第一个为相关系数,第二个为协方差 - * @param ind1 指标1 - * @param ind2 指标2 - * @ingroup Indicator - */ -Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n); - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index 51d3c032..cf1b37d2 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -93,10 +93,6 @@ string HKU_API getOPTypeName(IndicatorImp::OPType op) { name = "IF"; break; - case IndicatorImp::CORR: - name = "CORR"; - break; - default: name = "UNKNOWN"; break; @@ -534,10 +530,6 @@ string IndicatorImp::formula() const { << m_right->formula() << ")"; break; - case CORR: - buf << m_name << "(" << m_left->formula() << ", " << m_right->formula() << ")"; - break; - default: HKU_ERROR("Wrong optype! {}", int(m_optype)); break; @@ -797,10 +789,6 @@ Indicator IndicatorImp::calculate() { execute_if(); break; - case CORR: - execute_corr(); - break; - default: HKU_ERROR("Unkown Indicator::OPType! {}", int(m_optype)); break; @@ -1463,88 +1451,6 @@ void IndicatorImp::execute_if() { } } -void IndicatorImp::execute_corr() { - m_right->calculate(); - m_left->calculate(); - - const IndicatorImp *maxp, *minp; - if (m_right->size() > m_left->size()) { - maxp = m_right.get(); - minp = m_left.get(); - } else { - maxp = m_left.get(); - minp = m_right.get(); - } - - size_t total = maxp->size(); - size_t discard = maxp->size() - minp->size() + minp->discard(); - if (discard < maxp->discard()) { - discard = maxp->discard(); - } - - // 结果 0 存放相关系数结果 - // 结果 1 存放协方差(COV)结果 - _readyBuffer(total, 2); - - int n = getParam("n"); - if (n < 2 || discard + 2 > total) { - setDiscard(total); - return; - } - - size_t startPos = discard; - size_t first_end = startPos + n >= total ? total : startPos + n; - - value_t kx = maxp->get(discard); - value_t ky = minp->get(discard); - value_t ex = 0.0, ey = 0.0, exy = 0.0, varx = 0.0, vary = 0.0, cov = 0.0; - value_t ex2 = 0.0, ey2 = 0.0; - value_t ix, iy; - - auto *dst0 = this->data(0); - auto *dst1 = this->data(1); - auto const *maxdata = maxp->data(0); - auto const *mindata = minp->data(0); - for (size_t i = startPos + 1; i < first_end; i++) { - ix = maxdata[i] - kx; - iy = mindata[i] - ky; - ex += ix; - ey += iy; - value_t powx2 = ix * ix; - value_t powy2 = iy * iy; - value_t powxy = ix * iy; - exy += powxy; - ex2 += powx2; - ey2 += powy2; - size_t nobs = i - startPos; - varx = ex2 - powx2 / nobs; - vary = ey2 - powy2 / nobs; - cov = exy - powxy / nobs; - dst0[i] = cov / std::sqrt(varx * vary); - dst1[i] = cov / (nobs - 1); - } - - for (size_t i = first_end; i < total; i++) { - ix = maxdata[i] - kx; - iy = mindata[i] - ky; - ex += maxdata[i] - maxdata[i - n]; - ey += mindata[i] - mindata[i - n]; - value_t preix = maxdata[i - n] - kx; - value_t preiy = mindata[i - n] - ky; - ex2 += ix * ix - preix * preix; - ey2 += iy * iy - preiy * preiy; - exy += ix * iy - preix * preiy; - varx = (ex2 - ex * ex / n); - vary = (ey2 - ey * ey / n); - cov = (exy - ex * ey / n); - dst0[i] = cov / std::sqrt(varx * vary); - dst1[i] = cov / (n - 1); - } - - // 修正 discard - setDiscard(discard + 2); -} - void IndicatorImp::_dyn_calculate(const Indicator &ind) { // SPEND_TIME(IndicatorImp__dyn_calculate); const auto &ind_param = getIndParamImp("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h index 91e93952..ea43e041 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -48,7 +48,6 @@ public: OR, ///< 或 WEAVE, ///< 特殊的,需要两个指标作为参数的指标 OP_IF, /// if操作 - CORR, ///< 相关系数,需要两个指标作为参数 INVALID }; @@ -195,8 +194,6 @@ private: void execute_or(); void execute_weave(); void execute_if(); - void execute_corr(); - void execute_spearman(); std::vector getAllSubNodes(); void repeatALikeNodes(); diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 6105286c..f116d14d 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -27,6 +27,7 @@ #include "crt/BARSSINCE.h" #include "crt/BETWEEN.h" #include "crt/CEILING.h" +#include "crt/CORR.h" #include "crt/COS.h" #include "crt/COST.h" #include "crt/COUNT.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/CORR.h b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h new file mode 100644 index 00000000..ebafe4de --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +/** + * 计算样本相关系数与协方差。返回的结果集中,第一个为相关系数,第二个为协方差 + * @param ind1 指标1 + * @param ind2 指标2 + * @ingroup Indicator + */ +Indicator HKU_API CORR(int n = 10); +Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n = 10); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h index e559918a..b12f6c15 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h @@ -5,6 +5,7 @@ * Author: fasiondog */ +#pragma once #include "../Indicator.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp new file mode 100644 index 00000000..65c9a03a --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "ICorr.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::ICorr) +#endif + +namespace hku { + +ICorr::ICorr() : IndicatorImp("CORR") { + setParam("n", 10); +} + +ICorr::ICorr(int n) : IndicatorImp("CORR") { + setParam("n", n); +} + +ICorr::ICorr(const Indicator& ref_ind, int n) : IndicatorImp("CORR"), m_ref_ind(ref_ind) { + setParam("n", n); +} + +ICorr::~ICorr() {} + +bool ICorr::check() { + int n = getParam("n"); + return n == 0 || n >= 2; +} + +IndicatorImpPtr ICorr::_clone() { + ICorr* p = new ICorr(); + p->m_ref_ind = m_ref_ind.clone(); + return IndicatorImpPtr(p); +} + +void ICorr::_calculate(const Indicator& ind) { + auto k = getContext(); + m_ref_ind.setContext(k); + Indicator ref = m_ref_ind; + if (m_ref_ind.size() != ind.size()) { + ref = ALIGN(m_ref_ind, ind); + } + + size_t total = ind.size(); + _readyBuffer(total, 2); + HKU_IF_RETURN(total == 0, void()); + + int n = getParam("n"); + if (n == 0) { + n = total; + } + + m_discard = std::max(ind.discard(), ref.discard()); + size_t startPos = m_discard; + size_t first_end = startPos + n >= total ? total : startPos + n; + + auto const* datax = ind.data(); + auto const* datay = ref.data(); + value_t kx = datax[m_discard]; + value_t ky = datay[m_discard]; + value_t ex = 0.0, ey = 0.0, exy = 0.0, varx = 0.0, vary = 0.0, cov = 0.0; + value_t ex2 = 0.0, ey2 = 0.0; + value_t ix, iy; + + auto* dst0 = this->data(0); + auto* dst1 = this->data(1); + for (size_t i = startPos + 1; i < first_end; i++) { + ix = datax[i] - kx; + iy = datay[i] - ky; + ex += ix; + ey += iy; + value_t powx2 = ix * ix; + value_t powy2 = iy * iy; + value_t powxy = ix * iy; + exy += powxy; + ex2 += powx2; + ey2 += powy2; + size_t nobs = i - startPos; + varx = ex2 - powx2 / nobs; + vary = ey2 - powy2 / nobs; + cov = exy - powxy / nobs; + dst0[i] = cov / std::sqrt(varx * vary); + dst1[i] = cov / (nobs - 1); + } + + for (size_t i = first_end; i < total; i++) { + ix = datax[i] - kx; + iy = datay[i] - ky; + ex += datax[i] - datax[i - n]; + ey += datay[i] - datay[i - n]; + value_t preix = datax[i - n] - kx; + value_t preiy = datay[i - n] - ky; + ex2 += ix * ix - preix * preix; + ey2 += iy * iy - preiy * preiy; + exy += ix * iy - preix * preiy; + varx = (ex2 - ex * ex / n); + vary = (ey2 - ey * ey / n); + cov = (exy - ex * ey / n); + dst0[i] = cov / std::sqrt(varx * vary); + dst1[i] = cov / (n - 1); + } + + // 修正 discard + m_discard = (m_discard + 2 < total) ? m_discard + 2 : total; +} + +Indicator HKU_API CORR(int n) { + return make_shared(n); +} + +Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { + auto p = make_shared(ind2, n); + Indicator result(p); + return result(ind1); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h new file mode 100644 index 00000000..796f8b40 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class ICorr : public IndicatorImp { +public: + ICorr(); + ICorr(int n); + ICorr(const Indicator& ref_ind, int n); + virtual ~ICorr(); + + virtual bool check() override; + virtual void _calculate(const Indicator& data) override; + virtual IndicatorImpPtr _clone() override; + +private: + Indicator m_ref_ind; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(IndicatorImp); + ar& BOOST_SERIALIZATION_NVP(m_ref_ind); + } +#endif +}; + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index 7de300ec..d81dcc50 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -76,7 +76,6 @@ static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value void ISpearman::_calculate(const Indicator &ind) { auto k = getContext(); - m_ref_ind.setContext(k); Indicator ref = m_ref_ind; if (m_ref_ind.size() != ind.size()) { diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp index 312bd73a..26a7b592 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp @@ -10,6 +10,7 @@ #include "../test_config.h" #include #include +#include #include #include @@ -41,13 +42,13 @@ TEST_CASE("test_CORR") { Indicator y = PRICELIST(b); // 非法参数 n - result = CORR(x, y, 0); + result = CORR(x, y, -1); CHECK_UNARY(result.empty()); result = CORR(x, y, 1); CHECK_UNARY(result.empty()); // 正常情况 - result = CORR(x, y, a.size()); + result = CORR(x, y, 0); CHECK_EQ(result.name(), "CORR"); CHECK_EQ(result.discard(), 2); CHECK_EQ(result.size(), a.size()); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index 03d899a0..f2a26fb4 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -165,21 +165,14 @@ TEST_CASE("test_SPEARMAN") { price_t null_value = Null(); x = PRICELIST({3., 8., null_value, 4., 7., 2., null_value, null_value}); y = PRICELIST({null_value, 5., 10., 8., null_value, 10., 6., null_value}); - // expect = {null_value, , 1., 0.875, 1.}; - // nan, 8, nan, 4, nan, 2, nan - // nan, 5, nan, 8, nan, 10, nan, result = SPEARMAN(x, y, 4); - HKU_INFO("{}", result); - for (size_t i = result.discard(); i < result.size(); i++) { - HKU_INFO("{}: {}", i, result[i]); - } - - x = PRICELIST({8., 4., 2.}); - y = PRICELIST({5., 8., 10.}); - result = SPEARMAN(x, y, x.size()); - HKU_INFO("{}", result); - HKU_INFO("{}", std::pow(null_value, 2)); - HKU_INFO("{}", 1.0 * 6.0 * null_value / (std::pow(x.size(), 3) - x.size())); + CHECK_EQ(result.name(), "SPEARMAN"); + CHECK_EQ(result.discard(), 3); + CHECK_EQ(result.size(), x.size()); + CHECK_UNARY(std::isnan(result[0])); + CHECK_EQ(result[5], doctest::Approx(-1.)); + CHECK_EQ(result[6], doctest::Approx(-1.)); + CHECK_UNARY(std::isnan(result[7])); } //----------------------------------------------------------------------------- diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 2aa66225..395bc815 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -466,6 +466,9 @@ Indicator (*ZHBOND10_2)(const DatetimeList&, double) = ZHBOND10; Indicator (*ZHBOND10_3)(const KData& k, double) = ZHBOND10; Indicator (*ZHBOND10_4)(const Indicator&, double) = ZHBOND10; +Indicator (*CORR_1)(int) = CORR; +Indicator (*CORR_2)(const Indicator&, const Indicator&, int) = CORR; + Indicator (*SPEARMAN_1)(int) = SPEARMAN; Indicator (*SPEARMAN_2)(const Indicator&, const Indicator&, int) = SPEARMAN; @@ -850,13 +853,15 @@ void export_Indicator_build_in(py::module& m) { :param Indicator ind2: 指标2 :rtype: Indicator)"); - m.def("CORR", CORR, R"(CORR(ind1, ind2, n) + m.def("CORR", CORR_1, py::arg("n") = 10); + m.def("CORR", CORR_2, py::arg("ind1"), py::arg("ind2"), py::arg("n") = 10, + R"(CORR(ind1, ind2, n) 计算 ind1 和 ind2 的相关系数。返回中存在两个结果,第一个为相关系数,第二个为协方差。 :param Indicator ind1: 指标1 :param Indicator ind2: 指标2 - :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数 + :param int n: 按指定 n 的长度计算两个 ind 直接数据相关系数。如果为0,使用输入的ind长度。 :rtype: Indicator)"); m.def("IF", IF_1); From 5c0f2e283a4b22e7044ca30416d563c9cdf2d14a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 03:50:13 +0800 Subject: [PATCH 008/601] add IC --- hikyuu_cpp/hikyuu/indicator/crt/CORR.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 9 ++++----- hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp | 5 ++++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/CORR.h b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h index ebafe4de..bfe7ec51 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/CORR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h @@ -15,6 +15,7 @@ namespace hku { * 计算样本相关系数与协方差。返回的结果集中,第一个为相关系数,第二个为协方差 * @param ind1 指标1 * @param ind2 指标2 + * @param n 滚动窗口 (大于2或等于0),等于0时使用输入的ind实际长度。 * @ingroup Indicator */ Indicator HKU_API CORR(int n = 10); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index d2264355..4b2ee2b0 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -65,21 +65,20 @@ void IIc::_calculate(const Indicator& inputInd) { for (const auto& stk : m_stks) { auto k = stk.getKData(m_query); all_inds.push_back(ALIGN(ind(k), ref_dates)); - HKU_INFO("{} {}", stk.market_code(), ALIGN(ind(k), ref_dates)); all_returns.push_back(ROCP(k.close(), n)); } - PriceList tmp(total, Null()); - PriceList tmp_return(total, Null()); size_t ind_count = all_inds.size(); + PriceList tmp(ind_count, Null()); + PriceList tmp_return(ind_count, Null()); auto* dst = this->data(); for (size_t i = 0; i < total; i++) { for (size_t j = 0; j < ind_count; j++) { if (i >= all_inds[j].size() || i >= all_returns[j].size()) { continue; } - tmp[i] = all_inds[j][i]; - tmp_return[i] = all_returns[j][i]; + tmp[j] = all_inds[j][i]; + tmp_return[j] = all_returns[j][i]; } auto a = PRICELIST(tmp); auto b = PRICELIST(tmp_return); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index 13c46529..b9ae48a5 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -29,7 +29,10 @@ TEST_CASE("test_IC") { KQuery query = KQuery(-200); Indicator result = IC(stks, query, 1, ref_stk)(MA(CLOSE())); - HKU_INFO("{}", result); + // HKU_INFO("{}", result); + // for (size_t i = 0, total = result.size(); i < total; i++) { + // HKU_INFO("{}: {}", i, result[i]); + // } // PriceList a; // for (int i = 0; i < 10; ++i) { From df028e212b044867e0dfef5611c3449c02d78171 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 17:04:39 +0800 Subject: [PATCH 009/601] =?UTF-8?q?ALIGN=E5=8F=82=E6=95=B0=20use=5Fnull=20?= =?UTF-8?q?=E6=94=B9=E5=90=8D=E4=B8=BA=20fill=5Fnull?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 4 +- hikyuu_cpp/hikyuu/indicator/crt/ALIGN.h | 22 +++++----- hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp | 24 +++++------ .../unit_test/hikyuu/indicator/test_ALIGN.cpp | 40 +++++++++---------- hikyuu_pywrap/indicator/_build_in.cpp | 36 ++++++++++++----- 5 files changed, 72 insertions(+), 54 deletions(-) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 5aad9e1e..45b64337 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -40,13 +40,13 @@ :rtype: Indicator -.. py:function:: ALIGN(data, ref[, use_null=True]) +.. py:function:: ALIGN(data, ref[, fill_null=True]) 按指定的参考日期对齐 :param Indicator data: 输入数据 :param DatetimeList|Indicator|KData ref: 指定做为日期参考的 DatetimeList、Indicator 或 KData - :param bool use_null: 缺失数据使用 nan 填充; 否则使用小于对应日期且最接近对应日期的数据 + :param bool fill_null: 缺失数据使用 nan 填充; 否则使用小于对应日期且最接近对应日期的数据 :retype: Indicator .. py:function:: AMA([data, n=10, fast_n=2, slow_n=30]) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ALIGN.h b/hikyuu_cpp/hikyuu/indicator/crt/ALIGN.h index c60d961d..c4f3b4ed 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ALIGN.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ALIGN.h @@ -19,22 +19,22 @@ namespace hku { * 按指定日期对齐 * @ingroup Indicator */ -Indicator HKU_API ALIGN(bool use_null = true); -Indicator HKU_API ALIGN(const DatetimeList&, bool use_null = true); -Indicator ALIGN(const Indicator& ind, const DatetimeList& ref, bool use_null = true); -Indicator ALIGN(const Indicator& ind, const Indicator& ref, bool use_null = true); -Indicator ALIGN(const Indicator& ind, const KData& ref, bool use_null = true); +Indicator HKU_API ALIGN(bool fill_null = true); +Indicator HKU_API ALIGN(const DatetimeList&, bool fill_null = true); +Indicator ALIGN(const Indicator& ind, const DatetimeList& ref, bool fill_null = true); +Indicator ALIGN(const Indicator& ind, const Indicator& ref, bool fill_null = true); +Indicator ALIGN(const Indicator& ind, const KData& ref, bool fill_null = true); -inline Indicator ALIGN(const Indicator& ind, const DatetimeList& ref, bool use_null) { - return ALIGN(ref, use_null)(ind); +inline Indicator ALIGN(const Indicator& ind, const DatetimeList& ref, bool fill_null) { + return ALIGN(ref, fill_null)(ind); } -inline Indicator ALIGN(const Indicator& ind, const Indicator& ref, bool use_null) { - return ALIGN(ref.getDatetimeList(), use_null)(ind); +inline Indicator ALIGN(const Indicator& ind, const Indicator& ref, bool fill_null) { + return ALIGN(ref.getDatetimeList(), fill_null)(ind); } -inline Indicator ALIGN(const Indicator& ind, const KData& ref, bool use_null) { - return ALIGN(ref.getDatetimeList(), use_null)(ind); +inline Indicator ALIGN(const Indicator& ind, const KData& ref, bool fill_null) { + return ALIGN(ref.getDatetimeList(), fill_null)(ind); } } // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp index e86d2caa..ac0f6983 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp @@ -17,7 +17,7 @@ namespace hku { IAlign::IAlign() : IndicatorImp("ALIGN") { setParam("align_date_list", DatetimeList()); // 要对齐的日期序列(须已升序排列) - setParam("use_null", true); // 缺失的数据是否使用 nan 填充 + setParam("fill_null", true); // 缺失的数据是否使用 nan 填充 } IAlign::~IAlign() {} @@ -46,15 +46,15 @@ void IAlign::_calculate(const Indicator& ind) { return; } - bool use_null = getParam("use_null"); + bool fill_null = getParam("fill_null"); // 处理传入的数据本身没有上下文日期的指标,无法对标的情况: - // 1.如果 use_null, 则直接返回,m_discard 标记全部 + // 1.如果 fill_null, 则直接返回,m_discard 标记全部 // 2.数据长度小于等于日期序列长度,则按右对齐,即最后的数据对应最后的日期,前面缺失的数据做抛弃处理 // 3.数据长度大于日期序列长度,按右对其,前面超出日期序列的数据丢弃 DatetimeList ind_dates = ind.getDatetimeList(); if (ind_dates.size() == 0) { - if (use_null) { + if (fill_null) { m_discard = total; return; } @@ -95,7 +95,7 @@ void IAlign::_calculate(const Indicator& ind) { size_t ind_idx = ind.discard(); for (size_t i = 0; i < total; i++) { if (ind_idx >= ind_total) { - if (!use_null) { + if (!fill_null) { size_t pos = ind_total - 1; for (size_t r = 0; r < m_result_num; r++) { for (size_t j = i; j < total; j++) { @@ -120,7 +120,7 @@ void IAlign::_calculate(const Indicator& ind) { } if (j >= ind_total) { - if (!use_null) { + if (!fill_null) { for (size_t r = 0; r < m_result_num; r++) { price_t val = ind.get(j - 1, r); for (; i < total; i++) { @@ -135,7 +135,7 @@ void IAlign::_calculate(const Indicator& ind) { for (size_t r = 0; r < m_result_num; r++) { _set(ind.get(j, r), i, r); } - } else if (!use_null && j < ind_total) { + } else if (!fill_null && j < ind_total) { for (size_t r = 0; r < m_result_num; r++) { _set(ind.get(j - 1, r), i, r); } @@ -145,7 +145,7 @@ void IAlign::_calculate(const Indicator& ind) { } } - if (use_null) { + if (fill_null) { m_discard = total; } size_t i = 0; @@ -167,16 +167,16 @@ void IAlign::_calculate(const Indicator& ind) { } } -Indicator HKU_API ALIGN(bool use_null) { +Indicator HKU_API ALIGN(bool fill_null) { IndicatorImpPtr p = make_shared(); - p->setParam("use_null", use_null); + p->setParam("fill_null", fill_null); return Indicator(p); } -Indicator HKU_API ALIGN(const DatetimeList& ref, bool use_null) { +Indicator HKU_API ALIGN(const DatetimeList& ref, bool fill_null) { IndicatorImpPtr p = make_shared(); p->setParam("align_date_list", ref); - p->setParam("use_null", use_null); + p->setParam("fill_null", fill_null); return Indicator(p); } diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ALIGN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ALIGN.cpp index 3ed708b8..16d1d93c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ALIGN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ALIGN.cpp @@ -23,7 +23,7 @@ using namespace hku; */ /** @par 检测点 */ -TEST_CASE("test_ALIGN_use_null") { +TEST_CASE("test_ALIGN_fill_null") { Indicator result; Stock stk = getStock("sh000001"); @@ -36,7 +36,7 @@ TEST_CASE("test_ALIGN_use_null") { DatetimeList ref; Indicator data = PRICELIST(a); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(ref.size(), 0); CHECK_EQ(result.size(), 0); @@ -46,7 +46,7 @@ TEST_CASE("test_ALIGN_use_null") { ref = stk.getDatetimeList(KQuery(-10)); data = PRICELIST(a); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -60,7 +60,7 @@ TEST_CASE("test_ALIGN_use_null") { a.push_back(11); data = PRICELIST(a); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -70,7 +70,7 @@ TEST_CASE("test_ALIGN_use_null") { a.push_back(1); data = PRICELIST(a); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -79,7 +79,7 @@ TEST_CASE("test_ALIGN_use_null") { a.clear(); data = PRICELIST(a); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -97,7 +97,7 @@ TEST_CASE("test_ALIGN_use_null") { ref.push_back(Datetime(201901030000)); data = CLOSE(k); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -114,7 +114,7 @@ TEST_CASE("test_ALIGN_use_null") { ref.push_back(Datetime(191901030000)); data = CLOSE(k); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -128,7 +128,7 @@ TEST_CASE("test_ALIGN_use_null") { ref = k.getDatetimeList(); data = CLOSE(k); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 0); @@ -149,7 +149,7 @@ TEST_CASE("test_ALIGN_use_null") { ref.push_back(Datetime(201112100000)); data = CLOSE(k); result = ALIGN(data, ref); - REQUIRE(result.getParam("use_null")); + REQUIRE(result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 1); @@ -166,7 +166,7 @@ TEST_CASE("test_ALIGN_use_null") { } /** @par 检测点 */ -TEST_CASE("test_ALIGN_not_use_null") { +TEST_CASE("test_ALIGN_not_fill_null") { Indicator result; Stock stk = getStock("sh000001"); @@ -179,7 +179,7 @@ TEST_CASE("test_ALIGN_not_use_null") { DatetimeList ref; Indicator data = PRICELIST(a); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(ref.size(), 0); CHECK_EQ(result.size(), 0); @@ -189,7 +189,7 @@ TEST_CASE("test_ALIGN_not_use_null") { ref = stk.getDatetimeList(KQuery(-10)); data = PRICELIST(a); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 0); @@ -203,7 +203,7 @@ TEST_CASE("test_ALIGN_not_use_null") { a.push_back(11); data = PRICELIST(a); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 0); @@ -218,7 +218,7 @@ TEST_CASE("test_ALIGN_not_use_null") { a.push_back(1); data = PRICELIST(a); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 9); @@ -234,7 +234,7 @@ TEST_CASE("test_ALIGN_not_use_null") { a.clear(); data = PRICELIST(a); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -252,7 +252,7 @@ TEST_CASE("test_ALIGN_not_use_null") { ref.push_back(Datetime(201901030000)); data = CLOSE(k); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 0); @@ -270,7 +270,7 @@ TEST_CASE("test_ALIGN_not_use_null") { ref.push_back(Datetime(191901030000)); data = CLOSE(k); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), ref.size()); @@ -284,7 +284,7 @@ TEST_CASE("test_ALIGN_not_use_null") { ref = k.getDatetimeList(); data = CLOSE(k); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 0); @@ -305,7 +305,7 @@ TEST_CASE("test_ALIGN_not_use_null") { ref.push_back(Datetime(201112100000)); data = CLOSE(k); result = ALIGN(data, ref, false); - REQUIRE(!result.getParam("use_null")); + REQUIRE(!result.getParam("fill_null")); CHECK_EQ(result.name(), "ALIGN"); CHECK_EQ(result.size(), ref.size()); CHECK_EQ(result.discard(), 1); diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 395bc815..69eb7949 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -418,10 +418,10 @@ Indicator (*AD_2)(const KData&) = AD; Indicator (*COST_1)(double x) = COST; Indicator (*COST_2)(const KData&, double x) = COST; -Indicator (*ALIGN_1)(const DatetimeList&, bool use_null) = ALIGN; -Indicator (*ALIGN_2)(const Indicator&, const DatetimeList&, bool use_null) = ALIGN; -Indicator (*ALIGN_3)(const Indicator&, const Indicator&, bool use_null) = ALIGN; -Indicator (*ALIGN_4)(const Indicator&, const KData&, bool use_null) = ALIGN; +Indicator (*ALIGN_1)(const DatetimeList&, bool fill_null) = ALIGN; +Indicator (*ALIGN_2)(const Indicator&, const DatetimeList&, bool fill_null) = ALIGN; +Indicator (*ALIGN_3)(const Indicator&, const Indicator&, bool fill_null) = ALIGN; +Indicator (*ALIGN_4)(const Indicator&, const KData&, bool fill_null) = ALIGN; Indicator (*DROPNA_1)() = DROPNA; Indicator (*DROPNA_2)(const Indicator&) = DROPNA; @@ -1553,17 +1553,17 @@ void export_Indicator_build_in(py::module& m) { :param float x: x%获利价格, 0~100 :rtype: Indicator)"); - m.def("ALIGN", ALIGN_1, py::arg("ref"), py::arg("use_null") = true); - m.def("ALIGN", ALIGN_2, py::arg("data"), py::arg("ref"), py::arg("use_null") = true); - m.def("ALIGN", ALIGN_3, py::arg("data"), py::arg("ref"), py::arg("use_null") = true); - m.def("ALIGN", ALIGN_4, py::arg("data"), py::arg("ref"), py::arg("use_null") = true, + m.def("ALIGN", ALIGN_1, py::arg("ref"), py::arg("fill_null") = true); + m.def("ALIGN", ALIGN_2, py::arg("data"), py::arg("ref"), py::arg("fill_null") = true); + m.def("ALIGN", ALIGN_3, py::arg("data"), py::arg("ref"), py::arg("fill_null") = true); + m.def("ALIGN", ALIGN_4, py::arg("data"), py::arg("ref"), py::arg("fill_null") = true, R"(ALIGN(data, ref): 按指定的参考日期对齐 :param Indicator data: 输入数据 :param DatetimeList|Indicator|KData ref: 指定做为日期参考的 DatetimeList、Indicator 或 KData - :param bool use_null: 缺失数据使用 nan 填充; 否则使用小于对应日期且最接近对应日期的数据 + :param bool fill_null: 缺失数据使用 nan 填充; 否则使用小于对应日期且最接近对应日期的数据 :retype: Indicator)"); m.def("DROPNA", DROPNA_1); @@ -1662,4 +1662,22 @@ void export_Indicator_build_in(py::module& m) { :param Indicator ind1: 输入参数1 :param Indicator ind2: 输入参数2 :param int n: 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度)"); + + m.def( + "IC", + [](const Indicator& ind, const py::object& stks, const KQuery& query, int n, + const Stock& ref_stk) { + if (py::isinstance(stks)) { + const auto& blk = stks.cast(); + return IC(ind, blk, query, n, ref_stk); + } + + if (py::isinstance(stks)) { + StockList c_stks = python_list_to_vector(stks); + return IC(ind, c_stks, query, n, ref_stk); + } + + 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")); } From 3ecb3fd644aa0620f2b9e8ee68c34cc7041b6d4a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 17:11:09 +0800 Subject: [PATCH 010/601] =?UTF-8?q?IC=20=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 4b2ee2b0..9938ed15 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -20,11 +20,13 @@ namespace hku { IIc::IIc() : IndicatorImp("IC") { setParam("n", 1); + setParam("fill_null", true); } IIc::IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) : IndicatorImp("IC"), m_query(query), m_stks(stks), m_ref_stk(ref_stk) { setParam("n", n); + setParam("fill_null", true); } IIc::~IIc() {} @@ -56,6 +58,7 @@ void IIc::_calculate(const Indicator& inputInd) { HKU_WARN_IF_RETURN(total < 2, void(), "Insufficient data length! current lenght: {}", total); int n = getParam("n"); + bool fill_null = getParam("fill_null"); vector all_inds; all_inds.reserve(m_stks.size()); @@ -64,8 +67,8 @@ void IIc::_calculate(const Indicator& inputInd) { Indicator ind = inputInd; for (const auto& stk : m_stks) { auto k = stk.getKData(m_query); - all_inds.push_back(ALIGN(ind(k), ref_dates)); - all_returns.push_back(ROCP(k.close(), n)); + all_inds.push_back(ALIGN(ind(k), ref_dates, fill_null)); + all_returns.push_back(ALIGN(ROCP(k.close(), n), ref_dates, fill_null)); } size_t ind_count = all_inds.size(); From 6c6085a8a58c37e9451cfb35d0a4a93f29161777 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 20:34:51 +0800 Subject: [PATCH 011/601] add IC --- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 3 +- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 34 ++-- .../unit_test/hikyuu/indicator/test_IC.cpp | 153 +++++++++++------- 3 files changed, 121 insertions(+), 69 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index cf1b37d2..c159ea88 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -276,7 +276,8 @@ void IndicatorImp::_readyBuffer(size_t len, size_t result_num) { } for (size_t i = result_num; i < m_result_num; ++i) { - delete m_pBuffer[i]; + if (m_pBuffer[i]) + delete m_pBuffer[i]; m_pBuffer[i] = NULL; } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 9938ed15..b1dceb6a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -44,18 +44,18 @@ IndicatorImpPtr IIc::_clone() { } void IIc::_calculate(const Indicator& inputInd) { - HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "No ref_stk was specified!"); - HKU_ERROR_IF_RETURN(m_stks.size() < 2, void(), - "The number of stock is insufficient, need > 2! current: {}", - m_stks.size()); - for (const auto& stk : m_stks) { - HKU_ERROR_IF_RETURN(stk.isNull(), void(), "exist null stock!"); - } - + HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); auto ref_dates = m_ref_stk.getDatetimeList(m_query); size_t total = ref_dates.size(); _readyBuffer(total, 1); - HKU_WARN_IF_RETURN(total < 2, void(), "Insufficient data length! current lenght: {}", total); + + if (total < 2 || m_stks.size() < 2) { + HKU_WARN( + "The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, " + "current stock number: {}", + total, m_stks.size()); + m_discard = total; + } int n = getParam("n"); bool fill_null = getParam("fill_null"); @@ -66,12 +66,22 @@ void IIc::_calculate(const Indicator& inputInd) { all_returns.reserve(m_stks.size()); Indicator ind = inputInd; for (const auto& stk : m_stks) { - auto k = stk.getKData(m_query); - all_inds.push_back(ALIGN(ind(k), ref_dates, fill_null)); - all_returns.push_back(ALIGN(ROCP(k.close(), n), ref_dates, fill_null)); + if (stk.isNull()) { + HKU_WARN("Exist null stock, it was ignored!"); + } else { + auto k = stk.getKData(m_query); + all_inds.push_back(ALIGN(ind(k), ref_dates, fill_null)); + all_returns.push_back(ALIGN(ROCP(k.close(), n), ref_dates, fill_null)); + } } size_t ind_count = all_inds.size(); + if (all_inds.size() < 2) { + HKU_WARN("The number of exist stock is insufficient!"); + m_discard = total; + return; + } + PriceList tmp(ind_count, Null()); PriceList tmp_return(ind_count, Null()); auto* dst = this->data(); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index b9ae48a5..ac46ad1a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -26,32 +26,68 @@ TEST_CASE("test_IC") { StockManager& sm = StockManager::instance(); StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; Stock ref_stk = sm["sh000001"]; + KQuery query = KQuery(-100); + KData ref_k = ref_stk.getKData(query); + Indicator result; - KQuery query = KQuery(-200); - Indicator result = IC(stks, query, 1, ref_stk)(MA(CLOSE())); - // HKU_INFO("{}", result); - // for (size_t i = 0, total = result.size(); i < total; i++) { - // HKU_INFO("{}: {}", i, result[i]); - // } + /** @arg 传入非法 n */ + result = IC(MA(CLOSE()), stks, query, -1, ref_stk); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(result.empty()); - // PriceList a; - // for (int i = 0; i < 10; ++i) { - // a.push_back(-i); - // } + /** @arg 传入的 ref_stk 为 null */ + result = IC(stks, query, 1, Stock())(MA(CLOSE())); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(result.empty()); - // Indicator data = PRICELIST(a); + /** @arg 传入空的 stks */ + result = IC(MA(CLOSE()), StockList(), query, 1, ref_stk); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), ref_k.size()); + CHECK_EQ(result.discard(), result.size()); - // result = ABS(data); - // CHECK_UNARY(result.name() == "ABS"); - // CHECK_UNARY(result.discard() == 0); - // for (int i = 0; i < 10; ++i) { - // CHECK_UNARY(result[i] == -data[i]); - // } + /** @arg 传入的 stks 数量不足,需要大于等于2 */ + result = IC(MA(CLOSE()), {sm["sh600004"]}, query, 1, ref_stk); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), ref_k.size()); + CHECK_EQ(result.discard(), result.size()); - // result = ABS(-11); - // CHECK_UNARY(result.size() == 1); - // CHECK_UNARY(result.discard() == 0); - // CHECK_UNARY(result[0] == 11); + /** @arg ref_stk 数据长度不足 */ + result = IC(MA(CLOSE()), stks, KQuery(-1), 1, ref_stk); + 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); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), 2); + CHECK_EQ(result.discard(), 2); + CHECK_UNARY(std::isnan(result[0])); + CHECK_UNARY(std::isnan(result[1])); + + /** @arg 传入的 stks 长度为2,query 的长度为2*/ + result = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-2), 1, ref_stk); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), 2); + CHECK_EQ(result.discard(), 0); + CHECK_UNARY(std::isnan(result[0])); + CHECK_EQ(result[1], doctest::Approx(-1.0)); + + /** @arg 正常执行 */ + result = IC(MA(CLOSE()), stks, query, 1, ref_stk); + CHECK_EQ(result.name(), "IC"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), ref_k.size()); + CHECK_EQ(result.discard(), 0); + CHECK_EQ(result[0], doctest::Approx(-1.0)); + CHECK_EQ(result[1], doctest::Approx(0.8)); + CHECK_EQ(result[99], doctest::Approx(0.5)); } //----------------------------------------------------------------------------- @@ -59,19 +95,21 @@ TEST_CASE("test_IC") { //----------------------------------------------------------------------------- #if ENABLE_BENCHMARK_TEST TEST_CASE("test_IC_benchmark") { - // Stock stock = getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(0)); - // Indicator c = kdata.close(); - // int cycle = 1000; // 测试循环次数 + StockManager& sm = StockManager::instance(); + StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + Stock ref_stk = sm["sh000001"]; + KQuery query = KQuery(-1000); + KData ref_k = ref_stk.getKData(query); - // { - // BENCHMARK_TIME_MSG(test_ABS_benchmark, cycle, fmt::format("data len: {}", c.size())); - // SPEND_TIME_CONTROL(false); - // for (int i = 0; i < cycle; i++) { - // Indicator ind = ABS(); - // Indicator result = ind(c); - // } - // } + int cycle = 100; // 测试循环次数 + + { + 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); + } + } } #endif @@ -82,32 +120,35 @@ TEST_CASE("test_IC_benchmark") { /** @par 检测点 */ TEST_CASE("test_IC_export") { - // StockManager& sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/ABS.xml"; + StockManager& sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/IC.xml"; + StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + Stock ref_stk = sm["sh000001"]; - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator x1 = ABS(CLOSE(kdata)); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(x1); - // } + KQuery query = KQuery(-200); + Indicator x1 = IC(stks, query, 1, ref_stk)(MA(CLOSE())); - // Indicator x2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(x2); - // } + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(x1); + } - // CHECK_UNARY(x1.size() == x2.size()); - // CHECK_UNARY(x1.discard() == x2.discard()); - // CHECK_UNARY(x1.getResultNumber() == x2.getResultNumber()); - // for (size_t i = 0; i < x1.size(); ++i) { - // CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); - // } + Indicator x2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(x2); + } + + CHECK_EQ(x1.name(), x2.name()); + CHECK_EQ(x1.size(), x2.size()); + CHECK_EQ(x1.discard(), x2.discard()); + CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + for (size_t i = 0; i < x1.size(); ++i) { + CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); + } } #endif /* #if HKU_SUPPORT_SERIALIZATION */ From c716da4483b95e123a21f14b4b7bab3b2f619e6a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 23:06:52 +0800 Subject: [PATCH 012/601] add IR/ICIR, update docs --- docs/source/indicator/indicator.rst | 40 +++++++++++++++++++++ docs/source/indicator/overview.rst | 6 ++++ hikyuu_cpp/hikyuu/indicator/build_in.h | 2 ++ hikyuu_cpp/hikyuu/indicator/crt/IC.h | 8 +++++ hikyuu_cpp/hikyuu/indicator/crt/ICIR.h | 27 ++++++++++++++ hikyuu_cpp/hikyuu/indicator/crt/IR.h | 41 ++++++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp | 6 +++- hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp | 6 +++- hikyuu_pywrap/indicator/_build_in.cpp | 36 ++++++++++++++++++- 9 files changed, 169 insertions(+), 3 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/ICIR.h create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/IR.h diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 45b64337..d60ce7df 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -448,6 +448,46 @@ :param KData kdata: k线数据 :rtype: Indicator +.. py:function:: IC(ind, stks, query, n, ref_stk) + + 计算指定的因子相对于参考证券的 IC (实际为 RankIC) + + :param sequence(stock)|Block stks 证券组合 + :param Query query: 查询条件 + :param int n: 时间窗口 + :param Stock ref_stk: 参照证券,通常使用 sh000300 沪深300 + :rtype: Indicator + + +.. py:function:: ICIR(ic[,n]) + + 计算 IC 因子 IR = IC的多周期均值/IC的标准方差 + + :param Indicator: ic 已经计算出的 ic 值 + :param int n: 时间窗口 + :rtype: Indicator + + +.. py:function:: IR(p, b[, n=100]) + + 信息比率(Information Ratio,IR) + + 公式: (P-B) / TE + P: 组合收益率 + B: 比较基准收益率 + TE: 投资周期中每天的 p 和 b 之间的标准差 + 实际使用时,P 一般为 TM 的资产曲线,B 为沪深 3000 收盘价,如: + ref_k = sm["sh000300"].get_kdata(query) + funds = my_tm.get_funds_curve(ref_k.get_datetime.list()) + ir = IR(PRICELIST(funds), ref_k.close, 0) + + 如果希望计算因子 IC 的 IR 值,请使用 ICIR 指标 + + :param Indicator p: + :param Indicator b: + :param int n: 时间窗口(默认100),如果只想使用最后的值,可以使用 0, 或 len(p),len(b) 指定 + :rtype: Indicator + .. py:function:: IF(x, a, b) diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index d9393061..0c8f54d1 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -132,6 +132,12 @@ * :py:func:`HOUR` - 取得该周期的小时数 * :py:func:`MINUTE` - 取得该周期的分钟数 +**因子类指标** + +* :py:func:`IC` - 计算因子 IC 值 +* :py:func:`IR` - 用于计算账户收益与参照收益的IR +* :py:func:`ICIR` - 计算因子 IC 的 IR 值 + **Ta-lib指标** diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index f116d14d..6ba16951 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -49,6 +49,8 @@ #include "crt/HHVBARS.h" #include "crt/HSL.h" #include "crt/IC.h" +#include "crt/ICIR.h" +#include "crt/IR.h" #include "crt/INTPART.h" #include "crt/LAST.h" #include "crt/LIUTONGPAN.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/IC.h b/hikyuu_cpp/hikyuu/indicator/crt/IC.h index b70fc654..87af5613 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/IC.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/IC.h @@ -12,6 +12,14 @@ namespace hku { +/** + * @brief 计算指定的因子相对于参考证券的 IC (实际为 RankIC) + * @param stks 证券组合 + * @param query 查询条件 + * @param n 时间窗口 + * @param ref_stk 参照证券,默认 sh000300 沪深300 + * @return Indicator + */ Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n = 1, const Stock& ref_stk = getStock("sh000300")); diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h new file mode 100644 index 00000000..758a9683 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "MA.h" +#include "STDEV.h" + +namespace hku { + +/** + * @brief 因子 IR + * @details IR:信息比率(Information Ratio,简称IR)= + * IC的多周期均值/IC的标准方差,代表因子获取稳定Alpha的能力。 + * @param ic 已经计算出的 ic 值 + * @param n 时间窗口 + * @return Indicator + */ +inline Indicator ICIR(const Indicator& ic, int n = 10) { + return MA(ic, n) / STDEV(ic, n); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/IR.h b/hikyuu_cpp/hikyuu/indicator/crt/IR.h new file mode 100644 index 00000000..2dc96b2e --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/IR.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "ROCP.h" +#include "STDEV.h" + +namespace hku { + +/** + * 信息比率(Information Ratio,IR) + * @details + *
+ * 公式: (P-B) / TE
+ * P: 组合收益率
+ * B: 比较基准收益率
+ * TE: 投资周期中每天的 p 和 b 之间的标准差
+ * 实际使用时,P 一般为 TM 的资产曲线,B 为沪深 3000 收盘价,如:
+ * ref_k = sm["sh000300"].get_kdata(query)
+ * funds = my_tm.get_funds_curve(ref_k.get_datetime.list())
+ * ir = IR(PRICELIST(funds), ref_k.close, 0)
+ * 
+ * @note 如果希望通过IC值计算IR,请使用 ICIR 指标 + * @param p + * @param b + * @param n + * @ingroup Indicator + */ +inline Indicator IR(const Indicator& p, const Indicator& b, int n = 100) { + Indicator p_return = ROCP(p, n); + Indicator b_return = ROCP(b, n); + Indicator x = (p_return - b_return); + return x / STDEV(x, n); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp index b58b7919..654f8b82 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp @@ -21,7 +21,8 @@ IStdev::IStdev() : IndicatorImp("STDEV", 1) { IStdev::~IStdev() {} bool IStdev::check() { - return getParam("n") >= 2; + int n = getParam("n"); + return n == 0 || n >= 2; } void IStdev::_calculate(const Indicator& data) { @@ -33,6 +34,9 @@ void IStdev::_calculate(const Indicator& data) { } int n = getParam("n"); + if (0 == n) { + n = total; + } auto const* src = data.data(); auto* dst = this->data(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp index 62bbab4c..b3d25afd 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp @@ -23,7 +23,8 @@ IStdp::IStdp() : IndicatorImp("STDP", 1) { IStdp::~IStdp() {} bool IStdp::check() { - return getParam("n") >= 2; + int n = getParam("n"); + return n == 0 || n >= 2; } void IStdp::_calculate(const Indicator& data) { @@ -35,6 +36,9 @@ void IStdp::_calculate(const Indicator& data) { } int n = getParam("n"); + if (0 == n) { + n = total; + } auto const* src = data.data(); auto* dst = this->data(); diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 69eb7949..b7f129ba 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1663,6 +1663,25 @@ void export_Indicator_build_in(py::module& m) { :param Indicator ind2: 输入参数2 :param int n: 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度)"); + // IR(const Indicator& p, const Indicator& b, int n = 100) + m.def("IR", IR, py::arg("p"), py::arg("b"), py::arg("n") = 100, R"(IR(p, b[, n]) + + 信息比率(Information Ratio,IR) + + 公式: (P-B) / TE + P: 组合收益率 + B: 比较基准收益率 + TE: 投资周期中每天的 p 和 b 之间的标准差 + 实际使用时,P 一般为 TM 的资产曲线,B 为沪深 3000 收盘价,如: + ref_k = sm["sh000300"].get_kdata(query) + funds = my_tm.get_funds_curve(ref_k.get_datetime.list()) + ir = IR(PRICELIST(funds), ref_k.close, 0) + + :param Indicator p: + :param Indicator b: + :param int n: 时间窗口,如果只想使用最后的值,可以使用 0, 或 len(p),len(b) 指定 + )"); + m.def( "IC", [](const Indicator& ind, const py::object& stks, const KQuery& query, int n, @@ -1679,5 +1698,20 @@ void export_Indicator_build_in(py::module& m) { 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")); + py::arg("ind"), py::arg("stks"), py::arg("query"), py::arg("n"), py::arg("ref_stk"), + R"(IC(ind, stks, query, n, ref_stk) + + 计算指定的因子相对于参考证券的 IC (实际为 RankIC) + + :param sequence(stock)|Block stks 证券组合 + :param Query query: 查询条件 + :param int n: 时间窗口 + :param Stock ref_stk: 参照证券,通常使用 sh000300 沪深300)"); + + m.def("ICIR", ICIR, py::arg("ic"), py::arg("n") = 10, R"(ICIR(ic[,n]) + + 计算 IC 因子 IR = IC的多周期均值/IC的标准方差 + + :param Indicator: ic 已经计算出的 ic 值 + :param int n: 时间窗口)"); } From 4263aab2fd5f0522b7bb69b1ea66eea1abe0618c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 10 Mar 2024 23:11:40 +0800 Subject: [PATCH 013/601] update --- hikyuu_cpp/hikyuu/indicator/crt/ICIR.h | 5 ++++- hikyuu_cpp/hikyuu/indicator/crt/IR.h | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h index 758a9683..bc6d707e 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h @@ -21,7 +21,10 @@ namespace hku { * @return Indicator */ inline Indicator ICIR(const Indicator& ic, int n = 10) { - return MA(ic, n) / STDEV(ic, n); + Indicator x = MA(ic, n) / STDEV(ic, n); + x.name("IR"); + x.setParam("n", n); + return x; } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/IR.h b/hikyuu_cpp/hikyuu/indicator/crt/IR.h index 2dc96b2e..0c1ab7e4 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/IR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/IR.h @@ -35,7 +35,10 @@ inline Indicator IR(const Indicator& p, const Indicator& b, int n = 100) { Indicator p_return = ROCP(p, n); Indicator b_return = ROCP(b, n); Indicator x = (p_return - b_return); - return x / STDEV(x, n); + Indicator ret = x / STDEV(x, n); + ret.name("IR"); + ret.setParam("n", n); + return ret; } } // namespace hku \ No newline at end of file From 5b37786f76908d0d5c1840fd47f3da92bec72602 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 01:03:11 +0800 Subject: [PATCH 014/601] =?UTF-8?q?add=20ZSCORE=20=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=A0=87=E5=87=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 13 ++- docs/source/indicator/overview.rst | 1 + hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/IC.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/ICIR.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h | 29 +++++ hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp | 94 ++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IZScore.h | 24 +++++ .../hikyuu/indicator/test_ZSCORE.cpp | 102 ++++++++++++++++++ hikyuu_pywrap/indicator/_build_in.cpp | 17 +++ 10 files changed, 282 insertions(+), 1 deletion(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IZScore.h create mode 100644 hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index d60ce7df..462cc774 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -1081,4 +1081,15 @@ 获取10年期中国国债收益率 :param DatetimeList|KDate|Indicator data: 输入的日期参考,优先使用上下文中的日期 - :param float default_val: 如果输入的日期早于已有国债数据的最早记录,则使用此默认值 \ No newline at end of file + :param float default_val: 如果输入的日期早于已有国债数据的最早记录,则使用此默认值 + + +.. py:function:: ZSCORE([data, out_extreme, nsigma, recursive]) + + 对数据进行标准化(归一),可选进行极值排除 + + :param Indicator data: 待剔除异常值的数据 + :param bool outExtreme: 指示剔除极值,默认 False + :param float nsigma: 剔除极值时使用的 nsigma 倍 sigma ,默认 3.0 + :param bool recursive: 是否进行递归剔除极值, 默认 False + :rtype: Indicator diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index 0c8f54d1..6133ee0a 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -11,6 +11,7 @@ * :py:func:`DROPNA` - 删除 nan 值 * :py:func:`PRICELIST` - 将PriceList或Indicator的结果集包装为Indicator,同名 VALUE * :py:func:`WEAVE` - 将两个ind的结果合并到一个ind中 +* :py:func:`ZSCORE` - ZScore 标准化 **行情指标** diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 6ba16951..0634988e 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -102,5 +102,6 @@ #include "crt/VARP.h" #include "crt/VIGOR.h" #include "crt/ZHBOND10.h" +#include "crt/ZSCORE.h" #endif /* INDICATOR_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/indicator/crt/IC.h b/hikyuu_cpp/hikyuu/indicator/crt/IC.h index 87af5613..3b0c7cae 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/IC.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/IC.h @@ -19,6 +19,7 @@ namespace hku { * @param n 时间窗口 * @param ref_stk 参照证券,默认 sh000300 沪深300 * @return Indicator + * @ingroup Indicator */ Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n = 1, const Stock& ref_stk = getStock("sh000300")); diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h index bc6d707e..c5a6a470 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h @@ -19,6 +19,7 @@ namespace hku { * @param ic 已经计算出的 ic 值 * @param n 时间窗口 * @return Indicator + * @ingroup Indicator */ inline Indicator ICIR(const Indicator& ic, int n = 10) { Indicator x = MA(ic, n) / STDEV(ic, n); diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h b/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h new file mode 100644 index 00000000..249ac832 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +/** + * 对数据进行ZScore标准化,可选进行极值排除 + * @param outExtreme 指示剔除极值 + * @param nsigma 剔除极值时使用的 nsigma 倍 sigma + * @param recursive 是否进行递归剔除极值 + * @return Indicator + * @ingroup Indicator + */ +Indicator HKU_API ZSCORE(bool outExtreme = false, double nsigma = 3.0, bool recursive = false); + +inline Indicator HKU_API ZSCORE(const Indicator& data, bool outExtreme = false, double nsigma = 3.0, + bool recursive = false) { + return ZSCORE(outExtreme, nsigma, recursive)(data); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp new file mode 100644 index 00000000..e1c4ebbb --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#include "IZScore.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IZScore) +#endif + +namespace hku { + +IZScore::IZScore() : IndicatorImp("ZSCORE", 1) { + setParam("nsigma", 3); + setParam("out-extreme", false); + setParam("recursive", false); +} + +IZScore::IZScore(bool outExtreme, double nsigma, bool recursive) : IndicatorImp("ZSCORE", 1) { + setParam("nsigma", nsigma); + setParam("out-extreme", outExtreme); + setParam("recursive", recursive); +} + +IZScore::~IZScore() {} + +bool IZScore::check() { + return getParam("nsigma") > 0.; +} + +static void normalize(IndicatorImp::value_t *dst, Indicator::value_t const *src, size_t total, + bool outExtreme, double nsigma, bool recursive) { + IndicatorImp::value_t sum = 0.0; + for (size_t i = 0; i < total; i++) { + sum += src[i]; + } + IndicatorImp::value_t mean = sum / total; + + vector tmp(total); + sum = 0.0; + for (size_t i = 0; i < total; i++) { + tmp[i] = src[i] - mean; + sum += tmp[i] * tmp[i]; + } + IndicatorImp::value_t sigma = std::sqrt(sum / (total - 1)); + HKU_INFO("sigma: {}", sigma); + for (size_t i = 0; i < total; i++) { + dst[i] = (src[i] - mean) / sigma; + } + + if (outExtreme) { + IndicatorImp::value_t ulimit = mean + nsigma * sigma; + IndicatorImp::value_t llimit = mean - nsigma * sigma; + bool found = false; + for (size_t i = 0; i < total; i++) { + if (dst[i] > ulimit) { + dst[i] = ulimit; + found = true; + } else if (dst[i] < llimit) { + dst[i] = llimit; + found = true; + } + } + + if (found && recursive) { + normalize(dst, src, total, outExtreme, nsigma, recursive); + } + } +} + +void IZScore::_calculate(const Indicator &data) { + size_t total = data.size(); + m_discard = data.discard(); + if (m_discard + 1 >= total) { + m_discard = total; + return; + } + + double nsigma = getParam("nsigma"); + bool outExtreme = getParam("out-extreme"); + bool recursive = getParam("recursive"); + auto const *src = data.data() + m_discard; + auto *dst = this->data() + m_discard; + normalize(dst, src, total - m_discard, outExtreme, nsigma, recursive); +} + +Indicator HKU_API ZSCORE(bool outExtreme, double nsigma, bool recursive) { + return make_shared(outExtreme, nsigma, recursive); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h new file mode 100644 index 00000000..5a942552 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class IZScore : public IndicatorImp { + INDICATOR_IMP(IZScore) + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IZScore(); + IZScore(bool outExtreme, double nsigma, bool recursive); + virtual ~IZScore(); +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp new file mode 100644 index 00000000..4855a988 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp @@ -0,0 +1,102 @@ +/* + * test_ABS.cpp + * + * Created on: 2019年4月2日 + * Author: fasiondog + */ +#include "../test_config.h" +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_indicator_ZSCORE test_indicator_ZSCORE + * @ingroup test_hikyuu_indicator_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_ZSCORE") { + // Indicator result; + + // PriceList a; + // for (int i = 0; i < 10; ++i) { + // a.push_back(5 - i); + // } + + // vector expect = {0., 0., 0., 0., 0., 1., 1., 1., 1., 1.}; + + // Indicator data = PRICELIST(a); + + // result = NOT(data); + // CHECK_EQ(result.name(), "NOT"); + // CHECK_EQ(result.discard(), 0); + // CHECK_EQ(result[0], 0.0); + // for (int i = 1; i < 10; ++i) { + // CHECK_EQ(result[i], expect[i]); + // } +} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_ZSCORE_benchmark") { + Stock stock = getStock("sh000001"); + KData kdata = stock.getKData(KQuery(0)); + Indicator c = kdata.close(); + int cycle = 1000; // 测试循环次数 + + { + BENCHMARK_TIME_MSG(test_ZSCORE_benchmark, cycle, fmt::format("data len: {}", c.size())); + SPEND_TIME_CONTROL(false); + for (int i = 0; i < cycle; i++) { + Indicator ind = EMA(); + Indicator result = ind(c); + } + } +} +#endif + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_ZSCORE_export") { + // StockManager& sm = StockManager::instance(); + // string filename(sm.tmpdir()); + // filename += "/NOT.xml"; + + // Stock stock = sm.getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(-20)); + // Indicator x1 = NOT(CLOSE(kdata)); + // { + // std::ofstream ofs(filename); + // boost::archive::xml_oarchive oa(ofs); + // oa << BOOST_SERIALIZATION_NVP(x1); + // } + + // Indicator x2; + // { + // std::ifstream ifs(filename); + // boost::archive::xml_iarchive ia(ifs); + // ia >> BOOST_SERIALIZATION_NVP(x2); + // } + + // CHECK_EQ(x1.name(), x2.name()); + // CHECK_EQ(x1.size(), x2.size()); + // CHECK_EQ(x1.discard(), x2.discard()); + // CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + // for (size_t i = 0; i < x1.size(); ++i) { + // CHECK_EQ(x1[i], doctest::Approx(x2[i])); + // } +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index b7f129ba..7fedabb2 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -472,6 +472,9 @@ Indicator (*CORR_2)(const Indicator&, const Indicator&, int) = CORR; Indicator (*SPEARMAN_1)(int) = SPEARMAN; Indicator (*SPEARMAN_2)(const Indicator&, const Indicator&, int) = SPEARMAN; +Indicator (*ZSCORE_1)(bool, double, bool) = ZSCORE; +Indicator (*ZSCORE_2)(const Indicator&, bool, double, bool) = ZSCORE; + void export_Indicator_build_in(py::module& m) { m.def("KDATA", KDATA1); m.def("KDATA", KDATA3, R"(KDATA([data]) @@ -1714,4 +1717,18 @@ void export_Indicator_build_in(py::module& m) { :param Indicator: ic 已经计算出的 ic 值 :param int n: 时间窗口)"); + + m.def("ZSCORE", ZSCORE_1, py::arg("out_extreme") = false, py::arg("nsigma") = 3.0, + py::arg("recursive") = false); + m.def("ZSCORE", ZSCORE_2, py::arg("data"), py::arg("out_extreme") = false, + py::arg("nsigma") = 3.0, py::arg("recursive") = false, + R"(ZSCORE(data[, out_extreme, nsigma, recursive]) + + 对数据进行标准化(归一),可选进行极值排除 + + :param Indicator data: 待剔除异常值的数据 + :param bool outExtreme: 指示剔除极值,默认 False + :param float nsigma: 剔除极值时使用的 nsigma 倍 sigma,默认 3.0 + :param bool recursive: 是否进行递归剔除极值,默认 False + :rtype: Indicator)"); } From 56be0baa13bcd41cd7167ba27cf3ae23c3e51888 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 01:39:31 +0800 Subject: [PATCH 015/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=8F=8A=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 2 + hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp | 1 - .../hikyuu/indicator/test_ZSCORE.cpp | 91 +++++++++++-------- hikyuu_pywrap/indicator/_build_in.cpp | 2 + 5 files changed, 56 insertions(+), 41 deletions(-) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 462cc774..aed4df04 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -1087,6 +1087,8 @@ .. py:function:: ZSCORE([data, out_extreme, nsigma, recursive]) 对数据进行标准化(归一),可选进行极值排除 + + 注:非窗口滚动,如需窗口滚动的标准化,直接 (x - MA(x, n)) / STDEV(x, n) 即可。 :param Indicator data: 待剔除异常值的数据 :param bool outExtreme: 指示剔除极值,默认 False diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h b/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h index 249ac832..360b51c9 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ZSCORE.h @@ -13,6 +13,7 @@ namespace hku { /** * 对数据进行ZScore标准化,可选进行极值排除 + * @note 非窗口滚动,如需窗口滚动的标准化,直接 (x - MA(x, n)) / STDEV(x, n) 即可 * @param outExtreme 指示剔除极值 * @param nsigma 剔除极值时使用的 nsigma 倍 sigma * @param recursive 是否进行递归剔除极值 diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp index e1c4ebbb..15ed774f 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp @@ -46,7 +46,6 @@ static void normalize(IndicatorImp::value_t *dst, Indicator::value_t const *src, sum += tmp[i] * tmp[i]; } IndicatorImp::value_t sigma = std::sqrt(sum / (total - 1)); - HKU_INFO("sigma: {}", sigma); for (size_t i = 0; i < total; i++) { dst[i] = (src[i] - mean) / sigma; } diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp index 4855a988..d8267856 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp @@ -21,24 +21,35 @@ using namespace hku; /** @par 检测点 */ TEST_CASE("test_ZSCORE") { - // Indicator result; + /** @arg 只有一条有效数据 */ + PriceList a{0.3}; + Indicator data = PRICELIST(a); + REQUIRE(data.size() == 1); + Indicator result = ZSCORE(data); + CHECK_EQ(result.name(), "ZSCORE"); + CHECK_EQ(result.size(), 1); + CHECK_EQ(result.discard(), 1); + CHECK_UNARY(std::isnan(result[0])); - // PriceList a; - // for (int i = 0; i < 10; ++i) { - // a.push_back(5 - i); - // } + /** @arg 输入的 nsigma < 0 */ + KData k = getKData("SH000001", KQuery(-5)); + result = ZSCORE(k.close(), true, -0.5); + CHECK_EQ(result.name(), "ZSCORE"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), k.size()); + CHECK_EQ(result.discard(), result.size()); - // vector expect = {0., 0., 0., 0., 0., 1., 1., 1., 1., 1.}; + /** @arg 正常计算,不剔除异常值 */ + result = ZSCORE(k.close()); + CHECK_EQ(result.name(), "ZSCORE"); + CHECK_UNARY(!result.empty()); + CHECK_EQ(result.size(), k.size()); + CHECK_EQ(result.discard(), 0); - // Indicator data = PRICELIST(a); - - // result = NOT(data); - // CHECK_EQ(result.name(), "NOT"); - // CHECK_EQ(result.discard(), 0); - // CHECK_EQ(result[0], 0.0); - // for (int i = 1; i < 10; ++i) { - // CHECK_EQ(result[i], expect[i]); - // } + PriceList expect{-0.573824, 1.52671, 0.497154, -0.581095, -0.868942}; + for (size_t i = result.discard(), total = result.size(); i < total; i++) { + CHECK_EQ(result[i], doctest::Approx(expect[i])); + } } //----------------------------------------------------------------------------- @@ -55,7 +66,7 @@ TEST_CASE("test_ZSCORE_benchmark") { BENCHMARK_TIME_MSG(test_ZSCORE_benchmark, cycle, fmt::format("data len: {}", c.size())); SPEND_TIME_CONTROL(false); for (int i = 0; i < cycle; i++) { - Indicator ind = EMA(); + Indicator ind = ZSCORE(); Indicator result = ind(c); } } @@ -69,33 +80,33 @@ TEST_CASE("test_ZSCORE_benchmark") { /** @par 检测点 */ TEST_CASE("test_ZSCORE_export") { - // StockManager& sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/NOT.xml"; + StockManager& sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/ZSCORE.xml"; - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator x1 = NOT(CLOSE(kdata)); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(x1); - // } + Stock stock = sm.getStock("sh000001"); + KData kdata = stock.getKData(KQuery(-20)); + Indicator x1 = ZSCORE(CLOSE(kdata)); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(x1); + } - // Indicator x2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(x2); - // } + Indicator x2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(x2); + } - // CHECK_EQ(x1.name(), x2.name()); - // CHECK_EQ(x1.size(), x2.size()); - // CHECK_EQ(x1.discard(), x2.discard()); - // CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); - // for (size_t i = 0; i < x1.size(); ++i) { - // CHECK_EQ(x1[i], doctest::Approx(x2[i])); - // } + CHECK_EQ(x1.name(), x2.name()); + CHECK_EQ(x1.size(), x2.size()); + CHECK_EQ(x1.discard(), x2.discard()); + CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + for (size_t i = 0; i < x1.size(); ++i) { + CHECK_EQ(x1[i], doctest::Approx(x2[i])); + } } #endif /* #if HKU_SUPPORT_SERIALIZATION */ diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 7fedabb2..13c50a12 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1725,6 +1725,8 @@ void export_Indicator_build_in(py::module& m) { R"(ZSCORE(data[, out_extreme, nsigma, recursive]) 对数据进行标准化(归一),可选进行极值排除 + + 注:非窗口滚动,如需窗口滚动的标准化,直接 (x - MA(x, n)) / STDEV(x, n) 即可。 :param Indicator data: 待剔除异常值的数据 :param bool outExtreme: 指示剔除极值,默认 False From eb029b964ab5f7bee251ebccd849dea7e34eb704 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 02:28:37 +0800 Subject: [PATCH 016/601] fixed compile errors on ubuntu --- hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 5 ++--- hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp | 2 +- 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp index 65c9a03a..353d940e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp @@ -111,7 +111,7 @@ void ICorr::_calculate(const Indicator& ind) { } Indicator HKU_API CORR(int n) { - return make_shared(n); + return Indicator(make_shared(n)); } Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index b1dceb6a..e74b3af2 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -24,7 +24,7 @@ IIc::IIc() : IndicatorImp("IC") { } IIc::IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) -: IndicatorImp("IC"), m_query(query), m_stks(stks), m_ref_stk(ref_stk) { +: IndicatorImp("IC"), m_query(query), m_ref_stk(ref_stk), m_stks(stks) { setParam("n", n); setParam("fill_null", true); } @@ -101,8 +101,7 @@ void IIc::_calculate(const Indicator& inputInd) { } Indicator HKU_API IC(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) { - IndicatorImpPtr p = make_shared(stks, query, n, ref_stk); - return p; + return Indicator(make_shared(stks, query, n, ref_stk)); } Indicator HKU_API IC(const Block& blk, const KQuery& query, int n, const Stock& ref_stk) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index d81dcc50..f60c6320 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -140,7 +140,7 @@ void ISpearman::_calculate(const Indicator &ind) { } Indicator HKU_API SPEARMAN(int n) { - return make_shared(n); + return Indicator(make_shared(n)); } Indicator HKU_API SPEARMAN(const Indicator &ind1, const Indicator &ind2, int n) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp index 15ed774f..7f9a9b06 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp @@ -87,7 +87,7 @@ void IZScore::_calculate(const Indicator &data) { } Indicator HKU_API ZSCORE(bool outExtreme, double nsigma, bool recursive) { - return make_shared(outExtreme, nsigma, recursive); + return Indicator(make_shared(outExtreme, nsigma, recursive)); } } // namespace hku \ No newline at end of file From 435b030906b45e58b97c6cd38a8019d2d269211a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 13:59:28 +0800 Subject: [PATCH 017/601] =?UTF-8?q?system=20reset/clone=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E4=BE=9D=E6=8D=AE=E9=83=A8=E4=BB=B6=E5=85=B1=E4=BA=AB?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E8=BF=9B=E8=A1=8C=E5=AE=9E=E9=99=85=E6=93=8D?= =?UTF-8?q?=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/trade_sys/system.rst | 23 +++-- .../trade_sys/selector/SelectorBase.cpp | 11 ++- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 99 ++++++++++++++----- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 14 ++- hikyuu_pywrap/_analysis.cpp | 2 +- hikyuu_pywrap/trade_sys/_System.cpp | 16 +-- 6 files changed, 113 insertions(+), 52 deletions(-) diff --git a/docs/source/trade_sys/system.rst b/docs/source/trade_sys/system.rst index 8c48388e..d1a30686 100644 --- a/docs/source/trade_sys/system.rst +++ b/docs/source/trade_sys/system.rst @@ -18,6 +18,16 @@ * **ev_open_position=False** *(bool)*: 是否使用市场环境判定进行初始建仓 * **cn_open_position=False** *(bool)*: 是否使用系统有效性条件进行初始建仓 + * **shared_tm=False** *(bool)*: tm 部件是否为共享部件 + * **shared_ev=True** *(bool)*: ev 部件是否为共享部件 + * **shared_cn=False** *(bool)*: cv 部件是否为共享部件 + * **shared_mm=False** *(bool)*: mm 部件是否为共享部件 + * **shared_sg=False** *(bool)*: sg 部件是否为共享部件 + * **shared_st=False** *(bool)*: st 部件是否为共享部件 + * **shared_tp=False** *(bool)*: tp 部件是否为共享部件 + * **shared_pg=False** *(bool)*: pg 部件是否为共享部件 + * **shared_sp=False** *(bool)*: sp 部件是否为共享部件 + 创建系统并执行回测 ----------------------- @@ -197,16 +207,17 @@ :param Query query: K线数据查询条件 :param bool reset: 是否同时复位所有组件,尤其是tm实例 - .. py:method:: reset(self, with_tm, with_ev) + .. py:method:: reset(self) - 复位操作。TM、EV是和具体系统无关的策略组件,可以在不同的系统中进行共享,复位将引起系统运行时被重新清空并计算。尤其是在共享TM时需要注意! - - :param bool with_tm: 是否复位TM组件 - :param bool with_ev: 是否复位EV组件 + 复位操作,依据各个部件的共享属性进行复位,共享的部件不进行复位。 + .. py:methon:: force_reset_all(self) + + 忽略部件的共享属性,强制复位,包括所有部件。 + .. py:method:: clone(self) - 克隆操作。 + 克隆操作,会依据部件的共享特性进行克隆,共享部件不进行实际的克隆操作,保持共享 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index bf10e615..8431ebdd 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -45,8 +45,7 @@ void SelectorBase::removeAll() { void SelectorBase::reset() { SystemList::const_iterator iter = m_pro_sys_list.begin(); for (; iter != m_pro_sys_list.end(); ++iter) { - // 复位账户但不复位ev - (*iter)->reset(true, false); + (*iter)->reset(); } m_real_sys_list.clear(); @@ -90,7 +89,9 @@ bool SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { HKU_ERROR_IF_RETURN(!protoSys->getMM(), false, "protoSys has not MoneyManager!"); HKU_ERROR_IF_RETURN(!protoSys->getSG(), false, "protoSys has not Siganl!"); SYSPtr sys = protoSys->clone(); - sys->reset(true, false); + // 每个系统独立,不共享 tm + sys->setParam("shared_tm", false); + sys->reset(); sys->setStock(stock); m_pro_sys_list.emplace_back(sys); return true; @@ -102,7 +103,9 @@ bool SelectorBase::addStockList(const StockList& stkList, const SystemPtr& proto HKU_ERROR_IF_RETURN(!protoSys->getSG(), false, "protoSys has not Siganl!"); SYSPtr newProtoSys = protoSys->clone(); // 复位清除之前的数据,避免因原有数据过多导致下面循环时速度过慢 - newProtoSys->reset(true, false); + // 每个系统独立,不共享 tm + newProtoSys->setParam("shared_tm", false); + newProtoSys->reset(); StockList::const_iterator iter = stkList.begin(); for (; iter != stkList.end(); ++iter) { if (iter->isNull()) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 76b70565..962f5066 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -106,12 +106,64 @@ void System::initParam() { // 在没有持仓时,是否支持借入证券,融券 setParam("support_borrow_stock", false); + + // 以下参数控制各个部件的共享策略,影响 clone 和 reset 操作 + // 当为共享组件时,不会 clone 和 reset 相应的组件 + setParam("shared_tm", false); + setParam("shared_ev", true); + setParam("shared_cn", false); + setParam("shared_sg", false); + setParam("shared_mm", false); + setParam("shared_st", false); + setParam("shared_tp", false); + setParam("shared_pg", false); + setParam("shared_sp", false); } -void System::reset(bool with_tm, bool with_ev) { - if (with_tm && m_tm) +void System::reset() { + if (m_tm && !getParam("shared_tm")) m_tm->reset(); - if (with_ev && m_ev) + if (m_ev && !getParam("shared_ev")) + m_ev->reset(); + if (m_cn && !getParam("shared_cn")) + m_cn->reset(); + if (m_mm && !getParam("shared_mm")) + m_mm->reset(); + if (m_sg && !getParam("shared_sg")) + m_sg->reset(); + if (m_st && !getParam("shared_st")) + m_st->reset(); + if (m_tp && !getParam("shared_tp")) + m_tp->reset(); + if (m_pg && !getParam("shared_pg")) + m_pg->reset(); + if (m_sp && !getParam("shared_sp")) + m_sp->reset(); + + // 不能复位m_stock / m_kdata/ + // m_src_kdata,后续Portfolio需要使用,从意义上讲,sys实例和stock是一一绑定的关系, + // 一个sys实例绑定stock后,除非主动改变,否则不应该被reset + // m_stock + + m_pre_ev_valid = false; // true; + m_pre_cn_valid = false; // true; + + m_buy_days = 0; + m_sell_short_days = 0; + m_trade_list.clear(); + m_lastTakeProfit = 0.0; + m_lastShortTakeProfit = 0.0; + + m_buyRequest.clear(); + m_sellRequest.clear(); + m_sellShortRequest.clear(); + m_buyShortRequest.clear(); +} + +void System::forceResetAll() { + if (m_tm) + m_tm->reset(); + if (m_ev) m_ev->reset(); if (m_cn) m_cn->reset(); @@ -128,11 +180,6 @@ void System::reset(bool with_tm, bool with_ev) { if (m_sp) m_sp->reset(); - // 不能复位m_stock / m_kdata/ - // m_src_kdata,后续Portfolio需要使用,从意义上讲,sys实例和stock是一一绑定的关系, - // 一个sys实例绑定stock后,除非主动改变,否则不应该被reset - // m_stock - m_pre_ev_valid = false; // true; m_pre_cn_valid = false; // true; @@ -185,29 +232,26 @@ void System::setTO(const KData& kdata) { m_mm->setQuery(query); } -SystemPtr System::clone(bool with_tm, bool with_ev) { +SystemPtr System::clone() { SystemPtr p = make_shared(); - if (m_tm) { - p->m_tm = with_tm ? m_tm->clone() : m_tm; - } - if (m_ev) { - // ev 通常作为公共组件不进行克隆,使用同一实例 - p->m_ev = with_ev ? m_ev->clone() : m_ev; - } + if (m_tm) + p->m_tm = getParam("shared_tm") ? m_tm : m_tm->clone(); + if (m_ev) + p->m_ev = getParam("shared_ev") ? m_ev : m_ev->clone(); if (m_mm) - p->m_mm = m_mm->clone(); + p->m_mm = getParam("shared_mm") ? m_mm : m_mm->clone(); if (m_cn) - p->m_cn = m_cn->clone(); + p->m_cn = getParam("shared_cn") ? m_cn : m_cn->clone(); if (m_sg) - p->m_sg = m_sg->clone(); + p->m_sg = getParam("shared_sg") ? m_sg : m_sg->clone(); if (m_st) - p->m_st = m_st->clone(); + p->m_st = getParam("shared_st") ? m_st : m_st->clone(); if (m_tp) - p->m_tp = m_tp->clone(); + p->m_tp = getParam("shared_tp") ? m_tp : m_tp->clone(); if (m_pg) - p->m_pg = m_pg->clone(); + p->m_pg = getParam("shared_pg") ? m_pg : m_pg->clone(); if (m_sp) - p->m_sp = m_sp->clone(); + p->m_sp = getParam("shared_sp") ? m_sp : m_sp->clone(); p->m_params = m_params; p->m_name = m_name; @@ -281,8 +325,9 @@ void System::run(const KQuery& query, bool reset) { HKU_ERROR_IF_RETURN(m_stock.isNull(), void(), "m_stock is NULL!"); // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 + // System::run 供单体系统进行回测,需要强制复位所有的组件,忽略组件的共享属性 if (reset) - this->reset(true, true); + this->forceResetAll(); HKU_IF_RETURN(!readyForRun(), void()); @@ -310,8 +355,10 @@ void System::run(const KData& kdata, bool reset) { HKU_INFO_IF_RETURN(kdata.empty(), void(), "Input kdata is empty!"); // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 - if (reset) - this->reset(true, true); + if (reset) { + // System::run 供单体系统进行回测,需要强制复位所有的组件,忽略组件的共享属性 + this->forceResetAll(); + } HKU_IF_RETURN(!readyForRun(), void()); diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index c277d715..420c90a5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -146,21 +146,19 @@ public: /** * 复位 - * @param with_tm 是否复位TM组件 - * @param with_ev 是否复位EV组件 - * @note TM、EV都是和具体系统无关的策略组件,可以在不同的系统中进行共享,复位将引起系统 - * 运行时被重新清空并计算。尤其是在共享TM时需要注意! + * @note 实际复位操作依赖于系统中各个部件的共享参数 */ - void reset(bool with_tm, bool with_ev); + void reset(); + + /** 强制复位所有组件,忽略组件的共享属性 */ + void forceResetAll(); typedef shared_ptr SystemPtr; /** * 克隆操作,会依次调用所有部件的clone操作 - * @param with_tm 是否克隆 tm,默认为 true - * @param with_ev 是否克隆 ev,默认为 false,ev 通常作为公共组件不进行克隆,使用同一实例 */ - SystemPtr clone(bool with_tm = true, bool with_ev = false); + SystemPtr clone(); /** * 设置交易对象 diff --git a/hikyuu_pywrap/_analysis.cpp b/hikyuu_pywrap/_analysis.cpp index 74bf5281..3816c4a7 100644 --- a/hikyuu_pywrap/_analysis.cpp +++ b/hikyuu_pywrap/_analysis.cpp @@ -114,7 +114,7 @@ static py::dict analysis_sys_list(const py::object& pystk_list, const KQuery& qu SystemPtr sys_proto) { HKU_CHECK(sys_proto, "sys_proto is null!"); - sys_proto->reset(true, true); + sys_proto->forceResetAll(); SystemList sys_list; StockList stk_list; diff --git a/hikyuu_pywrap/trade_sys/_System.cpp b/hikyuu_pywrap/trade_sys/_System.cpp index 1ee29f26..a23145a0 100644 --- a/hikyuu_pywrap/trade_sys/_System.cpp +++ b/hikyuu_pywrap/trade_sys/_System.cpp @@ -155,18 +155,20 @@ void export_System(py::module& m) { .def("get_buy_short_trade_request", &System::getBuyShortTradeRequest, py::return_value_policy::copy) - .def("reset", &System::reset, py::arg("with_tm"), py::arg("with_ev"), - R"(reset(self, with_tm, with_ev) + .def("reset", &System::reset, + R"(reset(self) - 复位操作。TM、EV是和具体系统无关的策略组件,可以在不同的系统中进行共享,复位将引起系统运行时被重新清空并计算。尤其是在共享TM时需要注意! + 依据各个部件的共享属性进行复位操作。)") - :param bool with_tm: 是否复位TM组件 - :param bool with_ev: 是否复位EV组件)") + .def("force_reset_all", &System::forceResetAll, + R"(force_reset_all(self) - .def("clone", &System::clone, py::arg("with_tm") = true, py::arg("with_ev") = false, + 忽略各个部件的共享属性,强制复位所有部件。)") + + .def("clone", &System::clone, R"(clone(self) - 克隆操作。)") + 克隆操作,会依据部件的共享特性进行克隆,共享部件不进行实际的克隆操作,保持共享。)") .def("run", run_1, py::arg("query"), py::arg("reset") = true) .def("run", run_2, py::arg("kdata"), py::arg("reset") = true) From 3c79ccdf6a4cde01f6ba547e37e64899316db5cf Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 19:40:40 +0800 Subject: [PATCH 018/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20python=20log=20?= =?UTF-8?q?=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/__init__.py | 2 - hikyuu/draw/drawplot/bokeh_draw.py | 19 +++++---- hikyuu/draw/drawplot/common.py | 45 +-------------------- hikyuu/draw/drawplot/matplotlib_draw.py | 2 +- hikyuu/interactive.py | 11 ++++-- hikyuu/util/__init__.py | 9 +++-- hikyuu/util/notebook.py | 47 ++++++++++++++++++++++ hikyuu_cpp/hikyuu/Log.cpp | 2 +- hikyuu_cpp/hikyuu/Log.h | 52 ++++--------------------- hikyuu_cpp/hikyuu/StockManager.cpp | 3 +- hikyuu_cpp/hikyuu/StockManager.h | 20 +++++++++- hikyuu_pywrap/_StockManager.cpp | 7 ++++ hikyuu_pywrap/main.cpp | 2 + xmake.lua | 35 ++++++++++++++--- 14 files changed, 140 insertions(+), 116 deletions(-) create mode 100644 hikyuu/util/notebook.py diff --git a/hikyuu/draw/drawplot/__init__.py b/hikyuu/draw/drawplot/__init__.py index 44b78b3b..3197e52e 100644 --- a/hikyuu/draw/drawplot/__init__.py +++ b/hikyuu/draw/drawplot/__init__.py @@ -67,8 +67,6 @@ from .echarts_draw import iplot as ec_iplot from .echarts_draw import ibar as ec_ibar from .echarts_draw import kplot as ec_kplot -from .common import in_ipython_frontend - g_draw_engine = 'matplotlib' diff --git a/hikyuu/draw/drawplot/bokeh_draw.py b/hikyuu/draw/drawplot/bokeh_draw.py index 93c198a0..8bed80c2 100644 --- a/hikyuu/draw/drawplot/bokeh_draw.py +++ b/hikyuu/draw/drawplot/bokeh_draw.py @@ -5,7 +5,6 @@ """ from hikyuu import * -from .common import in_ipython_frontend from bokeh.plotting import figure, ColumnDataSource from bokeh.models import DatetimeTickFormatter, HoverTool, Title, Label @@ -65,7 +64,7 @@ def show_gcf(): def create_one_axes_figure(figsize=(800, 450)): """生成一个仅含有1个坐标轴的figure,并返回其坐标轴对象 - + :param figsize: (宽, 高) :return: ax """ @@ -78,7 +77,7 @@ def create_one_axes_figure(figsize=(800, 450)): def create_two_axes_figure(figsize=(800, 450)): """生成一个含有2个坐标轴的figure,并返回坐标轴列表 - + :param figsize: (宽, 高) :return: (ax1, ax2) """ @@ -93,7 +92,7 @@ def create_two_axes_figure(figsize=(800, 450)): def create_three_axes_figure(figsize=(800, 450)): """生成一个含有2个坐标轴的figure,并返回坐标轴列表 - + :param figsize: (宽, 高) :return: (ax1, ax2) """ @@ -132,7 +131,7 @@ def get_date_format(kdata): def kplot(kdata, new=True, axes=None, colorup='r', colordown='g'): """绘制K线图 - + :param KData kdata: K线数据 :param bool new: 是否在新窗口中显示,只在没有指定axes时生效 :param axes: 指定的坐标轴 @@ -226,7 +225,7 @@ def kplot(kdata, new=True, axes=None, colorup='r', colordown='g'): def mkplot(kdata, new=True, axes=None, colorup='r', colordown='g', ticksize=3): """绘制美式K线图 - + :param KData kdata: K线数据 :param bool new: 是否在新窗口中显示,只在没有指定axes时生效 :param axes: 指定的坐标轴 @@ -258,7 +257,7 @@ def iplot( **kwargs ): """绘制indicator曲线 - + :param Indicator indicator: indicator实例 :param axes: 指定的坐标轴 :param new: 是否在新窗口中显示,只在没有指定axes时生效 @@ -357,7 +356,7 @@ def ibar( **kwargs ): """绘制indicator柱状图 - + :param Indicator indicator: Indicator实例 :param axes: 指定的坐标轴 :param new: 是否在新窗口中显示,只在没有指定axes时生效 @@ -436,7 +435,7 @@ def ibar( def ax_draw_macd(axes, kdata, n1=12, n2=26, n3=9): """绘制MACD - + :param axes: 指定的坐标轴 :param KData kdata: KData :param int n1: 指标 MACD 的参数1 @@ -581,4 +580,4 @@ def sgplot(sg, new=True, axes=None, style=1, kdata=None): x=[d.datetime() for d in sell_dates], y=sell_y, fill_color='blue', line_color='blue', size=10 ) - return gcf() \ No newline at end of file + return gcf() diff --git a/hikyuu/draw/drawplot/common.py b/hikyuu/draw/drawplot/common.py index 70edd6ea..e6f1a647 100644 --- a/hikyuu/draw/drawplot/common.py +++ b/hikyuu/draw/drawplot/common.py @@ -24,10 +24,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -#=============================================================================== +# =============================================================================== # history: # 1. 20171122, Added by fasiondog -#=============================================================================== +# =============================================================================== from hikyuu import Query @@ -87,44 +87,3 @@ def get_draw_title(kdata): stitle = stock.market + "/" + stock.code + ' ' + name + s1 return stitle - - -def in_interactive_session() -> bool: - """ - Check if we're running in an interactive shell. - - Returns - ------- - bool - True if running under python/ipython interactive shell. - """ - def check_main(): - try: - import __main__ as main - except ModuleNotFoundError: - return False - return not hasattr(main, "__file__") - - try: - # error: Name '__IPYTHON__' is not defined - return __IPYTHON__ or check_main() # type: ignore[name-defined] - except NameError: - return check_main() - - -def in_ipython_frontend() -> bool: - """ - Check if we're inside an IPython zmq frontend. - - Returns - ------- - bool - """ - try: - # error: Name 'get_ipython' is not defined - ip = get_ipython() # type: ignore[name-defined] - return "zmq" in str(type(ip)).lower() - except NameError: - pass - - return False \ No newline at end of file diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 1a45c371..03255abf 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -16,7 +16,7 @@ from matplotlib.ticker import FuncFormatter, FixedLocator from hikyuu import * -from .common import get_draw_title, in_interactive_session +from .common import get_draw_title def set_mpl_params(): diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index ea120c44..8152b45e 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -58,9 +58,14 @@ import configparser from hikyuu.data.hku_config_template import generate_default_config from hikyuu import * -# 重定向C++ stdout/stderr输出至python -iodog = OstreamRedirect() -iodog.open() +if in_interactive_session(): + sm.python_in_interactive = True + +# 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python +if in_ipython_frontend(): + hku_info("hikyuu version: {}", get_version_with_build()) + iodog = OstreamRedirect() + iodog.open() # ============================================================================== # 引入扯线木偶 diff --git a/hikyuu/util/__init__.py b/hikyuu/util/__init__.py index 5b42234c..e4d41361 100644 --- a/hikyuu/util/__init__.py +++ b/hikyuu/util/__init__.py @@ -2,16 +2,17 @@ # -*- coding: utf8 -*- # cp936 -#=============================================================================== +# =============================================================================== # 作者:fasiondog # 历史:1)20090527, Added by fasiondog -#=============================================================================== +# =============================================================================== -#from singleton import Singleton +# from singleton import Singleton from .mylog import * from .check import * from .timeout import * +from .notebook import * __all__ = [ 'spend_time', @@ -41,4 +42,6 @@ __all__ = [ 'with_trace', 'capture_multiprocess_all_logger', 'LoggingContext', + 'in_interactive_session', + 'in_ipython_frontend', ] diff --git a/hikyuu/util/notebook.py b/hikyuu/util/notebook.py new file mode 100644 index 00000000..791c8b92 --- /dev/null +++ b/hikyuu/util/notebook.py @@ -0,0 +1,47 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# +# Create on: 2024-03-11 +# Author: fasiondog + +# 来源于 pandas 中对 ipython,notebook 环境的检测代码 + +def in_interactive_session() -> bool: + """ + Check if we're running in an interactive shell. + + Returns + ------- + bool + True if running under python/ipython interactive shell. + """ + def check_main(): + try: + import __main__ as main + except ModuleNotFoundError: + return False + return not hasattr(main, "__file__") + + try: + # error: Name '__IPYTHON__' is not defined + return __IPYTHON__ or check_main() # type: ignore[name-defined] + except NameError: + return check_main() + + +def in_ipython_frontend() -> bool: + """ + Check if we're inside an IPython zmq frontend. 检测是否在 jupyter 环境中 + + Returns + ------- + bool + """ + try: + # error: Name 'get_ipython' is not defined + ip = get_ipython() # type: ignore[name-defined] + return "zmq" in str(type(ip)).lower() + except NameError: + pass + + return False diff --git a/hikyuu_cpp/hikyuu/Log.cpp b/hikyuu_cpp/hikyuu/Log.cpp index 773fef51..81a045a9 100644 --- a/hikyuu_cpp/hikyuu/Log.cpp +++ b/hikyuu_cpp/hikyuu/Log.cpp @@ -26,7 +26,7 @@ namespace hku { static std::thread::id g_main_thread_id = std::this_thread::get_id(); -static int g_ioredirect_to_python_count = 0; +static std::atomic g_ioredirect_to_python_count = 0; bool isLogInMainThread() { return std::this_thread::get_id() == g_main_thread_id; diff --git a/hikyuu_cpp/hikyuu/Log.h b/hikyuu_cpp/hikyuu/Log.h index 5fc1e1d3..819eff6e 100644 --- a/hikyuu_cpp/hikyuu/Log.h +++ b/hikyuu_cpp/hikyuu/Log.h @@ -83,54 +83,12 @@ void HKU_API set_log_level(LOG_LEVEL level); std::shared_ptr HKU_API getHikyuuLogger(); -/*#define HKU_TRACE(...) SPDLOG_LOGGER_TRACE(hku::getHikyuuLogger(), __VA_ARGS__) +#define HKU_TRACE(...) SPDLOG_LOGGER_TRACE(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_DEBUG(...) SPDLOG_LOGGER_DEBUG(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_INFO(...) SPDLOG_LOGGER_INFO(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_WARN(...) SPDLOG_LOGGER_WARN(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_ERROR(...) SPDLOG_LOGGER_ERROR(hku::getHikyuuLogger(), __VA_ARGS__) -#define HKU_FATAL(...) SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__)*/ - -#define HKU_TRACE(...) \ - do { \ - if (hku::isLogInMainThread() || hku::getIORedirectToPythonCount() <= 0) { \ - SPDLOG_LOGGER_TRACE(hku::getHikyuuLogger(), __VA_ARGS__); \ - } \ - } while (0) - -#define HKU_DEBUG(...) \ - do { \ - if (hku::isLogInMainThread() || hku::getIORedirectToPythonCount() <= 0) { \ - SPDLOG_LOGGER_DEBUG(hku::getHikyuuLogger(), __VA_ARGS__); \ - } \ - } while (0) - -#define HKU_INFO(...) \ - do { \ - if (hku::isLogInMainThread() || hku::getIORedirectToPythonCount() <= 0) { \ - SPDLOG_LOGGER_INFO(hku::getHikyuuLogger(), __VA_ARGS__); \ - } \ - } while (0) - -#define HKU_WARN(...) \ - do { \ - if (hku::isLogInMainThread() || hku::getIORedirectToPythonCount() <= 0) { \ - SPDLOG_LOGGER_WARN(hku::getHikyuuLogger(), __VA_ARGS__); \ - } \ - } while (0) - -#define HKU_ERROR(...) \ - do { \ - if (hku::isLogInMainThread() || hku::getIORedirectToPythonCount() <= 0) { \ - SPDLOG_LOGGER_ERROR(hku::getHikyuuLogger(), __VA_ARGS__); \ - } \ - } while (0) - -#define HKU_FATAL(...) \ - do { \ - if (hku::isLogInMainThread() || hku::getIORedirectToPythonCount() <= 0) { \ - SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__); \ - } \ - } while (0) +#define HKU_FATAL(...) SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__) #if HKU_USE_SPDLOG_ASYNC_LOGGER void initLogger(); @@ -487,9 +445,13 @@ std::string HKU_API getLocalTime(); return ret; \ } +/** 用于 catch (...) 中打印,减少编译后代码大小 */ extern std::string g_unknown_error_msg; +#define HKU_TRACE_UNKNOWN HKU_TRACE(g_unknown_error_msg) +#define HKU_DEBUG_UNKNOWN HKU_DEBUG(g_unknown_error_msg) +#define HKU_INFO_UNKNOWN HKU_INFO(g_unknown_error_msg) #define HKU_ERROR_UNKNOWN HKU_ERROR(g_unknown_error_msg) -#define HKU_FATAL_UNKNOWN HKU_ERROR(g_unknown_error_msg) +#define HKU_FATAL_UNKNOWN HKU_FATAL(g_unknown_error_msg) /** @} */ diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 84904bb3..25bc845f 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -33,7 +33,8 @@ void StockManager::quit() { } } -StockManager::StockManager() : m_initializing(false) { +StockManager::StockManager() +: m_initializing(false), m_runningInPython(false), m_pythonIsInteractive(false) { m_stockDict_mutex = new std::mutex; m_marketInfoDict_mutex = new std::mutex; m_stockTypeInfo_mutex = new std::mutex; diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index bc842874..44852792 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -196,6 +196,22 @@ public: return m_thread_id; } + bool runningInPython() const { + return m_runningInPython; + } + + void runningInPython(bool inpython) { + m_runningInPython = inpython; + } + + bool pythonInteractive() const { + return m_pythonIsInteractive; + } + + void pythonInteractive(bool isInteractive) { + m_pythonIsInteractive = isInteractive; + } + public: typedef StockMapIterator const_iterator; const_iterator begin() const { @@ -235,7 +251,9 @@ private: private: static StockManager* m_sm; - bool m_initializing; + std::atomic_bool m_initializing; + std::atomic_bool m_runningInPython; // 是否是在 python 中运行 + std::atomic_bool m_pythonIsInteractive; // python 是否为交互模式 std::thread::id m_thread_id; // 记录线程id,用于判断Stratege是以独立进程方式还是线程方式运行 string m_tmpdir; string m_datadir; diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 594e8704..26e0b533 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -31,6 +31,13 @@ void export_StockManager(py::module& m) { param hikyuu_param 其他参数 param StrategyContext context 策略上下文, 默认加载全部证券)") + .def_property("running_in_python", + py::overload_cast<>(&StockManager::runningInPython, py::const_), + py::overload_cast(&StockManager::runningInPython)) + .def_property("python_in_interactive", + py::overload_cast<>(&StockManager::pythonInteractive, py::const_), + py::overload_cast(&StockManager::pythonInteractive)) + .def("reload", &StockManager::reload, "重新加载所有证券数据") .def("tmpdir", &StockManager::tmpdir, R"(tmpdir(self) -> str diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index f3e7e7c3..1ad080b0 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -62,6 +62,8 @@ PYBIND11_MODULE(core312, m) { PYBIND11_MODULE(core, m) { #endif + StockManager::instance().runningInPython(true); + #if HKU_ENABLE_SEND_FEEDBACK sendPythonVersionFeedBack(PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); #endif diff --git a/xmake.lua b/xmake.lua index 21b3c41b..e39d4431 100644 --- a/xmake.lua +++ b/xmake.lua @@ -80,6 +80,14 @@ option("low_precision") set_description("Enable send feedback.") option_end() +option("log_level") + set_default("info") + set_values("trace", "debug", "info", "warn", "error", "fatal", "off") + set_showmenu(true) + set_category("hikyuu") + set_description("set log level") +option_end() + -- project set_project("hikyuu") @@ -88,12 +96,27 @@ if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msa -- version set_version("1.3.5", {build = "%Y%m%d%H%M"}) -set_configvar("LOG_ACTIVE_LEVEL", 0) -- 激活的日志级别 --- if is_mode("debug") then --- set_configvar("LOG_ACTIVE_LEVEL", 0) -- 激活的日志级别 --- else --- set_configvar("LOG_ACTIVE_LEVEL", 2) -- 激活的日志级别 --- end + +local level = get_config("log_level") +if is_mode("debug") then + level = "trace" +end +if level == "trace" then + set_configvar("LOG_ACTIVE_LEVEL", 0) +elseif level == "debug" then + set_configvar("LOG_ACTIVE_LEVEL", 1) +elseif level == "info" then + set_configvar("LOG_ACTIVE_LEVEL", 2) +elseif level == "warn" then + set_configvar("LOG_ACTIVE_LEVEL", 3) +elseif level == "error" then + set_configvar("LOG_ACTIVE_LEVEL", 4) +elseif level == "fatal" then + set_configvar("LOG_ACTIVE_LEVEL", 5) +else + set_configvar("LOG_ACTIVE_LEVEL", 6) +end + set_configvar("USE_SPDLOG_LOGGER", 1) -- 是否使用spdlog作为日志输出 set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) From f57627f2c11c2064fbd456cb1f803b96e9187313 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 21:15:58 +0800 Subject: [PATCH 019/601] =?UTF-8?q?fixed=20=E4=B8=8D=E7=9F=A5=E4=BD=95?= =?UTF-8?q?=E6=97=B6=E8=A2=AB=E8=AF=AF=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index e39d4431..a139b2d5 100644 --- a/xmake.lua +++ b/xmake.lua @@ -215,7 +215,7 @@ add_requires("nlohmann_json", {system = false}) add_requires("cpp-httplib", {system = false, configs = {zlib = true, ssl = true}}) add_requires("zlib", {system = false}) -add_defines("SPDLOG_DISABLExm_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger +add_defines("SPDLOG_DISABLE_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger set_objectdir("$(buildir)/$(mode)/$(plat)/$(arch)/.objs") set_targetdir("$(buildir)/$(mode)/$(plat)/$(arch)/lib") From 67661074994744fb5641284f34df5d7cacca99c7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 11 Mar 2024 23:05:45 +0800 Subject: [PATCH 020/601] =?UTF-8?q?=E4=BC=98=E5=8C=96Python=E4=BA=A4?= =?UTF-8?q?=E4=BA=92=E6=97=A5=E5=BF=97=E8=BE=93=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 7 ++--- hikyuu_cpp/hikyuu/Log.cpp | 42 +++++++++++++++++++++--------- hikyuu_cpp/hikyuu/Log.h | 8 ++---- hikyuu_cpp/hikyuu/StockManager.cpp | 7 ++++- hikyuu_cpp/hikyuu/StockManager.h | 12 ++++----- hikyuu_pywrap/_StockManager.cpp | 6 ++--- 6 files changed, 48 insertions(+), 34 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 8152b45e..80251404 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -58,11 +58,10 @@ import configparser from hikyuu.data.hku_config_template import generate_default_config from hikyuu import * -if in_interactive_session(): - sm.python_in_interactive = True # 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python if in_ipython_frontend(): + sm.python_in_jupyter = True hku_info("hikyuu version: {}", get_version_with_build()) iodog = OstreamRedirect() iodog.open() @@ -123,10 +122,8 @@ for p in kdata_config: continue kdata_param[p] = ini.get('kdata', p) -# set_log_level(LOG_LEVEL.INFO) -# sm = StockManager.instance() sm.init(base_param, block_param, kdata_param, preload_param, hku_param) -set_log_level(LOG_LEVEL.INFO) +# set_log_level(LOG_LEVEL.INFO) # 启动行情接收代理 start_spot_agent(False) diff --git a/hikyuu_cpp/hikyuu/Log.cpp b/hikyuu_cpp/hikyuu/Log.cpp index 81a045a9..01d6b956 100644 --- a/hikyuu_cpp/hikyuu/Log.cpp +++ b/hikyuu_cpp/hikyuu/Log.cpp @@ -13,7 +13,7 @@ #if USE_SPDLOG_LOGGER // 使用 stdout_color 将无法将日志输出重定向至 python -// #include +#include #include #include "spdlog/sinks/ostream_sink.h" #include "spdlog/sinks/rotating_file_sink.h" @@ -61,16 +61,25 @@ std::shared_ptr getHikyuuLogger() { } #if HKU_USE_SPDLOG_ASYNC_LOGGER -void initLogger() { - auto stdout_sink = std::make_shared(std::cout, true); - // auto stdout_sink = std::make_shared(); +void initLogger(bool inJupyter) { + std::string logname = "hikyuu"; + std::shared_ptr logger = spdlog::get(logname); + if (logger) { + spdlog::drop(logname); + } + spdlog::sink_ptr stdout_sink; + if (inJupyter) { + stdout_sink = std::make_shared(std::cout, true); + } else { + stdout_sink = std::make_shared(); + } stdout_sink->set_level(spdlog::level::trace); spdlog::init_thread_pool(8192, 1); std::vector sinks{stdout_sink}; - auto logger = std::make_shared("hikyuu", sinks.begin(), sinks.end(), - spdlog::thread_pool(), - spdlog::async_overflow_policy::block); + logger = std::make_shared(logname, sinks.begin(), sinks.end(), + spdlog::thread_pool(), + spdlog::async_overflow_policy::block); logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::trace); @@ -80,16 +89,25 @@ void initLogger() { #else /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */ -void initLogger() { - auto stdout_sink = std::make_shared(std::cout, true); - // auto stdout_sink = std::make_shared(); +void initLogger(bool inJupyter) { + std::string logname = "hikyuu"; + std::shared_ptr logger = spdlog::get(logname); + if (logger) { + spdlog::drop(logname); + } + spdlog::sink_ptr stdout_sink; + if (inJupyter) { + stdout_sink = std::make_shared(std::cout, true); + } else { + stdout_sink = std::make_shared(); + } stdout_sink->set_level(spdlog::level::trace); std::string log_filename = fmt::format("{}/.hikyuu/hikyuu.log", getUserDir()); auto rotating_sink = std::make_shared(log_filename, 1024 * 1024 * 10, 3); rotating_sink->set_level(spdlog::level::warn); std::vector sinks{stdout_sink, rotating_sink}; - auto logger = std::make_shared("hikyuu", sinks.begin(), sinks.end()); + logger = std::make_shared(logname, sinks.begin(), sinks.end()); logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::trace); // logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v [%!] (%@)"); @@ -109,7 +127,7 @@ void set_log_level(LOG_LEVEL level) { /********************************************** * Use SPDLOG for logging *********************************************/ -void initLogger() {} +void initLogger(bool inJupyter) {} void set_log_level(LOG_LEVEL level) { g_log_level = level; diff --git a/hikyuu_cpp/hikyuu/Log.h b/hikyuu_cpp/hikyuu/Log.h index 819eff6e..9171e209 100644 --- a/hikyuu_cpp/hikyuu/Log.h +++ b/hikyuu_cpp/hikyuu/Log.h @@ -90,11 +90,7 @@ std::shared_ptr HKU_API getHikyuuLogger(); #define HKU_ERROR(...) SPDLOG_LOGGER_ERROR(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_FATAL(...) SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__) -#if HKU_USE_SPDLOG_ASYNC_LOGGER -void initLogger(); -#else -void initLogger(); -#endif +void initLogger(bool inJupyter = false); #else enum LOG_LEVEL { @@ -109,7 +105,7 @@ enum LOG_LEVEL { LOG_LEVEL HKU_API get_log_level(); void HKU_API set_log_level(LOG_LEVEL level); -void initLogger(); +void initLogger(bool inJupyter = false); /** 获取系统当前时间,精确到毫秒,如:2001-01-02 13:01:02.001 */ std::string HKU_API getLocalTime(); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 25bc845f..f9b30e04 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -34,7 +34,7 @@ void StockManager::quit() { } StockManager::StockManager() -: m_initializing(false), m_runningInPython(false), m_pythonIsInteractive(false) { +: m_initializing(false), m_runningInPython(false), m_pythonInJupyter(false) { m_stockDict_mutex = new std::mutex; m_marketInfoDict_mutex = new std::mutex; m_stockTypeInfo_mutex = new std::mutex; @@ -56,6 +56,11 @@ StockManager& StockManager::instance() { return (*m_sm); } +void StockManager::pythonInJupyter(bool inJupyter) { + m_pythonInJupyter = inJupyter; + initLogger(inJupyter); +} + Parameter default_preload_param() { Parameter param; param.set("day", true); diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 44852792..a9f322ed 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -204,13 +204,11 @@ public: m_runningInPython = inpython; } - bool pythonInteractive() const { - return m_pythonIsInteractive; + bool pythonInJupyter() const { + return m_pythonInJupyter; } - void pythonInteractive(bool isInteractive) { - m_pythonIsInteractive = isInteractive; - } + void pythonInJupyter(bool inJupyter); public: typedef StockMapIterator const_iterator; @@ -252,8 +250,8 @@ private: private: static StockManager* m_sm; std::atomic_bool m_initializing; - std::atomic_bool m_runningInPython; // 是否是在 python 中运行 - std::atomic_bool m_pythonIsInteractive; // python 是否为交互模式 + std::atomic_bool m_runningInPython; // 是否是在 python 中运行 + std::atomic_bool m_pythonInJupyter; // python 是否为交互模式 std::thread::id m_thread_id; // 记录线程id,用于判断Stratege是以独立进程方式还是线程方式运行 string m_tmpdir; string m_datadir; diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 26e0b533..0ed40065 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -34,9 +34,9 @@ void export_StockManager(py::module& m) { .def_property("running_in_python", py::overload_cast<>(&StockManager::runningInPython, py::const_), py::overload_cast(&StockManager::runningInPython)) - .def_property("python_in_interactive", - py::overload_cast<>(&StockManager::pythonInteractive, py::const_), - py::overload_cast(&StockManager::pythonInteractive)) + .def_property("python_in_jupyter", + py::overload_cast<>(&StockManager::pythonInJupyter, py::const_), + py::overload_cast(&StockManager::pythonInJupyter)) .def("reload", &StockManager::reload, "重新加载所有证券数据") From b15738b5d01d51db9d38b76bdb1dc83ee2db7759 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 12 Mar 2024 01:12:06 +0800 Subject: [PATCH 021/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=AD=96=E7=95=A5clone=E4=B8=8Ereset=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/trade_sys/system.rst | 9 +-- .../trade_sys/condition/ConditionBase.cpp | 5 +- .../moneymanager/MoneyManagerBase.cpp | 29 +++++--- .../trade_sys/moneymanager/MoneyManagerBase.h | 4 +- .../trade_sys/profitgoal/ProfitGoalBase.cpp | 8 ++- .../trade_sys/profitgoal/ProfitGoalBase.h | 4 -- .../hikyuu/trade_sys/signal/SignalBase.cpp | 3 +- .../trade_sys/slippage/SlippageBase.cpp | 7 +- .../hikyuu/trade_sys/slippage/SlippageBase.h | 4 -- .../trade_sys/stoploss/StoplossBase.cpp | 8 ++- .../hikyuu/trade_sys/stoploss/StoplossBase.h | 4 -- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 37 +++++++--- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 67 ++++++++++++++----- hikyuu_pywrap/trade_sys/_System.cpp | 18 ++--- 14 files changed, 138 insertions(+), 69 deletions(-) diff --git a/docs/source/trade_sys/system.rst b/docs/source/trade_sys/system.rst index d1a30686..49aba3b3 100644 --- a/docs/source/trade_sys/system.rst +++ b/docs/source/trade_sys/system.rst @@ -205,15 +205,16 @@ :param Stock stock: 交易的证券 :param Query query: K线数据查询条件 - :param bool reset: 是否同时复位所有组件,尤其是tm实例 - + :param bool reset: 执行前是否依据系统部件共享属性复位 + :param bool reset_all: 强制复位所有部件 + .. py:method:: reset(self) - 复位操作,依据各个部件的共享属性进行复位,共享的部件不进行复位。 + 复位,但不包括已有的交易对象,以及共享的部件 .. py:methon:: force_reset_all(self) - 忽略部件的共享属性,强制复位,包括所有部件。 + 强制复位所有组件以及清空已有的交易对象,忽略组件的共享属性 .. py:method:: clone(self) diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp index af6a8837..9e1a9e61 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp @@ -31,6 +31,9 @@ ConditionBase::ConditionBase(const string& name) : m_name(name) {} ConditionBase::~ConditionBase() {} void ConditionBase::reset() { + m_kdata = Null(); + m_tm.reset(); + m_sg.reset(); m_date_index.clear(); m_values.clear(); _reset(); @@ -63,7 +66,7 @@ ConditionPtr ConditionBase::clone() { } void ConditionBase::setTO(const KData& kdata) { - reset(); + HKU_IF_RETURN(kdata == m_kdata, void()); m_kdata = kdata; if (!kdata.empty()) { m_date_index.clear(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp index e42eeabc..168497b8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp @@ -44,6 +44,12 @@ void MoneyManagerBase::buyNotify(const TradeRecord&) {} void MoneyManagerBase::sellNotify(const TradeRecord&) {} +void MoneyManagerBase::reset() { + m_query = Null(); + m_tm.reset(); + _reset(); +} + MoneyManagerPtr MoneyManagerBase::clone() { MoneyManagerPtr p; try { @@ -65,14 +71,14 @@ MoneyManagerPtr MoneyManagerBase::clone() { return p; } -double MoneyManagerBase ::getSellNumber(const Datetime& datetime, const Stock& stock, price_t price, - price_t risk, SystemPart from) { +double MoneyManagerBase::getSellNumber(const Datetime& datetime, const Stock& stock, price_t price, + price_t risk, SystemPart from) { HKU_ERROR_IF_RETURN(!m_tm, 0.0, "m_tm is null! Datetime({}) Stock({}) price({:<.4f}) risk({:<.2f})", datetime, stock.market_code(), price, risk); if (PART_ENVIRONMENT == from) { - //强制全部卖出 + // 强制全部卖出 HKU_IF_RETURN(!getParam("disable_ev_force_clean_position"), MAX_DOUBLE); } @@ -80,14 +86,15 @@ double MoneyManagerBase ::getSellNumber(const Datetime& datetime, const Stock& s HKU_IF_RETURN(!getParam("disable_cn_force_clean_position"), MAX_DOUBLE); } - HKU_ERROR_IF_RETURN(risk <= 0.0, 0.0, + HKU_ERROR_IF_RETURN( + risk <= 0.0, 0.0, "risk is negative! Datetime({}) Stock({}) price({:<.3f}) risk({:<.2f}) Part({})", datetime, stock.market_code(), price, risk, getSystemPartName(from)); return _getSellNumber(datetime, stock, price, risk, from); } -double MoneyManagerBase ::getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, - price_t risk, SystemPart from) { +double MoneyManagerBase::getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, + price_t risk, SystemPart from) { HKU_ERROR_IF_RETURN(!m_tm, 0.0, "m_tm is null! Datetime({}) Stock({}) price({:<.3f}) risk({:<.2f})", datetime, stock.market_code(), price, risk); @@ -109,7 +116,7 @@ double MoneyManagerBase ::getBuyNumber(const Datetime& datetime, const Stock& st return 0; } - //转换为最小交易量的整数倍 + // 转换为最小交易量的整数倍 n = long(n / min_trade) * min_trade; double max_trade = stock.maxTradeNumber(); @@ -118,7 +125,7 @@ double MoneyManagerBase ::getBuyNumber(const Datetime& datetime, const Stock& st HKU_INFO("Over stock.maxTradeNumber({})!", max_trade); } - //在现金不足时,自动补充存入现金 + // 在现金不足时,自动补充存入现金 if (getParam("auto-checkin")) { price_t cash = m_tm->cash(datetime, m_query.kType()); CostRecord cost = m_tm->getBuyCost(datetime, stock, price, n); @@ -142,8 +149,8 @@ double MoneyManagerBase ::getBuyNumber(const Datetime& datetime, const Stock& st return n; } -double MoneyManagerBase ::getSellShortNumber(const Datetime& datetime, const Stock& stock, - price_t price, price_t risk, SystemPart from) { +double MoneyManagerBase::getSellShortNumber(const Datetime& datetime, const Stock& stock, + price_t price, price_t risk, SystemPart from) { HKU_ERROR_IF_RETURN(!m_tm, 0.0, "m_tm is null! Datetime({}) Stock({}) price({:<.3f}) risk({:<.2f})", datetime, stock.market_code(), price, risk); @@ -166,7 +173,7 @@ double MoneyManagerBase ::getBuyShortNumber(const Datetime& datetime, const Stoc double MoneyManagerBase::_getSellNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { - //默认卖出全部 + // 默认卖出全部 return MAX_DOUBLE; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h index 29349709..e9744e29 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h @@ -38,9 +38,7 @@ public: } /** 复位 */ - void reset() { - _reset(); - } + void reset(); /** * 设定交易账户 diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp index 47558be1..c6093724 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp @@ -29,6 +29,12 @@ ProfitGoalBase::ProfitGoalBase(const string& name) : m_name(name) {} ProfitGoalBase::~ProfitGoalBase() {} +void ProfitGoalBase::reset() { + m_kdata = Null(); + m_tm.reset(); + _reset(); +} + ProfitGoalPtr ProfitGoalBase::clone() { ProfitGoalPtr p; try { @@ -51,7 +57,7 @@ ProfitGoalPtr ProfitGoalBase::clone() { } void ProfitGoalBase::setTO(const KData& kdata) { - reset(); + HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; if (!kdata.empty()) { _calculate(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h index 63eb459b..90d22986 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h @@ -175,10 +175,6 @@ inline void ProfitGoalBase::name(const string& name) { m_name = name; } -inline void ProfitGoalBase::reset() { - _reset(); -} - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 38f14187..b9ca4d06 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -61,7 +61,7 @@ SignalPtr SignalBase::clone() { } void SignalBase::setTO(const KData& kdata) { - reset(); + HKU_IF_RETURN(m_kdata == kdata, void(), "No need to calculate."); m_kdata = kdata; if (!kdata.empty()) { _calculate(); @@ -69,6 +69,7 @@ void SignalBase::setTO(const KData& kdata) { } void SignalBase::reset() { + m_kdata = Null(); m_buySig.clear(); m_sellSig.clear(); m_hold_long = false; diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp index 86424ad7..e392fc8f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp @@ -27,6 +27,11 @@ SlippageBase::SlippageBase() : m_name("SlippageBase") {} SlippageBase::SlippageBase(const string& name) : m_name(name) {} +void SlippageBase::reset() { + m_kdata = Null(); + _reset(); +} + SlippagePtr SlippageBase::clone() { SlippagePtr p; try { @@ -48,7 +53,7 @@ SlippagePtr SlippageBase::clone() { } void SlippageBase::setTO(const KData& kdata) { - reset(); + HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; if (!kdata.empty()) { _calculate(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h index ed9accad..a4933f30 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h @@ -156,10 +156,6 @@ inline KData SlippageBase::getTO() const { return m_kdata; } -inline void SlippageBase::reset() { - _reset(); -} - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp index e840c537..bf346491 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp @@ -29,6 +29,12 @@ StoplossBase::StoplossBase(const string& name) : m_name(name) {} StoplossBase::~StoplossBase() {} +void StoplossBase::reset() { + m_kdata = Null(); + m_tm.reset(); + _reset(); +} + StoplossPtr StoplossBase::clone() { StoplossPtr p; try { @@ -50,7 +56,7 @@ StoplossPtr StoplossBase::clone() { } void StoplossBase::setTO(const KData& kdata) { - reset(); + HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; if (!kdata.empty()) { _calculate(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h index d22adfa1..ba57e9eb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h @@ -185,10 +185,6 @@ inline KData StoplossBase::getTO() const { return m_kdata; } -inline void StoplossBase::reset() { - _reset(); -} - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 962f5066..4210b243 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -33,6 +33,7 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SystemPtr& sys) { System::System() : m_name("SYS_Simple"), + m_part_changed(true), m_pre_ev_valid(true), // must true m_pre_cn_valid(true), // must true m_buy_days(0), @@ -44,6 +45,7 @@ System::System() System::System(const string& name) : m_name(name), + m_part_changed(true), m_pre_ev_valid(true), m_pre_cn_valid(true), m_buy_days(0), @@ -67,6 +69,7 @@ System::System(const TradeManagerPtr& tm, const MoneyManagerPtr& mm, const Envir m_pg(pg), m_sp(sp), m_name(name), + m_part_changed(true), m_pre_ev_valid(true), m_pre_cn_valid(true), m_buy_days(0), @@ -145,6 +148,7 @@ void System::reset() { // 一个sys实例绑定stock后,除非主动改变,否则不应该被reset // m_stock + m_part_changed = true; m_pre_ev_valid = false; // true; m_pre_cn_valid = false; // true; @@ -180,6 +184,12 @@ void System::forceResetAll() { if (m_sp) m_sp->reset(); + // 清理交易对象 + m_stock = Null(); + m_src_kdata = Null(); + m_kdata = Null(); + + m_part_changed = true; m_pre_ev_valid = false; // true; m_pre_cn_valid = false; // true; @@ -196,6 +206,7 @@ void System::forceResetAll() { } void System::setTO(const KData& kdata) { + HKU_TRACE_IF_RETURN(!m_part_changed && m_kdata == kdata, void(), "No need to calcule!"); m_kdata = kdata; m_stock = kdata.getStock(); @@ -259,6 +270,7 @@ SystemPtr System::clone() { p->m_kdata = m_kdata; p->m_src_kdata = m_src_kdata; + p->m_part_changed = m_part_changed; p->m_pre_ev_valid = m_pre_ev_valid; p->m_pre_cn_valid = m_pre_cn_valid; @@ -321,19 +333,21 @@ bool System::readyForRun() { return true; } -void System::run(const KQuery& query, bool reset) { +void System::run(const KQuery& query, bool reset, bool resetAll) { HKU_ERROR_IF_RETURN(m_stock.isNull(), void(), "m_stock is NULL!"); // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 - // System::run 供单体系统进行回测,需要强制复位所有的组件,忽略组件的共享属性 - if (reset) + if (resetAll) { this->forceResetAll(); + } else if (reset) { + this->reset(); + } HKU_IF_RETURN(!readyForRun(), void()); - // m_stock = stock; 在setTO里赋值 KData kdata = m_stock.getKData(query); HKU_IF_RETURN(kdata.empty(), void()); + HKU_DEBUG_IF_RETURN(!m_part_changed && m_kdata == kdata, void(), "Not need calculate."); setTO(kdata); size_t total = kdata.size(); @@ -344,20 +358,24 @@ void System::run(const KQuery& query, bool reset) { _runMoment(ks[i], src_ks[i]); } } + m_part_changed = false; } -void System::run(const Stock& stock, const KQuery& query, bool reset) { +void System::run(const Stock& stock, const KQuery& query, bool reset, bool resetAll) { m_stock = stock; - run(query, reset); + run(query, reset, resetAll); } -void System::run(const KData& kdata, bool reset) { +void System::run(const KData& kdata, bool reset, bool resetAll) { HKU_INFO_IF_RETURN(kdata.empty(), void(), "Input kdata is empty!"); + HKU_DEBUG_IF_RETURN(!m_part_changed && m_kdata == kdata, void(), "Not need calculate."); // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 - if (reset) { - // System::run 供单体系统进行回测,需要强制复位所有的组件,忽略组件的共享属性 + if (resetAll) { this->forceResetAll(); + } else if (reset) { + // System::run 供单体系统进行回测,需要强制复位所有的组件,忽略组件的共享属性 + this->reset(); } HKU_IF_RETURN(!readyForRun(), void()); @@ -371,6 +389,7 @@ void System::run(const KData& kdata, bool reset) { _runMoment(ks[i], src_ks[i]); } } + m_part_changed = false; } void System::clearDelayRequest() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index 420c90a5..13ff98ec 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -145,12 +145,12 @@ public: const TradeRequest& getBuyShortTradeRequest() const; /** - * 复位 + * 复位,但不包括已有的交易对象,以及共享的部件 * @note 实际复位操作依赖于系统中各个部件的共享参数 */ void reset(); - /** 强制复位所有组件,忽略组件的共享属性 */ + /** 强制复位所有组件以及清空已有的交易对象,忽略组件的共享属性 */ void forceResetAll(); typedef shared_ptr SystemPtr; @@ -169,24 +169,27 @@ public: /** * @brief 不指定stock的方式下run,需要事先通过setStock设定stock * @param query 查询条件 - * @param reset 执行前是否先复位 + * @param reset 执行前是否依据系统部件共享属性复位 + * @param resetAll 强制复位所有部件 */ - void run(const KQuery& query, bool reset = true); + void run(const KQuery& query, bool reset = true, bool resetAll = false); /** * @brief 运行系统策略 * @param stock 指定的证券 * @param query 指定查询条件 - * @param reset 执行前是否复位 + * @param reset 执行前是否依据系统部件共享属性复位 + * @param resetAll 强制复位所有部件 */ - void run(const Stock& stock, const KQuery& query, bool reset = true); + void run(const Stock& stock, const KQuery& query, bool reset = true, bool resetAll = false); /** * @brief 运行系统 * @param kdata 指定的交易对象 - * @param reset 执行前是否复位 + * @param reset 执行前是否依据系统部件共享属性复位 + * @param resetAll 强制复位所有部件 */ - void run(const KData& kdata, bool reset = true); + void run(const KData& kdata, bool reset = true, bool resetAll = false); /** * @brief 在指定的日期执行一步,仅由 PF 调用 @@ -277,6 +280,7 @@ protected: KData m_kdata; KData m_src_kdata; // 未复权的原始 K 线数据 + bool m_part_changed; // 记录部件是否发生变化,控制是否需要重新计算 bool m_pre_ev_valid; bool m_pre_cn_valid; @@ -318,6 +322,7 @@ private: // m_kdata中包含了stock和query的信息,不用保存m_stock ar& BOOST_SERIALIZATION_NVP(m_kdata); + ar& BOOST_SERIALIZATION_NVP(m_part_changed); ar& BOOST_SERIALIZATION_NVP(m_pre_ev_valid); ar& BOOST_SERIALIZATION_NVP(m_pre_cn_valid); @@ -352,6 +357,7 @@ private: ar& BOOST_SERIALIZATION_NVP(m_kdata); m_stock = m_kdata.getStock(); + ar& BOOST_SERIALIZATION_NVP(m_part_changed); ar& BOOST_SERIALIZATION_NVP(m_pre_ev_valid); ar& BOOST_SERIALIZATION_NVP(m_pre_cn_valid); @@ -431,39 +437,66 @@ inline SlippagePtr System::getSP() const { } inline void System::setTM(const TradeManagerPtr& tm) { - m_tm = tm; + if (m_tm != tm) { + m_tm = tm; + m_part_changed = true; + } } inline void System::setMM(const MoneyManagerPtr& mm) { - m_mm = mm; + if (m_mm != mm) { + m_mm = mm; + m_part_changed = true; + } } inline void System::setEV(const EnvironmentPtr& ev) { - m_ev = ev; + if (m_ev != ev) { + m_ev = ev; + m_part_changed = true; + } } inline void System::setCN(const ConditionPtr& cn) { - m_cn = cn; + if (m_cn != cn) { + m_cn = cn; + m_part_changed = true; + } } inline void System::setSG(const SignalPtr& sg) { - m_sg = sg; + if (m_sg != sg) { + m_sg = sg; + m_part_changed = true; + } } inline void System::setST(const StoplossPtr& st) { - m_st = st; + if (m_st != st) { + m_st = st; + m_part_changed = true; + } } inline void System::setTP(const StoplossPtr& tp) { - m_tp = tp; + if (m_tp != tp) { + m_tp = tp; + m_part_changed = true; + } } inline void System::setPG(const ProfitGoalPtr& pg) { - m_pg = pg; + if (m_pg != pg) { + m_pg = pg; + m_part_changed = true; + } } inline void System::setSP(const SlippagePtr& sp) { - m_sp = sp; + if (m_sp != sp) { + m_sp = sp; + m_part_changed = true; + } } inline Stock System::getStock() const { diff --git a/hikyuu_pywrap/trade_sys/_System.cpp b/hikyuu_pywrap/trade_sys/_System.cpp index a23145a0..7a5f17d5 100644 --- a/hikyuu_pywrap/trade_sys/_System.cpp +++ b/hikyuu_pywrap/trade_sys/_System.cpp @@ -15,9 +15,9 @@ using namespace hku; #pragma warning(disable : 4267) #endif -void (System::*run_1)(const KQuery&, bool) = &System::run; -void (System::*run_2)(const KData&, bool) = &System::run; -void (System::*run_3)(const Stock&, const KQuery&, bool reset) = &System::run; +void (System::*run_1)(const KQuery&, bool, bool) = &System::run; +void (System::*run_2)(const KData&, bool, bool) = &System::run; +void (System::*run_3)(const Stock&, const KQuery&, bool, bool) = &System::run; TradeRecord (System::*runMoment_1)(const Datetime&) = &System::runMoment; @@ -158,28 +158,30 @@ void export_System(py::module& m) { .def("reset", &System::reset, R"(reset(self) - 依据各个部件的共享属性进行复位操作。)") + 复位,但不包括已有的交易对象,以及共享的部件。)") .def("force_reset_all", &System::forceResetAll, R"(force_reset_all(self) - 忽略各个部件的共享属性,强制复位所有部件。)") + 强制复位所有组件以及清空已有的交易对象,忽略组件的共享属性。)") .def("clone", &System::clone, R"(clone(self) 克隆操作,会依据部件的共享特性进行克隆,共享部件不进行实际的克隆操作,保持共享。)") - .def("run", run_1, py::arg("query"), py::arg("reset") = true) - .def("run", run_2, py::arg("kdata"), py::arg("reset") = true) + .def("run", run_1, py::arg("query"), py::arg("reset") = true, py::arg("reset_all") = false) + .def("run", run_2, py::arg("kdata"), py::arg("reset") = true, py::arg("reset_all") = false) .def("run", run_3, py::arg("stock"), py::arg("query"), py::arg("reset") = true, + py::arg("reset_all") = false, R"(run(self, stock, query[, reset=True]) 运行系统,执行回测 :param Stock stock: 交易的证券 :param Query query: K线数据查询条件 - :param bool reset: 是否同时复位所有组件,尤其是tm实例)") + :param bool reset: 执行前是否依据系统部件共享属性复位 + :param bool reset_all: 强制复位所有部件)") .def("ready", &System::readyForRun) From 4711e25a760123e9a2e24fb31ecd50a93e4a17b6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 12 Mar 2024 01:55:48 +0800 Subject: [PATCH 022/601] fixed compile errors on ubuntu --- hikyuu_cpp/hikyuu/analysis/analysis_sys.h | 2 +- hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h index 9b32cdfe..b6f25779 100644 --- a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h +++ b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h @@ -46,7 +46,7 @@ inline vector analysisSystemListWith(const Container vector result; HKU_IF_RETURN(blk.size() == 0 || !sys_proto, result); - sys_proto->reset(true, true); + sys_proto->reset(); SystemList sys_list; StockList stk_list; for (const auto& stk : blk) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index b9ca4d06..35b0742f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -61,7 +61,7 @@ SignalPtr SignalBase::clone() { } void SignalBase::setTO(const KData& kdata) { - HKU_IF_RETURN(m_kdata == kdata, void(), "No need to calculate."); + HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; if (!kdata.empty()) { _calculate(); From e92ed832d2dde78e3a2feb752ce7324c5d1322ea Mon Sep 17 00:00:00 2001 From: Jet <344148042@qq.com> Date: Tue, 12 Mar 2024 14:05:36 +0800 Subject: [PATCH 023/601] =?UTF-8?q?fix=20bug:=20=E9=80=9A=E8=BE=BE?= =?UTF-8?q?=E4=BF=A1lc5=E5=AF=BC=E5=85=A5mysql=E6=97=B6=E5=80=99=E6=96=87?= =?UTF-8?q?=E4=BB=B6=E6=95=B0=E6=8D=AE=E8=A7=A3=E6=9E=90=E7=9A=84=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E8=AE=A1=E7=AE=97=E9=94=99=E8=AF=AF=E5=AF=BC=E8=87=B4?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E4=B8=8D=E4=BA=86=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/tdx_to_mysql.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu/data/tdx_to_mysql.py b/hikyuu/data/tdx_to_mysql.py index 0735e230..e857c996 100644 --- a/hikyuu/data/tdx_to_mysql.py +++ b/hikyuu/data/tdx_to_mysql.py @@ -258,7 +258,7 @@ def tdx_import_min_data_from_file(connect, filename, ktype, market, stock_record def get_date(pos): src_file.seek(pos * 32, SEEK_SET) data = src_file.read(4) - a = struct.unpack('hh', data) + a = struct.unpack('HH', data) return trans_date(a[0], a[1]) def find_pos(): @@ -297,7 +297,7 @@ def tdx_import_min_data_from_file(connect, filename, ktype, market, stock_record data = src_file.read(32) while data: - record = struct.unpack('hhfffffii', data) + record = struct.unpack('HHfffffii', data) if record[3] >= record[2] >= record[4] > 0\ and record[3] >= record[5] >= record[4] >0\ and record[5] >=0 \ From 824ab038d1f08a73a69637b703b4c07d6f0a90ee Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 12 Mar 2024 14:23:37 +0800 Subject: [PATCH 024/601] add SingleFactor --- hikyuu_cpp/hikyuu/factor/SingleFactor.cpp | 86 +++++++++++++++++++ hikyuu_cpp/hikyuu/factor/SingleFactor.h | 48 +++++++++++ .../trade_sys/signal/imp/SingleFactorSignal.h | 12 +++ 3 files changed, 146 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/factor/SingleFactor.cpp create mode 100644 hikyuu_cpp/hikyuu/factor/SingleFactor.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h diff --git a/hikyuu_cpp/hikyuu/factor/SingleFactor.cpp b/hikyuu_cpp/hikyuu/factor/SingleFactor.cpp new file mode 100644 index 00000000..fd6f2abc --- /dev/null +++ b/hikyuu_cpp/hikyuu/factor/SingleFactor.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "hikyuu/indicator/crt/ROCP.h" +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/IC.h" +#include "hikyuu/indicator/crt/ICIR.h" +#include "SingleFactor.h" + +namespace hku { + +SingleFactor::SingleFactor() { + setParam("fill_null", true); +} + +SingleFactor::SingleFactor(const Indicator& ind, const StockList& stks, const KQuery& query, + const Stock& ref_stk) +: m_ind(ind), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { + setParam("fill_null", true); +} + +const Indicator& SingleFactor::get(const Stock& stk) const { + const auto iter = m_stk_map.find(stk); + HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); + return m_all_inds[iter->second]; +} + +const vector>& SingleFactor::get(const Datetime& d) const { + const auto iter = m_date_index.find(d); + HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); + return m_stk_factor_by_date[iter->second]; +} + +Indicator SingleFactor::getIC(int ndays) const { + return IC(m_ind, m_stks, m_query, ndays, m_ref_stk); +} + +Indicator SingleFactor::getICIR(int ic_n, int ir_n) const { + return ICIR(IC(m_ind, m_stks, m_query, ic_n, m_ref_stk), ir_n); +} + +void SingleFactor::calculate() { + HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); + auto ref_dates = m_ref_stk.getDatetimeList(m_query); + size_t days_total = ref_dates.size(); + + HKU_WARN_IF_RETURN( + days_total < 2 || m_stks.size() < 2, void(), + "The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, " + "current stock number: {}", + days_total, m_stks.size()); + + bool fill_null = getParam("fill_null"); + + size_t stk_count = m_stks.size(); + m_all_inds.reserve(stk_count); + m_stk_map.reserve(stk_count); + for (size_t i = 0; i < stk_count; i++) { + const auto& stk = m_stks[i]; + HKU_WARN_IF(stk.isNull(), "Exist null stock!"); + auto k = stk.getKData(m_query); + m_stk_map[stk] = i; + m_all_inds.emplace_back(ALIGN(m_ind(k), ref_dates, fill_null)); + } + + m_stk_factor_by_date.resize(days_total); + vector> one_day(stk_count); + for (size_t i = 0; i < days_total; i++) { + for (size_t j = 0; j < stk_count; j++) { + one_day[i] = std::make_pair(m_stks[j], m_all_inds[j][i]); + } + std::sort(one_day.begin(), one_day.end(), + [](const std::pair& a, const std::pair& b) { + return a.second > b.second; + }); + m_stk_factor_by_date[i] = one_day; + m_date_index[ref_dates[i]] = i; + } +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/factor/SingleFactor.h b/hikyuu_cpp/hikyuu/factor/SingleFactor.h new file mode 100644 index 00000000..1b31a2c1 --- /dev/null +++ b/hikyuu_cpp/hikyuu/factor/SingleFactor.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/KData.h" +#include "hikyuu/indicator/Indicator.h" +#include "hikyuu/utilities/Parameter.h" + +namespace hku { + +class SingleFactor { + PARAMETER_SUPPORT + +public: + typedef Indicator::value_t value_t; + +public: + SingleFactor(); + SingleFactor(const Indicator& ind, const StockList& stks, const KQuery& query, + const Stock& ref_stk); + + void calculate(); + + const Indicator& get(const Stock&) const; + + const vector>& get(const Datetime&) const; + + Indicator getIC(int ndays) const; + Indicator getICIR(int ic_n, int ir_n) const; + +private: + Indicator m_ind; + StockList m_stks; + Stock m_ref_stk; + KQuery m_query; + unordered_map m_stk_map; + vector m_all_inds; + + unordered_map m_date_index; + vector>> m_stk_factor_by_date; +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h new file mode 100644 index 00000000..56669f67 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#pragma once + +#include "../SignalBase.h" + +namespace hku {} \ No newline at end of file From c2bec899c77c9dd9380b6570cdfce5f46e3b1d68 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 12 Mar 2024 18:40:29 +0800 Subject: [PATCH 025/601] add Multifactor --- hikyuu_cpp/hikyuu/factor/MultiFactor.cpp | 262 +++++++++++++++++++++++ hikyuu_cpp/hikyuu/factor/MultiFactor.h | 60 ++++++ 2 files changed, 322 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/factor/MultiFactor.cpp create mode 100644 hikyuu_cpp/hikyuu/factor/MultiFactor.h diff --git a/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp b/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp new file mode 100644 index 00000000..ef59262d --- /dev/null +++ b/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp @@ -0,0 +1,262 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "hikyuu/indicator/crt/ROCP.h" +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/IC.h" +#include "hikyuu/indicator/crt/ICIR.h" +#include "hikyuu/indicator/crt/SPEARMAN.h" +#include "MultiFactor.h" + +namespace hku { + +MultiFactor::MultiFactor() { + setParam("fill_null", true); +} + +MultiFactor::MultiFactor(const vector& inds, const StockList& stks, const KQuery& query, + const Stock& ref_stk) +: m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { + setParam("fill_null", true); + setParam("ic_n", 1); + setParam("ir_n", 10); +} + +const Indicator& MultiFactor::get(const Stock& stk) const { + const auto iter = m_stk_map.find(stk); + HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); + return m_all_factors[iter->second]; +} + +const vector>& MultiFactor::get(const Datetime& d) const { + const auto iter = m_date_index.find(d); + HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); + return m_stk_factor_by_date[iter->second]; +} + +vector MultiFactor::_getAllReturns(int ndays) const { + bool fill_null = getParam("fill_null"); + vector all_returns; + all_returns.reserve(m_stks.size()); + for (const auto& stk : m_stks) { + auto k = stk.getKData(m_query); + all_returns.push_back(ALIGN(ROCP(k.close(), ndays), m_ref_dates, fill_null)); + } + return all_returns; +} + +Indicator MultiFactor::getIC(int ndays) const { + size_t days_total = m_ref_dates.size(); + + bool fill_null = getParam("fill_null"); + + vector all_returns = _getAllReturns(ndays); + + size_t ind_count = m_all_factors.size(); + PriceList tmp(ind_count, Null()); + PriceList tmp_return(ind_count, Null()); + Indicator result = PRICELIST(PriceList(days_total, 0.0)); + auto* dst = result.data(); + for (size_t i = 0; i < days_total; i++) { + for (size_t j = 0; j < ind_count; j++) { + tmp[j] = m_all_factors[j][i]; + tmp_return[j] = all_returns[j][i]; + } + auto a = PRICELIST(tmp); + auto b = PRICELIST(tmp_return); + auto ic = hku::SPEARMAN(a, b, ind_count); + dst[i] = ic[ic.size() - 1]; + } + return result; +} + +Indicator MultiFactor::getICIR(int ic_n, int ir_n) const { + return ICIR(getIC(ic_n), ir_n); +} + +void MultiFactor::_calculateAllFactorsByEqualWeight() { + size_t days_total = m_ref_dates.size(); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + m_all_factors.resize(stk_count); + for (size_t si = 0; si < stk_count; si++) { + vector sumByDate(days_total, 0.0); + vector countByDate(days_total, 0); + + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = m_all_stk_inds[si][ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + // 均值权重 + for (size_t di = 0; di < days_total; di++) { + if (countByDate[di] == 0) { + sumByDate[di] = Null(); + } else { + sumByDate[di] = sumByDate[di] / countByDate[di]; + } + } + + m_all_factors[si] = PRICELIST(sumByDate); + } +} + +void MultiFactor::_calculateAllFactorsByIC() { + size_t days_total = m_ref_dates.size(); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + bool fill_null = getParam("fill_null"); + int ic_n = getParam("ic_n"); + + m_all_factors.resize(stk_count); + + vector ic(ind_count); + for (size_t ii = 0; ii < ind_count; ii++) { + ic[ii] = IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk); + } + + // 计算 IC 权重 + vector sumByDate(days_total, 0.0); + vector countByDate(days_total, 0); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = ic[ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + for (size_t di = 0; di < days_total; di++) { + if (countByDate[di] == 0) { + sumByDate[di] = Null(); + } else { + sumByDate[di] = sumByDate[di] / countByDate[di]; + } + } + + for (size_t si = 0; si < stk_count; si++) { + PriceList new_values(days_total); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = m_all_stk_inds[si][ii][di]; + new_values[di] = value * sumByDate[di]; + } + } + m_all_factors[si] = PRICELIST(new_values); + } +} + +void MultiFactor::_calculateAllFactorsByICIR() { + size_t days_total = m_ref_dates.size(); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + bool fill_null = getParam("fill_null"); + int ic_n = getParam("ic_n"); + int ir_n = getParam("ir_n"); + + m_all_factors.resize(stk_count); + + vector 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); + } + + // 计算 IC 权重 + vector sumByDate(days_total, 0.0); + vector countByDate(days_total, 0); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = icir[ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + for (size_t di = 0; di < days_total; di++) { + if (countByDate[di] == 0) { + sumByDate[di] = Null(); + } else { + sumByDate[di] = sumByDate[di] / countByDate[di]; + } + } + + for (size_t si = 0; si < stk_count; si++) { + PriceList new_values(days_total); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = m_all_stk_inds[si][ii][di]; + new_values[di] = value * sumByDate[di]; + } + } + m_all_factors[si] = PRICELIST(new_values); + } +} + +void MultiFactor::_alignAllInds() { + size_t days_total = m_ref_dates.size(); + bool fill_null = getParam("fill_null"); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + m_all_stk_inds.resize(stk_count); + for (size_t i = 0; i < stk_count; i++) { + const auto& stk = m_stks[i]; + auto kdata = stk.getKData(m_query); + m_all_stk_inds[i].resize(ind_count); + for (size_t j = 0; j < ind_count; j++) { + m_all_stk_inds[i][j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); + } + } +} + +void MultiFactor::calculate() { + HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); + auto m_ref_dates = m_ref_stk.getDatetimeList(m_query); + size_t days_total = m_ref_dates.size(); + + HKU_WARN_IF_RETURN( + days_total < 2 || m_stks.size() < 2, void(), + "The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, " + "current stock number: {}", + days_total, m_stks.size()); + + bool fill_null = getParam("fill_null"); + + size_t stk_count = m_stks.size(); + + _alignAllInds(); + _calculateAllFactorsByEqualWeight(); + + m_stk_factor_by_date.resize(days_total); + vector> one_day(stk_count); + for (size_t i = 0; i < days_total; i++) { + for (size_t j = 0; j < stk_count; j++) { + one_day[i] = std::make_pair(m_stks[j], m_all_factors[j][i]); + } + std::sort(one_day.begin(), one_day.end(), + [](const std::pair& a, const std::pair& b) { + return a.second > b.second; + }); + m_stk_factor_by_date[i] = one_day; + m_date_index[m_ref_dates[i]] = i; + } +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/factor/MultiFactor.h b/hikyuu_cpp/hikyuu/factor/MultiFactor.h new file mode 100644 index 00000000..9b93cee8 --- /dev/null +++ b/hikyuu_cpp/hikyuu/factor/MultiFactor.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/KData.h" +#include "hikyuu/indicator/Indicator.h" +#include "hikyuu/utilities/Parameter.h" + +namespace hku { + +class MultiFactor { + PARAMETER_SUPPORT + +public: + typedef Indicator::value_t value_t; + +public: + MultiFactor(); + MultiFactor(const vector& inds, const StockList& stks, const KQuery& query, + const Stock& ref_stk); + + void calculate(); + + /** 获取指定证券合成因子 */ + const Indicator& get(const Stock&) const; + + /** 获取指定日期截面的所有因子值 */ + const vector>& get(const Datetime&) const; + + Indicator getIC(int ndays) const; + Indicator getICIR(int ic_n, int ir_n) const; + +private: + void _alignAllInds(); + vector _getAllReturns(int ndays) const; + void _calculateAllFactorsByEqualWeight(); + void _calculateAllFactorsByIC(); + void _calculateAllFactorsByICIR(); + +private: + vector m_inds; + StockList m_stks; + Stock m_ref_stk; + KQuery m_query; + unordered_map m_stk_map; + vector m_all_factors; + + unordered_map m_date_index; + vector>> m_stk_factor_by_date; + + vector> m_all_stk_inds; // stock * inds + DatetimeList m_ref_dates; +}; + +} // namespace hku \ No newline at end of file From 929a71ed88731216babbce86f2da9c134af2f97b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 13 Mar 2024 02:06:08 +0800 Subject: [PATCH 026/601] =?UTF-8?q?fixed=20IC=20=E5=9B=A0=E5=AD=90IC?= =?UTF-8?q?=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 78 +++++++++---------- .../unit_test/hikyuu/indicator/test_IC.cpp | 12 +-- 2 files changed, 45 insertions(+), 45 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index e74b3af2..5ef3d891 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -7,6 +7,7 @@ #include "hikyuu/Block.h" #include "hikyuu/indicator/crt/ALIGN.h" +#include "hikyuu/indicator/crt/REF.h" #include "hikyuu/indicator/crt/ROCP.h" #include "hikyuu/indicator/crt/PRICELIST.h" #include "hikyuu/indicator/crt/SPEARMAN.h" @@ -18,13 +19,14 @@ BOOST_CLASS_EXPORT(hku::IIc) namespace hku { -IIc::IIc() : IndicatorImp("IC") { - setParam("n", 1); +IIc::IIc() : IndicatorImp("IC", 1) { + setParam("n", 1); // 调仓周期 + // 对齐时是否以 nan 值进行填充,否则以小于当前日期的最后值作为填充 setParam("fill_null", true); } IIc::IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk) -: IndicatorImp("IC"), m_query(query), m_ref_stk(ref_stk), m_stks(stks) { +: IndicatorImp("IC", 1), m_query(query), m_ref_stk(ref_stk), m_stks(stks) { setParam("n", n); setParam("fill_null", true); } @@ -44,58 +46,56 @@ IndicatorImpPtr IIc::_clone() { } void IIc::_calculate(const Indicator& inputInd) { - HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); + // 先申请内存,保持和参考日期等长 auto ref_dates = m_ref_stk.getDatetimeList(m_query); - size_t total = ref_dates.size(); - _readyBuffer(total, 1); + size_t days_total = ref_dates.size(); + _readyBuffer(days_total, 1); - if (total < 2 || m_stks.size() < 2) { - HKU_WARN( - "The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, " - "current stock number: {}", - total, m_stks.size()); - m_discard = total; + // 检测异常输入数据 + m_discard = days_total; + HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); + HKU_ERROR_IF_RETURN(days_total < 2, void(), + "The data length(>=2) is insufficient! current data len: {}", days_total); + + size_t stk_count = m_stks.size(); + HKU_ERROR_IF_RETURN(stk_count < 2, void(), + "The number(>=2) of stock is insufficient! current stock number: {}", + stk_count); + for (size_t i = 0; i < stk_count; i++) { + HKU_ERROR_IF_RETURN(m_stks[i].isNull(), void(), "The [{}] stock is null!", i); } int n = getParam("n"); + // 由于 ic 是当前收盘价和 n 日后收益的相关系数,需要避免未来函数,最终的 ic 需要右移 n 日 + // spearman 本身需要数据长度大于等于2,所以 n + 1 >= days_totals 时,直接抛弃 + HKU_IF_RETURN(n + 1 >= days_total, void()); + bool fill_null = getParam("fill_null"); - vector all_inds; - all_inds.reserve(m_stks.size()); - vector all_returns; - all_returns.reserve(m_stks.size()); + // 计算每支证券对齐后的因子值与 n 日收益率 + vector all_inds(stk_count); // 保存每支证券对齐后的因子值 + vector all_returns(stk_count); // 保存每支证券对齐后的 n 日收益率 Indicator ind = inputInd; - for (const auto& stk : m_stks) { - if (stk.isNull()) { - HKU_WARN("Exist null stock, it was ignored!"); - } else { - auto k = stk.getKData(m_query); - all_inds.push_back(ALIGN(ind(k), ref_dates, fill_null)); - all_returns.push_back(ALIGN(ROCP(k.close(), n), ref_dates, fill_null)); - } + 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); + // 计算 n 日收益率,同时需要右移 n 位,即第 i 日的因子值和第 i + n 的收益率对应 + all_returns[i] = ALIGN(REF(ROCP(k.close(), n), n), ref_dates, fill_null); } - size_t ind_count = all_inds.size(); - if (all_inds.size() < 2) { - HKU_WARN("The number of exist stock is insufficient!"); - m_discard = total; - return; - } - - PriceList tmp(ind_count, Null()); - PriceList tmp_return(ind_count, Null()); + m_discard = n; + PriceList tmp(stk_count, Null()); + PriceList tmp_return(stk_count, Null()); auto* dst = this->data(); - for (size_t i = 0; i < total; i++) { - for (size_t j = 0; j < ind_count; j++) { - if (i >= all_inds[j].size() || i >= all_returns[j].size()) { - continue; - } + for (size_t i = m_discard; i < days_total; i++) { + // 计算日截面 spearman 相关系数即 ic 值 + for (size_t j = 0; j < stk_count; j++) { tmp[j] = all_inds[j][i]; tmp_return[j] = all_returns[j][i]; } auto a = PRICELIST(tmp); auto b = PRICELIST(tmp_return); - auto ic = hku::SPEARMAN(a, b, ind_count); + auto ic = hku::SPEARMAN(a, b, stk_count); dst[i] = ic[ic.size() - 1]; } } diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index ac46ad1a..d3b42881 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -75,19 +75,19 @@ TEST_CASE("test_IC") { CHECK_EQ(result.name(), "IC"); CHECK_UNARY(!result.empty()); CHECK_EQ(result.size(), 2); - CHECK_EQ(result.discard(), 0); + CHECK_EQ(result.discard(), result.size()); CHECK_UNARY(std::isnan(result[0])); - CHECK_EQ(result[1], doctest::Approx(-1.0)); + CHECK_UNARY(std::isnan(result[1])); /** @arg 正常执行 */ result = IC(MA(CLOSE()), stks, query, 1, ref_stk); CHECK_EQ(result.name(), "IC"); CHECK_UNARY(!result.empty()); CHECK_EQ(result.size(), ref_k.size()); - CHECK_EQ(result.discard(), 0); - CHECK_EQ(result[0], doctest::Approx(-1.0)); - CHECK_EQ(result[1], doctest::Approx(0.8)); - CHECK_EQ(result[99], doctest::Approx(0.5)); + CHECK_EQ(result.discard(), 1); + CHECK_EQ(result[1], doctest::Approx(-1.0)); + CHECK_EQ(result[2], doctest::Approx(0.8)); + CHECK_EQ(result[99], doctest::Approx(-1)); } //----------------------------------------------------------------------------- From 980e38afff8c2f8d1c45632474ad45cbded31e68 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 13 Mar 2024 02:35:42 +0800 Subject: [PATCH 027/601] MultiFactor (continue) --- hikyuu_cpp/hikyuu/factor/MultiFactor.cpp | 69 ++++++++++++++---------- hikyuu_cpp/hikyuu/factor/MultiFactor.h | 15 +++--- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp b/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp index ef59262d..82dddcd5 100644 --- a/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp @@ -7,6 +7,7 @@ #include "hikyuu/indicator/crt/ALIGN.h" #include "hikyuu/indicator/crt/ROCP.h" +#include "hikyuu/indicator/crt/REF.h" #include "hikyuu/indicator/crt/PRICELIST.h" #include "hikyuu/indicator/crt/IC.h" #include "hikyuu/indicator/crt/ICIR.h" @@ -15,9 +16,12 @@ namespace hku { -MultiFactor::MultiFactor() { - setParam("fill_null", true); -} +// MultiFactor::MultiFactor() { +// setParam("fill_null", true); +// setParam("ic_n", 1); +// setParam("ir_n", 10); +// setParam("mode", "icir"); +// } MultiFactor::MultiFactor(const vector& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk) @@ -25,6 +29,13 @@ MultiFactor::MultiFactor(const vector& inds, const StockList& stks, c setParam("fill_null", true); setParam("ic_n", 1); setParam("ir_n", 10); + setParam("mode", "icir"); + + HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); + HKU_CHECK(!m_inds.empty(), "The reference stock must be set!"); + for (const auto& stk : m_stks) { + HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); + } } const Indicator& MultiFactor::get(const Stock& stk) const { @@ -45,7 +56,7 @@ vector MultiFactor::_getAllReturns(int ndays) const { all_returns.reserve(m_stks.size()); for (const auto& stk : m_stks) { auto k = stk.getKData(m_query); - all_returns.push_back(ALIGN(ROCP(k.close(), ndays), m_ref_dates, fill_null)); + all_returns.push_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null)); } return all_returns; } @@ -79,7 +90,7 @@ Indicator MultiFactor::getICIR(int ic_n, int ir_n) const { return ICIR(getIC(ic_n), ir_n); } -void MultiFactor::_calculateAllFactorsByEqualWeight() { +void MultiFactor::_calculateAllFactorsByEqualWeight(const vector>& all_stk_inds) { size_t days_total = m_ref_dates.size(); size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); @@ -91,7 +102,7 @@ void MultiFactor::_calculateAllFactorsByEqualWeight() { for (size_t di = 0; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = m_all_stk_inds[si][ii][di]; + auto value = all_stk_inds[si][ii][di]; if (!std::isnan(value)) { sumByDate[di] += value; countByDate[di] += 1; @@ -112,7 +123,7 @@ void MultiFactor::_calculateAllFactorsByEqualWeight() { } } -void MultiFactor::_calculateAllFactorsByIC() { +void MultiFactor::_calculateAllFactorsByIC(const vector>& all_stk_inds) { size_t days_total = m_ref_dates.size(); size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); @@ -152,7 +163,7 @@ void MultiFactor::_calculateAllFactorsByIC() { PriceList new_values(days_total); for (size_t di = 0; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = m_all_stk_inds[si][ii][di]; + auto value = all_stk_inds[si][ii][di]; new_values[di] = value * sumByDate[di]; } } @@ -160,7 +171,7 @@ void MultiFactor::_calculateAllFactorsByIC() { } } -void MultiFactor::_calculateAllFactorsByICIR() { +void MultiFactor::_calculateAllFactorsByICIR(const vector>& all_stk_inds) { size_t days_total = m_ref_dates.size(); size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); @@ -201,7 +212,7 @@ void MultiFactor::_calculateAllFactorsByICIR() { PriceList new_values(days_total); for (size_t di = 0; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = m_all_stk_inds[si][ii][di]; + auto value = all_stk_inds[si][ii][di]; new_values[di] = value * sumByDate[di]; } } @@ -209,41 +220,45 @@ void MultiFactor::_calculateAllFactorsByICIR() { } } -void MultiFactor::_alignAllInds() { +vector> MultiFactor::_alignAllInds() { size_t days_total = m_ref_dates.size(); bool fill_null = getParam("fill_null"); size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); - m_all_stk_inds.resize(stk_count); + vector> all_stk_inds; + all_stk_inds.resize(stk_count); for (size_t i = 0; i < stk_count; i++) { const auto& stk = m_stks[i]; auto kdata = stk.getKData(m_query); - m_all_stk_inds[i].resize(ind_count); + all_stk_inds[i].resize(ind_count); for (size_t j = 0; j < ind_count; j++) { - m_all_stk_inds[i][j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); + all_stk_inds[i][j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); } } + + return all_stk_inds; } void MultiFactor::calculate() { - HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); - auto m_ref_dates = m_ref_stk.getDatetimeList(m_query); - size_t days_total = m_ref_dates.size(); - - HKU_WARN_IF_RETURN( - days_total < 2 || m_stks.size() < 2, void(), - "The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, " - "current stock number: {}", - days_total, m_stks.size()); - - bool fill_null = getParam("fill_null"); + HKU_CHECK(!m_ref_stk.isNull(), "ref_stk is null!"); size_t stk_count = m_stks.size(); + HKU_CHECK(stk_count >= 2, "The number of stock is insufficient! current stock number: {}", + stk_count); - _alignAllInds(); - _calculateAllFactorsByEqualWeight(); + // 获取对齐的参考日期 + auto m_ref_dates = m_ref_stk.getDatetimeList(m_query); + size_t days_total = m_ref_dates.size(); + HKU_CHECK(days_total >= 2, "The dates len is insufficient! current len: {}", days_total); + // 获取所有证券所有对齐后的因子 + vector> all_stk_inds = _alignAllInds(); + + // 计算每支证券调整后的合成因子 + _calculateAllFactorsByEqualWeight(all_stk_inds); + + // 建立每日截面的索引,并每日降序排序 m_stk_factor_by_date.resize(days_total); vector> one_day(stk_count); for (size_t i = 0; i < days_total; i++) { diff --git a/hikyuu_cpp/hikyuu/factor/MultiFactor.h b/hikyuu_cpp/hikyuu/factor/MultiFactor.h index 9b93cee8..ae14bf36 100644 --- a/hikyuu_cpp/hikyuu/factor/MultiFactor.h +++ b/hikyuu_cpp/hikyuu/factor/MultiFactor.h @@ -20,7 +20,7 @@ public: typedef Indicator::value_t value_t; public: - MultiFactor(); + MultiFactor() = delete; MultiFactor(const vector& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk); @@ -32,17 +32,21 @@ public: /** 获取指定日期截面的所有因子值 */ const vector>& get(const Datetime&) const; + /** 获取合成因子的IC */ Indicator getIC(int ndays) const; + + /** 获取合成因子的 ICIR */ Indicator getICIR(int ic_n, int ir_n) const; private: - void _alignAllInds(); + vector> _alignAllInds(); vector _getAllReturns(int ndays) const; - void _calculateAllFactorsByEqualWeight(); - void _calculateAllFactorsByIC(); - void _calculateAllFactorsByICIR(); + void _calculateAllFactorsByEqualWeight(const vector>&); + void _calculateAllFactorsByIC(const vector>&); + void _calculateAllFactorsByICIR(const vector>&); private: + std::mutex m_mutex; vector m_inds; StockList m_stks; Stock m_ref_stk; @@ -53,7 +57,6 @@ private: unordered_map m_date_index; vector>> m_stk_factor_by_date; - vector> m_all_stk_inds; // stock * inds DatetimeList m_ref_dates; }; From a68d5548a049ea6ca3341570efd9346a2b92263e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 13 Mar 2024 02:39:32 +0800 Subject: [PATCH 028/601] update test_IC --- hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index d3b42881..a7c6b595 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -147,7 +147,11 @@ TEST_CASE("test_IC_export") { CHECK_EQ(x1.discard(), x2.discard()); CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); for (size_t i = 0; i < x1.size(); ++i) { - CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); + if (std::isnan(x1[i])) { + CHECK_UNARY(std::isnan(x2[i])); + } else { + CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); + } } } #endif /* #if HKU_SUPPORT_SERIALIZATION */ From c82d43863971da15fa7a8e7ffcb8982e778ae08c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 13 Mar 2024 16:03:55 +0800 Subject: [PATCH 029/601] MultiFactor (continue) --- hikyuu_cpp/hikyuu/doc.h | 4 + hikyuu_cpp/hikyuu/factor/MultiFactor.cpp | 277 ------------------ hikyuu_cpp/hikyuu/factor/MultiFactor.h | 63 ---- hikyuu_cpp/hikyuu/factor/SingleFactor.cpp | 86 ------ hikyuu_cpp/hikyuu/factor/SingleFactor.h | 48 --- hikyuu_cpp/hikyuu/indicator/Indicator.h | 3 + .../trade_sys/factor/MultiFactorBase.cpp | 176 +++++++++++ .../hikyuu/trade_sys/factor/MultiFactorBase.h | 149 ++++++++++ hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h | 12 + .../trade_sys/factor/crt/MF_EqualWeight.h | 8 + .../trade_sys/factor/crt/MF_ICIRWeight.h | 8 + .../hikyuu/trade_sys/factor/crt/MF_ICWeight.h | 17 ++ .../factor/imp/EqualWeightMultiFactor.cpp | 61 ++++ .../factor/imp/EqualWeightMultiFactor.h | 24 ++ .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 83 ++++++ .../trade_sys/factor/imp/ICIRMultiFactor.h | 24 ++ .../trade_sys/factor/imp/ICMultiFactor.cpp | 85 ++++++ .../trade_sys/factor/imp/ICMultiFactor.h | 24 ++ .../hikyuu/factor/test_MultiFactor.cpp | 81 +++++ hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 85 ++++++ 20 files changed, 844 insertions(+), 474 deletions(-) delete mode 100644 hikyuu_cpp/hikyuu/factor/MultiFactor.cpp delete mode 100644 hikyuu_cpp/hikyuu/factor/MultiFactor.h delete mode 100644 hikyuu_cpp/hikyuu/factor/SingleFactor.cpp delete mode 100644 hikyuu_cpp/hikyuu/factor/SingleFactor.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h create mode 100644 hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp create mode 100644 hikyuu_pywrap/trade_sys/_MultiFactor.cpp diff --git a/hikyuu_cpp/hikyuu/doc.h b/hikyuu_cpp/hikyuu/doc.h index 347cb271..e300405f 100644 --- a/hikyuu_cpp/hikyuu/doc.h +++ b/hikyuu_cpp/hikyuu/doc.h @@ -133,6 +133,10 @@ * @details 交易系统框架 * @ingroup TradeSystem * + * @defgroup MultiFactor MultiFactor 合成多因子 + * @details 合成多因子 + * @ingroup TradeSystem + * * @defgroup SystemInstance SystemInstance 系统实例 * @details 系统实例 * @ingroup Hikyuu diff --git a/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp b/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp deleted file mode 100644 index 82dddcd5..00000000 --- a/hikyuu_cpp/hikyuu/factor/MultiFactor.cpp +++ /dev/null @@ -1,277 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-12 - * Author: fasiondog - */ - -#include "hikyuu/indicator/crt/ALIGN.h" -#include "hikyuu/indicator/crt/ROCP.h" -#include "hikyuu/indicator/crt/REF.h" -#include "hikyuu/indicator/crt/PRICELIST.h" -#include "hikyuu/indicator/crt/IC.h" -#include "hikyuu/indicator/crt/ICIR.h" -#include "hikyuu/indicator/crt/SPEARMAN.h" -#include "MultiFactor.h" - -namespace hku { - -// MultiFactor::MultiFactor() { -// setParam("fill_null", true); -// setParam("ic_n", 1); -// setParam("ir_n", 10); -// setParam("mode", "icir"); -// } - -MultiFactor::MultiFactor(const vector& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk) -: m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { - setParam("fill_null", true); - setParam("ic_n", 1); - setParam("ir_n", 10); - setParam("mode", "icir"); - - HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); - HKU_CHECK(!m_inds.empty(), "The reference stock must be set!"); - for (const auto& stk : m_stks) { - HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); - } -} - -const Indicator& MultiFactor::get(const Stock& stk) const { - const auto iter = m_stk_map.find(stk); - HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); - return m_all_factors[iter->second]; -} - -const vector>& MultiFactor::get(const Datetime& d) const { - const auto iter = m_date_index.find(d); - HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); - return m_stk_factor_by_date[iter->second]; -} - -vector MultiFactor::_getAllReturns(int ndays) const { - bool fill_null = getParam("fill_null"); - vector all_returns; - all_returns.reserve(m_stks.size()); - for (const auto& stk : m_stks) { - auto k = stk.getKData(m_query); - all_returns.push_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null)); - } - return all_returns; -} - -Indicator MultiFactor::getIC(int ndays) const { - size_t days_total = m_ref_dates.size(); - - bool fill_null = getParam("fill_null"); - - vector all_returns = _getAllReturns(ndays); - - size_t ind_count = m_all_factors.size(); - PriceList tmp(ind_count, Null()); - PriceList tmp_return(ind_count, Null()); - Indicator result = PRICELIST(PriceList(days_total, 0.0)); - auto* dst = result.data(); - for (size_t i = 0; i < days_total; i++) { - for (size_t j = 0; j < ind_count; j++) { - tmp[j] = m_all_factors[j][i]; - tmp_return[j] = all_returns[j][i]; - } - auto a = PRICELIST(tmp); - auto b = PRICELIST(tmp_return); - auto ic = hku::SPEARMAN(a, b, ind_count); - dst[i] = ic[ic.size() - 1]; - } - return result; -} - -Indicator MultiFactor::getICIR(int ic_n, int ir_n) const { - return ICIR(getIC(ic_n), ir_n); -} - -void MultiFactor::_calculateAllFactorsByEqualWeight(const vector>& all_stk_inds) { - size_t days_total = m_ref_dates.size(); - size_t stk_count = m_stks.size(); - size_t ind_count = m_inds.size(); - - m_all_factors.resize(stk_count); - for (size_t si = 0; si < stk_count; si++) { - vector sumByDate(days_total, 0.0); - vector countByDate(days_total, 0); - - for (size_t di = 0; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - auto value = all_stk_inds[si][ii][di]; - if (!std::isnan(value)) { - sumByDate[di] += value; - countByDate[di] += 1; - } - } - } - - // 均值权重 - for (size_t di = 0; di < days_total; di++) { - if (countByDate[di] == 0) { - sumByDate[di] = Null(); - } else { - sumByDate[di] = sumByDate[di] / countByDate[di]; - } - } - - m_all_factors[si] = PRICELIST(sumByDate); - } -} - -void MultiFactor::_calculateAllFactorsByIC(const vector>& all_stk_inds) { - size_t days_total = m_ref_dates.size(); - size_t stk_count = m_stks.size(); - size_t ind_count = m_inds.size(); - - bool fill_null = getParam("fill_null"); - int ic_n = getParam("ic_n"); - - m_all_factors.resize(stk_count); - - vector ic(ind_count); - for (size_t ii = 0; ii < ind_count; ii++) { - ic[ii] = IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk); - } - - // 计算 IC 权重 - vector sumByDate(days_total, 0.0); - vector countByDate(days_total, 0); - for (size_t di = 0; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - auto value = ic[ii][di]; - if (!std::isnan(value)) { - sumByDate[di] += value; - countByDate[di] += 1; - } - } - } - - for (size_t di = 0; di < days_total; di++) { - if (countByDate[di] == 0) { - sumByDate[di] = Null(); - } else { - sumByDate[di] = sumByDate[di] / countByDate[di]; - } - } - - for (size_t si = 0; si < stk_count; si++) { - PriceList new_values(days_total); - for (size_t di = 0; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - auto value = all_stk_inds[si][ii][di]; - new_values[di] = value * sumByDate[di]; - } - } - m_all_factors[si] = PRICELIST(new_values); - } -} - -void MultiFactor::_calculateAllFactorsByICIR(const vector>& all_stk_inds) { - size_t days_total = m_ref_dates.size(); - size_t stk_count = m_stks.size(); - size_t ind_count = m_inds.size(); - - bool fill_null = getParam("fill_null"); - int ic_n = getParam("ic_n"); - int ir_n = getParam("ir_n"); - - m_all_factors.resize(stk_count); - - vector 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); - } - - // 计算 IC 权重 - vector sumByDate(days_total, 0.0); - vector countByDate(days_total, 0); - for (size_t di = 0; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - auto value = icir[ii][di]; - if (!std::isnan(value)) { - sumByDate[di] += value; - countByDate[di] += 1; - } - } - } - - for (size_t di = 0; di < days_total; di++) { - if (countByDate[di] == 0) { - sumByDate[di] = Null(); - } else { - sumByDate[di] = sumByDate[di] / countByDate[di]; - } - } - - for (size_t si = 0; si < stk_count; si++) { - PriceList new_values(days_total); - for (size_t di = 0; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - auto value = all_stk_inds[si][ii][di]; - new_values[di] = value * sumByDate[di]; - } - } - m_all_factors[si] = PRICELIST(new_values); - } -} - -vector> MultiFactor::_alignAllInds() { - size_t days_total = m_ref_dates.size(); - bool fill_null = getParam("fill_null"); - size_t stk_count = m_stks.size(); - size_t ind_count = m_inds.size(); - - vector> all_stk_inds; - all_stk_inds.resize(stk_count); - for (size_t i = 0; i < stk_count; i++) { - const auto& stk = m_stks[i]; - auto kdata = stk.getKData(m_query); - all_stk_inds[i].resize(ind_count); - for (size_t j = 0; j < ind_count; j++) { - all_stk_inds[i][j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); - } - } - - return all_stk_inds; -} - -void MultiFactor::calculate() { - HKU_CHECK(!m_ref_stk.isNull(), "ref_stk is null!"); - - size_t stk_count = m_stks.size(); - HKU_CHECK(stk_count >= 2, "The number of stock is insufficient! current stock number: {}", - stk_count); - - // 获取对齐的参考日期 - auto m_ref_dates = m_ref_stk.getDatetimeList(m_query); - size_t days_total = m_ref_dates.size(); - HKU_CHECK(days_total >= 2, "The dates len is insufficient! current len: {}", days_total); - - // 获取所有证券所有对齐后的因子 - vector> all_stk_inds = _alignAllInds(); - - // 计算每支证券调整后的合成因子 - _calculateAllFactorsByEqualWeight(all_stk_inds); - - // 建立每日截面的索引,并每日降序排序 - m_stk_factor_by_date.resize(days_total); - vector> one_day(stk_count); - for (size_t i = 0; i < days_total; i++) { - for (size_t j = 0; j < stk_count; j++) { - one_day[i] = std::make_pair(m_stks[j], m_all_factors[j][i]); - } - std::sort(one_day.begin(), one_day.end(), - [](const std::pair& a, const std::pair& b) { - return a.second > b.second; - }); - m_stk_factor_by_date[i] = one_day; - m_date_index[m_ref_dates[i]] = i; - } -} - -} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/factor/MultiFactor.h b/hikyuu_cpp/hikyuu/factor/MultiFactor.h deleted file mode 100644 index ae14bf36..00000000 --- a/hikyuu_cpp/hikyuu/factor/MultiFactor.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-12 - * Author: fasiondog - */ - -#pragma once - -#include "hikyuu/KData.h" -#include "hikyuu/indicator/Indicator.h" -#include "hikyuu/utilities/Parameter.h" - -namespace hku { - -class MultiFactor { - PARAMETER_SUPPORT - -public: - typedef Indicator::value_t value_t; - -public: - MultiFactor() = delete; - MultiFactor(const vector& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk); - - void calculate(); - - /** 获取指定证券合成因子 */ - const Indicator& get(const Stock&) const; - - /** 获取指定日期截面的所有因子值 */ - const vector>& get(const Datetime&) const; - - /** 获取合成因子的IC */ - Indicator getIC(int ndays) const; - - /** 获取合成因子的 ICIR */ - Indicator getICIR(int ic_n, int ir_n) const; - -private: - vector> _alignAllInds(); - vector _getAllReturns(int ndays) const; - void _calculateAllFactorsByEqualWeight(const vector>&); - void _calculateAllFactorsByIC(const vector>&); - void _calculateAllFactorsByICIR(const vector>&); - -private: - std::mutex m_mutex; - vector m_inds; - StockList m_stks; - Stock m_ref_stk; - KQuery m_query; - unordered_map m_stk_map; - vector m_all_factors; - - unordered_map m_date_index; - vector>> m_stk_factor_by_date; - - DatetimeList m_ref_dates; -}; - -} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/factor/SingleFactor.cpp b/hikyuu_cpp/hikyuu/factor/SingleFactor.cpp deleted file mode 100644 index fd6f2abc..00000000 --- a/hikyuu_cpp/hikyuu/factor/SingleFactor.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-12 - * Author: fasiondog - */ - -#include "hikyuu/indicator/crt/ALIGN.h" -#include "hikyuu/indicator/crt/ROCP.h" -#include "hikyuu/indicator/crt/PRICELIST.h" -#include "hikyuu/indicator/crt/IC.h" -#include "hikyuu/indicator/crt/ICIR.h" -#include "SingleFactor.h" - -namespace hku { - -SingleFactor::SingleFactor() { - setParam("fill_null", true); -} - -SingleFactor::SingleFactor(const Indicator& ind, const StockList& stks, const KQuery& query, - const Stock& ref_stk) -: m_ind(ind), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { - setParam("fill_null", true); -} - -const Indicator& SingleFactor::get(const Stock& stk) const { - const auto iter = m_stk_map.find(stk); - HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); - return m_all_inds[iter->second]; -} - -const vector>& SingleFactor::get(const Datetime& d) const { - const auto iter = m_date_index.find(d); - HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); - return m_stk_factor_by_date[iter->second]; -} - -Indicator SingleFactor::getIC(int ndays) const { - return IC(m_ind, m_stks, m_query, ndays, m_ref_stk); -} - -Indicator SingleFactor::getICIR(int ic_n, int ir_n) const { - return ICIR(IC(m_ind, m_stks, m_query, ic_n, m_ref_stk), ir_n); -} - -void SingleFactor::calculate() { - HKU_ERROR_IF_RETURN(m_ref_stk.isNull(), void(), "ref_stk is null!"); - auto ref_dates = m_ref_stk.getDatetimeList(m_query); - size_t days_total = ref_dates.size(); - - HKU_WARN_IF_RETURN( - days_total < 2 || m_stks.size() < 2, void(), - "The number(>=2) of stock or data length(>=2) is insufficient! current data len: {}, " - "current stock number: {}", - days_total, m_stks.size()); - - bool fill_null = getParam("fill_null"); - - size_t stk_count = m_stks.size(); - m_all_inds.reserve(stk_count); - m_stk_map.reserve(stk_count); - for (size_t i = 0; i < stk_count; i++) { - const auto& stk = m_stks[i]; - HKU_WARN_IF(stk.isNull(), "Exist null stock!"); - auto k = stk.getKData(m_query); - m_stk_map[stk] = i; - m_all_inds.emplace_back(ALIGN(m_ind(k), ref_dates, fill_null)); - } - - m_stk_factor_by_date.resize(days_total); - vector> one_day(stk_count); - for (size_t i = 0; i < days_total; i++) { - for (size_t j = 0; j < stk_count; j++) { - one_day[i] = std::make_pair(m_stks[j], m_all_inds[j][i]); - } - std::sort(one_day.begin(), one_day.end(), - [](const std::pair& a, const std::pair& b) { - return a.second > b.second; - }); - m_stk_factor_by_date[i] = one_day; - m_date_index[ref_dates[i]] = i; - } -} - -} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/factor/SingleFactor.h b/hikyuu_cpp/hikyuu/factor/SingleFactor.h deleted file mode 100644 index 1b31a2c1..00000000 --- a/hikyuu_cpp/hikyuu/factor/SingleFactor.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-12 - * Author: fasiondog - */ - -#pragma once - -#include "hikyuu/KData.h" -#include "hikyuu/indicator/Indicator.h" -#include "hikyuu/utilities/Parameter.h" - -namespace hku { - -class SingleFactor { - PARAMETER_SUPPORT - -public: - typedef Indicator::value_t value_t; - -public: - SingleFactor(); - SingleFactor(const Indicator& ind, const StockList& stks, const KQuery& query, - const Stock& ref_stk); - - void calculate(); - - const Indicator& get(const Stock&) const; - - const vector>& get(const Datetime&) const; - - Indicator getIC(int ndays) const; - Indicator getICIR(int ic_n, int ir_n) const; - -private: - Indicator m_ind; - StockList m_stks; - Stock m_ref_stk; - KQuery m_query; - unordered_map m_stk_map; - vector m_all_inds; - - unordered_map m_date_index; - vector>> m_stk_factor_by_date; -}; - -} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index 677474e8..bf4ba647 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -197,6 +197,9 @@ private: #endif /* HKU_SUPPORT_SERIALIZATION */ }; +/** @ingroup Indicator */ +typedef vector IndicatorList; + inline string Indicator::name() const { return m_imp ? m_imp->name() : "IndicatorImp"; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp new file mode 100644 index 00000000..b17e76b5 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "hikyuu/indicator/crt/ROCP.h" +#include "hikyuu/indicator/crt/REF.h" +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/IC.h" +#include "hikyuu/indicator/crt/ICIR.h" +#include "hikyuu/indicator/crt/SPEARMAN.h" +#include "MultiFactorBase.h" + +namespace hku { + +MultiFactorBase::MultiFactorBase() { + setParam("fill_null", true); +} + +MultiFactorBase::MultiFactorBase(const string& name) { + setParam("fill_null", true); +} + +MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk, const string& name) +: m_name(name), m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { + setParam("fill_null", true); + HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); + HKU_CHECK(!m_inds.empty(), "The reference stock must be set!"); + for (const auto& stk : m_stks) { + HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); + } +} + +MultiFactorPtr MultiFactorBase::clone() { + MultiFactorPtr p; + try { + p = _clone(); + } catch (...) { + HKU_ERROR("Subclass _clone failed!"); + p = MultiFactorPtr(); + } + + if (!p || p.get() == this) { + HKU_ERROR("Failed clone! Will use self-ptr!"); + return shared_from_this(); + } + + p->m_params = m_params; + p->m_inds = m_inds; + p->m_stks = m_stks; + p->m_ref_stk = m_ref_stk; + p->m_query = m_query; + p->m_stk_map = m_stk_map; + p->m_all_factors = m_all_factors; + p->m_date_index = m_date_index; + p->m_stk_factor_by_date = m_stk_factor_by_date; + p->m_ref_dates = m_ref_dates; + return p; +} + +const Indicator& MultiFactorBase::get(const Stock& stk) const { + const auto iter = m_stk_map.find(stk); + HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); + return m_all_factors[iter->second]; +} + +const vector>& MultiFactorBase::get( + const Datetime& d) const { + const auto iter = m_date_index.find(d); + HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); + return m_stk_factor_by_date[iter->second]; +} + +IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { + bool fill_null = getParam("fill_null"); + vector all_returns; + all_returns.reserve(m_stks.size()); + for (const auto& stk : m_stks) { + auto k = stk.getKData(m_query); + all_returns.push_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null)); + } + return all_returns; +} + +Indicator MultiFactorBase::getIC(int ndays) const { + size_t days_total = m_ref_dates.size(); + + bool fill_null = getParam("fill_null"); + + vector all_returns = _getAllReturns(ndays); + + size_t ind_count = m_all_factors.size(); + PriceList tmp(ind_count, Null()); + PriceList tmp_return(ind_count, Null()); + Indicator result = PRICELIST(PriceList(days_total, 0.0)); + auto* dst = result.data(); + for (size_t i = 0; i < days_total; i++) { + for (size_t j = 0; j < ind_count; j++) { + tmp[j] = m_all_factors[j][i]; + tmp_return[j] = all_returns[j][i]; + } + auto a = PRICELIST(tmp); + auto b = PRICELIST(tmp_return); + auto ic = hku::SPEARMAN(a, b, ind_count); + dst[i] = ic[ic.size() - 1]; + } + return result; +} + +Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) const { + return ICIR(getIC(ic_n), ir_n); +} + +vector MultiFactorBase::_alignAllInds() { + size_t days_total = m_ref_dates.size(); + bool fill_null = getParam("fill_null"); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + vector all_stk_inds; + all_stk_inds.resize(stk_count); + for (size_t i = 0; i < stk_count; i++) { + const auto& stk = m_stks[i]; + auto kdata = stk.getKData(m_query); + all_stk_inds[i].resize(ind_count); + for (size_t j = 0; j < ind_count; j++) { + all_stk_inds[i][j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); + } + } + + return all_stk_inds; +} + +void MultiFactorBase::calculate() { + HKU_CHECK(!m_ref_stk.isNull(), "ref_stk is null!"); + + size_t stk_count = m_stks.size(); + HKU_CHECK(stk_count >= 2, "The number of stock is insufficient! current stock number: {}", + stk_count); + + // 获取对齐的参考日期 + auto m_ref_dates = m_ref_stk.getDatetimeList(m_query); + size_t days_total = m_ref_dates.size(); + HKU_CHECK(days_total >= 2, "The dates len is insufficient! current len: {}", days_total); + + // 获取所有证券所有对齐后的因子 + vector> all_stk_inds = _alignAllInds(); + + if (m_inds.size() == 1) { + m_all_factors = std::move(all_stk_inds[0]); + } else { + // 计算每支证券调整后的合成因子 + m_all_factors = _calculate(all_stk_inds); + } + + // 建立每日截面的索引,并每日降序排序 + m_stk_factor_by_date.resize(days_total); + vector> one_day(stk_count); + for (size_t i = 0; i < days_total; i++) { + for (size_t j = 0; j < stk_count; j++) { + one_day[i] = std::make_pair(m_stks[j], m_all_factors[j][i]); + } + std::sort(one_day.begin(), one_day.end(), + [](const std::pair& a, const std::pair& b) { + return a.second > b.second; + }); + m_stk_factor_by_date[i] = one_day; + m_date_index[m_ref_dates[i]] = i; + } +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h new file mode 100644 index 00000000..d76d45e8 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -0,0 +1,149 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-12 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/KData.h" +#include "hikyuu/indicator/Indicator.h" +#include "hikyuu/utilities/Parameter.h" + +namespace hku { + +/** + * 合成多因子 + * @ingroup MultiFactor + */ +class HKU_API MultiFactorBase : public enable_shared_from_this { + PARAMETER_SUPPORT + +public: + typedef Indicator::value_t value_t; + +public: + MultiFactorBase(); + MultiFactorBase(const string& name); + MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query, + const Stock& ref_stk, const string& name); + + void calculate(); + + /** 获取指定证券合成因子 */ + const Indicator& get(const Stock&) const; + + /** 获取指定日期截面的所有因子值 */ + const vector>& get(const Datetime&) const; + + /** 获取合成因子的IC */ + Indicator getIC(int ndays) const; + + /** 获取合成因子的 ICIR */ + Indicator getICIR(int ic_n, int ir_n) const; + + typedef std::shared_ptr MultiFactorPtr; + MultiFactorPtr clone(); + + virtual IndicatorList _calculate(const vector&) = 0; + + virtual MultiFactorPtr _clone() = 0; + +protected: + vector _alignAllInds(); + IndicatorList _getAllReturns(int ndays) const; + +protected: + string m_name; + + std::mutex m_mutex; + IndicatorList m_inds; + StockList m_stks; + Stock m_ref_stk; + KQuery m_query; + unordered_map m_stk_map; + IndicatorList m_all_factors; + + unordered_map m_date_index; + vector>> m_stk_factor_by_date; + + DatetimeList m_ref_dates; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void save(Archive& ar, const unsigned int version) const { + ar& BOOST_SERIALIZATION_NVP(m_inds); + ar& BOOST_SERIALIZATION_NVP(m_stks); + ar& BOOST_SERIALIZATION_NVP(m_ref_stk); + ar& BOOST_SERIALIZATION_NVP(m_query); + ar& BOOST_SERIALIZATION_NVP(m_stk_map); + ar& BOOST_SERIALIZATION_NVP(m_all_factors); + ar& BOOST_SERIALIZATION_NVP(m_date_index); + ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); + ar& BOOST_SERIALIZATION_NVP(m_ref_dates); + } + + template + void load(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_NVP(m_inds); + ar& BOOST_SERIALIZATION_NVP(m_stks); + ar& BOOST_SERIALIZATION_NVP(m_ref_stk); + ar& BOOST_SERIALIZATION_NVP(m_query); + ar& BOOST_SERIALIZATION_NVP(m_stk_map); + ar& BOOST_SERIALIZATION_NVP(m_all_factors); + ar& BOOST_SERIALIZATION_NVP(m_date_index); + ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); + ar& BOOST_SERIALIZATION_NVP(m_ref_dates); + } + + BOOST_SERIALIZATION_SPLIT_MEMBER() +#endif /* HKU_SUPPORT_SERIALIZATION */ +}; + +#if HKU_SUPPORT_SERIALIZATION +BOOST_SERIALIZATION_ASSUME_ABSTRACT(MultiFactorBase) +#endif + +#if HKU_SUPPORT_SERIALIZATION +/** + * 对于没有私有变量的继承子类,可直接使用该宏定义序列化 + * @code + * class Drived: public MultiFactorBase { + * MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION + * + * public: + * Drived(); + * ... + * }; + * @endcode + * @ingroup MultiFactor + */ +#define MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION \ +private: \ + friend class boost::serialization::access; \ + template \ + void serialize(Archive& ar, const unsigned int version) { \ + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(MultiFactorBase); \ + } +#else +#define MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION +#endif + +typedef std::shared_ptr FactorPtr; +typedef std::shared_ptr MultiFactorPtr; +typedef std::shared_ptr MFPtr; + +#define MULTIFACTOR_IMP(classname) \ +public: \ + virtual MultiFactorPtr _clone() override { \ + return std::make_shared(); \ + } \ + virtual IndicatorList _calculate(const vector&) override; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h new file mode 100644 index 00000000..8eedca34 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once + +#include "crt/MF_EqualWeight.h" +#include "crt/MF_ICWeight.h" +#include "crt/MF_ICIRWeight.h" \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h new file mode 100644 index 00000000..d184e486 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h new file mode 100644 index 00000000..d184e486 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h new file mode 100644 index 00000000..d57c302a --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once + +#include "../MultiFactorBase.h" + +namespace hku { + +MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk); + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp new file mode 100644 index 00000000..d667db81 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/IC.h" +#include "hikyuu/indicator/crt/ICIR.h" +#include "hikyuu/indicator/crt/SPEARMAN.h" +#include "EqualWeightMultiFactor.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::EqualWeightMultiFactor) +#endif + +namespace hku { + +EqualWeightMultiFactor::EqualWeightMultiFactor() : MultiFactorBase("MF_ICWeight") {} + +EqualWeightMultiFactor::EqualWeightMultiFactor(const vector& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight") {} + +vector EqualWeightMultiFactor::_calculate( + const vector>& all_stk_inds) { + size_t days_total = m_ref_dates.size(); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + vector all_factors(stk_count); + for (size_t si = 0; si < stk_count; si++) { + vector sumByDate(days_total, 0.0); + vector countByDate(days_total, 0); + + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = all_stk_inds[si][ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + // 均值权重 + for (size_t di = 0; di < days_total; di++) { + if (countByDate[di] == 0) { + sumByDate[di] = Null(); + } else { + sumByDate[di] = sumByDate[di] / countByDate[di]; + } + } + + all_factors[si] = PRICELIST(sumByDate); + } + return all_factors; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h new file mode 100644 index 00000000..11cbe977 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once + +#include "../MultiFactorBase.h" + +namespace hku { + +class EqualWeightMultiFactor : public MultiFactorBase { + MULTIFACTOR_IMP(EqualWeightMultiFactor) + MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + EqualWeightMultiFactor(); + EqualWeightMultiFactor(const vector& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk); +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp new file mode 100644 index 00000000..dbcdd11f --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/IC.h" +#include "hikyuu/indicator/crt/ICIR.h" +#include "hikyuu/indicator/crt/SPEARMAN.h" +#include "ICIRMultiFactor.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::ICIRMultiFactor) +#endif + +namespace hku { + +ICIRMultiFactor::ICIRMultiFactor() : MultiFactorBase("MF_ICIRWeight") { + setParam("ic_n", 1); + setParam("ir_n", 10); +} + +ICIRMultiFactor::ICIRMultiFactor(const vector& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICIRWeight") { + setParam("ic_n", 1); + setParam("ir_n", 10); +} + +IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_inds) { + size_t days_total = m_ref_dates.size(); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + bool fill_null = getParam("fill_null"); + int ic_n = getParam("ic_n"); + int ir_n = getParam("ir_n"); + + vector all_factors(stk_count); + + vector 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); + } + + // 计算 IC 权重 + vector sumByDate(days_total, 0.0); + vector countByDate(days_total, 0); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = icir[ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + for (size_t di = 0; di < days_total; di++) { + if (countByDate[di] == 0) { + sumByDate[di] = Null(); + } else { + sumByDate[di] = sumByDate[di] / countByDate[di]; + } + } + + for (size_t si = 0; si < stk_count; si++) { + PriceList new_values(days_total); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = all_stk_inds[si][ii][di]; + new_values[di] = value * sumByDate[di]; + } + } + all_factors[si] = PRICELIST(new_values); + } + + return all_factors; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h new file mode 100644 index 00000000..3add2ea5 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once + +#include "../MultiFactorBase.h" + +namespace hku { + +class ICIRMultiFactor : public MultiFactorBase { + MULTIFACTOR_IMP(ICIRMultiFactor) + MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + ICIRMultiFactor(); + ICIRMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, + const Stock& ref_stk); +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp new file mode 100644 index 00000000..e4a290ac --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/PRICELIST.h" +#include "hikyuu/indicator/crt/IC.h" +#include "hikyuu/indicator/crt/ICIR.h" +#include "hikyuu/indicator/crt/SPEARMAN.h" +#include "ICMultiFactor.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::ICMultiFactor) +#endif + +namespace hku { + +ICMultiFactor::ICMultiFactor() : MultiFactorBase("MF_ICWeight") { + setParam("ic_n", 1); +} + +ICMultiFactor::ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, + const Stock& ref_stk) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight") { + setParam("ic_n", 1); +} + +IndicatorList ICMultiFactor::_calculate(const vector& all_stk_inds) { + size_t days_total = m_ref_dates.size(); + size_t stk_count = m_stks.size(); + size_t ind_count = m_inds.size(); + + bool fill_null = getParam("fill_null"); + int ic_n = getParam("ic_n"); + + IndicatorList all_factors(stk_count); + + IndicatorList ic(ind_count); + for (size_t ii = 0; ii < ind_count; ii++) { + ic[ii] = IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk); + } + + // 计算 IC 权重 + vector sumByDate(days_total, 0.0); + vector countByDate(days_total, 0); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = ic[ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + for (size_t di = 0; di < days_total; di++) { + if (countByDate[di] == 0) { + sumByDate[di] = Null(); + } else { + sumByDate[di] = sumByDate[di] / countByDate[di]; + } + } + + for (size_t si = 0; si < stk_count; si++) { + PriceList new_values(days_total); + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto value = all_stk_inds[si][ii][di]; + new_values[di] = value * sumByDate[di]; + } + } + all_factors[si] = PRICELIST(new_values); + } + + return all_factors; +} + +MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk) { + return std::make_shared(inds, stks, query, ref_stk); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h new file mode 100644 index 00000000..65af4762 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#pragma once + +#include "../MultiFactorBase.h" + +namespace hku { + +class ICMultiFactor : public MultiFactorBase { + MULTIFACTOR_IMP(ICMultiFactor) + MULTIFACTOR_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + ICMultiFactor(); + ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, + const Stock& ref_stk); +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp b/hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp new file mode 100644 index 00000000..ee4ed9ab --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp @@ -0,0 +1,81 @@ +/* + * test_ABS.cpp + * + * Created on: 2019年4月2日 + * Author: fasiondog + */ + +#include "../test_config.h" +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_indicator_ABS test_indicator_ABS + * @ingroup test_hikyuu_indicator_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_MultiFactor") {} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_MultiFactor_benchmark") { + // Stock stock = getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(0)); + // Indicator c = kdata.close(); + // int cycle = 1000; // 测试循环次数 + + // { + // BENCHMARK_TIME_MSG(test_ABS_benchmark, cycle, fmt::format("data len: {}", c.size())); + // SPEND_TIME_CONTROL(false); + // for (int i = 0; i < cycle; i++) { + // Indicator ind = ABS(); + // Indicator result = ind(c); + // } + // } +} +#endif + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_MultiFactor_export") { + // StockManager& sm = StockManager::instance(); + // string filename(sm.tmpdir()); + // filename += "/ABS.xml"; + + // Stock stock = sm.getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(-20)); + // Indicator x1 = ABS(CLOSE(kdata)); + // { + // std::ofstream ofs(filename); + // boost::archive::xml_oarchive oa(ofs); + // oa << BOOST_SERIALIZATION_NVP(x1); + // } + + // Indicator x2; + // { + // std::ifstream ifs(filename); + // boost::archive::xml_iarchive ia(ifs); + // ia >> BOOST_SERIALIZATION_NVP(x2); + // } + + // CHECK_UNARY(x1.size() == x2.size()); + // CHECK_UNARY(x1.discard() == x2.discard()); + // CHECK_UNARY(x1.getResultNumber() == x2.getResultNumber()); + // for (size_t i = 0; i < x1.size(); ++i) { + // CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); + // } +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp new file mode 100644 index 00000000..25aa6bf5 --- /dev/null +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -0,0 +1,85 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-13 + * Author: fasiondog + */ + +#include +#include "../pybind_utils.h" + +namespace py = pybind11; +using namespace hku; + +class PyMultiFactor : public MultiFactorBase { + PY_CLONE(PyMultiFactor, MultiFactorBase) + +public: + using MultiFactorBase::MultiFactorBase; + + IndicatorList _calculate(const vector& all_stk_inds) { + PYBIND11_OVERLOAD_PURE_NAME(IndicatorList, MultiFactorBase, "_calculate", _calculate, + all_stk_inds); + } +}; + +void export_MultiFactor(py::module& m) { + py::class_(m, "MultiFactor", + R"(市场环境判定策略基类 + +自定义市场环境判定策略接口: + + - _calculate : 【必须】子类计算接口 + - _clone : 【必须】克隆接口 + - _reset : 【可选】重载私有变量)") + .def(py::init<>()) + // .def(py::init()) + + // .def("__str__", to_py_str) + // .def("__repr__", to_py_str) + + // .def_property("name", py::overload_cast<>(&EnvironmentBase::name, py::const_), + // py::overload_cast(&EnvironmentBase::name), + // py::return_value_policy::copy, "名称") + // .def_property("query", &EnvironmentBase::getQuery, &EnvironmentBase::setQuery, + // py::return_value_policy::copy, "设置或获取查询条件") + + .def("get_param", &MultiFactorBase::getParam, R"(get_param(self, name) + + 获取指定的参数 + + :param str name: 参数名称 + :return: 参数值 + :raises out_of_range: 无此参数)") + + .def("set_param", &MultiFactorBase::setParam, R"(set_param(self, name, value) + + 设置参数 + + :param str name: 参数名称 + :param value: 参数值 + :raises logic_error: Unsupported type! 不支持的参数类型)") + + .def("haveParam", &MultiFactorBase::haveParam, "是否存在指定参数") + + // .def("is_valid", &EnvironmentBase::isValid, R"(is_valid(self, datetime) + + // 指定时间系统是否有效 + + // :param Datetime datetime: 指定时间 + // :return: True 有效 | False 无效)") + + // .def("_add_valid", &EnvironmentBase::_addValid, R"(_add_valid(self, datetime) + + // 加入有效时间,在_calculate中调用 + + // :param Datetime datetime: 有效时间)") + + // .def("reset", &EnvironmentBase::reset, "复位操作") + // .def("clone", &EnvironmentBase::clone, "克隆操作") + // .def("_reset", &EnvironmentBase::_reset, + // "【重载接口】子类复位接口,用于复位内部私有变量") .def("_calculate", + // &EnvironmentBase::_calculate, "【重载接口】子类计算接口") + + DEF_PICKLE(MultiFactorPtr); +} \ No newline at end of file From 4cfeba4d35cb43930efc527fe16ebee1013de610 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 13 Mar 2024 16:40:13 +0800 Subject: [PATCH 030/601] fixed compile errors on ubuntu --- hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp | 7 +------ .../hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp | 2 +- hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp | 1 - hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp | 1 - 4 files changed, 2 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index b17e76b5..67c71357 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -87,12 +87,8 @@ IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { } Indicator MultiFactorBase::getIC(int ndays) const { - size_t days_total = m_ref_dates.size(); - - bool fill_null = getParam("fill_null"); - vector all_returns = _getAllReturns(ndays); - + size_t days_total = m_ref_dates.size(); size_t ind_count = m_all_factors.size(); PriceList tmp(ind_count, Null()); PriceList tmp_return(ind_count, Null()); @@ -116,7 +112,6 @@ Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) const { } vector MultiFactorBase::_alignAllInds() { - size_t days_total = m_ref_dates.size(); bool fill_null = getParam("fill_null"); size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp index d667db81..bb9a98f9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp @@ -31,7 +31,7 @@ vector EqualWeightMultiFactor::_calculate( vector all_factors(stk_count); for (size_t si = 0; si < stk_count; si++) { - vector sumByDate(days_total, 0.0); + vector sumByDate(days_total, 0.0); vector countByDate(days_total, 0); for (size_t di = 0; di < days_total; di++) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index dbcdd11f..0bd0bceb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -34,7 +34,6 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); - bool fill_null = getParam("fill_null"); int ic_n = getParam("ic_n"); int ir_n = getParam("ir_n"); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index e4a290ac..967644be 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -32,7 +32,6 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); - bool fill_null = getParam("fill_null"); int ic_n = getParam("ic_n"); IndicatorList all_factors(stk_count); From 145c1bf2113d76be1582546b1ad96d9f3c4066f8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 13 Mar 2024 23:51:01 +0800 Subject: [PATCH 031/601] =?UTF-8?q?=E6=8C=87=E6=A0=87=E8=AE=A1=E7=AE=97?= =?UTF-8?q?=E6=97=B6=EF=BC=8C=E5=A6=82=E6=9E=9C=E7=BC=BA=E5=A4=B1=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E4=B8=8A=E4=B8=8B=E6=96=87=E7=9A=84=E5=8F=B6=E5=AD=90?= =?UTF-8?q?=E8=8A=82=E7=82=B9=EF=BC=8C=E7=BB=99=E5=87=BA=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index c159ea88..f7967173 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -704,6 +704,8 @@ Indicator IndicatorImp::calculate() { switch (m_optype) { case LEAF: + HKU_WARN_IF(!isNeedContext(), + "The indicator({}) is missing a leaf node that requires context!", name()); if (m_ind_params.empty()) { _calculate(Indicator()); } else { From 29fa234ba568e87bde6b4a1714a9cee47d2d8124 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 02:07:24 +0800 Subject: [PATCH 032/601] =?UTF-8?q?Indicator=20=E5=A2=9E=E5=8A=A0=20equal/?= =?UTF-8?q?isSame=20=E6=96=B9=E6=B3=95=EF=BC=8C=E7=AE=80=E5=8C=96=E4=B8=80?= =?UTF-8?q?=E4=BA=9B=E6=B5=8B=E8=AF=95=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 11 +++++++++++ hikyuu_cpp/hikyuu/indicator/Indicator.h | 11 +++++++++++ hikyuu_pywrap/indicator/_Indicator.cpp | 2 ++ 3 files changed, 24 insertions(+) diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 323c5e84..84cc8b7d 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -52,6 +52,17 @@ bool Indicator::alike(const Indicator& other) const { return m_imp->alike(*other.m_imp); } +bool Indicator::equal(const Indicator& other) const { + HKU_IF_RETURN(this == &other || m_imp == other.m_imp, true); + HKU_IF_RETURN(size() != other.size(), false); + auto const* d1 = this->data(); + auto const* d2 = other.data(); + for (size_t i = 0, total = size(); i < total; i++) { + HKU_IF_RETURN(std::fabs(d1[i] - d2[2]) >= 0.0001, false); + } + return true; +} + Indicator& Indicator::operator=(const Indicator& indicator) { HKU_IF_RETURN(this == &indicator, *this); m_imp = indicator.m_imp; diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index bf4ba647..dfbf1074 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -184,6 +184,17 @@ public: return m_imp ? m_imp->data(result_num) : nullptr; } + /** + * 判断两个ind的值是否相等 + * @note operator==重载生成新的新的Indicator,此函数用于对两个ind进行值比较 + */ + bool equal(const Indicator& other) const; + + /** 判断是否是同一个实例 */ + bool isSame(const Indicator& other) const { + return !m_imp && m_imp == other.m_imp; + } + protected: IndicatorImpPtr m_imp; diff --git a/hikyuu_pywrap/indicator/_Indicator.cpp b/hikyuu_pywrap/indicator/_Indicator.cpp index c18ff46c..f2e31d2a 100644 --- a/hikyuu_pywrap/indicator/_Indicator.cpp +++ b/hikyuu_pywrap/indicator/_Indicator.cpp @@ -167,6 +167,8 @@ set_context(self, stock, query) :rtype: KData)") + .def("equal", &Indicator::equal) + .def("is_same", &Indicator::isSame) .def("get_imp", &Indicator::getImp) .def("__len__", &Indicator::size) From e74065a7d065a4b41109965575e1145ed024da78 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 03:38:01 +0800 Subject: [PATCH 033/601] MultiFactor (continue) --- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 8 +- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 2 - hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 11 +- .../trade_sys/factor/MultiFactorBase.cpp | 190 +++++++++++++----- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 60 ++++-- .../trade_sys/factor/crt/MF_EqualWeight.h | 10 +- .../hikyuu/trade_sys/factor/crt/MF_ICWeight.h | 2 +- .../factor/imp/EqualWeightMultiFactor.cpp | 11 +- .../factor/imp/EqualWeightMultiFactor.h | 2 +- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 4 +- .../trade_sys/factor/imp/ICIRMultiFactor.h | 2 +- .../trade_sys/factor/imp/ICMultiFactor.cpp | 14 +- .../trade_sys/factor/imp/ICMultiFactor.h | 2 +- .../hikyuu/factor/test_MultiFactor.cpp | 81 -------- .../unit_test/hikyuu/indicator/test_IC.cpp | 5 +- .../trade_sys/factor/test_MF_EqualWeight.cpp | 133 ++++++++++++ 16 files changed, 361 insertions(+), 176 deletions(-) delete mode 100644 hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 84cc8b7d..93a065d2 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -54,11 +54,15 @@ bool Indicator::alike(const Indicator& other) const { bool Indicator::equal(const Indicator& other) const { HKU_IF_RETURN(this == &other || m_imp == other.m_imp, true); - HKU_IF_RETURN(size() != other.size(), false); + HKU_IF_RETURN(size() != other.size() || discard() != other.discard(), false); auto const* d1 = this->data(); auto const* d2 = other.data(); for (size_t i = 0, total = size(); i < total; i++) { - HKU_IF_RETURN(std::fabs(d1[i] - d2[2]) >= 0.0001, false); + HKU_IF_RETURN( + (std::isnan(d1[i]) && !std::isnan(d2[i])) || (!std::isnan(d1[i]) && std::isnan(d2[i])), + false); + HKU_IF_RETURN( + (!std::isnan(d1[i]) && !std::isnan(d2[i])) && (std::abs(d1[i] - d2[i]) >= 0.0001), false); } return true; } diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index f7967173..c159ea88 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -704,8 +704,6 @@ Indicator IndicatorImp::calculate() { switch (m_optype) { case LEAF: - HKU_WARN_IF(!isNeedContext(), - "The indicator({}) is missing a leaf node that requires context!", name()); if (m_ind_params.empty()) { _calculate(Indicator()); } else { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 5ef3d891..0bf1f1b7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -76,14 +76,23 @@ void IIc::_calculate(const Indicator& inputInd) { vector all_inds(stk_count); // 保存每支证券对齐后的因子值 vector all_returns(stk_count); // 保存每支证券对齐后的 n 日收益率 Indicator ind = inputInd; + size_t discard = n; for (size_t i = 0; i < stk_count; i++) { auto k = m_stks[i].getKData(m_query); all_inds[i] = ALIGN(ind(k), ref_dates, fill_null); + if (all_inds[i].discard() > discard) { + discard = all_inds[i].discard(); + } // 计算 n 日收益率,同时需要右移 n 位,即第 i 日的因子值和第 i + n 的收益率对应 all_returns[i] = ALIGN(REF(ROCP(k.close(), n), n), ref_dates, fill_null); + if (all_returns[i].discard() > discard) { + discard = all_returns[i].discard(); + } } - m_discard = n; + m_discard = discard; + HKU_IF_RETURN(m_discard >= days_total, void()); + PriceList tmp(stk_count, Null()); PriceList tmp_return(stk_count, Null()); auto* dst = this->data(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 67c71357..e73c9ed9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -18,24 +18,40 @@ namespace hku { MultiFactorBase::MultiFactorBase() { setParam("fill_null", true); + setParam("ic_n", 1); } MultiFactorBase::MultiFactorBase(const string& name) { setParam("fill_null", true); + setParam("ic_n", 1); } MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk, const string& name) + const KQuery& query, const Stock& ref_stk, const string& name, + int ic_n) : m_name(name), m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { setParam("fill_null", true); + setParam("ic_n", ic_n); + HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); - HKU_CHECK(!m_inds.empty(), "The reference stock must be set!"); + HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); + + // 后续计算需要保持对齐,夹杂 Null stock 处理麻烦,直接抛出异常屏蔽 for (const auto& stk : m_stks) { HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); } + + // 获取用于对齐的参考日期 + m_ref_dates = m_ref_stk.getDatetimeList(m_query); + HKU_CHECK(m_ref_dates.size() >= 2, "The dates len is insufficient! current len: {}", + m_ref_dates.size()); + + HKU_CHECK(m_stks.size() >= 2, "The number of stock is insufficient! current stock number: {}", + m_stks.size()); } MultiFactorPtr MultiFactorBase::clone() { + std::lock_guard lock(m_mutex); MultiFactorPtr p; try { p = _clone(); @@ -50,7 +66,6 @@ MultiFactorPtr MultiFactorBase::clone() { } p->m_params = m_params; - p->m_inds = m_inds; p->m_stks = m_stks; p->m_ref_stk = m_ref_stk; p->m_query = m_query; @@ -59,22 +74,101 @@ MultiFactorPtr MultiFactorBase::clone() { p->m_date_index = m_date_index; p->m_stk_factor_by_date = m_stk_factor_by_date; p->m_ref_dates = m_ref_dates; + p->m_ic = m_ic.clone(); + + p->m_inds.reserve(m_inds.size()); + for (const auto& ind : m_inds) { + p->m_inds.emplace_back(ind.clone()); + } + + p->m_all_factors.reserve(m_all_factors.size()); + for (const auto& ind : m_all_factors) { + p->m_all_factors.emplace_back(ind.clone()); + } return p; } -const Indicator& MultiFactorBase::get(const Stock& stk) const { +const Indicator& MultiFactorBase::get(const Stock& stk) { + calculate(); + std::lock_guard lock(m_mutex); const auto iter = m_stk_map.find(stk); HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); return m_all_factors[iter->second]; } -const vector>& MultiFactorBase::get( - const Datetime& d) const { +const vector>& MultiFactorBase::get(const Datetime& d) { + calculate(); + std::lock_guard lock(m_mutex); const auto iter = m_date_index.find(d); HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); return m_stk_factor_by_date[iter->second]; } +Indicator MultiFactorBase::getIC(int ndays) { + calculate(); + std::lock_guard lock(m_mutex); + + // 如果 ndays 和 ic_n 参数相同,优先取缓存的 ic 结果 + // 新的因子计算 IC,本质上不需要缓存(如等权重),但通过 IC 或 ICIR 计算权重的新因子 + // 虽然可以用不同的 N 日收益率来计算新因子IC,但最好还是使用相同值 + // 通过IC/ICIR计算权重的情况较多,所以这里直接缓存一份,减少重复计算 + // 实际使用时,最好保证 getIC(ndays) 中的 ndays 和 ic_n 一致 + int ic_n = getParam("ic_n"); + HKU_IF_RETURN(ic_n == ndays && !m_ic.empty(), m_ic.clone()); + + size_t days_total = m_ref_dates.size(); + Indicator result = PRICELIST(PriceList(days_total, Null())); + result.name("IC"); + if (ndays + 1 >= days_total) { + result.setDiscard(days_total); + return result; + } + + auto all_returns = _getAllReturns(ndays); + + size_t discard = ndays; + size_t ind_count = m_all_factors.size(); + for (size_t i = 0; i < ind_count; i++) { + if (all_returns[i].discard() > discard) { + discard = all_returns[i].discard(); + } + if (m_all_factors[i].discard() > discard) { + discard = m_all_factors[i].discard(); + } + } + + if (discard >= days_total) { + result.setDiscard(days_total); + return result; + } + + result.setDiscard(discard); + + PriceList tmp(ind_count, Null()); + PriceList tmp_return(ind_count, Null()); + auto* dst = result.data(); + for (size_t i = discard; i < days_total; i++) { + for (size_t j = 0; j < ind_count; j++) { + tmp[j] = m_all_factors[j][i]; + tmp_return[j] = all_returns[j][i]; + } + auto a = PRICELIST(tmp); + auto b = PRICELIST(tmp_return); + auto ic = hku::SPEARMAN(a, b, ind_count); + dst[i] = ic[ic.size() - 1]; + } + + // 如果 ndays 和 ic_n 参数相同,缓存计算结果 + if (ic_n == ndays) { + m_ic = result; + } + return result; +} + +Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) { + return ICIR(getIC(ic_n), ir_n); +} + IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { bool fill_null = getParam("fill_null"); vector all_returns; @@ -86,31 +180,6 @@ IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { return all_returns; } -Indicator MultiFactorBase::getIC(int ndays) const { - vector all_returns = _getAllReturns(ndays); - size_t days_total = m_ref_dates.size(); - size_t ind_count = m_all_factors.size(); - PriceList tmp(ind_count, Null()); - PriceList tmp_return(ind_count, Null()); - Indicator result = PRICELIST(PriceList(days_total, 0.0)); - auto* dst = result.data(); - for (size_t i = 0; i < days_total; i++) { - for (size_t j = 0; j < ind_count; j++) { - tmp[j] = m_all_factors[j][i]; - tmp_return[j] = all_returns[j][i]; - } - auto a = PRICELIST(tmp); - auto b = PRICELIST(tmp_return); - auto ic = hku::SPEARMAN(a, b, ind_count); - dst[i] = ic[ic.size() - 1]; - } - return result; -} - -Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) const { - return ICIR(getIC(ic_n), ir_n); -} - vector MultiFactorBase::_alignAllInds() { bool fill_null = getParam("fill_null"); size_t stk_count = m_stks.size(); @@ -130,29 +199,15 @@ vector MultiFactorBase::_alignAllInds() { return all_stk_inds; } -void MultiFactorBase::calculate() { - HKU_CHECK(!m_ref_stk.isNull(), "ref_stk is null!"); - +void MultiFactorBase::_buildCrossSession() { size_t stk_count = m_stks.size(); - HKU_CHECK(stk_count >= 2, "The number of stock is insufficient! current stock number: {}", - stk_count); - - // 获取对齐的参考日期 - auto m_ref_dates = m_ref_stk.getDatetimeList(m_query); - size_t days_total = m_ref_dates.size(); - HKU_CHECK(days_total >= 2, "The dates len is insufficient! current len: {}", days_total); - - // 获取所有证券所有对齐后的因子 - vector> all_stk_inds = _alignAllInds(); - - if (m_inds.size() == 1) { - m_all_factors = std::move(all_stk_inds[0]); - } else { - // 计算每支证券调整后的合成因子 - m_all_factors = _calculate(all_stk_inds); + HKU_TRACE_IF(stk_count != m_all_factors.size(), "some errors"); + for (size_t i = 0; i < stk_count; i++) { + m_stk_map[m_stks[i]] = i; } // 建立每日截面的索引,并每日降序排序 + size_t days_total = m_ref_dates.size(); m_stk_factor_by_date.resize(days_total); vector> one_day(stk_count); for (size_t i = 0; i < days_total; i++) { @@ -168,4 +223,39 @@ void MultiFactorBase::calculate() { } } +void MultiFactorBase::calculate() { + SPEND_TIME(MultiFactorBase_calculate); + std::lock_guard lock(m_mutex); + HKU_IF_RETURN(m_calculated, void()); + + // 获取所有证券所有对齐后的原始因子 + vector> all_stk_inds = _alignAllInds(); + + try { + if (m_inds.size() == 1) { + // 直接使用原始因子 + size_t stk_count = m_stks.size(); + m_all_factors.resize(stk_count); + for (size_t i = 0; i < stk_count; i++) { + m_all_factors[i] = std::move(all_stk_inds[i][0]); + } + + } else { + // 计算每支证券调整后的合成因子 + m_all_factors = _calculate(all_stk_inds); + } + + // 计算完成后创建截面索引 + _buildCrossSession(); + + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } + + // 更新计算状态 + m_calculated = true; +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index d76d45e8..bdffc41d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -27,48 +27,66 @@ public: MultiFactorBase(); MultiFactorBase(const string& name); MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk, const string& name); + const Stock& ref_stk, const string& name, int ic_n); - void calculate(); + /** 获取名称 */ + const string& name() const { + return m_name; + } + + /** 设置名称 */ + void name(const string& name) { + m_name = name; + } + + const DatetimeList& getDatetimeList() const { + return m_ref_dates; + } /** 获取指定证券合成因子 */ - const Indicator& get(const Stock&) const; + const Indicator& get(const Stock&); /** 获取指定日期截面的所有因子值 */ - const vector>& get(const Datetime&) const; + const vector>& get(const Datetime&); /** 获取合成因子的IC */ - Indicator getIC(int ndays) const; + Indicator getIC(int ndays); /** 获取合成因子的 ICIR */ - Indicator getICIR(int ic_n, int ir_n) const; + Indicator getICIR(int ic_n, int ir_n); + + /** 执行计算 */ + void calculate(); typedef std::shared_ptr MultiFactorPtr; MultiFactorPtr clone(); - virtual IndicatorList _calculate(const vector&) = 0; - virtual MultiFactorPtr _clone() = 0; + virtual IndicatorList _calculate(const vector&) = 0; protected: vector _alignAllInds(); + void _buildCrossSession(); // 计算完成后创建截面索引 IndicatorList _getAllReturns(int ndays) const; protected: string m_name; + IndicatorList m_inds; // 输入的原始因子列表 + StockList m_stks; // 证券组合 + Stock m_ref_stk; // 指定的参考证券 + KQuery m_query; // 计算的日期范围条件 - std::mutex m_mutex; - IndicatorList m_inds; - StockList m_stks; - Stock m_ref_stk; - KQuery m_query; - unordered_map m_stk_map; - IndicatorList m_all_factors; - + // 以下变量为计算后生成 + DatetimeList m_ref_dates; // 依据参考证券和query计算的参考日期,合成因子和该日期对齐 + unordered_map m_stk_map; // 证券->合成后因子位置索引 + IndicatorList m_all_factors; // 保存所有证券合成后的新因子 unordered_map m_date_index; vector>> m_stk_factor_by_date; + Indicator m_ic; - DatetimeList m_ref_dates; +private: + std::mutex m_mutex; + bool m_calculated{false}; //============================================ // 序列化支持 @@ -78,28 +96,32 @@ private: friend class boost::serialization::access; template void save(Archive& ar, const unsigned int version) const { + ar& BOOST_SERIALIZATION_NVP(m_name); ar& BOOST_SERIALIZATION_NVP(m_inds); ar& BOOST_SERIALIZATION_NVP(m_stks); ar& BOOST_SERIALIZATION_NVP(m_ref_stk); ar& BOOST_SERIALIZATION_NVP(m_query); + ar& BOOST_SERIALIZATION_NVP(m_ref_dates); ar& BOOST_SERIALIZATION_NVP(m_stk_map); ar& BOOST_SERIALIZATION_NVP(m_all_factors); ar& BOOST_SERIALIZATION_NVP(m_date_index); ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); - ar& BOOST_SERIALIZATION_NVP(m_ref_dates); + ar& BOOST_SERIALIZATION_NVP(m_ic); } template void load(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_NVP(m_name); ar& BOOST_SERIALIZATION_NVP(m_inds); ar& BOOST_SERIALIZATION_NVP(m_stks); ar& BOOST_SERIALIZATION_NVP(m_ref_stk); ar& BOOST_SERIALIZATION_NVP(m_query); + ar& BOOST_SERIALIZATION_NVP(m_ref_dates); ar& BOOST_SERIALIZATION_NVP(m_stk_map); ar& BOOST_SERIALIZATION_NVP(m_all_factors); ar& BOOST_SERIALIZATION_NVP(m_date_index); ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); - ar& BOOST_SERIALIZATION_NVP(m_ref_dates); + ar& BOOST_SERIALIZATION_NVP(m_ic); } BOOST_SERIALIZATION_SPLIT_MEMBER() diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h index d184e486..65783bec 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h @@ -5,4 +5,12 @@ * Author: fasiondog */ -#pragma once \ No newline at end of file +#pragma once +#include "../MultiFactorBase.h" + +namespace hku { + +MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk, int ic_n = 5); + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h index d57c302a..dbeff888 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h @@ -12,6 +12,6 @@ namespace hku { MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk); + const KQuery& query, const Stock& ref_stk, int ic_n = 5); } \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp index bb9a98f9..8ecfdc2e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp @@ -17,11 +17,11 @@ BOOST_CLASS_EXPORT(hku::EqualWeightMultiFactor) namespace hku { -EqualWeightMultiFactor::EqualWeightMultiFactor() : MultiFactorBase("MF_ICWeight") {} +EqualWeightMultiFactor::EqualWeightMultiFactor() : MultiFactorBase("MF_EqualWeight") {} EqualWeightMultiFactor::EqualWeightMultiFactor(const vector& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk) -: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight") {} + const KQuery& query, const Stock& ref_stk, int ic_n) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_EqualWeight", ic_n) {} vector EqualWeightMultiFactor::_calculate( const vector>& all_stk_inds) { @@ -58,4 +58,9 @@ vector EqualWeightMultiFactor::_calculate( return all_factors; } +MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk, int ic_n) { + return make_shared(inds, stks, query, ref_stk, ic_n); +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h index 11cbe977..471f1649 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h @@ -18,7 +18,7 @@ class EqualWeightMultiFactor : public MultiFactorBase { public: EqualWeightMultiFactor(); EqualWeightMultiFactor(const vector& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk); + const KQuery& query, const Stock& ref_stk, int ic_n); }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index 0bd0bceb..ad0a1d0e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -23,8 +23,8 @@ ICIRMultiFactor::ICIRMultiFactor() : MultiFactorBase("MF_ICIRWeight") { } ICIRMultiFactor::ICIRMultiFactor(const vector& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk) -: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICIRWeight") { + const KQuery& query, const Stock& ref_stk, int ic_n) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICIRWeight", ic_n) { setParam("ic_n", 1); setParam("ir_n", 10); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h index 3add2ea5..ca95e4c4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h @@ -18,7 +18,7 @@ class ICIRMultiFactor : public MultiFactorBase { public: ICIRMultiFactor(); ICIRMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk); + const Stock& ref_stk, int ic_n); }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index 967644be..5cc9cd6b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -17,15 +17,11 @@ BOOST_CLASS_EXPORT(hku::ICMultiFactor) namespace hku { -ICMultiFactor::ICMultiFactor() : MultiFactorBase("MF_ICWeight") { - setParam("ic_n", 1); -} +ICMultiFactor::ICMultiFactor() : MultiFactorBase("MF_ICWeight") {} ICMultiFactor::ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk) -: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight") { - setParam("ic_n", 1); -} + const Stock& ref_stk, int ic_n) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight", ic_n) {} IndicatorList ICMultiFactor::_calculate(const vector& all_stk_inds) { size_t days_total = m_ref_dates.size(); @@ -77,8 +73,8 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind } MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk) { - return std::make_shared(inds, stks, query, ref_stk); + const KQuery& query, const Stock& ref_stk, int ic_n) { + return std::make_shared(inds, stks, query, ref_stk, ic_n); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h index 65af4762..bfbbb70a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h @@ -18,7 +18,7 @@ class ICMultiFactor : public MultiFactorBase { public: ICMultiFactor(); ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk); + const Stock& ref_stk, int ic_n); }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp b/hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp deleted file mode 100644 index ee4ed9ab..00000000 --- a/hikyuu_cpp/unit_test/hikyuu/factor/test_MultiFactor.cpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * test_ABS.cpp - * - * Created on: 2019年4月2日 - * Author: fasiondog - */ - -#include "../test_config.h" -#include -#include -#include - -using namespace hku; - -/** - * @defgroup test_indicator_ABS test_indicator_ABS - * @ingroup test_hikyuu_indicator_suite - * @{ - */ - -/** @par 检测点 */ -TEST_CASE("test_MultiFactor") {} - -//----------------------------------------------------------------------------- -// benchmark -//----------------------------------------------------------------------------- -#if ENABLE_BENCHMARK_TEST -TEST_CASE("test_MultiFactor_benchmark") { - // Stock stock = getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(0)); - // Indicator c = kdata.close(); - // int cycle = 1000; // 测试循环次数 - - // { - // BENCHMARK_TIME_MSG(test_ABS_benchmark, cycle, fmt::format("data len: {}", c.size())); - // SPEND_TIME_CONTROL(false); - // for (int i = 0; i < cycle; i++) { - // Indicator ind = ABS(); - // Indicator result = ind(c); - // } - // } -} -#endif - -//----------------------------------------------------------------------------- -// test export -//----------------------------------------------------------------------------- -#if HKU_SUPPORT_SERIALIZATION - -/** @par 检测点 */ -TEST_CASE("test_MultiFactor_export") { - // StockManager& sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/ABS.xml"; - - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator x1 = ABS(CLOSE(kdata)); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(x1); - // } - - // Indicator x2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(x2); - // } - - // CHECK_UNARY(x1.size() == x2.size()); - // CHECK_UNARY(x1.discard() == x2.discard()); - // CHECK_UNARY(x1.getResultNumber() == x2.getResultNumber()); - // for (size_t i = 0; i < x1.size(); ++i) { - // CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); - // } -} -#endif /* #if HKU_SUPPORT_SERIALIZATION */ - -/** @} */ diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index a7c6b595..9b53fb51 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -84,8 +84,9 @@ TEST_CASE("test_IC") { CHECK_EQ(result.name(), "IC"); CHECK_UNARY(!result.empty()); CHECK_EQ(result.size(), ref_k.size()); - CHECK_EQ(result.discard(), 1); - CHECK_EQ(result[1], doctest::Approx(-1.0)); + CHECK_EQ(result.discard(), 2); + CHECK_UNARY(std::isnan(result[0])); + CHECK_UNARY(std::isnan(result[1])); CHECK_EQ(result[2], doctest::Approx(0.8)); CHECK_EQ(result[99], doctest::Approx(-1)); } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp new file mode 100644 index 00000000..52b20bd8 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -0,0 +1,133 @@ +/* + * test_ABS.cpp + * + * Created on: 2019年4月2日 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_MF_EqualWeight test_MF_EqualWeight + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_MF_EqualWeight") { + StockManager& sm = StockManager::instance(); + StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + Stock ref_stk = sm["sh000001"]; + KQuery query = KQuery(-100); + KData ref_k = ref_stk.getKData(query); + DatetimeList ref_dates = ref_k.getDatetimeList(); + IndicatorList src_inds{MA(CLOSE()), AMA(CLOSE(), EMA(CLOSE()))}; + + /** @arg 输入的股票列表中含有空股票 */ + CHECK_THROWS_AS(MF_EqualWeight(src_inds, {Null()}, query, ref_stk), std::exception); + + /** @arg 输入的原始因子列表为空 */ + CHECK_THROWS_AS(MF_EqualWeight({}, stks, query, ref_stk), std::exception); + + /** @arg 输入的参考证券为空 */ + CHECK_THROWS_AS(MF_EqualWeight({}, stks, query, Null()), std::exception); + + /** @arg 数据长度不足 */ + CHECK_THROWS_AS(MF_EqualWeight(src_inds, stks, KQuery(-1), ref_stk), std::exception); + + /** @arg 证券列表数量不足 */ + CHECK_THROWS_AS(MF_EqualWeight(src_inds, {sm["sh600004"]}, KQuery(-2), ref_stk), + std::exception); + + /** @arg 临界状态, 原始因子数量为1, 数据长度为2 */ + auto mf = MF_EqualWeight({MA(CLOSE())}, {sm["sh600004"], sm["sh600005"]}, KQuery(-2), ref_stk); + CHECK_EQ(mf->name(), "MF_EqualWeight"); + CHECK_THROWS_AS(mf->get(sm["sz000001"]), std::exception); + auto dates = ref_stk.getKData(KQuery(-2)).getDatetimeList(); + CHECK_EQ(mf->getDatetimeList(), dates); + auto ind1 = mf->get(sm["sh600004"]); + auto ind2 = MA(CLOSE(sm["sh600004"].getKData(KQuery(-2)))); + CHECK_UNARY(ind1.equal(ind2)); + ind1 = mf->get(sm["sh600005"]); + ind2 = MA(CLOSE(sm["sh600005"].getKData(KQuery(-2)))); + CHECK_UNARY(ind1.equal(ind2)); + auto ic1 = mf->getIC(1); + auto ic2 = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-2), 1, ref_stk); + CHECK_UNARY(ic1.equal(ic2)); + + // HKU_INFO("{}", mf->getDatetimeList()); + + // CHECK_EQ(mf->name(), "MF_EqualWeight"); + // CHECK(mf->get(sm["sh600004"]).empty()); + // MFPtr mf = MF_EqualWeight(src_inds, stks, + // query, ref_stk); mf->calculate(); +} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_MF_EqualWeight_benchmark") { + // Stock stock = getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(0)); + // Indicator c = kdata.close(); + // int cycle = 1000; // 测试循环次数 + + // { + // BENCHMARK_TIME_MSG(test_ABS_benchmark, cycle, fmt::format("data len: {}", c.size())); + // SPEND_TIME_CONTROL(false); + // for (int i = 0; i < cycle; i++) { + // Indicator ind = ABS(); + // Indicator result = ind(c); + // } + // } +} +#endif + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_MF_EqualWeight_export") { + // StockManager& sm = StockManager::instance(); + // string filename(sm.tmpdir()); + // filename += "/ABS.xml"; + + // Stock stock = sm.getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(-20)); + // Indicator x1 = ABS(CLOSE(kdata)); + // { + // std::ofstream ofs(filename); + // boost::archive::xml_oarchive oa(ofs); + // oa << BOOST_SERIALIZATION_NVP(x1); + // } + + // Indicator x2; + // { + // std::ifstream ifs(filename); + // boost::archive::xml_iarchive ia(ifs); + // ia >> BOOST_SERIALIZATION_NVP(x2); + // } + + // CHECK_UNARY(x1.size() == x2.size()); + // CHECK_UNARY(x1.discard() == x2.discard()); + // CHECK_UNARY(x1.getResultNumber() == x2.getResultNumber()); + // for (size_t i = 0; i < x1.size(); ++i) { + // CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); + // } +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ From 7fa63b00d9d8d5d416f79cf4fe42f1aa35492ed0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 03:51:55 +0800 Subject: [PATCH 034/601] update --- hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index e73c9ed9..384491ca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -121,6 +121,9 @@ Indicator MultiFactorBase::getIC(int ndays) { result.name("IC"); if (ndays + 1 >= days_total) { result.setDiscard(days_total); + if (ic_n == ndays) { + m_ic = result; + } return result; } @@ -139,6 +142,9 @@ Indicator MultiFactorBase::getIC(int ndays) { if (discard >= days_total) { result.setDiscard(days_total); + if (ic_n == ndays) { + m_ic = result; + } return result; } @@ -224,7 +230,6 @@ void MultiFactorBase::_buildCrossSession() { } void MultiFactorBase::calculate() { - SPEND_TIME(MultiFactorBase_calculate); std::lock_guard lock(m_mutex); HKU_IF_RETURN(m_calculated, void()); From 97c24af203cf353151ebbbd36da106acba040428 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 14:32:27 +0800 Subject: [PATCH 035/601] update --- .../hikyuu/trade_sys/factor/MultiFactorBase.cpp | 1 + .../hikyuu/trade_sys/factor/MultiFactorBase.h | 7 +++++++ .../trade_sys/factor/test_MF_EqualWeight.cpp | 15 +++++++++++++++ 3 files changed, 23 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 384491ca..d9857143 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -93,6 +93,7 @@ const Indicator& MultiFactorBase::get(const Stock& stk) { std::lock_guard lock(m_mutex); const auto iter = m_stk_map.find(stk); HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); + HKU_CHECK(iter->second <= m_all_factors.size(), "存在错误"); return m_all_factors[iter->second]; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index bdffc41d..e2254e9f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -46,6 +46,13 @@ public: /** 获取指定证券合成因子 */ const Indicator& get(const Stock&); + /** + * 获取所有证券合成后的新因子,顺序与传入的证券组合相同 + */ + const IndicatorList& getAll() const { + return m_all_factors; + } + /** 获取指定日期截面的所有因子值 */ const vector>& get(const Datetime&); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 52b20bd8..9cfb394a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -65,6 +65,21 @@ TEST_CASE("test_MF_EqualWeight") { auto ic2 = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-2), 1, ref_stk); CHECK_UNARY(ic1.equal(ic2)); + mf = MF_EqualWeight({MA(CLOSE())}, {sm["sh600004"], sm["sh600005"]}, KQuery(-3), ref_stk); + CHECK_EQ(mf->name(), "MF_EqualWeight"); + CHECK_THROWS_AS(mf->get(sm["sz000001"]), std::exception); + // dates = ref_stk.getKData(KQuery(-3)).getDatetimeList(); + // CHECK_EQ(mf->getDatetimeList(), dates); + // ind1 = mf->get(sm["sh600004"]); + // ind2 = MA(CLOSE(sm["sh600004"].getKData(KQuery(-3)))); + // CHECK_UNARY(ind1.equal(ind2)); + // ind1 = mf->get(sm["sh600005"]); + // ind2 = MA(CLOSE(sm["sh600005"].getKData(KQuery(-3)))); + // CHECK_UNARY(ind1.equal(ind2)); + // ic1 = mf->getIC(1); + // ic2 = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-3), 1, ref_stk); + // CHECK_UNARY(ic1.equal(ic2)); + // HKU_INFO("{}", mf->getDatetimeList()); // CHECK_EQ(mf->name(), "MF_EqualWeight"); From ba2849f291189f2b9b1f7220f34346a8c0380cbf Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 14:33:45 +0800 Subject: [PATCH 036/601] =?UTF-8?q?fixed=20python=20=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/util/mylog.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu/util/mylog.py b/hikyuu/util/mylog.py index 217448f2..6b3c7c04 100644 --- a/hikyuu/util/mylog.py +++ b/hikyuu/util/mylog.py @@ -31,8 +31,8 @@ hku_logger_name = 'hikyuu' hku_logger = logging.getLogger(hku_logger_name) _usrdir = os.path.expanduser("~") -if not os.path.lexists(_usrdir): - os.makedirs(_usrdir) +if not os.path.lexists(f"{_usrdir}/.hikyuu"): + os.makedirs(f"{_usrdir}/.hikyuu") _logfile = logging.handlers.RotatingFileHandler( f"{_usrdir}/.hikyuu/hikyuu_py.log", maxBytes=10240, backupCount=3, encoding="utf-8") _logfile.setFormatter(logging.Formatter(FORMAT)) From 085d82e6f9ec59f91aaa5adf4a61e78b6b53b9c7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 17:38:14 +0800 Subject: [PATCH 037/601] update --- hikyuu_cpp/hikyuu/indicator/crt/ROCP.h | 2 +- hikyuu_cpp/hikyuu/indicator/crt/ROCR.h | 2 +- .../trade_sys/factor/MultiFactorBase.cpp | 40 ++++++++---- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 17 ++--- hikyuu_cpp/unit_test/hikyuu/test_config.h | 2 +- .../trade_sys/factor/test_MF_EqualWeight.cpp | 63 +++++++++++++++---- 6 files changed, 93 insertions(+), 33 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ROCP.h b/hikyuu_cpp/hikyuu/indicator/crt/ROCP.h index 917189d2..83f198f8 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ROCP.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ROCP.h @@ -16,7 +16,7 @@ namespace hku { /** - * 变动率指标 (price - prePrice) / prevPrice + * 变动率指标 (price - prePrice) / prevPrice N 日收益率 (本金之外的盈利) * @ingroup Indicator */ Indicator HKU_API ROCP(int n = 10); diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ROCR.h b/hikyuu_cpp/hikyuu/indicator/crt/ROCR.h index d0bb29ca..bdebad3f 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ROCR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ROCR.h @@ -16,7 +16,7 @@ namespace hku { /** - * 变动率指标 (price / prevPrice) + * 变动率指标 (price / prevPrice), N 日累积收益率 (包含本金) * @ingroup Indicator */ Indicator HKU_API ROCR(int n = 10); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index d9857143..78061749 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -75,6 +75,7 @@ MultiFactorPtr MultiFactorBase::clone() { p->m_stk_factor_by_date = m_stk_factor_by_date; p->m_ref_dates = m_ref_dates; p->m_ic = m_ic.clone(); + p->m_calculated = m_calculated; p->m_inds.reserve(m_inds.size()); for (const auto& ind : m_inds) { @@ -88,23 +89,31 @@ MultiFactorPtr MultiFactorBase::clone() { return p; } -const Indicator& MultiFactorBase::get(const Stock& stk) { +const Indicator& MultiFactorBase::getFactor(const Stock& stk) { calculate(); - std::lock_guard lock(m_mutex); const auto iter = m_stk_map.find(stk); HKU_CHECK(iter != m_stk_map.cend(), "Could not find this stock: {}", stk); - HKU_CHECK(iter->second <= m_all_factors.size(), "存在错误"); return m_all_factors[iter->second]; } -const vector>& MultiFactorBase::get(const Datetime& d) { +const IndicatorList& MultiFactorBase::getAllFactors() { + calculate(); + return m_all_factors; +} + +const vector>& MultiFactorBase::getCross( + const Datetime& d) { calculate(); - std::lock_guard lock(m_mutex); const auto iter = m_date_index.find(d); HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); return m_stk_factor_by_date[iter->second]; } +const vector>>& MultiFactorBase::getAllCross() { + calculate(); + return m_stk_factor_by_date; +} + Indicator MultiFactorBase::getIC(int ndays) { calculate(); std::lock_guard lock(m_mutex); @@ -206,9 +215,9 @@ vector MultiFactorBase::_alignAllInds() { return all_stk_inds; } -void MultiFactorBase::_buildCrossSession() { +void MultiFactorBase::_buildIndex() { size_t stk_count = m_stks.size(); - HKU_TRACE_IF(stk_count != m_all_factors.size(), "some errors"); + HKU_ASSERT(stk_count == m_all_factors.size()); for (size_t i = 0; i < stk_count; i++) { m_stk_map[m_stks[i]] = i; } @@ -216,21 +225,30 @@ void MultiFactorBase::_buildCrossSession() { // 建立每日截面的索引,并每日降序排序 size_t days_total = m_ref_dates.size(); m_stk_factor_by_date.resize(days_total); - vector> one_day(stk_count); + vector> one_day; for (size_t i = 0; i < days_total; i++) { + one_day.resize(stk_count); for (size_t j = 0; j < stk_count; j++) { - one_day[i] = std::make_pair(m_stks[j], m_all_factors[j][i]); + one_day[j] = std::make_pair(m_stks[j], m_all_factors[j][i]); } std::sort(one_day.begin(), one_day.end(), [](const std::pair& a, const std::pair& b) { + if (std::isnan(a.second) && std::isnan(b.second)) { + return false; + } else if (!std::isnan(a.second) && std::isnan(b.second)) { + return true; + } else if (std::isnan(a.second) && !std::isnan(b.second)) { + return false; + } return a.second > b.second; }); - m_stk_factor_by_date[i] = one_day; + m_stk_factor_by_date[i] = std::move(one_day); m_date_index[m_ref_dates[i]] = i; } } void MultiFactorBase::calculate() { + SPEND_TIME(MultiFactorBase_calculate); std::lock_guard lock(m_mutex); HKU_IF_RETURN(m_calculated, void()); @@ -252,7 +270,7 @@ void MultiFactorBase::calculate() { } // 计算完成后创建截面索引 - _buildCrossSession(); + _buildIndex(); } catch (const std::exception& e) { HKU_ERROR(e.what()); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index e2254e9f..37a4c4f5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -44,17 +44,18 @@ public: } /** 获取指定证券合成因子 */ - const Indicator& get(const Stock&); + const Indicator& getFactor(const Stock&); /** * 获取所有证券合成后的新因子,顺序与传入的证券组合相同 */ - const IndicatorList& getAll() const { - return m_all_factors; - } + const IndicatorList& getAllFactors(); - /** 获取指定日期截面的所有因子值 */ - const vector>& get(const Datetime&); + /** 获取指定日期截面的所有因子值,已经降序排列 */ + const vector>& getCross(const Datetime&); + + /** 获取所有截面数据,已按降序排列 */ + const vector>>& getAllCross(); /** 获取合成因子的IC */ Indicator getIC(int ndays); @@ -73,7 +74,7 @@ public: protected: vector _alignAllInds(); - void _buildCrossSession(); // 计算完成后创建截面索引 + void _buildIndex(); // 计算完成后创建截面索引 IndicatorList _getAllReturns(int ndays) const; protected: @@ -114,6 +115,7 @@ private: ar& BOOST_SERIALIZATION_NVP(m_date_index); ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); ar& BOOST_SERIALIZATION_NVP(m_ic); + ar& BOOST_SERIALIZATION_NVP(m_calculated); } template @@ -129,6 +131,7 @@ private: ar& BOOST_SERIALIZATION_NVP(m_date_index); ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); ar& BOOST_SERIALIZATION_NVP(m_ic); + ar& BOOST_SERIALIZATION_NVP(m_calculated); } BOOST_SERIALIZATION_SPLIT_MEMBER() diff --git a/hikyuu_cpp/unit_test/hikyuu/test_config.h b/hikyuu_cpp/unit_test/hikyuu/test_config.h index 4ebfbde0..ee5418d8 100644 --- a/hikyuu_cpp/unit_test/hikyuu/test_config.h +++ b/hikyuu_cpp/unit_test/hikyuu/test_config.h @@ -12,4 +12,4 @@ using namespace hku; -#define ENABLE_BENCHMARK_TEST 0 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file +#define ENABLE_BENCHMARK_TEST 1 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 9cfb394a..7229e3d7 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -12,7 +12,9 @@ #include #include #include +#include #include +#include #include using namespace hku; @@ -49,25 +51,62 @@ TEST_CASE("test_MF_EqualWeight") { CHECK_THROWS_AS(MF_EqualWeight(src_inds, {sm["sh600004"]}, KQuery(-2), ref_stk), std::exception); - /** @arg 临界状态, 原始因子数量为1, 数据长度为2 */ - auto mf = MF_EqualWeight({MA(CLOSE())}, {sm["sh600004"], sm["sh600005"]}, KQuery(-2), ref_stk); + /** @arg 临界状态, 原始因子数量为1, 证券数量2, 数据长度为2 */ + src_inds = {MA(CLOSE())}; + stks = {sm["sh600005"], sm["sh600004"]}; + query = KQuery(-2); + ref_k = ref_stk.getKData(query); + ref_dates = ref_k.getDatetimeList(); + auto mf = MF_EqualWeight(src_inds, stks, query, ref_stk); CHECK_EQ(mf->name(), "MF_EqualWeight"); - CHECK_THROWS_AS(mf->get(sm["sz000001"]), std::exception); - auto dates = ref_stk.getKData(KQuery(-2)).getDatetimeList(); - CHECK_EQ(mf->getDatetimeList(), dates); - auto ind1 = mf->get(sm["sh600004"]); - auto ind2 = MA(CLOSE(sm["sh600004"].getKData(KQuery(-2)))); + CHECK_THROWS_AS(mf->getFactor(sm["sz000001"]), std::exception); + CHECK_EQ(mf->getDatetimeList(), ref_dates); + + const auto& all_factors = mf->getAllFactors(); + CHECK_EQ(all_factors.size(), 2); + auto ind1 = mf->getFactor(sm["sh600004"]); + HKU_INFO("{}", ind1); + auto ind2 = MA(CLOSE(sm["sh600004"].getKData(query))); CHECK_UNARY(ind1.equal(ind2)); - ind1 = mf->get(sm["sh600005"]); - ind2 = MA(CLOSE(sm["sh600005"].getKData(KQuery(-2)))); + CHECK_UNARY(all_factors[1].equal(ind2)); + ind1 = mf->getFactor(sm["sh600005"]); + ind2 = MA(CLOSE(sm["sh600005"].getKData(query))); CHECK_UNARY(ind1.equal(ind2)); + CHECK_UNARY(all_factors[0].equal(ind2)); auto ic1 = mf->getIC(1); - auto ic2 = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-2), 1, ref_stk); + auto ic2 = IC(MA(CLOSE()), stks, query, 1, ref_stk); CHECK_UNARY(ic1.equal(ic2)); - mf = MF_EqualWeight({MA(CLOSE())}, {sm["sh600004"], sm["sh600005"]}, KQuery(-3), ref_stk); + CHECK_THROWS_AS(mf->getCross(Datetime(20111204)), std::exception); + auto cross = mf->getCross(Datetime(20111205)); + cross = mf->getCross(Datetime(20111206)); + for (size_t i = 0; i < cross.size(); i++) { + HKU_INFO("{}: {}, {}", i, cross[i].first.market_code(), cross[i].second); + } + for (size_t i = 0; i < ref_dates.size(); i++) { + HKU_INFO("{}: {}", i, ref_dates[i]); + } + + /** @arg 原始因子数量为3, 证券数量4, 数据长度为20, 指定比较收益率 3 日 */ + int ndays = 3; + src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays), EMA(ROCR(CLOSE(), ndays)))}; + stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + query = KQuery(-20); + ref_k = ref_stk.getKData(query); + ref_dates = ref_k.getDatetimeList(); + mf = MF_EqualWeight(src_inds, stks, query, ref_stk); CHECK_EQ(mf->name(), "MF_EqualWeight"); - CHECK_THROWS_AS(mf->get(sm["sz000001"]), std::exception); + CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception); + ind1 = mf->getFactor(sm["sh600005"]); + ind2 = MA(ROCR(CLOSE(sm["sh600005"].getKData(query)), ndays)); + HKU_INFO("{}", ind1); + HKU_INFO("{}", ind2); + + ind1 = mf->getFactor(sm["sh600004"]); + ind2 = MA(ROCR(CLOSE(sm["sh600004"].getKData(query)), ndays)); + HKU_INFO("{}", ind1); + HKU_INFO("{}", ind2); + // dates = ref_stk.getKData(KQuery(-3)).getDatetimeList(); // CHECK_EQ(mf->getDatetimeList(), dates); // ind1 = mf->get(sm["sh600004"]); From 33c252cabd97690850e4562a8143d648aedf8daa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 17:43:15 +0800 Subject: [PATCH 038/601] =?UTF-8?q?fixed=20get=5Ftrans=5Flist=20=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp index 0ae63738..67dcd61b 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp @@ -294,7 +294,7 @@ TransList MySQLKDataDriver::_getTransListByDate(const string& market, const stri SQLStatementPtr st = m_connect->getStatement( fmt::format("select `date`, `price`, `vol`, `buyorsell` from {} where date >= {} and " "date < {} order by date", - table, query.startDatetime().number(), query.endDatetime().number())); + table, query.startDatetime().ymdhms(), query.endDatetime().ymdhms())); m_connect->transaction(); st->exec(); From cf22c8517fcb902dfcadec5db80e2eb02a5f9dbd Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 14 Mar 2024 18:21:32 +0800 Subject: [PATCH 039/601] update --- hikyuu_cpp/unit_test/hikyuu/test_config.h | 2 +- .../trade_sys/factor/test_MF_EqualWeight.cpp | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/test_config.h b/hikyuu_cpp/unit_test/hikyuu/test_config.h index ee5418d8..4ebfbde0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/test_config.h +++ b/hikyuu_cpp/unit_test/hikyuu/test_config.h @@ -12,4 +12,4 @@ using namespace hku; -#define ENABLE_BENCHMARK_TEST 1 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file +#define ENABLE_BENCHMARK_TEST 0 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 7229e3d7..8947b871 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -25,6 +25,34 @@ using namespace hku; * @{ */ +TEST_CASE("test_nan_sort_in_MF") { + PriceList t(10, Null()); + t[0] = 1; + t[2] = 3; + t[3] = 4; + t[8] = 5; + + std::sort(t.begin(), t.end(), [](price_t a, price_t b) { + if (std::isnan(a) && std::isnan(b)) { + return false; + } else if (!std::isnan(a) && std::isnan(b)) { + return true; + } else if (std::isnan(a) && !std::isnan(b)) { + return false; + } + return a > b; + }); + + PriceList expect(10, Null()); + CHECK_EQ(t[0], 5); + CHECK_EQ(t[1], 4); + CHECK_EQ(t[2], 3); + CHECK_EQ(t[3], 1); + for (size_t i = 4, len = t.size(); i < len; i++) { + CHECK_UNARY(std::isnan(t[i])); + } +} + /** @par 检测点 */ TEST_CASE("test_MF_EqualWeight") { StockManager& sm = StockManager::instance(); From f7b80fb44611cdc5e4caddc4b30e98451a48243c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 15 Mar 2024 02:31:58 +0800 Subject: [PATCH 040/601] MultiFactor (continue) --- hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp | 2 +- .../trade_sys/factor/MultiFactorBase.cpp | 69 +++++++- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 77 ++++++--- .../factor/imp/EqualWeightMultiFactor.cpp | 28 +++- hikyuu_cpp/unit_test/hikyuu/test_config.h | 2 +- .../trade_sys/factor/test_MF_EqualWeight.cpp | 147 +++++++++--------- 6 files changed, 220 insertions(+), 105 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp index cffe9d5d..2690ca2b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp @@ -15,7 +15,7 @@ BOOST_CLASS_EXPORT(hku::IEma) namespace hku { -IEma::IEma() : IndicatorImp("IEma", 1) { +IEma::IEma() : IndicatorImp("EMA", 1) { setParam("n", 22); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 78061749..aa345761 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -16,6 +16,63 @@ namespace hku { +HKU_API std::ostream& operator<<(std::ostream& out, + const std::pair& td) { + out << std::fixed; + out.precision(4); + out << "(" << td.first.market_code() << ", " << td.second << ")" << std::endl; + out.unsetf(std::ostream::floatfield); + out.precision(); + return out; +} + +HKU_API std::ostream& operator<<(std::ostream& out, + const vector>& td) { + out << std::fixed; + out.precision(4); + size_t total = td.size(); + if (total < 10) { + for (size_t i = 0; i < total; i++) { + out << i << ": " << td[i]; + } + } else { + for (size_t i = 0; i < 5; i++) { + out << i << ": " << td[i]; + } + out << "......" << std::endl; + for (size_t i = total - 5; i < total; i++) { + out << i << ": " << td[i]; + } + } + out.unsetf(std::ostream::floatfield); + out.precision(); + return out; +} + +HKU_API std::ostream& operator<<( + std::ostream& out, const vector>>& td) { + out << std::fixed; + out.precision(4); + size_t total = td.size(); + if (total <= 2) { + for (size_t i = 0; i < total; i++) { + out << "========= " << i << " =========" << std::endl; + out << td[i]; + } + } else { + out << "========= 0 =========" << std::endl; + out << td[0]; + out << "......" << std::endl; + out << "......" << std::endl; + out << "========= " << total - 1 << " =========" << std::endl; + out << td[total - 1]; + } + + out.unsetf(std::ostream::floatfield); + out.precision(); + return out; +} + MultiFactorBase::MultiFactorBase() { setParam("fill_null", true); setParam("ic_n", 1); @@ -124,12 +181,15 @@ Indicator MultiFactorBase::getIC(int ndays) { // 通过IC/ICIR计算权重的情况较多,所以这里直接缓存一份,减少重复计算 // 实际使用时,最好保证 getIC(ndays) 中的 ndays 和 ic_n 一致 int ic_n = getParam("ic_n"); + if (ndays == 0) { + ndays = ic_n; + } HKU_IF_RETURN(ic_n == ndays && !m_ic.empty(), m_ic.clone()); size_t days_total = m_ref_dates.size(); Indicator result = PRICELIST(PriceList(days_total, Null())); result.name("IC"); - if (ndays + 1 >= days_total) { + if (ndays + 1 >= days_total || ndays < 0) { result.setDiscard(days_total); if (ic_n == ndays) { m_ic = result; @@ -206,9 +266,10 @@ vector MultiFactorBase::_alignAllInds() { for (size_t i = 0; i < stk_count; i++) { const auto& stk = m_stks[i]; auto kdata = stk.getKData(m_query); - all_stk_inds[i].resize(ind_count); + auto& cur_stk_inds = all_stk_inds[i]; + cur_stk_inds.resize(ind_count); for (size_t j = 0; j < ind_count; j++) { - all_stk_inds[i][j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); + cur_stk_inds[j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); } } @@ -248,7 +309,7 @@ void MultiFactorBase::_buildIndex() { } void MultiFactorBase::calculate() { - SPEND_TIME(MultiFactorBase_calculate); + // SPEND_TIME(MultiFactorBase_calculate); std::lock_guard lock(m_mutex); HKU_IF_RETURN(m_calculated, void()); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 37a4c4f5..b200c15c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -57,14 +57,21 @@ public: /** 获取所有截面数据,已按降序排列 */ const vector>>& getAllCross(); - /** 获取合成因子的IC */ - Indicator getIC(int ndays); + /** + * 获取合成因子的IC + * @note ndays 对于使用 IC/ICIR 加权的新因子,最好保持好 ic_n 一致, + * 但对于等权计算的新因子,不一定非要使用 ic_n 计算。 + * 所以,ndays 增加了一个特殊值 0, 表示直接使用 ic_n 参数计算 IC + * @param ndays 计算相对 ndays 日收益率的IC值 + */ + Indicator getIC(int ndays = 0); - /** 获取合成因子的 ICIR */ - Indicator getICIR(int ic_n, int ir_n); - - /** 执行计算 */ - void calculate(); + /** + * 获取合成因子的 ICIR + * @param ir_n 计算 IR 的 n 窗口 + * @param ic_n 计算 IC 的 n 窗口 + */ + Indicator getICIR(int ir_n, int ic_n = 0); typedef std::shared_ptr MultiFactorPtr; MultiFactorPtr clone(); @@ -72,6 +79,10 @@ public: virtual MultiFactorPtr _clone() = 0; virtual IndicatorList _calculate(const vector&) = 0; +private: + /** 执行计算 */ + void calculate(); + protected: vector _alignAllInds(); void _buildIndex(); // 计算完成后创建截面索引 @@ -105,33 +116,36 @@ private: template void save(Archive& ar, const unsigned int version) const { ar& BOOST_SERIALIZATION_NVP(m_name); + ar& BOOST_SERIALIZATION_NVP(m_params); ar& BOOST_SERIALIZATION_NVP(m_inds); ar& BOOST_SERIALIZATION_NVP(m_stks); ar& BOOST_SERIALIZATION_NVP(m_ref_stk); ar& BOOST_SERIALIZATION_NVP(m_query); ar& BOOST_SERIALIZATION_NVP(m_ref_dates); - ar& BOOST_SERIALIZATION_NVP(m_stk_map); - ar& BOOST_SERIALIZATION_NVP(m_all_factors); - ar& BOOST_SERIALIZATION_NVP(m_date_index); - ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); - ar& BOOST_SERIALIZATION_NVP(m_ic); - ar& BOOST_SERIALIZATION_NVP(m_calculated); + // 以下不需要保存,加载后重新计算 + // ar& BOOST_SERIALIZATION_NVP(m_stk_map); + // ar& BOOST_SERIALIZATION_NVP(m_all_factors); + // ar& BOOST_SERIALIZATION_NVP(m_date_index); + // ar& BOOST_SERIALIZATION_NVP(m_ic); + // ar& BOOST_SERIALIZATION_NVP(m_calculated); + // ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); } template void load(Archive& ar, const unsigned int version) { ar& BOOST_SERIALIZATION_NVP(m_name); + ar& BOOST_SERIALIZATION_NVP(m_params); ar& BOOST_SERIALIZATION_NVP(m_inds); ar& BOOST_SERIALIZATION_NVP(m_stks); ar& BOOST_SERIALIZATION_NVP(m_ref_stk); ar& BOOST_SERIALIZATION_NVP(m_query); ar& BOOST_SERIALIZATION_NVP(m_ref_dates); - ar& BOOST_SERIALIZATION_NVP(m_stk_map); - ar& BOOST_SERIALIZATION_NVP(m_all_factors); - ar& BOOST_SERIALIZATION_NVP(m_date_index); - ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); - ar& BOOST_SERIALIZATION_NVP(m_ic); - ar& BOOST_SERIALIZATION_NVP(m_calculated); + // ar& BOOST_SERIALIZATION_NVP(m_stk_map); + // ar& BOOST_SERIALIZATION_NVP(m_all_factors); + // ar& BOOST_SERIALIZATION_NVP(m_date_index); + // ar& BOOST_SERIALIZATION_NVP(m_ic); + // ar& BOOST_SERIALIZATION_NVP(m_calculated); + // ar& BOOST_SERIALIZATION_NVP(m_stk_factor_by_date); } BOOST_SERIALIZATION_SPLIT_MEMBER() @@ -178,4 +192,27 @@ public: \ } \ virtual IndicatorList _calculate(const vector&) override; -} // namespace hku \ No newline at end of file +HKU_API std::ostream& operator<<(std::ostream& out, + const std::pair& td); + +HKU_API std::ostream& operator<<(std::ostream& out, + const vector>& td); + +HKU_API std::ostream& operator<<( + std::ostream& out, const vector>>& td); + +} // namespace hku + +#if FMT_VERSION >= 90000 +template <> +struct fmt::formatter> : ostream_formatter {}; + +template <> +struct fmt::formatter>> +: ostream_formatter {}; + +template <> +struct fmt::formatter< + std::vector>>> +: ostream_formatter {}; +#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp index 8ecfdc2e..76670bc4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp @@ -29,14 +29,18 @@ vector EqualWeightMultiFactor::_calculate( size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); + value_t null_value = Null(); + vector sumByDate(days_total); + vector countByDate(days_total); vector all_factors(stk_count); for (size_t si = 0; si < stk_count; si++) { - vector sumByDate(days_total, 0.0); - vector countByDate(days_total, 0); + memset(sumByDate.data(), 0, sizeof(price_t) * days_total); + memset(countByDate.data(), 0, sizeof(size_t) * days_total); + const auto& curStkInds = all_stk_inds[si]; for (size_t di = 0; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = all_stk_inds[si][ii][di]; + const auto& value = curStkInds[ii][di]; if (!std::isnan(value)) { sumByDate[di] += value; countByDate[di] += 1; @@ -46,15 +50,23 @@ vector EqualWeightMultiFactor::_calculate( // 均值权重 for (size_t di = 0; di < days_total; di++) { - if (countByDate[di] == 0) { - sumByDate[di] = Null(); - } else { - sumByDate[di] = sumByDate[di] / countByDate[di]; - } + sumByDate[di] = (countByDate[di] == 0) ? null_value : sumByDate[di] / countByDate[di]; } all_factors[si] = PRICELIST(sumByDate); + + // 更新 discard + for (size_t di = 0; di < days_total; di++) { + if (!std::isnan(all_factors[si][di])) { + all_factors[si].setDiscard(di); + break; + } + if (di == days_total - 1 && std::isnan(all_factors[si][di])) { + all_factors[si].setDiscard(di); + } + } } + return all_factors; } diff --git a/hikyuu_cpp/unit_test/hikyuu/test_config.h b/hikyuu_cpp/unit_test/hikyuu/test_config.h index 4ebfbde0..ee5418d8 100644 --- a/hikyuu_cpp/unit_test/hikyuu/test_config.h +++ b/hikyuu_cpp/unit_test/hikyuu/test_config.h @@ -12,4 +12,4 @@ using namespace hku; -#define ENABLE_BENCHMARK_TEST 0 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file +#define ENABLE_BENCHMARK_TEST 1 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 8947b871..f9c1b93d 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -93,7 +93,6 @@ TEST_CASE("test_MF_EqualWeight") { const auto& all_factors = mf->getAllFactors(); CHECK_EQ(all_factors.size(), 2); auto ind1 = mf->getFactor(sm["sh600004"]); - HKU_INFO("{}", ind1); auto ind2 = MA(CLOSE(sm["sh600004"].getKData(query))); CHECK_UNARY(ind1.equal(ind2)); CHECK_UNARY(all_factors[1].equal(ind2)); @@ -101,23 +100,29 @@ TEST_CASE("test_MF_EqualWeight") { ind2 = MA(CLOSE(sm["sh600005"].getKData(query))); CHECK_UNARY(ind1.equal(ind2)); CHECK_UNARY(all_factors[0].equal(ind2)); - auto ic1 = mf->getIC(1); + auto ic1 = mf->getIC(); auto ic2 = IC(MA(CLOSE()), stks, query, 1, ref_stk); CHECK_UNARY(ic1.equal(ic2)); CHECK_THROWS_AS(mf->getCross(Datetime(20111204)), std::exception); auto cross = mf->getCross(Datetime(20111205)); + CHECK_EQ(cross.size(), 2); + CHECK_EQ(cross[0].first, sm["sh600004"]); + CHECK_EQ(cross[0].second, doctest::Approx(6.85)); + CHECK_EQ(cross[1].first, sm["sh600005"]); + CHECK_EQ(cross[1].second, doctest::Approx(3.13)); + cross = mf->getCross(Datetime(20111206)); - for (size_t i = 0; i < cross.size(); i++) { - HKU_INFO("{}: {}, {}", i, cross[i].first.market_code(), cross[i].second); - } - for (size_t i = 0; i < ref_dates.size(); i++) { - HKU_INFO("{}: {}", i, ref_dates[i]); - } + CHECK_EQ(cross.size(), 2); + CHECK_EQ(cross[0].first, sm["sh600004"]); + CHECK_EQ(cross[0].second, doctest::Approx(6.855)); + CHECK_EQ(cross[1].first, sm["sh600005"]); + CHECK_EQ(cross[1].second, doctest::Approx(3.14)); + // HKU_INFO("\n{}", mf->getAllCross()); /** @arg 原始因子数量为3, 证券数量4, 数据长度为20, 指定比较收益率 3 日 */ int ndays = 3; - src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays), EMA(ROCR(CLOSE(), ndays)))}; + src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), EMA(ROCR(CLOSE(), ndays))}; stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; query = KQuery(-20); ref_k = ref_stk.getKData(query); @@ -125,34 +130,19 @@ TEST_CASE("test_MF_EqualWeight") { mf = MF_EqualWeight(src_inds, stks, query, ref_stk); CHECK_EQ(mf->name(), "MF_EqualWeight"); CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception); - ind1 = mf->getFactor(sm["sh600005"]); - ind2 = MA(ROCR(CLOSE(sm["sh600005"].getKData(query)), ndays)); - HKU_INFO("{}", ind1); - HKU_INFO("{}", ind2); - ind1 = mf->getFactor(sm["sh600004"]); - ind2 = MA(ROCR(CLOSE(sm["sh600004"].getKData(query)), ndays)); - HKU_INFO("{}", ind1); - HKU_INFO("{}", ind2); - - // dates = ref_stk.getKData(KQuery(-3)).getDatetimeList(); - // CHECK_EQ(mf->getDatetimeList(), dates); - // ind1 = mf->get(sm["sh600004"]); - // ind2 = MA(CLOSE(sm["sh600004"].getKData(KQuery(-3)))); - // CHECK_UNARY(ind1.equal(ind2)); - // ind1 = mf->get(sm["sh600005"]); - // ind2 = MA(CLOSE(sm["sh600005"].getKData(KQuery(-3)))); - // CHECK_UNARY(ind1.equal(ind2)); - // ic1 = mf->getIC(1); - // ic2 = IC(MA(CLOSE()), {sm["sh600004"], sm["sh600005"]}, KQuery(-3), 1, ref_stk); - // CHECK_UNARY(ic1.equal(ic2)); - - // HKU_INFO("{}", mf->getDatetimeList()); - - // CHECK_EQ(mf->name(), "MF_EqualWeight"); - // CHECK(mf->get(sm["sh600004"]).empty()); - // MFPtr mf = MF_EqualWeight(src_inds, stks, - // query, ref_stk); mf->calculate(); + auto stk = sm["sh600004"]; + ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays)); + ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ind4 = mf->getFactor(stk); + CHECK_EQ(ind4.discard(), 3); + for (size_t i = 0; i < ind4.discard(); i++) { + CHECK_UNARY(std::isnan(ind4[i])); + } + for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { + CHECK_EQ(ind4[i], doctest::Approx((ind1[i] + ind2[i] + ind3[i]) / 3.0)); + } } //----------------------------------------------------------------------------- @@ -160,19 +150,27 @@ TEST_CASE("test_MF_EqualWeight") { //----------------------------------------------------------------------------- #if ENABLE_BENCHMARK_TEST TEST_CASE("test_MF_EqualWeight_benchmark") { - // Stock stock = getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(0)); - // Indicator c = kdata.close(); - // int cycle = 1000; // 测试循环次数 + StockManager& sm = StockManager::instance(); + int ndays = 3; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(0); + Stock ref_stk = sm["sh000001"]; + auto ref_k = ref_stk.getKData(query); + auto ref_dates = ref_k.getDatetimeList(); - // { - // BENCHMARK_TIME_MSG(test_ABS_benchmark, cycle, fmt::format("data len: {}", c.size())); - // SPEND_TIME_CONTROL(false); - // for (int i = 0; i < cycle; i++) { - // Indicator ind = ABS(); - // Indicator result = ind(c); - // } - // } + int cycle = 10; // 测试循环次数 + + { + BENCHMARK_TIME_MSG(test_MF_EqualWeight_benchmark, cycle, + fmt::format("data len: {}", ref_k.size())); + SPEND_TIME_CONTROL(false); + for (int i = 0; i < cycle; i++) { + auto mf = MF_EqualWeight(src_inds, stks, query, ref_stk); + auto ic = mf->getIC(); + } + } } #endif @@ -183,32 +181,39 @@ TEST_CASE("test_MF_EqualWeight_benchmark") { /** @par 检测点 */ TEST_CASE("test_MF_EqualWeight_export") { - // StockManager& sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/ABS.xml"; + StockManager& sm = StockManager::instance(); + int ndays = 3; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(0); + Stock ref_stk = sm["sh000001"]; + auto ref_k = ref_stk.getKData(query); + auto ref_dates = ref_k.getDatetimeList(); - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator x1 = ABS(CLOSE(kdata)); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(x1); - // } + string filename(sm.tmpdir()); + filename += "/MF_EqualWeight.xml"; - // Indicator x2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(x2); - // } + auto mf1 = MF_EqualWeight(src_inds, stks, query, ref_stk); + auto ic1 = mf1->getIC(); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(mf1); + } - // CHECK_UNARY(x1.size() == x2.size()); - // CHECK_UNARY(x1.discard() == x2.discard()); - // CHECK_UNARY(x1.getResultNumber() == x2.getResultNumber()); - // for (size_t i = 0; i < x1.size(); ++i) { - // CHECK_EQ(x1[i], doctest::Approx(x2[i]).epsilon(0.00001)); - // } + MFPtr mf2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(mf2); + } + + CHECK_EQ(mf2->name(), mf1->name()); + auto ic2 = mf2->getIC(); + CHECK_EQ(ic1.size(), ic2.size()); + CHECK_EQ(ic1.discard(), ic2.discard()); + CHECK_UNARY(ic1.equal(ic2)); } #endif /* #if HKU_SUPPORT_SERIALIZATION */ From 7e40666f1bc3b1bad2a156a53e3a274f14d6fae4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 15 Mar 2024 03:16:55 +0800 Subject: [PATCH 041/601] MultiFactor (continue) --- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 4 ++++ .../trade_sys/factor/crt/MF_ICIRWeight.h | 11 +++++++++- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 5 +++++ hikyuu_cpp/unit_test/hikyuu/test_config.h | 2 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 20 ++++++++++++++----- hikyuu_pywrap/trade_sys/trade_sys_main.cpp | 2 ++ 6 files changed, 37 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index b200c15c..ed56e0a1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -43,6 +43,10 @@ public: return m_ref_dates; } + const KQuery& getQuery() const { + return m_query; + } + /** 获取指定证券合成因子 */ const Indicator& getFactor(const Stock&); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h index d184e486..8e22aaef 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h @@ -5,4 +5,13 @@ * Author: fasiondog */ -#pragma once \ No newline at end of file +#pragma once + +#include "../MultiFactorBase.h" + +namespace hku { + +MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk, int ic_n = 5); + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index ad0a1d0e..f24f069a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -79,4 +79,9 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i return all_factors; } +MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, + const KQuery& query, const Stock& ref_stk, int ic_n) { + return make_shared(inds, stks, query, ref_stk, ic_n); +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/test_config.h b/hikyuu_cpp/unit_test/hikyuu/test_config.h index ee5418d8..4ebfbde0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/test_config.h +++ b/hikyuu_cpp/unit_test/hikyuu/test_config.h @@ -12,4 +12,4 @@ using namespace hku; -#define ENABLE_BENCHMARK_TEST 1 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file +#define ENABLE_BENCHMARK_TEST 0 // 是否开启性能测试相关用例执行,默认不开启 \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 25aa6bf5..dfaf06d1 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -38,11 +38,10 @@ void export_MultiFactor(py::module& m) { // .def("__str__", to_py_str) // .def("__repr__", to_py_str) - // .def_property("name", py::overload_cast<>(&EnvironmentBase::name, py::const_), - // py::overload_cast(&EnvironmentBase::name), - // py::return_value_policy::copy, "名称") - // .def_property("query", &EnvironmentBase::getQuery, &EnvironmentBase::setQuery, - // py::return_value_policy::copy, "设置或获取查询条件") + .def_property("name", py::overload_cast<>(&MultiFactorBase::name, py::const_), + py::overload_cast(&MultiFactorBase::name), + py::return_value_policy::copy, "名称") + .def("get_query", &MultiFactorBase::getQuery, py::return_value_policy::copy) .def("get_param", &MultiFactorBase::getParam, R"(get_param(self, name) @@ -82,4 +81,15 @@ void export_MultiFactor(py::module& m) { // &EnvironmentBase::_calculate, "【重载接口】子类计算接口") DEF_PICKLE(MultiFactorPtr); + + m.def( + "MF_EqualWeight", + [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, + const Stock& ref_stk, int ic_n) { + // MF_EqualWeight + IndicatorList c_inds = python_list_to_vector(inds); + StockList c_stks = python_list_to_vector(stks); + return MF_EqualWeight(c_inds, c_stks, query, ref_stk, ic_n); + }, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5); } \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/trade_sys_main.cpp b/hikyuu_pywrap/trade_sys/trade_sys_main.cpp index f58c86de..fba234b9 100644 --- a/hikyuu_pywrap/trade_sys/trade_sys_main.cpp +++ b/hikyuu_pywrap/trade_sys/trade_sys_main.cpp @@ -20,6 +20,7 @@ void export_System(py::module& m); void export_Selector(py::module& m); void export_Portfolio(py::module& m); void export_AllocateFunds(py::module& m); +void export_MultiFactor(py::module& m); void export_trade_sys_main(py::module& m) { export_Environment(m); @@ -33,4 +34,5 @@ void export_trade_sys_main(py::module& m) { export_Selector(m); export_AllocateFunds(m); export_Portfolio(m); + export_MultiFactor(m); } From 2efcc6577c017d5794939bcb355d19f1e9c3c741 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 15 Mar 2024 21:19:56 +0800 Subject: [PATCH 042/601] MultiFactor (continue) --- .../trade_sys/factor/MultiFactorBase.cpp | 43 +++++++++++++ .../hikyuu/trade_sys/factor/MultiFactorBase.h | 10 ++++ .../trade_sys/factor/test_MF_EqualWeight.cpp | 2 +- .../trade_sys/factor/test_MF_ICIRWeigtht.cpp | 58 ++++++++++++++++++ .../trade_sys/factor/test_MF_ICWeight.cpp | 58 ++++++++++++++++++ hikyuu_pywrap/trade_sys/_Environment.cpp | 2 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 60 +++++++++++++------ 7 files changed, 212 insertions(+), 21 deletions(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index aa345761..ac4cad04 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include #include "hikyuu/indicator/crt/ALIGN.h" #include "hikyuu/indicator/crt/ROCP.h" #include "hikyuu/indicator/crt/REF.h" @@ -16,6 +17,47 @@ namespace hku { +HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorBase& mf) { + out << "MultiFactor{" + << "\n name: " << mf.name() << "\n params: " << mf.getParameter() + << "\n query: " << mf.getQuery() << "\n ref stock: " << mf.m_ref_stk; + + out << "\n src inds count: " << mf.m_inds.size() << " ["; + if (mf.m_inds.size() <= 5) { + for (const auto& ind : mf.m_inds) { + out << ind.name() << ", "; + } + } else { + for (size_t i = 0; i < 5; i++) { + out << mf.m_inds[i].name() << ", "; + } + out << "......"; + } + out << "]"; + + out << "\n stocks count: " << mf.m_stks.size() << " ["; + size_t print_stk_len = std::min(5, mf.m_stks.size()); + for (size_t i = 0; i < print_stk_len; i++) { + out << mf.m_stks[i].market_code() << ", "; + } + if (mf.m_stks.size() > 5) { + out << "......"; + } + out << "]"; + + out << "\n}"; + return out; +} + +HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorPtr& mf) { + if (mf) { + out << *mf; + } else { + out << "MultiFactor(NULL)"; + } + return out; +} + HKU_API std::ostream& operator<<(std::ostream& out, const std::pair& td) { out << std::fixed; @@ -270,6 +312,7 @@ vector MultiFactorBase::_alignAllInds() { cur_stk_inds.resize(ind_count); for (size_t j = 0; j < ind_count; j++) { cur_stk_inds[j] = ALIGN(m_inds[j](kdata), m_ref_dates, fill_null); + cur_stk_inds[j].name(m_inds[j].name()); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index ed56e0a1..5f456181 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -22,6 +22,7 @@ class HKU_API MultiFactorBase : public enable_shared_from_this public: typedef Indicator::value_t value_t; + friend HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorBase&); public: MultiFactorBase(); @@ -196,6 +197,9 @@ public: \ } \ virtual IndicatorList _calculate(const vector&) override; +HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorBase&); +HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorPtr&); + HKU_API std::ostream& operator<<(std::ostream& out, const std::pair& td); @@ -208,6 +212,12 @@ HKU_API std::ostream& operator<<( } // namespace hku #if FMT_VERSION >= 90000 +template <> +struct fmt::formatter : ostream_formatter {}; + +template <> +struct fmt::formatter : ostream_formatter {}; + template <> struct fmt::formatter> : ostream_formatter {}; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index f9c1b93d..67d17d09 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -14,7 +14,6 @@ #include #include #include -#include #include using namespace hku; @@ -143,6 +142,7 @@ TEST_CASE("test_MF_EqualWeight") { for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { CHECK_EQ(ind4[i], doctest::Approx((ind1[i] + ind2[i] + ind3[i]) / 3.0)); } + HKU_INFO("\n{}", mf); } //----------------------------------------------------------------------------- diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp new file mode 100644 index 00000000..4ccfd342 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-15 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_MF_ICIRWeight test_MF_ICIRWeight + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +TEST_CASE("test_MF_ICIRWeight") {} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_MF_ICIRWeight_benchmark") { + StockManager& sm = StockManager::instance(); + int ndays = 3; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(0); + Stock ref_stk = sm["sh000001"]; + auto ref_k = ref_stk.getKData(query); + auto ref_dates = ref_k.getDatetimeList(); + + int cycle = 10; // 测试循环次数 + + { + BENCHMARK_TIME_MSG(test_MF_ICIRWeight_benchmark, cycle, + fmt::format("data len: {}", ref_k.size())); + SPEND_TIME_CONTROL(false); + for (int i = 0; i < cycle; i++) { + auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk); + auto ic = mf->getIC(); + } + } +} +#endif + +/** @} */ \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp new file mode 100644 index 00000000..cfee211e --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-15 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_MF_ICWeight test_MF_ICWeight + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +TEST_CASE("test_MF_ICWeight") {} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_MF_ICWeight_benchmark") { + StockManager& sm = StockManager::instance(); + int ndays = 3; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(0); + Stock ref_stk = sm["sh000001"]; + auto ref_k = ref_stk.getKData(query); + auto ref_dates = ref_k.getDatetimeList(); + + int cycle = 10; // 测试循环次数 + + { + BENCHMARK_TIME_MSG(test_MF_ICWeight_benchmark, cycle, + fmt::format("data len: {}", ref_k.size())); + SPEND_TIME_CONTROL(false); + for (int i = 0; i < cycle; i++) { + auto mf = MF_ICWeight(src_inds, stks, query, ref_stk); + auto ic = mf->getIC(); + } + } +} +#endif + +/** @} */ \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Environment.cpp b/hikyuu_pywrap/trade_sys/_Environment.cpp index ccfd0698..11d99aba 100644 --- a/hikyuu_pywrap/trade_sys/_Environment.cpp +++ b/hikyuu_pywrap/trade_sys/_Environment.cpp @@ -72,7 +72,7 @@ void export_Environment(py::module& m) { :param value: 参数值 :raises logic_error: Unsupported type! 不支持的参数类型)") - .def("haveParam", &EnvironmentBase::haveParam, "是否存在指定参数") + .def("have_param", &EnvironmentBase::haveParam, "是否存在指定参数") .def("is_valid", &EnvironmentBase::isValid, R"(is_valid(self, datetime) diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index dfaf06d1..452a4818 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -33,10 +33,9 @@ void export_MultiFactor(py::module& m) { - _clone : 【必须】克隆接口 - _reset : 【可选】重载私有变量)") .def(py::init<>()) - // .def(py::init()) - // .def("__str__", to_py_str) - // .def("__repr__", to_py_str) + .def("__str__", to_py_str) + .def("__repr__", to_py_str) .def_property("name", py::overload_cast<>(&MultiFactorBase::name, py::const_), py::overload_cast(&MultiFactorBase::name), @@ -59,28 +58,51 @@ void export_MultiFactor(py::module& m) { :param value: 参数值 :raises logic_error: Unsupported type! 不支持的参数类型)") - .def("haveParam", &MultiFactorBase::haveParam, "是否存在指定参数") + .def("have_param", &MultiFactorBase::haveParam, "是否存在指定参数") - // .def("is_valid", &EnvironmentBase::isValid, R"(is_valid(self, datetime) + .def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy) - // 指定时间系统是否有效 + .def("get_all_factors", + [](MultiFactorBase& self) { + // return vector_to_python_list() + auto factors = self.getAllFactors(); + IndicatorList copy_factors; + copy_factors.reserve(factors.size()); + for (const auto& factor : factors) { + copy_factors.emplace_back(factor.clone()); + } + return vector_to_python_list(copy_factors); + }) - // :param Datetime datetime: 指定时间 - // :return: True 有效 | False 无效)") + .def("get_ic", &MultiFactorBase::getIC, py::arg("ndays") = 0) + .def("get_icir", &MultiFactorBase::getICIR, py::arg("ir_n"), py::arg("ic_n") = 0) + .def("clone", &MultiFactorBase::clone) - // .def("_add_valid", &EnvironmentBase::_addValid, R"(_add_valid(self, datetime) + .def("get_cross", + [](MultiFactorBase& self, const Datetime& date) { + py::list ret; + auto cross = self.getCross(date); + for (const auto& item : cross) { + ret.append(py::make_tuple(item.first, item.second)); + } + return ret; + }) - // 加入有效时间,在_calculate中调用 + .def("get_all_cross", + [](MultiFactorBase& self) { + py::list ret; + auto all_cross = self.getAllCross(); + for (const auto& one_day : all_cross) { + py::list one; + for (const auto& item : one_day) { + one.append(py::make_tuple(item.first, item.second)); + } + ret.append(std::move(one)); + } + return ret; + }) - // :param Datetime datetime: 有效时间)") - - // .def("reset", &EnvironmentBase::reset, "复位操作") - // .def("clone", &EnvironmentBase::clone, "克隆操作") - // .def("_reset", &EnvironmentBase::_reset, - // "【重载接口】子类复位接口,用于复位内部私有变量") .def("_calculate", - // &EnvironmentBase::_calculate, "【重载接口】子类计算接口") - - DEF_PICKLE(MultiFactorPtr); + DEF_PICKLE(MultiFactorPtr); m.def( "MF_EqualWeight", From f7c238b54f049b14f3f257d71d785ebd56ad9fbf Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 16 Mar 2024 03:08:09 +0800 Subject: [PATCH 043/601] MultiFactor --- hikyuu_cpp/hikyuu/indicator/crt/ICIR.h | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp | 6 ++ .../trade_sys/factor/crt/MF_ICIRWeight.h | 3 +- .../hikyuu/trade_sys/factor/crt/MF_ICWeight.h | 3 +- .../factor/imp/EqualWeightMultiFactor.cpp | 1 + .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 60 +++++++----- .../trade_sys/factor/imp/ICIRMultiFactor.h | 2 +- .../trade_sys/factor/imp/ICMultiFactor.cpp | 70 +++++++++----- .../trade_sys/factor/imp/ICMultiFactor.h | 2 +- .../unit_test/hikyuu/indicator/test_STDEV.cpp | 4 +- .../trade_sys/factor/test_MF_EqualWeight.cpp | 2 +- .../trade_sys/factor/test_MF_ICIRWeight.cpp | 95 +++++++++++++++++++ .../trade_sys/factor/test_MF_ICIRWeigtht.cpp | 58 ----------- .../trade_sys/factor/test_MF_ICWeight.cpp | 38 +++++++- hikyuu_pywrap/indicator/_build_in.cpp | 2 +- 15 files changed, 236 insertions(+), 112 deletions(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp delete mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h index c5a6a470..fb71d304 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h @@ -21,7 +21,7 @@ namespace hku { * @return Indicator * @ingroup Indicator */ -inline Indicator ICIR(const Indicator& ic, int n = 10) { +inline Indicator ICIR(const Indicator& ic, int n = 120) { Indicator x = MA(ic, n) / STDEV(ic, n); x.name("IR"); x.setParam("n", n); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp index 654f8b82..73a8b21a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp @@ -67,6 +67,12 @@ void IStdev::_calculate(const Indicator& data) { ex2 += d_pow; dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / n) / (n - 1)); } + + // 排除第一位的0值 + if (m_discard < total) { + dst[0] = Null(); + m_discard += 1; + } } void IStdev::_dyn_run_one_step(const Indicator& ind, size_t curPos, size_t step) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h index 8e22aaef..5cb1657f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h @@ -12,6 +12,7 @@ namespace hku { MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk, int ic_n = 5); + const KQuery& query, const Stock& ref_stk, int ic_n = 5, + int ic_rolling_n = 120); } \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h index dbeff888..5ef6ab64 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h @@ -12,6 +12,7 @@ namespace hku { MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk, int ic_n = 5); + const KQuery& query, const Stock& ref_stk, int ic_n = 5, + int ic_rolling_n = 120); } \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp index 76670bc4..42a37523 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp @@ -54,6 +54,7 @@ vector EqualWeightMultiFactor::_calculate( } all_factors[si] = PRICELIST(sumByDate); + all_factors[si].name("IC"); // 更新 discard for (size_t di = 0; di < days_total; di++) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index f24f069a..cc776a26 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -18,15 +18,14 @@ BOOST_CLASS_EXPORT(hku::ICIRMultiFactor) namespace hku { ICIRMultiFactor::ICIRMultiFactor() : MultiFactorBase("MF_ICIRWeight") { - setParam("ic_n", 1); - setParam("ir_n", 10); + setParam("ic_rolling_n", 120); } ICIRMultiFactor::ICIRMultiFactor(const vector& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk, int ic_n) + const KQuery& query, const Stock& ref_stk, int ic_n, + int ic_rolling_n) : MultiFactorBase(inds, stks, query, ref_stk, "MF_ICIRWeight", ic_n) { - setParam("ic_n", 1); - setParam("ir_n", 10); + setParam("ic_rolling_n", ic_rolling_n); } IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_inds) { @@ -35,21 +34,23 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i size_t ind_count = m_inds.size(); int ic_n = getParam("ic_n"); - int ir_n = getParam("ir_n"); - - vector all_factors(stk_count); + int ir_n = getParam("ic_rolling_n"); + size_t discard = 0; vector icir(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { icir[ii] = ICIR(IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk), ir_n); + if (icir[ii].discard() > discard) { + discard = icir[ii].discard(); + } } // 计算 IC 权重 vector sumByDate(days_total, 0.0); vector countByDate(days_total, 0); - for (size_t di = 0; di < days_total; di++) { + for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = icir[ii][di]; + const auto& value = icir[ii][di]; if (!std::isnan(value)) { sumByDate[di] += value; countByDate[di] += 1; @@ -57,31 +58,46 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i } } - for (size_t di = 0; di < days_total; di++) { - if (countByDate[di] == 0) { - sumByDate[di] = Null(); - } else { - sumByDate[di] = sumByDate[di] / countByDate[di]; - } + for (size_t di = discard; di < days_total; di++) { + sumByDate[di] = (countByDate[di] == 0) ? Null() : sumByDate[di] / countByDate[di]; } + vector all_factors(stk_count); + PriceList new_values(days_total); for (size_t si = 0; si < stk_count; si++) { - PriceList new_values(days_total); - for (size_t di = 0; di < days_total; di++) { + memset(new_values.data(), 0, sizeof(price_t) * days_total); + for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = all_stk_inds[si][ii][di]; - new_values[di] = value * sumByDate[di]; + const auto& value = all_stk_inds[si][ii][di]; + new_values[di] += value * sumByDate[di]; } } + for (size_t di = discard; di < days_total; di++) { + new_values[di] = + (countByDate[di] == 0) ? Null() : new_values[di] / countByDate[di]; + } all_factors[si] = PRICELIST(new_values); + all_factors[si].name("IC"); + + // 更新 discard + for (size_t di = discard; di < days_total; di++) { + if (!std::isnan(all_factors[si][di])) { + all_factors[si].setDiscard(di); + break; + } + if (di == days_total - 1 && std::isnan(all_factors[si][di])) { + all_factors[si].setDiscard(di); + } + } } return all_factors; } MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk, int ic_n) { - return make_shared(inds, stks, query, ref_stk, ic_n); + const KQuery& query, const Stock& ref_stk, int ic_n, + int ic_rolling_n) { + return make_shared(inds, stks, query, ref_stk, ic_n, ic_rolling_n); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h index ca95e4c4..17ca3255 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h @@ -18,7 +18,7 @@ class ICIRMultiFactor : public MultiFactorBase { public: ICIRMultiFactor(); ICIRMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk, int ic_n); + const Stock& ref_stk, int ic_n, int ic_rolling_n); }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index 5cc9cd6b..0ec83a9d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -7,7 +7,7 @@ #include "hikyuu/indicator/crt/PRICELIST.h" #include "hikyuu/indicator/crt/IC.h" -#include "hikyuu/indicator/crt/ICIR.h" +#include "hikyuu/indicator/crt/MA.h" #include "hikyuu/indicator/crt/SPEARMAN.h" #include "ICMultiFactor.h" @@ -17,11 +17,15 @@ BOOST_CLASS_EXPORT(hku::ICMultiFactor) namespace hku { -ICMultiFactor::ICMultiFactor() : MultiFactorBase("MF_ICWeight") {} +ICMultiFactor::ICMultiFactor() : MultiFactorBase("MF_ICWeight") { + setParam("ic_rolling_n", 120); // 计算滚动ic的滚动周期, 通常取 120 或 250 +} ICMultiFactor::ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk, int ic_n) -: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight", ic_n) {} + const Stock& ref_stk, int ic_n, int ic_rolling_n) +: MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight", ic_n) { + setParam("ic_rolling_n", ic_rolling_n); +} IndicatorList ICMultiFactor::_calculate(const vector& all_stk_inds) { size_t days_total = m_ref_dates.size(); @@ -29,20 +33,24 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind size_t ind_count = m_inds.size(); int ic_n = getParam("ic_n"); + int ic_rolling_n = getParam("ic_rolling_n"); - IndicatorList all_factors(stk_count); - + // 计算每个原始因子的IC值 + size_t discard = 0; IndicatorList ic(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { - ic[ii] = IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk); + ic[ii] = MA(IC(m_inds[ii], m_stks, m_query, ic_n, m_ref_stk), ic_rolling_n); + if (ic[ii].discard() > discard) { + discard = ic[ii].discard(); + } } - // 计算 IC 权重 - vector sumByDate(days_total, 0.0); + // 计算每个原始因子的滚动 IC 权重 + vector sumByDate(days_total, 0.0); // 累积合、权重均值 vector countByDate(days_total, 0); - for (size_t di = 0; di < days_total; di++) { + for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = ic[ii][di]; + const auto& value = ic[ii][di]; if (!std::isnan(value)) { sumByDate[di] += value; countByDate[di] += 1; @@ -50,31 +58,47 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind } } - for (size_t di = 0; di < days_total; di++) { - if (countByDate[di] == 0) { - sumByDate[di] = Null(); - } else { - sumByDate[di] = sumByDate[di] / countByDate[di]; - } + for (size_t di = discard; di < days_total; di++) { + sumByDate[di] = (countByDate[di] == 0) ? Null() : sumByDate[di] / countByDate[di]; } + // 对每支证券计算根据滚动 IC 权重计算合成因子 + IndicatorList all_factors(stk_count); + PriceList new_values(days_total, 0); for (size_t si = 0; si < stk_count; si++) { - PriceList new_values(days_total); - for (size_t di = 0; di < days_total; di++) { + memset(new_values.data(), 0, sizeof(price_t) * days_total); + for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto value = all_stk_inds[si][ii][di]; - new_values[di] = value * sumByDate[di]; + const auto& value = all_stk_inds[si][ii][di]; + new_values[di] += value * sumByDate[di]; } } + for (size_t di = discard; di < days_total; di++) { + new_values[di] = + (countByDate[di] == 0) ? Null() : new_values[di] / countByDate[di]; + } all_factors[si] = PRICELIST(new_values); + all_factors[si].name("IC"); + + // 更新 discard + for (size_t di = discard; di < days_total; di++) { + if (!std::isnan(all_factors[si][di])) { + all_factors[si].setDiscard(di); + break; + } + if (di == days_total - 1 && std::isnan(all_factors[si][di])) { + all_factors[si].setDiscard(di); + } + } } return all_factors; } MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, - const KQuery& query, const Stock& ref_stk, int ic_n) { - return std::make_shared(inds, stks, query, ref_stk, ic_n); + const KQuery& query, const Stock& ref_stk, int ic_n, + int ic_rolling_n) { + return std::make_shared(inds, stks, query, ref_stk, ic_n, ic_rolling_n); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h index bfbbb70a..ce435c9c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h @@ -18,7 +18,7 @@ class ICMultiFactor : public MultiFactorBase { public: ICMultiFactor(); ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, - const Stock& ref_stk, int ic_n); + const Stock& ref_stk, int ic_n, int ic_rolling_n); }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp index 11db7825..458ee196 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp @@ -34,12 +34,14 @@ TEST_CASE("test_STDEV") { Indicator ind = PRICELIST(d); Indicator dev = STDEV(ind, 10); + CHECK_EQ(dev.discard(), 1); CHECK_EQ(dev.size(), 15); vector expected{0, 0.707107, 1, 1.29099, 1.58114, 1.47196, 1.97605, 1.83225, 2.44949, 2.92309, 3.14289, 2.83039, 3.26769, 3.653, 4.00139}; - for (size_t i = 0; i < dev.size(); i++) { + CHECK_UNARY(std::isnan(dev[0])); + for (size_t i = dev.discard(); i < dev.size(); i++) { CHECK_EQ(dev[i], doctest::Approx(expected[i]).epsilon(0.0001)); } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 67d17d09..64e79cdc 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -126,7 +126,7 @@ TEST_CASE("test_MF_EqualWeight") { query = KQuery(-20); ref_k = ref_stk.getKData(query); ref_dates = ref_k.getDatetimeList(); - mf = MF_EqualWeight(src_inds, stks, query, ref_stk); + mf = MF_EqualWeight(src_inds, stks, query, ref_stk, ndays); CHECK_EQ(mf->name(), "MF_EqualWeight"); CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp new file mode 100644 index 00000000..03d39b53 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-15 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_MF_ICIRWeight test_MF_ICIRWeight + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +TEST_CASE("test_MF_ICIRWeight") { + StockManager& sm = StockManager::instance(); + int ndays = 3; + int ic_rolling_n = 3; + Stock ref_stk = sm["sh000001"]; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(-20); + KData ref_k = ref_stk.getKData(query); + DatetimeList ref_dates = ref_k.getDatetimeList(); + auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk, ndays, ic_rolling_n); + CHECK_EQ(mf->name(), "MF_ICIRWeight"); + CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception); + + auto stk = sm["sh600004"]; + auto ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ic1 = ICIR(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + auto ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ic2 = ICIR(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ic3 = ICIR(IC(EMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + + auto ind4 = mf->getFactor(stk); + for (size_t i = 0; i < ind4.discard(); i++) { + CHECK_UNARY(std::isnan(ind4[i])); + } + CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); + for (size_t i = 0; i < ind4.discard(); i++) { + CHECK_UNARY(std::isnan(ind4[i])); + } + for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { + Indicator::value_t w = (ic1[i] + ic2[i] + ic3[i]) / 3.0; + CHECK_EQ(ind4[i], doctest::Approx((ind1[i] * w + ind2[i] * w + ind3[i] * w) / 3.0)); + } +} + +//----------------------------------------------------------------------------- +// benchmark +//----------------------------------------------------------------------------- +#if ENABLE_BENCHMARK_TEST +TEST_CASE("test_MF_ICIRWeight_benchmark") { + StockManager& sm = StockManager::instance(); + int ndays = 3; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(0); + Stock ref_stk = sm["sh000001"]; + auto ref_k = ref_stk.getKData(query); + auto ref_dates = ref_k.getDatetimeList(); + + int cycle = 10; // 测试循环次数 + + { + BENCHMARK_TIME_MSG(test_MF_ICIRWeight_benchmark, cycle, + fmt::format("data len: {}", ref_k.size())); + SPEND_TIME_CONTROL(false); + for (int i = 0; i < cycle; i++) { + auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk); + auto ic = mf->getIC(); + } + } +} +#endif + +/** @} */ \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp deleted file mode 100644 index 4ccfd342..00000000 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeigtht.cpp +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-15 - * Author: fasiondog - */ - -#include "../../test_config.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace hku; - -/** - * @defgroup test_MF_ICIRWeight test_MF_ICIRWeight - * @ingroup test_hikyuu_trade_sys_suite - * @{ - */ - -TEST_CASE("test_MF_ICIRWeight") {} - -//----------------------------------------------------------------------------- -// benchmark -//----------------------------------------------------------------------------- -#if ENABLE_BENCHMARK_TEST -TEST_CASE("test_MF_ICIRWeight_benchmark") { - StockManager& sm = StockManager::instance(); - int ndays = 3; - IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), - EMA(ROCR(CLOSE(), ndays))}; - StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; - KQuery query = KQuery(0); - Stock ref_stk = sm["sh000001"]; - auto ref_k = ref_stk.getKData(query); - auto ref_dates = ref_k.getDatetimeList(); - - int cycle = 10; // 测试循环次数 - - { - BENCHMARK_TIME_MSG(test_MF_ICIRWeight_benchmark, cycle, - fmt::format("data len: {}", ref_k.size())); - SPEND_TIME_CONTROL(false); - for (int i = 0; i < cycle; i++) { - auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk); - auto ic = mf->getIC(); - } - } -} -#endif - -/** @} */ \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp index cfee211e..d3dac126 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -7,6 +7,7 @@ #include "../../test_config.h" #include +#include #include #include #include @@ -24,7 +25,42 @@ using namespace hku; * @{ */ -TEST_CASE("test_MF_ICWeight") {} +TEST_CASE("test_MF_ICWeight") { + StockManager& sm = StockManager::instance(); + int ndays = 3; + int ic_rolling_n = 3; + Stock ref_stk = sm["sh000001"]; + IndicatorList src_inds = {MA(ROCR(CLOSE(), ndays)), AMA(ROCR(CLOSE(), ndays)), + EMA(ROCR(CLOSE(), ndays))}; + StockList stks = {sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + KQuery query = KQuery(-20); + KData ref_k = ref_stk.getKData(query); + DatetimeList ref_dates = ref_k.getDatetimeList(); + auto mf = MF_ICWeight(src_inds, stks, query, ref_stk, ndays, ic_rolling_n); + CHECK_EQ(mf->name(), "MF_ICWeight"); + CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception); + + auto stk = sm["sh600004"]; + auto ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ic1 = MA(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + auto ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ic2 = MA(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays)); + auto ic3 = MA(IC(EMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + + auto ind4 = mf->getFactor(stk); + for (size_t i = 0; i < ind4.discard(); i++) { + CHECK_UNARY(std::isnan(ind4[i])); + } + CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); + for (size_t i = 0; i < ind4.discard(); i++) { + CHECK_UNARY(std::isnan(ind4[i])); + } + for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { + Indicator::value_t w = (ic1[i] + ic2[i] + ic3[i]) / 3.0; + CHECK_EQ(ind4[i], doctest::Approx((ind1[i] * w + ind2[i] * w + ind3[i] * w) / 3.0)); + } +} //----------------------------------------------------------------------------- // benchmark diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 13c50a12..31f5dff1 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1711,7 +1711,7 @@ void export_Indicator_build_in(py::module& m) { :param int n: 时间窗口 :param Stock ref_stk: 参照证券,通常使用 sh000300 沪深300)"); - m.def("ICIR", ICIR, py::arg("ic"), py::arg("n") = 10, R"(ICIR(ic[,n]) + m.def("ICIR", ICIR, py::arg("ic"), py::arg("n") = 120, R"(ICIR(ic[,n]) 计算 IC 因子 IR = IC的多周期均值/IC的标准方差 From 1f537588165d3ca5c0a22a536de22e8cdfffe0be Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 16 Mar 2024 03:12:28 +0800 Subject: [PATCH 044/601] update --- .../unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 64e79cdc..a3494b70 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -142,7 +142,6 @@ TEST_CASE("test_MF_EqualWeight") { for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { CHECK_EQ(ind4[i], doctest::Approx((ind1[i] + ind2[i] + ind3[i]) / 3.0)); } - HKU_INFO("\n{}", mf); } //----------------------------------------------------------------------------- From d62e2fb8d7d1133813901fa2f0591f4389f91b7b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 16 Mar 2024 04:24:31 +0800 Subject: [PATCH 045/601] update --- hikyuu/trade_sys/trade_sys.py | 98 +++++++++---------- .../trade_sys/factor/crt/MF_EqualWeight.h | 11 ++- .../trade_sys/factor/crt/MF_ICIRWeight.h | 10 ++ .../hikyuu/trade_sys/factor/crt/MF_ICWeight.h | 10 ++ hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 59 ++++++++++- 5 files changed, 136 insertions(+), 52 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index dc3bc281..d525411f 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -3,7 +3,7 @@ from hikyuu.util.slice import list_getitem from hikyuu.core import ( System, SystemPart, ConditionBase, EnvironmentBase, MoneyManagerBase, - ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase + ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase, AllocateFundsBase ) @@ -15,6 +15,14 @@ def part_iter(self): ConditionBase.__iter__ = part_iter +def part_init(self, name, params): + super(self.__class__, self).__init__(name) + self._name = name + self._params = params + for k, v in params.items(): + self.set_param(k, v) + + # ------------------------------------------------------------------ # System # ------------------------------------------------------------------ @@ -33,14 +41,6 @@ System.INVALID = System.Part.INVALID # ------------------------------------------------------------------ # condition # ------------------------------------------------------------------ -def cn_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - def crtCN(func, params={}, name='crtCN'): """ 快速创建自定义不带私有属性的系统有效条件 @@ -50,7 +50,7 @@ def crtCN(func, params={}, name='crtCN'): :param str name: 自定义名称 :return: 自定义系统有效条件实例 """ - meta_x = type(name, (ConditionBase, ), {'__init__': cn_init}) + meta_x = type(name, (ConditionBase, ), {'__init__': part_init}) meta_x._clone = lambda self: meta_x(self._name, self._params) meta_x._calculate = func return meta_x(name, params) @@ -59,14 +59,6 @@ def crtCN(func, params={}, name='crtCN'): # ------------------------------------------------------------------ # environment # ------------------------------------------------------------------ -def ev_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - def crtEV(func, params={}, name='crtEV'): """ 快速创建自定义不带私有属性的市场环境判断策略 @@ -76,7 +68,7 @@ def crtEV(func, params={}, name='crtEV'): :param str name: 自定义名称 :return: 自定义市场环境判断策略实例 """ - meta_x = type(name, (EnvironmentBase, ), {'__init__': ev_init}) + meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init}) meta_x._clone = lambda self: meta_x(self._name, self._params) meta_x._calculate = func return meta_x(name, params) @@ -85,14 +77,6 @@ def crtEV(func, params={}, name='crtEV'): # ------------------------------------------------------------------ # moneymanager # ------------------------------------------------------------------ -def mm_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - def crtMM(func, params={}, name='crtMM'): """ 快速创建自定义不带私有属性的资金管理策略 @@ -102,7 +86,7 @@ def crtMM(func, params={}, name='crtMM'): :param str name: 自定义名称 :return: 自定义资金管理策略实例 """ - meta_x = type(name, (MoneyManagerBase, ), {'__init__': mm_init}) + meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init}) meta_x._clone = lambda self: meta_x(self._name, self._params) meta_x._calculate = func return meta_x(name, params) @@ -111,14 +95,6 @@ def crtMM(func, params={}, name='crtMM'): # ------------------------------------------------------------------ # profitgoal # ------------------------------------------------------------------ -def pg_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - def crtPG(func, params={}, name='crtPG'): """ 快速创建自定义不带私有属性的盈利目标策略 @@ -128,7 +104,7 @@ def crtPG(func, params={}, name='crtPG'): :param str name: 自定义名称 :return: 盈利目标策略实例 """ - meta_x = type(name, (ProfitGoalBase, ), {'__init__': pg_init}) + meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init}) meta_x._clone = lambda self: meta_x(self._name, self._params) meta_x._calculate = func return meta_x(name, params) @@ -137,14 +113,6 @@ def crtPG(func, params={}, name='crtPG'): # ------------------------------------------------------------------ # signal # ------------------------------------------------------------------ -def sig_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - def crtSG(func, params={}, name='crtSG'): """ 快速创建自定义不带私有属性的信号指示器 @@ -154,7 +122,7 @@ def crtSG(func, params={}, name='crtSG'): :param str name: 自定义名称 :return: 自定义信号指示器实例 """ - meta_x = type(name, (SignalBase, ), {'__init__': sig_init}) + meta_x = type(name, (SignalBase, ), {'__init__': part_init}) meta_x._clone = lambda self: meta_x(self._name, self._params) meta_x._calculate = func return meta_x(name, params) @@ -175,11 +143,43 @@ def se_add_stock_list(self, stk_list, proto_sys): SelectorBase.add_stock_list = se_add_stock_list + +def crtSE(func, params={}, name='crtSE'): + """ + 快速创建自定义不带私有属性的交易对象选择算法 + + :param func: 交易对象选择算法 + :param {} params: 参数字典 + :param str name: 自定义名称 + :return: 自定义交易对象选择算法实例 + """ + meta_x = type(name, (SelectorBase, ), {'__init__': part_init}) + meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._calculate = func + return meta_x(name, params) + + +# ------------------------------------------------------------------ +# allocatefunds +# ------------------------------------------------------------------ +def crtAF(func, params={}, name='crtAF'): + """ + 快速创建自定义不带私有属性的资产分配算法 + + :param func: 资产分配算法 + :param {} params: 参数字典 + :param str name: 自定义名称 + :return: 自定义资产分配算法实例 + """ + meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init}) + meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._calculate = func + return meta_x(name, params) + + # ------------------------------------------------------------------ # slippage # ------------------------------------------------------------------ - - def sl_init(self, name, params): super(self.__class__, self).__init__(name) self._name = name @@ -197,7 +197,7 @@ def crtSL(func, params={}, name='crtSL'): :param str name: 自定义名称 :return: 移滑价差算法实例 """ - meta_x = type(name, (SlippageBase, ), {'__init__': sl_init}) + meta_x = type(name, (SlippageBase, ), {'__init__': part_init}) meta_x._clone = lambda self: meta_x(self._name, self._params) meta_x._calculate = func return meta_x(name, params) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h index 65783bec..27f5548f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h @@ -10,7 +10,16 @@ namespace hku { +/** + * @brief 等权重合成因子 + * @param inds 原始因子列表 + * @param stks 计算证券列表 + * @param query 日期范围 + * @param ref_stk 参考证券 + * @param ic_n 默认 IC 对应的 N 日收益率 + * @return MultiFactorPtr + */ MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n = 5); -} \ No newline at end of file +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h index 5cb1657f..fdda528e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h @@ -11,6 +11,16 @@ namespace hku { +/** + * @brief 滚动ICIR权重合成因子 + * @param inds 原始因子列表 + * @param stks 计算证券列表 + * @param query 日期范围 + * @param ref_stk 参考证券 + * @param ic_n 默认 IC 对应的 N 日收益率 + * @param ic_rolling_n IC 滚动窗口 + * @return MultiFactorPtr + */ MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n = 5, int ic_rolling_n = 120); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h index 5ef6ab64..aa27b52a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h @@ -11,6 +11,16 @@ namespace hku { +/** + * @brief 滚动IC权重合成因子 + * @param inds 原始因子列表 + * @param stks 计算证券列表 + * @param query 日期范围 + * @param ref_stk 参考证券 + * @param ic_n 默认 IC 对应的 N 日收益率 + * @param ic_rolling_n IC 滚动窗口 + * @return MultiFactorPtr + */ MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n = 5, int ic_rolling_n = 120); diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 452a4818..be51217c 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -108,10 +108,65 @@ void export_MultiFactor(py::module& m) { "MF_EqualWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, const Stock& ref_stk, int ic_n) { - // MF_EqualWeight IndicatorList c_inds = python_list_to_vector(inds); StockList c_stks = python_list_to_vector(stks); return MF_EqualWeight(c_inds, c_stks, query, ref_stk, ic_n); }, - py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5); + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5, + R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5]) + + 等权重合成因子 + + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 + :param int ic_n: 默认 IC 对应的 N 日收益率 + :rtype: MultiFactorPtr)"); + + m.def( + "MF_ICWeight", + [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, + const Stock& ref_stk, int ic_n, int ic_rolling_n) { + // MF_EqualWeight + IndicatorList c_inds = python_list_to_vector(inds); + StockList c_stks = python_list_to_vector(stks); + return MF_ICWeight(c_inds, c_stks, query, ref_stk, ic_n); + }, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5, + py::arg("ic_rolling_n") = 120, + R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) + + 滚动IC权重合成因子 + + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 + :param int ic_n: 默认 IC 对应的 N 日收益率 + :param int ic_rolling_n: IC 滚动周期 + :rtype: MultiFactorPtr)"); + + m.def( + "MF_ICIRWeight", + [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, + const Stock& ref_stk, int ic_n, int ic_rolling_n) { + // MF_EqualWeight + IndicatorList c_inds = python_list_to_vector(inds); + StockList c_stks = python_list_to_vector(stks); + return MF_ICIRWeight(c_inds, c_stks, query, ref_stk, ic_n); + }, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5, + py::arg("ic_rolling_n") = 120, + R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) + + 滚动ICIR权重合成因子 + + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 + :param int ic_n: 默认 IC 对应的 N 日收益率 + :param int ic_rolling_n: IC 滚动周期 + :rtype: MultiFactorPtr)"); } \ No newline at end of file From 61e7107bf76d156a4c9a0467ca7d4b1016478b32 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 16 Mar 2024 16:56:37 +0800 Subject: [PATCH 046/601] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E9=83=A8=E4=BB=B6=E5=BF=AB=E9=80=9F=E5=88=9B=E5=BB=BA=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_sys/trade_sys.py | 94 +++++++++++++++++------------------ 1 file changed, 46 insertions(+), 48 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index d525411f..bfd23923 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -41,17 +41,18 @@ System.INVALID = System.Part.INVALID # ------------------------------------------------------------------ # condition # ------------------------------------------------------------------ -def crtCN(func, params={}, name='crtCN'): +def crtCN(func, params={}, name='crtCN', clone=None): """ - 快速创建自定义不带私有属性的系统有效条件 + 快速创建系统有效条件 :param func: 系统有效条件函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 自定义系统有效条件实例 """ meta_x = type(name, (ConditionBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -59,17 +60,18 @@ def crtCN(func, params={}, name='crtCN'): # ------------------------------------------------------------------ # environment # ------------------------------------------------------------------ -def crtEV(func, params={}, name='crtEV'): +def crtEV(func, params={}, name='crtEV', clone=None): """ - 快速创建自定义不带私有属性的市场环境判断策略 + 快速创建市场环境判断策略 :param func: 市场环境判断策略函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 自定义市场环境判断策略实例 """ meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -77,17 +79,18 @@ def crtEV(func, params={}, name='crtEV'): # ------------------------------------------------------------------ # moneymanager # ------------------------------------------------------------------ -def crtMM(func, params={}, name='crtMM'): +def crtMM(func, params={}, name='crtMM', clone=None): """ - 快速创建自定义不带私有属性的资金管理策略 + 快速创建资金管理策略 :param func: 资金管理策略计算函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 自定义资金管理策略实例 """ meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -95,17 +98,18 @@ def crtMM(func, params={}, name='crtMM'): # ------------------------------------------------------------------ # profitgoal # ------------------------------------------------------------------ -def crtPG(func, params={}, name='crtPG'): +def crtPG(func, params={}, name='crtPG', clone=None): """ - 快速创建自定义不带私有属性的盈利目标策略 + 快速创建盈利目标策略 :param func: 盈利目标策略函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 盈利目标策略实例 """ meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -113,17 +117,18 @@ def crtPG(func, params={}, name='crtPG'): # ------------------------------------------------------------------ # signal # ------------------------------------------------------------------ -def crtSG(func, params={}, name='crtSG'): +def crtSG(func, params={}, name='crtSG', clone=None): """ - 快速创建自定义不带私有属性的信号指示器 + 快速创建信号指示器 :param func: 信号策略函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 自定义信号指示器实例 """ meta_x = type(name, (SignalBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -144,61 +149,61 @@ def se_add_stock_list(self, stk_list, proto_sys): SelectorBase.add_stock_list = se_add_stock_list -def crtSE(func, params={}, name='crtSE'): +def crtSE(calculate, get_selected_on_close, get_selected_on_open, is_match_af=None, params={}, name='crtSE', clone=None): """ - 快速创建自定义不带私有属性的交易对象选择算法 + 快速创建交易对象选择算法 - :param func: 交易对象选择算法 + :param calculate function: 计算函数 + :param get_selected_on_close function: 收盘时刻选择算法 + :param get_selected_on_open function: 开盘时刻选择算法 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 自定义交易对象选择算法实例 """ meta_x = type(name, (SelectorBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) - meta_x._calculate = func + 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.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) # ------------------------------------------------------------------ # allocatefunds # ------------------------------------------------------------------ -def crtAF(func, params={}, name='crtAF'): +def crtAF(allocate_weight_func, params={}, name='crtAF', clone=None): """ - 快速创建自定义不带私有属性的资产分配算法 + 快速创建资产分配算法 - :param func: 资产分配算法 + :param allocate_weight_func: 资产分配算法 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 自定义资产分配算法实例 """ meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) - meta_x._calculate = func + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._allocate_weight = allocate_weight_func return meta_x(name, params) # ------------------------------------------------------------------ # slippage # ------------------------------------------------------------------ -def sl_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - -def crtSL(func, params={}, name='crtSL'): +def crtSL(func, params={}, name='crtSL', clone=None): """ - 快速创建自定义不带私有属性的移滑价差算法 + 快速创建移滑价差算法 :param func: 移滑价差算法函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 移滑价差算法实例 """ meta_x = type(name, (SlippageBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -206,24 +211,17 @@ def crtSL(func, params={}, name='crtSL'): # ------------------------------------------------------------------ # stoploss # ------------------------------------------------------------------ -def st_init(self, name, params): - super(self.__class__, self).__init__(name) - self._name = name - self._params = params - for k, v in params.items(): - self.set_param(k, v) - - -def crtST(func, params={}, name='crtST'): +def crtST(func, params={}, name='crtST', clone=None): """ - 快速创建自定义不带私有属性的止损/止盈策略 + 快速创建止损/止盈策略 :param func: 止损/止盈策略函数 :param {} params: 参数字典 :param str name: 自定义名称 + :param clone: 带有私有属性时自定义的clone函数 :return: 止损/止盈策略实例 """ - meta_x = type(name, (StoplossBase, ), {'__init__': st_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) + meta_x = type(name, (StoplossBase, ), {'__init__': part_init}) + meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone meta_x._calculate = func return meta_x(name, params) From 19e3d6280f5ccbe4f6bd9fb21bc64ef455557e26 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 16 Mar 2024 16:57:09 +0800 Subject: [PATCH 047/601] =?UTF-8?q?MultiFactor=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E5=B1=9E=E6=80=A7=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/factor/MultiFactorBase.cpp | 19 +++++++++++++ .../hikyuu/trade_sys/factor/MultiFactorBase.h | 25 +++++++++++++++++ hikyuu_pywrap/bind_stl.cpp | 3 +++ hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 27 ++++++++++++------- 4 files changed, 65 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index ac4cad04..98d19563 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -208,6 +208,25 @@ const vector>& MultiFactorBase::getCr return m_stk_factor_by_date[iter->second]; } +vector> MultiFactorBase::getCross(const Datetime& date, + size_t start, + size_t end) { + vector> ret; + HKU_IF_RETURN(start >= end, ret); + + const auto& cross = getCross(date); + if (end == Null() || end > cross.size()) { + end = cross.size(); + } + + ret.resize(end - start); + for (size_t i = start; i < end; i++) { + ret[i] = cross[i]; + } + + return ret; +} + const vector>>& MultiFactorBase::getAllCross() { calculate(); return m_stk_factor_by_date; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 5f456181..334b4575 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -40,14 +40,36 @@ public: m_name = name; } + /** 获取参考日期列表 */ const DatetimeList& getDatetimeList() const { return m_ref_dates; } + /** 获取查询范围 */ const KQuery& getQuery() const { return m_query; } + /** 获取参考证券 */ + const Stock& getRefStock() const { + return m_ref_stk; + } + + /** 获取证券列表 */ + const StockList& getStockList() const { + return m_stks; + } + + /** 获取证券列表当前证券数量 */ + size_t getStockListNumber() const { + return m_stks.size(); + } + + /** 获取原始因子公式列表 */ + const IndicatorList& getRefIndicators() const { + return m_inds; + } + /** 获取指定证券合成因子 */ const Indicator& getFactor(const Stock&); @@ -59,6 +81,9 @@ public: /** 获取指定日期截面的所有因子值,已经降序排列 */ const vector>& getCross(const Datetime&); + vector> getCross(const Datetime& date, size_t start, + size_t end = Null()); + /** 获取所有截面数据,已按降序排列 */ const vector>>& getAllCross(); diff --git a/hikyuu_pywrap/bind_stl.cpp b/hikyuu_pywrap/bind_stl.cpp index 2c89f01f..58e18ade 100644 --- a/hikyuu_pywrap/bind_stl.cpp +++ b/hikyuu_pywrap/bind_stl.cpp @@ -5,6 +5,7 @@ * 1. 20231231 added by fasiondog */ +#include #include "pybind_utils.h" #include "bind_stl.h" @@ -16,6 +17,8 @@ void export_bind_stl(py::module& m) { // py::bind_vector(m, "StringList"); py::bind_vector(m, "DatetimeList"); py::bind_vector(m, "KRecordList"); + py::bind_vector(m, "StockList"); + // py::bind_vector(m, "KRecordList"); // could't compile py::bind_vector(m, "StockWeightList"); py::bind_vector(m, "TimeLineList"); py::bind_vector(m, "TransList"); diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index be51217c..df7f5eeb 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -24,6 +24,7 @@ public: }; void export_MultiFactor(py::module& m) { + size_t null_size = Null(); py::class_(m, "MultiFactor", R"(市场环境判定策略基类 @@ -60,6 +61,12 @@ void export_MultiFactor(py::module& m) { .def("have_param", &MultiFactorBase::haveParam, "是否存在指定参数") + .def("get_ref_stock", &MultiFactorBase::getRefStock, py::return_value_policy::copy) + .def("get_datetime_list", &MultiFactorBase::getDatetimeList, py::return_value_policy::copy) + .def("get_stock_list", &MultiFactorBase::getStockList, py::return_value_policy::copy) + .def("get_stock_list_num", &MultiFactorBase::getStockListNumber) + .def("get_ref_indicators", &MultiFactorBase::getRefIndicators, py::return_value_policy::copy) + .def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy) .def("get_all_factors", @@ -78,15 +85,17 @@ void export_MultiFactor(py::module& m) { .def("get_icir", &MultiFactorBase::getICIR, py::arg("ir_n"), py::arg("ic_n") = 0) .def("clone", &MultiFactorBase::clone) - .def("get_cross", - [](MultiFactorBase& self, const Datetime& date) { - py::list ret; - auto cross = self.getCross(date); - for (const auto& item : cross) { - ret.append(py::make_tuple(item.first, item.second)); - } - return ret; - }) + .def( + "get_cross", + [](MultiFactorBase& self, const Datetime& date, size_t start, size_t end) { + py::list ret; + auto cross = self.getCross(date, start, end); + for (const auto& item : cross) { + ret.append(py::make_tuple(item.first, item.second)); + } + return ret; + }, + py::arg("date"), py::arg("start") = 0, py::arg("end") = null_size) .def("get_all_cross", [](MultiFactorBase& self) { From fa19200b628de66d944a9ccd783a02fc23bba75b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 16 Mar 2024 19:12:01 +0800 Subject: [PATCH 048/601] update --- .../hikyuu/trade_sys/selector/SelectorBase.cpp | 13 +++++++++++-- hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h | 2 -- hikyuu_pywrap/trade_sys/_Selector.cpp | 4 ++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 8431ebdd..4d5312c6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -68,14 +68,23 @@ SelectorPtr SelectorBase::clone() { p->m_params = m_params; p->m_name = m_name; - p->m_real_sys_list = m_real_sys_list; - p->m_pro_sys_list = m_pro_sys_list; + + p->m_real_sys_list.reserve(m_real_sys_list.size()); + for (const auto& sys : m_real_sys_list) { + p->m_real_sys_list.emplace_back(sys->clone()); + } + + p->m_pro_sys_list.reserve(m_pro_sys_list.size()); + for (const auto& sys : m_real_sys_list) { + p->m_pro_sys_list.emplace_back(sys->clone()); + } return p; } void SelectorBase::calculate(const SystemList& sysList, const KQuery& query) { m_real_sys_list = sysList; if (getParam("run_proto_sys")) { + // 用于手工测试 for (auto& sys : m_pro_sys_list) { sys->run(query); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 68da182b..b7b7e91b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -104,9 +104,7 @@ public: virtual bool isMatchAF(const AFPtr& af) = 0; -private: friend class HKU_API Portfolio; - /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ void calculate(const SystemList& sysList, const KQuery& query); diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index e59b7e3c..f0f682a3 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -105,6 +105,10 @@ void export_Selector(py::module& m) { :param StockList stk_list: 加入的初始标的列表 :param System sys: 系统策略原型)") + .def("get_proto_sys_list", &SelectorBase::getProtoSystemList, py::return_value_policy::copy) + .def("get_real_sys_list", &SelectorBase::getRealSystemList, py::return_value_policy::copy) + .def("calculate", &SelectorBase::calculate) + .def("_reset", &SelectorBase::_reset, "子类复位操作实现") .def("_calculate", &SelectorBase::_calculate, "【重载接口】子类计算接口") From 2d94414f9f8c2ce60cafdf335abfafe7bfe1e241 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 01:04:16 +0800 Subject: [PATCH 049/601] fixed Fatal Python error: bool_dealloc: deallocating True or False --- hikyuu_pywrap/convert_any.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/hikyuu_pywrap/convert_any.h b/hikyuu_pywrap/convert_any.h index 61ab37d6..74c4a57c 100644 --- a/hikyuu_pywrap/convert_any.h +++ b/hikyuu_pywrap/convert_any.h @@ -90,8 +90,11 @@ public: /* Extract PyObject from handle */ PyObject* src = source.ptr(); + object obj = reinterpret_borrow(source); if (PyBool_Check(src)) { - value = bool(PyLong_AsLong(src)); + // value = bool(PyLong_AsLong(src)); + bool tmp = obj.cast(); + value = tmp; return true; } @@ -123,7 +126,6 @@ public: return true; } - object obj = reinterpret_borrow(source); if (isinstance(obj)) { value = obj.cast(); return true; @@ -176,7 +178,11 @@ public: static handle cast(boost::any x, return_value_policy /* policy */, handle /* parent */) { if (x.type() == typeid(bool)) { bool tmp = boost::any_cast(x); - return tmp ? Py_True : Py_False; + if (tmp) { + Py_RETURN_TRUE; + } else { + Py_RETURN_FALSE; + } } else if (x.type() == typeid(int)) { return Py_BuildValue("n", boost::any_cast(x)); } else if (x.type() == typeid(double)) { From e1a653069f86e06d97663489f7a2613f7e38dcd8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 02:59:39 +0800 Subject: [PATCH 050/601] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=A4=8D=E6=9D=83?= =?UTF-8?q?=E6=8C=87=E6=A0=87=EF=BC=8C=E6=96=B9=E4=BE=BF=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h | 40 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp | 133 +++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IRecover.h | 27 ++++ 4 files changed, 201 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IRecover.h diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 0634988e..684e68a5 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -71,6 +71,7 @@ #include "crt/POS.h" #include "crt/POW.h" #include "crt/PRICELIST.h" +#include "crt/RECOVER.h" #include "crt/REF.h" #include "crt/REVERSE.h" #include "crt/ROC.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h b/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h new file mode 100644 index 00000000..57e5e068 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019~2023, hikyuu.org + * + * History: + * 1. 20240317 added by fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +Indicator HKU_API RECOVER_FORWARD(); +Indicator HKU_API RECOVER_BACKWARD(); +Indicator HKU_API RECOVER_EQUAL_FORWARD(); +Indicator HKU_API RECOVER_EQUAL_BACKWARD(); + +Indicator HKU_API RECOVER_FORWARD(const Indicator&); +Indicator HKU_API RECOVER_BACKWARD(const Indicator&); +Indicator HKU_API RECOVER_EQUAL_FORWARD(const Indicator&); +Indicator HKU_API RECOVER_EQUAL_BACKWARD(const Indicator&); + +inline Indicator RECOVER_FORWARD(const KData& kdata) { + return RECOVER_FORWARD(kdata.close()); +} + +inline Indicator RECOVER_BACKWARD(const KData& kdata) { + return RECOVER_BACKWARD(kdata.close()); +} + +inline Indicator RECOVER_EQUAL_FORWARD(const KData& kdata) { + return RECOVER_EQUAL_FORWARD(kdata.close()); +} + +inline Indicator RECOVER_EQUAL_BACKWARD(const KData& kdata) { + return RECOVER_EQUAL_BACKWARD(kdata.close()); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp new file mode 100644 index 00000000..0c6b12d6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2019~2023, hikyuu.org + * + * History: + * 1. 20240317 added by fasiondog + */ + +#include "IKData.h" +#include "IRecover.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IRecover) +#endif + +namespace hku { + +IRecover::IRecover() : IndicatorImp("RECOVER") { + setParam("recover_type", KQuery::NO_RECOVER); +} + +IRecover::IRecover(int recoverType) { + setParam("recover_type", recoverType); +} + +IRecover::IRecover(const KData& kdata, int recoverType) : IndicatorImp("RECOVER") { + setParam("recover_type", recoverType); + setParam("kdata", kdata); +} + +IRecover::~IRecover() {} + +bool IRecover::check() { + int recover_type = getParam("recover_type"); + return recover_type >= KQuery::NO_RECOVER && recover_type < KQuery::INVALID_RECOVER_TYPE; +} + +void IRecover::checkInputIndicator(const Indicator& ind) { + HKU_CHECK(typeid(*(ind.getImp())) == typeid(IKData), + "Only the following indicators are accepted: OPEN|HIGH|CLOSE|LOW"); + string part = ind.getParam("kpart"); + HKU_CHECK(part == "CLOSE" || part == "OPEN" || part == "HIGH" || part == "LOW", + "Only the following indicators are accepted: OPEN|HIGH|CLOSE|LOW"); +} + +void IRecover::_calculate(const Indicator& ind) { + auto kdata = getContext(); + auto query = kdata.getQuery(); + + auto ktype = query.kType(); + // 日线以下数据,不执行复权 + if (!(ktype == KQuery::WEEK || ktype == KQuery::MONTH || ktype == KQuery::QUARTER || + ktype == KQuery::HALFYEAR || ktype == KQuery::YEAR)) { + size_t total = ind.size(); + _readyBuffer(total, 1); + const auto* src = ind.data(); + auto* dst = this->data(); + memcpy(dst, src, sizeof(value_t) * total); + return; + } + + KQuery::RecoverType recover_type = + static_cast(getParam("recover_type")); + query.recoverType(recover_type); + + KData new_k = kdata.getStock().getKData(query); + HKU_ASSERT(new_k.size() == ind.size()); + + m_name = KQuery::getRecoverTypeName(recover_type); + size_t total = new_k.size(); + _readyBuffer(total, 1); + + string part_name = ind.getParam("kpart"); + const auto* data = new_k.data(); + auto* dst = this->data(); + if ("CLOSE" == part_name) { + for (size_t i = 0; i < total; i++) { + dst[i] = data[i].closePrice; + } + + } else if ("OPEN" == part_name) { + for (size_t i = 0; i < total; i++) { + dst[i] = data[i].openPrice; + } + + } else if ("HIGH" == part_name) { + for (size_t i = 0; i < total; i++) { + dst[i] = data[i].highPrice; + } + + } else { + for (size_t i = 0; i < total; i++) { + dst[i] = data[i].lowPrice; + } + } +} + +Indicator HKU_API RECOVER_FORWARD() { + return Indicator(make_shared(KQuery::FORWARD)); +} + +Indicator HKU_API RECOVER_BACKWARD() { + return Indicator(make_shared(KQuery::BACKWARD)); +} + +Indicator HKU_API RECOVER_EQUAL_FORWARD() { + return Indicator(make_shared(KQuery::EQUAL_FORWARD)); +} + +Indicator HKU_API RECOVER_EQUAL_BACKWARD() { + return Indicator(make_shared(KQuery::EQUAL_BACKWARD)); +} + +Indicator HKU_API RECOVER_FORWARD(const Indicator& ind) { + IRecover::checkInputIndicator(ind); + return RECOVER_FORWARD()(ind); +} + +Indicator HKU_API RECOVER_BACKWARD(const Indicator& ind) { + IRecover::checkInputIndicator(ind); + return RECOVER_BACKWARD()(ind); +} + +Indicator HKU_API RECOVER_EQUAL_FORWARD(const Indicator& ind) { + IRecover::checkInputIndicator(ind); + return RECOVER_EQUAL_FORWARD()(ind); +} + +Indicator HKU_API RECOVER_EQUAL_BACKWARD(const Indicator& ind) { + IRecover::checkInputIndicator(ind); + return RECOVER_EQUAL_BACKWARD()(ind); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h new file mode 100644 index 00000000..3610ceb4 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2019~2023, hikyuu.org + * + * History: + * 1. 20240317 added by fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class IRecover : public IndicatorImp { + INDICATOR_IMP(IRecover) + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IRecover(); + IRecover(int recoverType); + IRecover(const KData&, int recoverType); + virtual ~IRecover(); + + static void checkInputIndicator(const Indicator& ind); +}; + +} // namespace hku \ No newline at end of file From dc6e262077774dd6ca9f3aa91954d4a003d00874 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 03:08:45 +0800 Subject: [PATCH 051/601] update --- .../hikyuu/indicator/test_RECOVER.cpp | 77 +++++++++++++++++++ 1 file changed, 77 insertions(+) create mode 100644 hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp new file mode 100644 index 00000000..4df1e8fc --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp @@ -0,0 +1,77 @@ +/* + * test_IKData.cpp + * + * Created on: 2013-2-12 + * Author: fasiondog + */ + +#include "doctest/doctest.h" +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_indicator_RECOVER test_indicator_RECOVER + * @ingroup test_hikyuu_indicator_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_RECOVER") { + StockManager& sm = StockManager::instance(); + Stock stock = sm.getStock("sh000001"); + KData kdata; + KQuery query; + + /** @arg 非法 ind */ + CHECK_THROWS_AS(RECOVER_FORWARD(MA()), std::exception); + CHECK_THROWS_AS(RECOVER_BACKWARD(MA()), std::exception); + CHECK_THROWS_AS(RECOVER_EQUAL_FORWARD(MA()), std::exception); + CHECK_THROWS_AS(RECOVER_EQUAL_BACKWARD(MA()), std::exception); +} + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_RECOVER_export") { + // StockManager& sm = StockManager::instance(); + // string filename(sm.tmpdir()); + // filename += "/IKDATA.xml"; + + // Stock stock = sm.getStock("sh000001"); + // KData kdata = stock.getKData(KQuery(-20)); + // Indicator ma1 = KDATA(kdata); + // { + // std::ofstream ofs(filename); + // boost::archive::xml_oarchive oa(ofs); + // oa << BOOST_SERIALIZATION_NVP(ma1); + // } + + // Indicator ma2; + // { + // std::ifstream ifs(filename); + // boost::archive::xml_iarchive ia(ifs); + // ia >> BOOST_SERIALIZATION_NVP(ma2); + // } + + // CHECK_EQ(ma1.size(), ma2.size()); + // CHECK_EQ(ma1.discard(), ma2.discard()); + // CHECK_EQ(ma1.getResultNumber(), ma2.getResultNumber()); + // for (size_t i = 0; i < ma1.size(); ++i) { + // CHECK_EQ(ma1.get(i, 0), doctest::Approx(ma2.get(i, 0))); + // CHECK_EQ(ma1.get(i, 1), doctest::Approx(ma2.get(i, 1))); + // CHECK_EQ(ma1.get(i, 2), doctest::Approx(ma2.get(i, 2))); + // CHECK_EQ(ma1.get(i, 3), doctest::Approx(ma2.get(i, 3))); + // CHECK_EQ(ma1.get(i, 4), doctest::Approx(ma2.get(i, 4))); + // CHECK_EQ(ma1.get(i, 5), doctest::Approx(ma2.get(i, 5))); + // } +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ From f955f110421950492bc1e502e7746447a0be46f8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 15:30:26 +0800 Subject: [PATCH 052/601] =?UTF-8?q?=E5=A4=8D=E6=9D=83=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 24 ++- hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp | 18 +- .../hikyuu/indicator/test_RECOVER.cpp | 185 +++++++++++++++--- 3 files changed, 174 insertions(+), 53 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 93a065d2..7482fb42 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -54,15 +54,21 @@ bool Indicator::alike(const Indicator& other) const { bool Indicator::equal(const Indicator& other) const { HKU_IF_RETURN(this == &other || m_imp == other.m_imp, true); - HKU_IF_RETURN(size() != other.size() || discard() != other.discard(), false); - auto const* d1 = this->data(); - auto const* d2 = other.data(); - for (size_t i = 0, total = size(); i < total; i++) { - HKU_IF_RETURN( - (std::isnan(d1[i]) && !std::isnan(d2[i])) || (!std::isnan(d1[i]) && std::isnan(d2[i])), - false); - HKU_IF_RETURN( - (!std::isnan(d1[i]) && !std::isnan(d2[i])) && (std::abs(d1[i] - d2[i]) >= 0.0001), false); + HKU_IF_RETURN(size() != other.size() || discard() != other.discard() || + getResultNumber() != other.getResultNumber(), + false); + + for (size_t r = 0, result_num = getResultNumber(); r < result_num; r++) { + auto const* d1 = this->data(r); + auto const* d2 = other.data(r); + for (size_t i = 0, total = size(); i < total; i++) { + HKU_IF_RETURN((std::isnan(d1[i]) && !std::isnan(d2[i])) || + (!std::isnan(d1[i]) && std::isnan(d2[i])), + false); + HKU_IF_RETURN( + (!std::isnan(d1[i]) && !std::isnan(d2[i])) && (std::abs(d1[i] - d2[i]) >= 0.0001), + false); + } } return true; } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp index 0c6b12d6..c0b2b685 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp @@ -43,29 +43,17 @@ void IRecover::checkInputIndicator(const Indicator& ind) { } void IRecover::_calculate(const Indicator& ind) { - auto kdata = getContext(); + auto kdata = ind.getContext(); auto query = kdata.getQuery(); - auto ktype = query.kType(); - // 日线以下数据,不执行复权 - if (!(ktype == KQuery::WEEK || ktype == KQuery::MONTH || ktype == KQuery::QUARTER || - ktype == KQuery::HALFYEAR || ktype == KQuery::YEAR)) { - size_t total = ind.size(); - _readyBuffer(total, 1); - const auto* src = ind.data(); - auto* dst = this->data(); - memcpy(dst, src, sizeof(value_t) * total); - return; - } - KQuery::RecoverType recover_type = static_cast(getParam("recover_type")); - query.recoverType(recover_type); + m_name = fmt::format("RECOVER_{}", KQuery::getRecoverTypeName(recover_type)); + query.recoverType(recover_type); KData new_k = kdata.getStock().getKData(query); HKU_ASSERT(new_k.size() == ind.size()); - m_name = KQuery::getRecoverTypeName(recover_type); size_t total = new_k.size(); _readyBuffer(total, 1); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp index 4df1e8fc..e309a358 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_RECOVER.cpp @@ -22,7 +22,7 @@ using namespace hku; /** @par 检测点 */ TEST_CASE("test_RECOVER") { StockManager& sm = StockManager::instance(); - Stock stock = sm.getStock("sh000001"); + Stock stock = sm.getStock("sz000001"); KData kdata; KQuery query; @@ -31,6 +31,139 @@ TEST_CASE("test_RECOVER") { CHECK_THROWS_AS(RECOVER_BACKWARD(MA()), std::exception); CHECK_THROWS_AS(RECOVER_EQUAL_FORWARD(MA()), std::exception); CHECK_THROWS_AS(RECOVER_EQUAL_BACKWARD(MA()), std::exception); + + /** @arg 日线以下复权 */ + Datetime start = Datetime(199509250000); + Datetime end = Null(); + query = KQueryByDate(start, end, KQuery::MIN60); + auto weights = stock.getWeight(start, end); + REQUIRE(weights.size() > 0); + + kdata = stock.getKData(query); + REQUIRE(kdata.size() > 0); + + auto query2 = KQueryByDate(start, end, KQuery::MIN60, KQuery::FORWARD); + auto kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.close().equal(kdata2.close())); + auto result = RECOVER_FORWARD(kdata.close()); + CHECK_EQ(result.name(), "RECOVER_FORWARD"); + CHECK_EQ(result.size(), kdata.size()); + CHECK_UNARY(result.equal(kdata2.close())); + + query2 = KQueryByDate(start, end, KQuery::MIN60, KQuery::BACKWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.open().equal(kdata2.open())); + result = RECOVER_BACKWARD(kdata.open()); + CHECK_EQ(result.name(), "RECOVER_BACKWARD"); + CHECK_UNARY(result.equal(kdata2.open())); + + query2 = KQueryByDate(start, end, KQuery::MIN60, KQuery::EQUAL_BACKWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.high().equal(kdata2.high())); + result = RECOVER_EQUAL_BACKWARD(kdata.high()); + CHECK_EQ(result.name(), "RECOVER_EQUAL_BACKWARD"); + CHECK_UNARY(result.equal(kdata2.high())); + + query2 = KQueryByDate(start, end, KQuery::MIN60, KQuery::EQUAL_FORWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.low().equal(kdata2.low())); + result = RECOVER_EQUAL_FORWARD(kdata.low()); + CHECK_EQ(result.name(), "RECOVER_EQUAL_FORWARD"); + CHECK_UNARY(result.equal(kdata2.low())); + + /** @arg 日线复权 */ + query = KQueryByDate(start, end, KQuery::DAY); + kdata = stock.getKData(query); + REQUIRE(kdata.size() > 0); + + query2 = KQueryByDate(start, end, KQuery::DAY, KQuery::FORWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.close().equal(kdata2.close())); + result = RECOVER_FORWARD(kdata.close()); + CHECK_EQ(result.name(), "RECOVER_FORWARD"); + CHECK_EQ(result.size(), kdata.size()); + CHECK_UNARY(result.equal(kdata2.close())); + + query2 = KQueryByDate(start, end, KQuery::DAY, KQuery::BACKWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.open().equal(kdata2.open())); + result = RECOVER_BACKWARD(kdata.open()); + CHECK_EQ(result.name(), "RECOVER_BACKWARD"); + CHECK_UNARY(result.equal(kdata2.open())); + + query2 = KQueryByDate(start, end, KQuery::DAY, KQuery::EQUAL_BACKWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.high().equal(kdata2.high())); + result = RECOVER_EQUAL_BACKWARD(kdata.high()); + CHECK_EQ(result.name(), "RECOVER_EQUAL_BACKWARD"); + CHECK_UNARY(result.equal(kdata2.high())); + + query2 = KQueryByDate(start, end, KQuery::DAY, KQuery::EQUAL_FORWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.low().equal(kdata2.low())); + result = RECOVER_EQUAL_FORWARD(kdata.low()); + CHECK_EQ(result.name(), "RECOVER_EQUAL_FORWARD"); + CHECK_UNARY(result.equal(kdata2.low())); + + /** @arg 日线以上复权 */ + query = KQueryByDate(start, end, KQuery::WEEK); + kdata = stock.getKData(query); + REQUIRE(kdata.size() > 0); + + query2 = KQueryByDate(start, end, KQuery::WEEK, KQuery::FORWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.close().equal(kdata2.close())); + result = RECOVER_FORWARD(kdata.close()); + CHECK_EQ(result.name(), "RECOVER_FORWARD"); + CHECK_EQ(result.size(), kdata.size()); + CHECK_UNARY(result.equal(kdata2.close())); + + query2 = KQueryByDate(start, end, KQuery::WEEK, KQuery::BACKWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.open().equal(kdata2.open())); + result = RECOVER_BACKWARD(kdata.open()); + CHECK_EQ(result.name(), "RECOVER_BACKWARD"); + CHECK_UNARY(result.equal(kdata2.open())); + + query2 = KQueryByDate(start, end, KQuery::WEEK, KQuery::EQUAL_BACKWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.high().equal(kdata2.high())); + result = RECOVER_EQUAL_BACKWARD(kdata.high()); + CHECK_EQ(result.name(), "RECOVER_EQUAL_BACKWARD"); + CHECK_UNARY(result.equal(kdata2.high())); + + query2 = KQueryByDate(start, end, KQuery::WEEK, KQuery::EQUAL_FORWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.low().equal(kdata2.low())); + result = RECOVER_EQUAL_FORWARD(kdata.low()); + CHECK_EQ(result.name(), "RECOVER_EQUAL_FORWARD"); + CHECK_UNARY(result.equal(kdata2.low())); + + /** @arg 直接以 KData 作为输入参数 */ + query = KQueryByDate(start, end, KQuery::WEEK); + kdata = stock.getKData(query); + REQUIRE(kdata.size() > 0); + + query2 = KQueryByDate(start, end, KQuery::WEEK, KQuery::FORWARD); + kdata2 = stock.getKData(query2); + REQUIRE(kdata.size() == kdata2.size()); + REQUIRE(!kdata.close().equal(kdata2.close())); + result = RECOVER_FORWARD(kdata); + CHECK_EQ(result.name(), "RECOVER_FORWARD"); + CHECK_EQ(result.size(), kdata.size()); + CHECK_UNARY(result.equal(kdata2.close())); } //----------------------------------------------------------------------------- @@ -40,37 +173,31 @@ TEST_CASE("test_RECOVER") { /** @par 检测点 */ TEST_CASE("test_RECOVER_export") { - // StockManager& sm = StockManager::instance(); - // string filename(sm.tmpdir()); - // filename += "/IKDATA.xml"; + StockManager& sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/RECOVER.xml"; - // Stock stock = sm.getStock("sh000001"); - // KData kdata = stock.getKData(KQuery(-20)); - // Indicator ma1 = KDATA(kdata); - // { - // std::ofstream ofs(filename); - // boost::archive::xml_oarchive oa(ofs); - // oa << BOOST_SERIALIZATION_NVP(ma1); - // } + Stock stock = sm.getStock("sz000001"); + KData kdata = stock.getKData(KQuery(-20)); + Indicator x1 = RECOVER_BACKWARD(kdata); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(x1); + } - // Indicator ma2; - // { - // std::ifstream ifs(filename); - // boost::archive::xml_iarchive ia(ifs); - // ia >> BOOST_SERIALIZATION_NVP(ma2); - // } + Indicator x2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(x2); + } - // CHECK_EQ(ma1.size(), ma2.size()); - // CHECK_EQ(ma1.discard(), ma2.discard()); - // CHECK_EQ(ma1.getResultNumber(), ma2.getResultNumber()); - // for (size_t i = 0; i < ma1.size(); ++i) { - // CHECK_EQ(ma1.get(i, 0), doctest::Approx(ma2.get(i, 0))); - // CHECK_EQ(ma1.get(i, 1), doctest::Approx(ma2.get(i, 1))); - // CHECK_EQ(ma1.get(i, 2), doctest::Approx(ma2.get(i, 2))); - // CHECK_EQ(ma1.get(i, 3), doctest::Approx(ma2.get(i, 3))); - // CHECK_EQ(ma1.get(i, 4), doctest::Approx(ma2.get(i, 4))); - // CHECK_EQ(ma1.get(i, 5), doctest::Approx(ma2.get(i, 5))); - // } + CHECK_EQ(x1.name(), x2.name()); + CHECK_EQ(x1.size(), x2.size()); + CHECK_EQ(x1.discard(), x2.discard()); + CHECK_EQ(x1.getResultNumber(), x2.getResultNumber()); + CHECK_UNARY(x1.equal(x2)); } #endif /* #if HKU_SUPPORT_SERIALIZATION */ From 3385d40b1b54add924c2adbb8c9f52fb9937343b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 15:51:27 +0800 Subject: [PATCH 053/601] =?UTF-8?q?=E5=A4=8D=E6=9D=83=E6=8C=87=E6=A0=87?= =?UTF-8?q?=E5=BC=95=E5=87=BA=E5=88=B0python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 32 ++++++++++++++ docs/source/indicator/overview.rst | 4 ++ hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h | 5 +++ hikyuu_pywrap/indicator/_build_in.cpp | 52 +++++++++++++++++++++++ 4 files changed, 93 insertions(+) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index aed4df04..67109377 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -761,6 +761,38 @@ :rtype: Indicator +.. py:function:: RECOVER_BACKWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行后向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator + + +.. py:function:: RECOVER_FORWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行前向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator + + +.. py:function:: RECOVER_EQUAL_BACKWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行等比后向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator + + +.. py:function:: RECOVER_EQUAL_FORWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行等比前向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator + + .. py:function:: REVERSE([data]) 求相反数,REVERSE(X)返回-X diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index 6133ee0a..8baefa4a 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -24,6 +24,10 @@ * :py:func:`CLOSE` - 包装KData的收盘价成Indicator * :py:func:`AMO` - 包装KData的成交金额成Indicator * :py:func:`VOL` - 包装KData的成交量成Indicator +* :py:func:`RECOVER_FORWARD` - 前向复权 +* :py:func:`RECOVER_BACKWARD` - 后向复权 +* :py:func:`RECOVER_EQUAL_FORWARD` - 等比前向复权 +* :py:func:`RECOVER_EQUAL_BACKWARD` - 等比后向复权 * :py:func:`HSL` - 换手率 * :py:func:`CAPITAL` - 流通盘,同名:LIUTONGPAN * :py:func:`TIMELINE` - 分时价格 diff --git a/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h b/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h index 57e5e068..67a10283 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/RECOVER.h @@ -11,6 +11,11 @@ namespace hku { +/** + * 复权类指标,可接收 CLOSE/OPEN/HIGH/LOW 指标,或 KData(默认使用 close) 作为输入参数 + * @return Indicator + * @ingroup Indicator + */ Indicator HKU_API RECOVER_FORWARD(); Indicator HKU_API RECOVER_BACKWARD(); Indicator HKU_API RECOVER_EQUAL_FORWARD(); diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 31f5dff1..c50233d7 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -35,6 +35,22 @@ Indicator (*VOL3)() = VOL; Indicator (*KDATA_PART1)(const KData& kdata, const string& part) = KDATA_PART; Indicator (*KDATA_PART3)(const string& part) = KDATA_PART; +Indicator (*RECOVER_FORWARD_1)() = RECOVER_FORWARD; +Indicator (*RECOVER_FORWARD_2)(const Indicator&) = RECOVER_FORWARD; +Indicator (*RECOVER_FORWARD_3)(const KData&) = RECOVER_FORWARD; + +Indicator (*RECOVER_BACKWARD_1)() = RECOVER_BACKWARD; +Indicator (*RECOVER_BACKWARD_2)(const Indicator&) = RECOVER_BACKWARD; +Indicator (*RECOVER_BACKWARD_3)(const KData&) = RECOVER_BACKWARD; + +Indicator (*RECOVER_EQUAL_FORWARD_1)() = RECOVER_EQUAL_FORWARD; +Indicator (*RECOVER_EQUAL_FORWARD_2)(const Indicator&) = RECOVER_EQUAL_FORWARD; +Indicator (*RECOVER_EQUAL_FORWARD_3)(const KData&) = RECOVER_EQUAL_FORWARD; + +Indicator (*RECOVER_EQUAL_BACKWARD_1)() = RECOVER_EQUAL_BACKWARD; +Indicator (*RECOVER_EQUAL_BACKWARD_2)(const Indicator&) = RECOVER_EQUAL_BACKWARD; +Indicator (*RECOVER_EQUAL_BACKWARD_3)(const KData&) = RECOVER_EQUAL_BACKWARD; + Indicator (*DATE1)() = hku::DATE; Indicator (*DATE2)(const KData&) = hku::DATE; @@ -541,6 +557,42 @@ void export_Indicator_build_in(py::module& m) { :param string kpart: KDATA|OPEN|HIGH|LOW|CLOSE|AMO|VOL :rtype: Indicator)"); + m.def("RECOVER_FORWARD", RECOVER_FORWARD_1); + m.def("RECOVER_FORWARD", RECOVER_FORWARD_2); + m.def("RECOVER_FORWARD", RECOVER_FORWARD_3, R"(RECOVER_FORWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行前向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator)"); + + m.def("RECOVER_BACKWARD", RECOVER_BACKWARD_1); + m.def("RECOVER_BACKWARD", RECOVER_BACKWARD_2); + m.def("RECOVER_BACKWARD", RECOVER_BACKWARD_3, R"(RECOVER_BACKWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行后向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator)"); + + m.def("RECOVER_EQUAL_FORWARD", RECOVER_EQUAL_FORWARD_1); + m.def("RECOVER_EQUAL_FORWARD", RECOVER_EQUAL_FORWARD_2); + m.def("RECOVER_EQUAL_FORWARD", RECOVER_EQUAL_FORWARD_3, R"(RECOVER_EQUAL_FORWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行等比前向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator)"); + + m.def("RECOVER_EQUAL_BACKWARD", RECOVER_EQUAL_BACKWARD_1); + m.def("RECOVER_EQUAL_BACKWARD", RECOVER_EQUAL_BACKWARD_2); + m.def("RECOVER_EQUAL_BACKWARD", RECOVER_EQUAL_BACKWARD_3, R"(RECOVER_EQUAL_BACKWARD([data]) + + 对输入的指标数据 (CLOSE|OPEN|HIGH|LOW) 进行等比后向复权 + + :param Indicator|KData data: 只接受 CLOSE|OPEN|HIGH|LOW 指标,或 KData(此时默认使用 KData 的收盘价) + :rtype: Indicator)"); + m.def("DATE", DATE1); m.def("DATE", DATE2, R"(DATE([data]) From 517e1b5db0f83fbaa3f6cc4e63fb2e46657a20cc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 18:57:14 +0800 Subject: [PATCH 054/601] update --- .../hikyuu/trade_sys/selector/SelectorBase.cpp | 2 +- hikyuu_pywrap/bind_stl.cpp | 2 +- hikyuu_pywrap/bind_stl.h | 5 ++++- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 13 +------------ 4 files changed, 7 insertions(+), 15 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 4d5312c6..9c73c977 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -109,7 +109,7 @@ bool SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { bool SelectorBase::addStockList(const StockList& stkList, const SystemPtr& protoSys) { HKU_ERROR_IF_RETURN(!protoSys, false, "Try add Null protoSys, will be discard!"); HKU_ERROR_IF_RETURN(!protoSys->getMM(), false, "protoSys has not MoneyManager!"); - HKU_ERROR_IF_RETURN(!protoSys->getSG(), false, "protoSys has not Siganl!"); + HKU_ERROR_IF_RETURN(!protoSys->getSG(), false, "protoSys has not Signal!"); SYSPtr newProtoSys = protoSys->clone(); // 复位清除之前的数据,避免因原有数据过多导致下面循环时速度过慢 // 每个系统独立,不共享 tm diff --git a/hikyuu_pywrap/bind_stl.cpp b/hikyuu_pywrap/bind_stl.cpp index 58e18ade..73aeaa92 100644 --- a/hikyuu_pywrap/bind_stl.cpp +++ b/hikyuu_pywrap/bind_stl.cpp @@ -18,8 +18,8 @@ void export_bind_stl(py::module& m) { py::bind_vector(m, "DatetimeList"); py::bind_vector(m, "KRecordList"); py::bind_vector(m, "StockList"); - // py::bind_vector(m, "KRecordList"); // could't compile py::bind_vector(m, "StockWeightList"); + // py::bind_vector(m, "Indicatorist"); py::bind_vector(m, "TimeLineList"); py::bind_vector(m, "TransList"); py::bind_vector(m, "BorrowRecordList"); diff --git a/hikyuu_pywrap/bind_stl.h b/hikyuu_pywrap/bind_stl.h index 6a793ab9..c992d3cc 100644 --- a/hikyuu_pywrap/bind_stl.h +++ b/hikyuu_pywrap/bind_stl.h @@ -14,12 +14,15 @@ using namespace hku; // pybind stl 绑定必须在其他 type_caster 之前 -// 简单类型的 vector 不再导出,只导出复杂的 struct(影响性能) +// 让自定义的 vector 在 python 中表现的像 list 一样 +// 简单类型的 vector 不再导出,只导出复杂的 structList,避免影响性能 // PYBIND11_MAKE_OPAQUE(StringList); // PYBIND11_MAKE_OPAQUE(PriceList); PYBIND11_MAKE_OPAQUE(DatetimeList); PYBIND11_MAKE_OPAQUE(KRecordList); +PYBIND11_MAKE_OPAQUE(StockList); PYBIND11_MAKE_OPAQUE(StockWeightList); +// PYBIND11_MAKE_OPAQUE(IndicatorList); // 无法编译 PYBIND11_MAKE_OPAQUE(TimeLineList); PYBIND11_MAKE_OPAQUE(TransList); PYBIND11_MAKE_OPAQUE(BorrowRecordList); diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index df7f5eeb..55177772 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -68,18 +68,7 @@ void export_MultiFactor(py::module& m) { .def("get_ref_indicators", &MultiFactorBase::getRefIndicators, py::return_value_policy::copy) .def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy) - - .def("get_all_factors", - [](MultiFactorBase& self) { - // return vector_to_python_list() - auto factors = self.getAllFactors(); - IndicatorList copy_factors; - copy_factors.reserve(factors.size()); - for (const auto& factor : factors) { - copy_factors.emplace_back(factor.clone()); - } - return vector_to_python_list(copy_factors); - }) + .def("get_all_factors", &MultiFactorBase::getAllFactors, py::return_value_policy::copy) .def("get_ic", &MultiFactorBase::getIC, py::arg("ndays") = 0) .def("get_icir", &MultiFactorBase::getICIR, py::arg("ir_n"), py::arg("ic_n") = 0) From c05a559d0c255522c09ccc6df1d055e126110b91 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 17 Mar 2024 23:30:24 +0800 Subject: [PATCH 055/601] =?UTF-8?q?MultiFactor=E6=8E=A5=E5=8F=A3=E5=91=BD?= =?UTF-8?q?=E5=90=8D=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/all.h | 1 + .../trade_sys/factor/MultiFactorBase.cpp | 95 ++++--------------- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 33 +------ .../hikyuu/trade_sys/factor/ScoreRecord.cpp | 86 +++++++++++++++++ .../hikyuu/trade_sys/factor/ScoreRecord.h | 50 ++++++++++ .../trade_sys/factor/test_MF_EqualWeight.cpp | 22 ++--- hikyuu_pywrap/bind_stl.cpp | 1 + hikyuu_pywrap/bind_stl.h | 3 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 31 +++--- 9 files changed, 184 insertions(+), 138 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/all.h b/hikyuu_cpp/hikyuu/trade_sys/all.h index 83c7a449..1ef6898f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/all.h +++ b/hikyuu_cpp/hikyuu/trade_sys/all.h @@ -19,5 +19,6 @@ #include "system/build_in.h" #include "allocatefunds/build_in.h" #include "selector/build_in.h" +#include "factor/build_in.h" #endif /* ALL_TRADE_SYS_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 98d19563..fd8e052f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -58,63 +58,6 @@ HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorPtr& mf) { return out; } -HKU_API std::ostream& operator<<(std::ostream& out, - const std::pair& td) { - out << std::fixed; - out.precision(4); - out << "(" << td.first.market_code() << ", " << td.second << ")" << std::endl; - out.unsetf(std::ostream::floatfield); - out.precision(); - return out; -} - -HKU_API std::ostream& operator<<(std::ostream& out, - const vector>& td) { - out << std::fixed; - out.precision(4); - size_t total = td.size(); - if (total < 10) { - for (size_t i = 0; i < total; i++) { - out << i << ": " << td[i]; - } - } else { - for (size_t i = 0; i < 5; i++) { - out << i << ": " << td[i]; - } - out << "......" << std::endl; - for (size_t i = total - 5; i < total; i++) { - out << i << ": " << td[i]; - } - } - out.unsetf(std::ostream::floatfield); - out.precision(); - return out; -} - -HKU_API std::ostream& operator<<( - std::ostream& out, const vector>>& td) { - out << std::fixed; - out.precision(4); - size_t total = td.size(); - if (total <= 2) { - for (size_t i = 0; i < total; i++) { - out << "========= " << i << " =========" << std::endl; - out << td[i]; - } - } else { - out << "========= 0 =========" << std::endl; - out << td[0]; - out << "......" << std::endl; - out << "......" << std::endl; - out << "========= " << total - 1 << " =========" << std::endl; - out << td[total - 1]; - } - - out.unsetf(std::ostream::floatfield); - out.precision(); - return out; -} - MultiFactorBase::MultiFactorBase() { setParam("fill_null", true); setParam("ic_n", 1); @@ -200,21 +143,18 @@ const IndicatorList& MultiFactorBase::getAllFactors() { return m_all_factors; } -const vector>& MultiFactorBase::getCross( - const Datetime& d) { +const ScoreRecordList& MultiFactorBase::getScore(const Datetime& d) { calculate(); const auto iter = m_date_index.find(d); HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); return m_stk_factor_by_date[iter->second]; } -vector> MultiFactorBase::getCross(const Datetime& date, - size_t start, - size_t end) { - vector> ret; +ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, size_t end) { + ScoreRecordList ret; HKU_IF_RETURN(start >= end, ret); - const auto& cross = getCross(date); + const auto& cross = getScore(date); if (end == Null() || end > cross.size()) { end = cross.size(); } @@ -227,7 +167,7 @@ vector> MultiFactorBase::getCross(con return ret; } -const vector>>& MultiFactorBase::getAllCross() { +const vector& MultiFactorBase::getAllScores() { calculate(); return m_stk_factor_by_date; } @@ -348,23 +288,22 @@ void MultiFactorBase::_buildIndex() { // 建立每日截面的索引,并每日降序排序 size_t days_total = m_ref_dates.size(); m_stk_factor_by_date.resize(days_total); - vector> one_day; + ScoreRecordList one_day; for (size_t i = 0; i < days_total; i++) { one_day.resize(stk_count); for (size_t j = 0; j < stk_count; j++) { - one_day[j] = std::make_pair(m_stks[j], m_all_factors[j][i]); + one_day[j] = ScoreRecord(m_stks[j], m_all_factors[j][i]); } - std::sort(one_day.begin(), one_day.end(), - [](const std::pair& a, const std::pair& b) { - if (std::isnan(a.second) && std::isnan(b.second)) { - return false; - } else if (!std::isnan(a.second) && std::isnan(b.second)) { - return true; - } else if (std::isnan(a.second) && !std::isnan(b.second)) { - return false; - } - return a.second > b.second; - }); + std::sort(one_day.begin(), one_day.end(), [](const ScoreRecord& a, const ScoreRecord& b) { + if (std::isnan(a.value) && std::isnan(b.value)) { + return false; + } else if (!std::isnan(a.value) && std::isnan(b.value)) { + return true; + } else if (std::isnan(a.value) && !std::isnan(b.value)) { + return false; + } + return a.value > b.value; + }); m_stk_factor_by_date[i] = std::move(one_day); m_date_index[m_ref_dates[i]] = i; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 334b4575..a1259aa4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -8,8 +8,7 @@ #pragma once #include "hikyuu/KData.h" -#include "hikyuu/indicator/Indicator.h" -#include "hikyuu/utilities/Parameter.h" +#include "ScoreRecord.h" namespace hku { @@ -79,13 +78,12 @@ public: const IndicatorList& getAllFactors(); /** 获取指定日期截面的所有因子值,已经降序排列 */ - const vector>& getCross(const Datetime&); + const ScoreRecordList& getScore(const Datetime&); - vector> getCross(const Datetime& date, size_t start, - size_t end = Null()); + ScoreRecordList getScore(const Datetime& date, size_t start, size_t end = Null()); /** 获取所有截面数据,已按降序排列 */ - const vector>>& getAllCross(); + const vector& getAllScores(); /** * 获取合成因子的IC @@ -130,7 +128,7 @@ protected: unordered_map m_stk_map; // 证券->合成后因子位置索引 IndicatorList m_all_factors; // 保存所有证券合成后的新因子 unordered_map m_date_index; - vector>> m_stk_factor_by_date; + vector m_stk_factor_by_date; Indicator m_ic; private: @@ -225,15 +223,6 @@ public: \ HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorBase&); HKU_API std::ostream& operator<<(std::ostream&, const MultiFactorPtr&); -HKU_API std::ostream& operator<<(std::ostream& out, - const std::pair& td); - -HKU_API std::ostream& operator<<(std::ostream& out, - const vector>& td); - -HKU_API std::ostream& operator<<( - std::ostream& out, const vector>>& td); - } // namespace hku #if FMT_VERSION >= 90000 @@ -242,16 +231,4 @@ struct fmt::formatter : ostream_formatter {}; template <> struct fmt::formatter : ostream_formatter {}; - -template <> -struct fmt::formatter> : ostream_formatter {}; - -template <> -struct fmt::formatter>> -: ostream_formatter {}; - -template <> -struct fmt::formatter< - std::vector>>> -: ostream_formatter {}; #endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.cpp new file mode 100644 index 00000000..c144b70a --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.cpp @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-17 + * Author: fasiondog + */ + +#include "ScoreRecord.h" + +namespace hku { + +HKU_API std::ostream& operator<<(std::ostream& out, const ScoreRecord& td) { + out << std::fixed; + out.precision(4); + out << "(" << td.stock.market_code() << ", " << td.value << ")" << std::endl; + out.unsetf(std::ostream::floatfield); + out.precision(); + return out; +} + +HKU_API std::ostream& operator<<(std::ostream& out, const ScoreRecordList& td) { + out << std::fixed; + out.precision(4); + size_t total = td.size(); + if (total < 10) { + for (size_t i = 0; i < total; i++) { + out << i << ": " << td[i]; + } + } else { + for (size_t i = 0; i < 5; i++) { + out << i << ": " << td[i]; + } + out << "......" << std::endl; + for (size_t i = total - 5; i < total; i++) { + out << i << ": " << td[i]; + } + } + out.unsetf(std::ostream::floatfield); + out.precision(); + return out; +} + +HKU_API std::ostream& operator<<(std::ostream& out, const vector& td) { + out << std::fixed; + out.precision(4); + size_t total = td.size(); + if (total <= 2) { + for (size_t i = 0; i < total; i++) { + out << "========= " << i << " =========" << std::endl; + out << td[i]; + } + } else { + out << "========= 0 =========" << std::endl; + out << td[0]; + out << "......" << std::endl; + out << "......" << std::endl; + out << "========= " << total - 1 << " =========" << std::endl; + out << td[total - 1]; + } + + out.unsetf(std::ostream::floatfield); + out.precision(); + return out; +} + +ScoreRecord::ScoreRecord(const Stock& stock_, value_t value_) : stock(stock_), value(value_) {} + +ScoreRecord::ScoreRecord(const ScoreRecord& other) : stock(other.stock), value(other.value) {} + +ScoreRecord::ScoreRecord(ScoreRecord&& rv) : stock(std::move(rv.stock)), value(rv.value) {} + +ScoreRecord& ScoreRecord::operator=(const ScoreRecord& rv) { + HKU_IF_RETURN(this == &rv, *this); + stock = rv.stock; + value = rv.value; + return *this; +} + +ScoreRecord& ScoreRecord::operator=(ScoreRecord&& rv) { + HKU_IF_RETURN(this == &rv, *this); + stock = std::move(rv.stock); + value = rv.value; + return *this; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h new file mode 100644 index 00000000..5420a519 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-17 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/Stock.h" +#include "hikyuu/indicator/Indicator.h" + +namespace hku { + +struct HKU_API ScoreRecord { + typedef Indicator::value_t value_t; + + Stock stock; + value_t value; + + ScoreRecord() = default; + ScoreRecord(const Stock& stock_, value_t value_); + + ScoreRecord(const ScoreRecord&); + ScoreRecord(ScoreRecord&&); + + ScoreRecord& operator=(const ScoreRecord&); + ScoreRecord& operator=(ScoreRecord&&); +}; + +typedef vector ScoreRecordList; + +HKU_API std::ostream& operator<<(std::ostream& out, const ScoreRecord& td); + +HKU_API std::ostream& operator<<(std::ostream& out, const ScoreRecordList& td); + +HKU_API std::ostream& operator<<(std::ostream& out, const vector& td); + +} // namespace hku + +#if FMT_VERSION >= 90000 +template <> +struct fmt::formatter : ostream_formatter {}; + +template <> +struct fmt::formatter : ostream_formatter {}; + +template <> +struct fmt::formatter> : ostream_formatter {}; +#endif \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index a3494b70..6ab83539 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -103,20 +103,20 @@ TEST_CASE("test_MF_EqualWeight") { auto ic2 = IC(MA(CLOSE()), stks, query, 1, ref_stk); CHECK_UNARY(ic1.equal(ic2)); - CHECK_THROWS_AS(mf->getCross(Datetime(20111204)), std::exception); - auto cross = mf->getCross(Datetime(20111205)); + CHECK_THROWS_AS(mf->getScore(Datetime(20111204)), std::exception); + auto cross = mf->getScore(Datetime(20111205)); CHECK_EQ(cross.size(), 2); - CHECK_EQ(cross[0].first, sm["sh600004"]); - CHECK_EQ(cross[0].second, doctest::Approx(6.85)); - CHECK_EQ(cross[1].first, sm["sh600005"]); - CHECK_EQ(cross[1].second, doctest::Approx(3.13)); + CHECK_EQ(cross[0].stock, sm["sh600004"]); + CHECK_EQ(cross[0].value, doctest::Approx(6.85)); + CHECK_EQ(cross[1].stock, sm["sh600005"]); + CHECK_EQ(cross[1].value, doctest::Approx(3.13)); - cross = mf->getCross(Datetime(20111206)); + cross = mf->getScore(Datetime(20111206)); CHECK_EQ(cross.size(), 2); - CHECK_EQ(cross[0].first, sm["sh600004"]); - CHECK_EQ(cross[0].second, doctest::Approx(6.855)); - CHECK_EQ(cross[1].first, sm["sh600005"]); - CHECK_EQ(cross[1].second, doctest::Approx(3.14)); + CHECK_EQ(cross[0].stock, sm["sh600004"]); + CHECK_EQ(cross[0].value, doctest::Approx(6.855)); + CHECK_EQ(cross[1].stock, sm["sh600005"]); + CHECK_EQ(cross[1].value, doctest::Approx(3.14)); // HKU_INFO("\n{}", mf->getAllCross()); /** @arg 原始因子数量为3, 证券数量4, 数据长度为20, 指定比较收益率 3 日 */ diff --git a/hikyuu_pywrap/bind_stl.cpp b/hikyuu_pywrap/bind_stl.cpp index 73aeaa92..3327d848 100644 --- a/hikyuu_pywrap/bind_stl.cpp +++ b/hikyuu_pywrap/bind_stl.cpp @@ -28,4 +28,5 @@ void export_bind_stl(py::module& m) { py::bind_vector(m, "TradeRecordList"); py::bind_vector(m, "SystemWeightList"); py::bind_vector(m, "SystemList"); + py::bind_vector(m, "ScoreRecordList"); } \ No newline at end of file diff --git a/hikyuu_pywrap/bind_stl.h b/hikyuu_pywrap/bind_stl.h index c992d3cc..7012a8bd 100644 --- a/hikyuu_pywrap/bind_stl.h +++ b/hikyuu_pywrap/bind_stl.h @@ -30,4 +30,5 @@ PYBIND11_MAKE_OPAQUE(LoanRecordList); PYBIND11_MAKE_OPAQUE(PositionRecordList); PYBIND11_MAKE_OPAQUE(TradeRecordList); PYBIND11_MAKE_OPAQUE(SystemWeightList); -PYBIND11_MAKE_OPAQUE(SystemList); \ No newline at end of file +PYBIND11_MAKE_OPAQUE(SystemList); +PYBIND11_MAKE_OPAQUE(ScoreRecordList); \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 55177772..ea108c05 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -24,6 +24,14 @@ public: }; void export_MultiFactor(py::module& m) { + py::class_(m, "ScoreRecord", "") + .def(py::init<>()) + .def(py::init()) + .def("__str__", to_py_str) + .def("__repr__", to_py_str) + .def_readwrite("stock", &ScoreRecord::stock, "时间") + .def_readwrite("value", &ScoreRecord::value, "时间"); + size_t null_size = Null(); py::class_(m, "MultiFactor", R"(市场环境判定策略基类 @@ -75,30 +83,13 @@ void export_MultiFactor(py::module& m) { .def("clone", &MultiFactorBase::clone) .def( - "get_cross", + "get_score", [](MultiFactorBase& self, const Datetime& date, size_t start, size_t end) { - py::list ret; - auto cross = self.getCross(date, start, end); - for (const auto& item : cross) { - ret.append(py::make_tuple(item.first, item.second)); - } - return ret; + return self.getScore(date, start, end); }, py::arg("date"), py::arg("start") = 0, py::arg("end") = null_size) - .def("get_all_cross", - [](MultiFactorBase& self) { - py::list ret; - auto all_cross = self.getAllCross(); - for (const auto& one_day : all_cross) { - py::list one; - for (const auto& item : one_day) { - one.append(py::make_tuple(item.first, item.second)); - } - ret.append(std::move(one)); - } - return ret; - }) + .def("get_all_scores", &MultiFactorBase::getAllScores, py::return_value_policy::copy) DEF_PICKLE(MultiFactorPtr); From fc73f5899b591e6b4566a1fa5146daeff3ee1009 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 18 Mar 2024 00:48:19 +0800 Subject: [PATCH 056/601] update docs --- docs/source/trade_sys/multifactor.rst | 174 ++++++++++++++++++ docs/source/trade_sys/trade_sys.rst | 3 +- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 2 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 88 +++++++-- 4 files changed, 246 insertions(+), 21 deletions(-) create mode 100644 docs/source/trade_sys/multifactor.rst diff --git a/docs/source/trade_sys/multifactor.rst b/docs/source/trade_sys/multifactor.rst new file mode 100644 index 00000000..bfc39b76 --- /dev/null +++ b/docs/source/trade_sys/multifactor.rst @@ -0,0 +1,174 @@ +.. py:currentmodule:: hikyuu.trade_sys +.. highlight:: python + +多因子合成 +============ + +内建对因子合成算法 +---------------- + +.. py:function:: MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5]) + + 等权重合成因子 + + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 + :param int ic_n: 默认 IC 对应的 N 日收益率 + :rtype: MultiFactorBase + + +.. py:function:: MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) + + 滚动IC权重合成因子 + + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 + :param int ic_n: 默认 IC 对应的 N 日收益率 + :param int ic_rolling_n: IC 滚动周期 + :rtype: MultiFactorBase + + +.. py:function:: MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) + + 滚动ICIR权重合成因子 + + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 + :param int ic_n: 默认 IC 对应的 N 日收益率 + :param int ic_rolling_n: IC 滚动周期 + :rtype: MultiFactorBase + + +自定义多因子合成算法基类 +-------------------- + +自定义多因子合成算法接口: + +* :py:meth:`MultiFactorBase._calculate` - 【必须】计算合成因子 + + +多因子合成算法基类 +------------------------ + +.. py:class:: MultiFactorBase + + 多因子合成基类 + + .. py:attribute:: name 名称 + + .. py:method:: __init__(self) + + 初始化构造函数 + + :param str name: 名称 + + .. py:method:: get_param(self, name) + + 获取指定的参数 + + :param str name: 参数名称 + :return: 参数值 + :raises out_of_range: 无此参数 + + .. py:method:: set_param(self, name, value) + + 设置参数 + + :param str name: 参数名称 + :param value: 参数值 + :type value: int | bool | float | string + :raises logic_error: Unsupported type! 不支持的参数类型 + + .. py:method:: clone(self) + + 克隆操作 + + .. py:method:: get_query(self) + + 查询条件范围 + + .. py:method:: get_ref_stock(self) + + 获取参考证券 + + .. py:method:: get_stock_list(self) + + 获取创建时指定的证券列表 + + .. py:method:: get_stock_list_num(self) + + 获取创建时指定的证券列表中证券数量 + + .. py:method:: get_datetime_list(self) + + 获取参考日期列表(由参考证券通过查询条件获得) + + .. py:method:: get_ref_indicators(self) + + 获取创建时输入的原始因子列表 + + .. py:method:: get_factor(self, stock) + + 获取指定证券合成后的新因子 + + :param Stock stock: 指定证券 + + .. py:method:: get_all_factors(self) + + 获取所有证券合成后的因子列表 + + :return: [factor1, factor2, ...] 顺序与参考证券顺序相同 + + .. py:method:: get_ic(self[, ndays=0]) + + 获取合成因子的IC, 长度与参考日期同 + + ndays 对于使用 IC/ICIR 加权的新因子,最好保持好 ic_n 一致, + 但对于等权计算的新因子,不一定非要使用 ic_n 计算。 + 所以,ndays 增加了一个特殊值 0, 表示直接使用 ic_n 参数计算 IC + + :param int ndays: ic 的 ndays 日收益率 + :rtype: Indicator + + .. py:method:: get_icir(self, ir_n[, ic_n=0]) + + 获取合成因子的 ICIR + + :param int ir_n: 计算 IR 的 n 窗口 + :param int ic_n: 计算 IC 的 n 窗口 (同 get_ic 中的 ndays) + + .. py:method:: get_score(self, date[, start=0, end=Null]) + + 获取指定日期截面的所有因子值,已经降序排列,相当于各证券日期截面评分。 + + :param Datetime date: 指定日期 + :param int start: 取当日排名开始 + :param int end: 取当日排名结束(不包含本身) + :rtype: ScoreRecordList + + .. py:method:: get_all_scores(self) + + 获取所有日期的所有评分,长度与参考日期相同 + + :return: 每日 ScoreRecordList 结果的 list + + .. py:method:: _calculate(self, stks_inds) + + 计算每日证券合成因子,输入参数由上层函数计算后传入,如: + + 待计算的证券列表 - stk1, stk2 + 原始因子列表 - ind1, ind2 + 则传入的 stks_inds 为:[IndicatorList(stk1)[ind1, ind2], IndicatorList(stk2)[ind1, ind2]] + + :param list stks_inds: 与证券列表顺序相同已经计算好的所有证券的原始因子列表 + :return: 按证券列表顺序存放的所有新的因子 + + + + \ No newline at end of file diff --git a/docs/source/trade_sys/trade_sys.rst b/docs/source/trade_sys/trade_sys.rst index 324a2fe7..53385801 100644 --- a/docs/source/trade_sys/trade_sys.rst +++ b/docs/source/trade_sys/trade_sys.rst @@ -13,4 +13,5 @@ stoploss money_manager profitgoal - slippage \ No newline at end of file + slippage + multifactor \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index a1259aa4..36030fbc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -86,7 +86,7 @@ public: const vector& getAllScores(); /** - * 获取合成因子的IC + * 获取合成因子的IC, 长度与参考日期同 * @note ndays 对于使用 IC/ICIR 加权的新因子,最好保持好 ic_n 一致, * 但对于等权计算的新因子,不一定非要使用 ic_n 计算。 * 所以,ndays 增加了一个特殊值 0, 表示直接使用 ic_n 参数计算 IC diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index ea108c05..0bfbde41 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -18,8 +18,13 @@ public: using MultiFactorBase::MultiFactorBase; IndicatorList _calculate(const vector& all_stk_inds) { - PYBIND11_OVERLOAD_PURE_NAME(IndicatorList, MultiFactorBase, "_calculate", _calculate, - all_stk_inds); + // PYBIND11_OVERLOAD_PURE_NAME(IndicatorList, MultiFactorBase, "_calculate", _calculate, + // all_stk_inds); + auto self = py::cast(this); + auto func = self.attr("_calculate")(); + auto py_all_stk_inds = vector_to_python_list(all_stk_inds); + auto py_ret = func(py_all_stk_inds); + return py_ret.cast(); } }; @@ -33,7 +38,7 @@ void export_MultiFactor(py::module& m) { .def_readwrite("value", &ScoreRecord::value, "时间"); size_t null_size = Null(); - py::class_(m, "MultiFactor", + py::class_(m, "MultiFactorBase", R"(市场环境判定策略基类 自定义市场环境判定策略接口: @@ -49,7 +54,7 @@ void export_MultiFactor(py::module& m) { .def_property("name", py::overload_cast<>(&MultiFactorBase::name, py::const_), py::overload_cast(&MultiFactorBase::name), py::return_value_policy::copy, "名称") - .def("get_query", &MultiFactorBase::getQuery, py::return_value_policy::copy) + .def("get_query", &MultiFactorBase::getQuery, py::return_value_policy::copy, R"(查询条件)") .def("get_param", &MultiFactorBase::getParam, R"(get_param(self, name) @@ -69,27 +74,72 @@ void export_MultiFactor(py::module& m) { .def("have_param", &MultiFactorBase::haveParam, "是否存在指定参数") - .def("get_ref_stock", &MultiFactorBase::getRefStock, py::return_value_policy::copy) - .def("get_datetime_list", &MultiFactorBase::getDatetimeList, py::return_value_policy::copy) - .def("get_stock_list", &MultiFactorBase::getStockList, py::return_value_policy::copy) - .def("get_stock_list_num", &MultiFactorBase::getStockListNumber) - .def("get_ref_indicators", &MultiFactorBase::getRefIndicators, py::return_value_policy::copy) + .def("get_ref_stock", &MultiFactorBase::getRefStock, py::return_value_policy::copy, + "获取参考证券") + .def("get_datetime_list", &MultiFactorBase::getDatetimeList, py::return_value_policy::copy, + "获取参考日期列表(由参考证券通过查询条件获得)") + .def("get_stock_list", &MultiFactorBase::getStockList, py::return_value_policy::copy, + "获取创建时指定的证券列表") + .def("get_stock_list_num", &MultiFactorBase::getStockListNumber, + "获取创建时指定的证券列表中证券数量") + .def("get_ref_indicators", &MultiFactorBase::getRefIndicators, py::return_value_policy::copy, + "获取创建时输入的原始因子列表") - .def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy) - .def("get_all_factors", &MultiFactorBase::getAllFactors, py::return_value_policy::copy) + .def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy, + py::arg("stock"), R"(get_factor(self, stock) - .def("get_ic", &MultiFactorBase::getIC, py::arg("ndays") = 0) - .def("get_icir", &MultiFactorBase::getICIR, py::arg("ir_n"), py::arg("ic_n") = 0) - .def("clone", &MultiFactorBase::clone) + 获取指定证券合成后的新因子 + + :param Stock stock: 指定证券)") + + .def("get_all_factors", &MultiFactorBase::getAllFactors, py::return_value_policy::copy, + R"(get_all_factors(self) + + 获取所有证券合成后的因子列表 + + :return: [factor1, factor2, ...] 顺序与参考证券顺序相同)") + + .def("get_ic", &MultiFactorBase::getIC, py::arg("ndays") = 0, R"(get_ic(self[, ndays=0]) + + 获取合成因子的IC, 长度与参考日期同 + + ndays 对于使用 IC/ICIR 加权的新因子,最好保持好 ic_n 一致, + 但对于等权计算的新因子,不一定非要使用 ic_n 计算。 + 所以,ndays 增加了一个特殊值 0, 表示直接使用 ic_n 参数计算 IC + + :rtype: Indicator)") + + .def("get_icir", &MultiFactorBase::getICIR, py::arg("ir_n"), py::arg("ic_n") = 0, + R"(get_icir(self, ir_n[, ic_n=0]) + + 获取合成因子的 ICIR + + :param int ir_n: 计算 IR 的 n 窗口 + :param int ic_n: 计算 IC 的 n 窗口 (同 get_ic 中的 ndays))") + + .def("clone", &MultiFactorBase::clone, "克隆操作") .def( "get_score", [](MultiFactorBase& self, const Datetime& date, size_t start, size_t end) { return self.getScore(date, start, end); }, - py::arg("date"), py::arg("start") = 0, py::arg("end") = null_size) + py::arg("datet"), py::arg("start") = 0, py::arg("end") = null_size, + R"(get_score(self, date[, start=0, end=Null]) - .def("get_all_scores", &MultiFactorBase::getAllScores, py::return_value_policy::copy) + 获取指定日期截面的所有因子值,已经降序排列,相当于各证券日期截面评分。 + + :param Datetime date: 指定日期 + :param int start: 取当日排名开始 + :param int end: 取当日排名结束(不包含本身) + :rtype: ScoreRecordList)") + + .def("get_all_scores", &MultiFactorBase::getAllScores, py::return_value_policy::copy, + R"(get_all_scores(self) + + 获取所有日期的所有评分,长度与参考日期相同 + + :return: 每日 ScoreRecordList 结果的 list)") DEF_PICKLE(MultiFactorPtr); @@ -111,7 +161,7 @@ void export_MultiFactor(py::module& m) { :param Query query: 日期范围 :param Stock ref_stk: 参考证券 :param int ic_n: 默认 IC 对应的 N 日收益率 - :rtype: MultiFactorPtr)"); + :rtype: MultiFactor)"); m.def( "MF_ICWeight", @@ -134,7 +184,7 @@ void export_MultiFactor(py::module& m) { :param Stock ref_stk: 参考证券 :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 - :rtype: MultiFactorPtr)"); + :rtype: MultiFactor)"); m.def( "MF_ICIRWeight", @@ -157,5 +207,5 @@ void export_MultiFactor(py::module& m) { :param Stock ref_stk: 参考证券 :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 - :rtype: MultiFactorPtr)"); + :rtype: MultiFactor)"); } \ No newline at end of file From 21ccefaee6ce724cc5fd63ed56bdf40aca7cca39 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 18 Mar 2024 04:13:51 +0800 Subject: [PATCH 057/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20Selector,=20?= =?UTF-8?q?=E9=80=89=E4=B8=AD=E6=97=B6=E5=90=8C=E6=97=B6=E8=BF=94=E5=9B=9E?= =?UTF-8?q?=E5=AF=B9=E7=B3=BB=E7=BB=9F=E7=9A=84=E6=89=93=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allocatefunds/AllocateFundsBase.cpp | 56 ++++----- .../allocatefunds/AllocateFundsBase.h | 18 +-- .../trade_sys/allocatefunds/SystemWeight.h | 111 ------------------ .../imp/EqualWeightAllocateFunds.cpp | 4 +- .../imp/FixedWeightAllocateFunds.cpp | 4 +- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 36 +++--- .../hikyuu/trade_sys/portfolio/Portfolio.h | 6 +- .../trade_sys/selector/SelectorBase.cpp | 1 - .../hikyuu/trade_sys/selector/SelectorBase.h | 23 ++-- .../SystemWeight.cpp | 14 +-- .../hikyuu/trade_sys/selector/SystemWeight.h | 70 +++++++++++ .../trade_sys/selector/imp/FixedSelector.cpp | 12 +- .../trade_sys/selector/imp/SignalSelector.cpp | 14 +-- .../trade_sys/selector/imp/SignalSelector.h | 4 +- hikyuu_cpp/hikyuu/xmake.lua | 3 +- .../allocatefunds/test_AllocateFunds.cpp | 2 +- .../allocatefunds/test_SystemWeight.cpp | 18 +-- .../trade_sys/selector/test_SE_Fixed.cpp | 2 +- 18 files changed, 181 insertions(+), 217 deletions(-) delete mode 100644 hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.h rename hikyuu_cpp/hikyuu/trade_sys/{allocatefunds => selector}/SystemWeight.cpp (72%) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index eefd5f72..87dcd69e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -92,9 +92,9 @@ void AllocateFundsBase::setReservePercent(double percent) { m_reserve_percent = percent; } -void AllocateFundsBase::adjustFunds(const Datetime& date, const SystemList& se_list, +void AllocateFundsBase::adjustFunds(const Datetime& date, const SystemWeightList& se_list, const std::list& running_list, - const SystemList& ignore_list) { + const SystemWeightList& ignore_list) { if (getParam("adjust_running_sys")) { _adjust_with_running(date, se_list, running_list, ignore_list); } else { @@ -150,9 +150,9 @@ bool AllocateFundsBase::_returnAssets(const SYSPtr& sys, const Datetime& date) { } // 调整运行中子系统持仓 -void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemList& se_list, +void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemWeightList& se_list, const std::list& running_list, - const SystemList& ignore_list) { + const SystemWeightList& ignore_list) { // 计算当前选中系统列表的权重 SystemWeightList sw_list = _allocateWeight(date, se_list); HKU_IF_RETURN(sw_list.size() == 0, void()); @@ -160,14 +160,14 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL // 构建实际分配权重大于零的的系统集合,权重小于等于0的将被忽略 std::unordered_set selected_sets; for (auto& sw : sw_list) { - if (sw.getWeight() > 0.0) { - selected_sets.insert(sw.getSYS().get()); + if (sw.weight > 0.0) { + selected_sets.insert(sw.sys.get()); } } std::unordered_set ignore_sets; for (auto& sys : ignore_list) { - ignore_sets.insert(sys.get()); + ignore_sets.insert(sys.sys.get()); } std::unordered_set selected_running_sets; @@ -188,8 +188,8 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL // 按权重升序排序(注意:无法保证等权重的相对顺序,即使用stable_sort也一样,后面要倒序遍历) std::sort( sw_list.begin(), sw_list.end(), - std::bind(std::less(), std::bind(&SystemWeight::m_weight, std::placeholders::_1), - std::bind(&SystemWeight::m_weight, std::placeholders::_2))); + std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), + std::bind(&SystemWeight::weight, std::placeholders::_2))); // 过滤掉超出允许范围的系统 // 按权重从大到小遍历,构建不超过最大允许的运行子系统数的新权重列表(此时按从大到小顺序存放) @@ -199,7 +199,7 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL size_t count = 0; for (auto iter = sw_list.rbegin(); iter != sw_list.rend(); ++iter) { // 忽略小于等于零的权重 - if (iter->getWeight() <= 0.0) { + if (iter->weight <= 0.0) { break; } @@ -211,8 +211,8 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL } // 处理超出允许的最大系统数范围外的系统,尝试强制清仓,但不在放入权重列表(即后续不参与资金分配) - if (selected_running_sets.find(iter->getSYS().get()) != selected_running_sets.end()) { - _returnAssets(iter->getSYS(), date); + if (selected_running_sets.find(iter->sys.get()) != selected_running_sets.end()) { + _returnAssets(iter->sys, date); } } @@ -249,11 +249,11 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL // 3.如果当前资产大于期望分配的资产,则子账户是否有现金可以取出抵扣,否则卖掉部分股票 for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) { // 选中系统的分配权重已经小于等于0,则退出 - if (iter->getWeight() <= 0.0) { + if (iter->weight <= 0.0) { break; } - auto& sys = iter->getSYS(); + auto& sys = iter->sys; // 如果在忽略列表中,则跳过 if (ignore_sets.find(sys.get()) != ignore_sets.end()) { @@ -270,7 +270,7 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL 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; + price_t will_funds_value = (iter->weight / weight_unit) * per_weight_funds; if (funds_value == will_funds_value) { // 如果当前资产已经等于期望分配的资产,则忽略 continue; @@ -387,7 +387,8 @@ void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemL } } -void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemList& se_list, +void AllocateFundsBase::_adjust_without_running(const Datetime& date, + const SystemWeightList& se_list, const std::list& running_list) { HKU_IF_RETURN(se_list.size() == 0, void()); @@ -402,10 +403,10 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, const Syst } // 计算不包含运行中系统的子系统列表 - 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 pure_se_list; + for (auto& sw : se_list) { + if (hold_sets.find(sw.sys.get()) == hold_sets.end()) { + pure_se_list.push_back(sw); } } @@ -416,16 +417,16 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, const Syst // 按权重升序排序(注意:无法保证等权重的相对顺序,即使用stable_sort也一样,后面要倒序遍历) std::sort( sw_list.begin(), sw_list.end(), - std::bind(std::less(), std::bind(&SystemWeight::m_weight, std::placeholders::_1), - std::bind(&SystemWeight::m_weight, std::placeholders::_2))); + std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), + std::bind(&SystemWeight::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) + if (sw_iter->weight <= 0.0) break; - if (sw_iter->getSYS()->getSG()->shouldBuy(date)) { + if (sw_iter->sys->getSG()->shouldBuy(date)) { new_sw_list.emplace_back(*sw_iter); } } @@ -459,7 +460,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, const Syst 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(per_cash * (sw_iter->weight / weight_unit), precision); if (will_cash <= 0.0) { continue; } @@ -468,13 +469,14 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, const Syst price_t need_cash = will_cash <= can_allocate_cash ? will_cash : can_allocate_cash; // 检测是否可以发生交易,不能的话,忽略 - auto krecord = sw_iter->getSYS()->getTO().getKRecord(date); + + auto krecord = sw_iter->sys->getTO().getKRecord(date); if (krecord.closePrice < need_cash) { continue; } // 尝试从总账户中取出资金存入子账户 - TMPtr sub_tm = sw_iter->getSYS()->getTM(); + TMPtr sub_tm = sw_iter->sys->getTM(); if (m_shadow_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision); diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 051a5f8b..e313e398 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -10,7 +10,7 @@ #define TRADE_SYS_ALLOCATEFUNDS_ALLOCATEFUNDSBASE_H_ #include "../../utilities/Parameter.h" -#include "../allocatefunds/SystemWeight.h" +#include "../selector/SystemWeight.h" namespace hku { @@ -49,8 +49,8 @@ public: * @param ignore_list 忽略不进行调仓的运行中系统 * @return */ - void adjustFunds(const Datetime& date, const SystemList& se_list, - const std::list& running_list, const SystemList& ignore_list); + void adjustFunds(const Datetime& date, const SystemWeightList& se_list, + const std::list& running_list, const SystemWeightList& ignore_list); /** 获取交易账户 */ const TMPtr& getTM() const; @@ -101,15 +101,17 @@ public: * @param se_list 系统实例选择器选出的系统实例 * @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& running_list, const SystemList& ignore_list); + void _adjust_with_running(const Datetime& date, const SystemWeightList& se_list, + const std::list& running_list, + const SystemWeightList& ignore_list); /* 不调整已在运行中的子系统 */ - void _adjust_without_running(const Datetime& date, const SystemList& se_list, + void _adjust_without_running(const Datetime& date, const SystemWeightList& se_list, const std::list& running_list); /* 计算当前的资产总值 */ @@ -187,7 +189,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 AllocateFundsPtr; typedef shared_ptr AFPtr; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.h deleted file mode 100644 index 48c53ebd..00000000 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.h +++ /dev/null @@ -1,111 +0,0 @@ -/* - * SystemWeight.h - * - * Created on: 2018年1月29日 - * 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 - void save(Archive& ar, const unsigned int version) const { - ar& BOOST_SERIALIZATION_NVP(m_sys); - ar& BOOST_SERIALIZATION_NVP(m_weight); - } - - template - 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 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 : ostream_formatter {}; -#endif - -#endif /* TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp index e6bbe1cb..6a2f2366 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp @@ -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(); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { - result.emplace_back(*iter, weight); + result.emplace_back(iter->sys, weight); } return result; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp index f0b01595..66a4c81c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp @@ -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("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; diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 0057c0f5..2be01657 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -170,9 +170,9 @@ void Portfolio::_runMoment(const Datetime& date) { // 释放掉临时数据占用的内存 m_running_sys_set = std::unordered_set(); m_running_sys_list = std::list(); - m_tmp_selected_list_on_open = SystemList(); - m_tmp_selected_list_on_close = SystemList(); - m_tmp_will_remove_sys = SystemList(); + m_tmp_selected_list_on_open = SystemWeightList(); + m_tmp_selected_list_on_close = SystemWeightList(); + m_tmp_will_remove_sys = SystemWeightList(); } void Portfolio::_runMomentOnOpen(const Datetime& date) { @@ -199,22 +199,22 @@ void Portfolio::_runMomentOnOpen(const Datetime& date) { sub_tm->checkout(date, cash); m_shadow_tm->checkin(date, cash); } - m_tmp_will_remove_sys.push_back(running_sys); + m_tmp_will_remove_sys.emplace_back(running_sys, 0.); } } // 依据待移除列表将系统从运行中系统列表里删除 for (auto& sub_sys : m_tmp_will_remove_sys) { - m_running_sys_list.remove(sub_sys); - m_running_sys_set.erase(sub_sys.get()); + m_running_sys_list.remove(sub_sys.sys); + m_running_sys_set.erase(sub_sys.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()); + price_t cash = sub_sys.sys->getTM()->cash(date, m_query.kType()); + if (cash > 0.0 && m_running_sys_set.find(sub_sys.sys.get()) == m_running_sys_set.end()) { + m_running_sys_list.push_back(sub_sys.sys); + m_running_sys_set.insert(sub_sys.sys.get()); } } @@ -240,22 +240,22 @@ void Portfolio::_runMomentOnClose(const Datetime& date) { (!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())); + HKU_INFO("select on open: {}, cash: {}", sys.sys->getTO().getStock(), + sys.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())); + HKU_INFO("select on close: {}, cash: {}", sys.sys->getTO().getStock(), + sys.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 (m_running_sys_set.find(sys.sys.get()) == m_running_sys_set.end()) { + TMPtr tm = sys.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()); + m_running_sys_list.push_back(sys.sys); + m_running_sys_set.insert(sys.sys.get()); } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index 91fa6431..c4291675 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -120,9 +120,9 @@ protected: // 用于中间计算的临时数据 std::unordered_set m_running_sys_set; // 当前仍在运行的子系统集合 std::list m_running_sys_list; // 当前仍在运行的子系统列表 - SystemList m_tmp_selected_list_on_open; - SystemList m_tmp_selected_list_on_close; - SystemList m_tmp_will_remove_sys; + SystemWeightList m_tmp_selected_list_on_open; + SystemWeightList m_tmp_selected_list_on_close; + SystemWeightList m_tmp_will_remove_sys; //============================================ // 序列化支持 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 9c73c977..89dcd5f3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -6,7 +6,6 @@ */ #include "SelectorBase.h" -#include "../portfolio/Portfolio.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index b7b7e91b..3d3bf21d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -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 { @@ -97,10 +98,10 @@ public: virtual void _calculate() = 0; /** 子类获取指定时刻开盘时选中的标的 */ - virtual SystemList getSelectedOnOpen(Datetime date) = 0; + virtual SystemWeightList getSelectedOnOpen(Datetime date) = 0; /** 子类获取指定时刻收盘时选中的标的 */ - virtual SystemList getSelectedOnClose(Datetime date) = 0; + virtual SystemWeightList getSelectedOnClose(Datetime date) = 0; virtual bool isMatchAF(const AFPtr& af) = 0; @@ -166,14 +167,14 @@ 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 getSelectedOnOpen(Datetime date) override; \ + virtual SystemWeightList getSelectedOnClose(Datetime date) override; \ + virtual bool isMatchAF(const AFPtr& af) override; \ virtual void _calculate() override; /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.cpp similarity index 72% rename from hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.cpp rename to hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.cpp index ae1f19e2..445e205e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/SystemWeight.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.cpp @@ -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; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h new file mode 100644 index 00000000..412567d7 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h @@ -0,0 +1,70 @@ +/* + * SystemWeight.h + * + * Created on: 2018年1月29日 + * 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; + double weight; + + 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 + void save(Archive& ar, const unsigned int version) const { + ar& BOOST_SERIALIZATION_NVP(sys); + ar& BOOST_SERIALIZATION_NVP(weight); + } + + template + 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 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 : ostream_formatter {}; +#endif + +#endif /* TRADE_SYS_ALLOCATEFUNDS_SYSTEMWEIGHT_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp index 24d9cf73..7f0c0308 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp @@ -21,21 +21,21 @@ bool FixedSelector::isMatchAF(const AFPtr& af) { return true; } -SystemList FixedSelector::getSelectedOnOpen(Datetime date) { - SystemList result; +SystemWeightList FixedSelector::getSelectedOnOpen(Datetime date) { + SystemWeightList result; for (auto& sys : m_real_sys_list) { if (!sys->getParam("buy_delay")) { - result.emplace_back(sys); + result.emplace_back(sys, 1.0); } } return result; } -SystemList FixedSelector::getSelectedOnClose(Datetime date) { - SystemList result; +SystemWeightList FixedSelector::getSelectedOnClose(Datetime date) { + SystemWeightList result; for (auto& sys : m_real_sys_list) { if (sys->getParam("buy_delay")) { - result.emplace_back(sys); + result.emplace_back(sys, 1.0); } } return result; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp index a490a4cc..f882e85a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp @@ -24,14 +24,14 @@ bool SignalSelector::isMatchAF(const AFPtr& af) { return true; } -SystemList SignalSelector::getSelectedOnOpen(Datetime date) { +SystemWeightList SignalSelector::getSelectedOnOpen(Datetime date) { auto iter = m_sys_dict_on_open.find(date); - return iter != m_sys_dict_on_open.end() ? iter->second : SystemList(); + return iter != m_sys_dict_on_open.end() ? iter->second : SystemWeightList(); } -SystemList SignalSelector::getSelectedOnClose(Datetime date) { +SystemWeightList SignalSelector::getSelectedOnClose(Datetime date) { auto iter = m_sys_dict_on_close.find(date); - return iter != m_sys_dict_on_close.end() ? iter->second : SystemList(); + return iter != m_sys_dict_on_close.end() ? iter->second : SystemWeightList(); } void SignalSelector::_calculate() { @@ -40,14 +40,14 @@ void SignalSelector::_calculate() { auto& sys = m_real_sys_list[i]; auto sg = sys->getSG(); auto dates = sg->getBuySignal(); - unordered_map* date_dict; + unordered_map* date_dict; date_dict = sys->getParam("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); + iter->second.emplace_back(sys, 1.0); } else { - (*date_dict)[date] = {sys}; + (*date_dict)[date] = {SystemWeight(sys, 1.0)}; } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h index 40edf923..f2e63eb7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h @@ -25,8 +25,8 @@ public: } private: - unordered_map m_sys_dict_on_open; - unordered_map m_sys_dict_on_close; + unordered_map m_sys_dict_on_open; + unordered_map m_sys_dict_on_close; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index f51992ce..e37354e6 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -76,7 +76,8 @@ 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("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp|strategy/*.cpp") add_files("./data_driver/*.cpp") if get_config("hdf5") or get_config("sqlite") then add_files("./data_driver/base_info/sqlite/**.cpp") diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp index a20f128a..d94df149 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp @@ -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; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_SystemWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_SystemWeight.cpp index 2bea78b4..1fb9d339 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_SystemWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_SystemWeight.cpp @@ -6,7 +6,7 @@ */ #include "doctest/doctest.h" -#include +// #include // #include #include #include @@ -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)); } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp index 1b24896a..cbeee55a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp @@ -31,7 +31,7 @@ TEST_CASE("test_SE_Fixed") { /** @arg 试图加入一个不存在的stock */ se->addStock(Stock(), sys); - SystemList result = se->getSelectedOnOpen(Datetime(200001010000L)); + SystemWeightList result = se->getSelectedOnOpen(Datetime(200001010000L)); CHECK_EQ(result.size(), 0); /** @arg 试图加入一个空的系统策略原型 */ From 4bd3660bd4bf63b531826de38fab1823aa95ab82 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 18 Mar 2024 04:25:14 +0800 Subject: [PATCH 058/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=AF=B9=E5=BA=94?= =?UTF-8?q?=E7=9A=84=20python=20=E9=83=A8=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/selector/SystemWeight.h | 2 +- hikyuu_cpp/hikyuu/xmake.lua | 2 +- hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 22 +++++-------------- hikyuu_pywrap/trade_sys/_Selector.cpp | 19 ++++++++++++---- 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h index 412567d7..94eb9348 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h @@ -20,7 +20,7 @@ namespace hku { */ struct HKU_API SystemWeight { SystemPtr sys; - double weight; + price_t weight; SystemWeight() = default; SystemWeight(const SystemPtr& sys_, double weight_) : sys(sys_), weight(weight_) {} diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index e37354e6..1d8a7864 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -77,7 +77,7 @@ target("hikyuu") -- add files -- add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp") - add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp|strategy/*.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 add_files("./data_driver/base_info/sqlite/**.cpp") diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index 912a9799..a12ea0cf 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -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_(m, "SystemWeight", - "系统权重系数结构,在资产分配时,指定对应系统的资产占比系数") - .def(py::init<>()) - .def(py::init()) - .def("__str__", to_py_str) - .def("__repr__", to_py_str) - .def_property("sys", py::overload_cast<>(&SystemWeight::getSYS, py::const_), - py::overload_cast(&SystemWeight::setSYS), - "对应的 System 实例") - .def_property("weight", &SystemWeight::getWeight, &SystemWeight::setWeight, - "对应的权重系数,有效范围为 [0, 1]") - - DEF_PICKLE(SystemWeight); - - py::class_(m, "AllocateFundsBase", - R"(资产分配算法基类, 子类接口: + py::class_( + m, "AllocateFundsBase", + R"(资产分配算法基类, 子类接口: - _allocateWeight : 【必须】子类资产分配调整实现 - _clone : 【必须】克隆接口 diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index f0f682a3..ccd13aeb 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -25,13 +25,13 @@ public: PYBIND11_OVERLOAD_PURE(void, SelectorBase, _calculate, ); } - SystemList getSelectedOnOpen(Datetime date) override { - PYBIND11_OVERLOAD_PURE_NAME(SystemList, SelectorBase, "get_selected_on_open", + SystemWeightList getSelectedOnOpen(Datetime date) override { + PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected_on_open", getSelectedOnOpen, date); } - SystemList getSelectedOnClose(Datetime date) override { - PYBIND11_OVERLOAD_PURE_NAME(SystemList, SelectorBase, "get_selected_on_close", + SystemWeightList getSelectedOnClose(Datetime date) override { + PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected_on_close", getSelectedOnClose, date); } @@ -41,6 +41,17 @@ public: }; void export_Selector(py::module& m) { + py::class_(m, "SystemWeight", + "系统权重系数结构,在资产分配时,指定对应系统的资产占比系数") + .def(py::init<>()) + .def(py::init()) + .def("__str__", to_py_str) + .def("__repr__", to_py_str) + .def_readwrite("sys", &SystemWeight::sys, "对应的 System 实例") + .def_readwrite("weight", &SystemWeight::weight) + + DEF_PICKLE(SystemWeight); + py::class_( m, "SelectorBase", R"(选择器策略基类,实现标的、系统策略的评估和选取算法,自定义选择器策略子类接口: From 34695d9b11fc064f3ea942c18cf3eedb2e8005b7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 18 Mar 2024 04:33:53 +0800 Subject: [PATCH 059/601] update --- .../trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp | 2 +- .../trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp index 6a2f2366..0dfe7d87 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp @@ -20,7 +20,7 @@ EqualWeightAllocateFunds::~EqualWeightAllocateFunds() {} SystemWeightList EqualWeightAllocateFunds ::_allocateWeight(const Datetime& date, const SystemWeightList& se_list) { SystemWeightList result; - double weight = 1.0 / se_list.size(); + price_t weight = 1.0 / se_list.size(); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { result.emplace_back(iter->sys, weight); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp index 66a4c81c..40c4b945 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp @@ -22,7 +22,7 @@ FixedWeightAllocateFunds::~FixedWeightAllocateFunds() {} SystemWeightList FixedWeightAllocateFunds ::_allocateWeight(const Datetime& date, const SystemWeightList& se_list) { SystemWeightList result; - double weight = getParam("weight"); + price_t weight = getParam("weight"); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { result.emplace_back(iter->sys, weight); } From cc66922e21e22297d88563e2d4669099ad493a3d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 19 Mar 2024 05:02:00 +0800 Subject: [PATCH 060/601] debug pf --- .../allocatefunds/AllocateFundsBase.cpp | 148 ++++++++++++------ .../allocatefunds/AllocateFundsBase.h | 3 + .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 110 +++++++------ hikyuu_cpp/hikyuu/xmake.lua | 2 +- hikyuu_cpp/unit_test/xmake.lua | 4 +- hikyuu_pywrap/trade_manage/_TradeManager.cpp | 2 +- hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 2 + hikyuu_pywrap/xmake.lua | 2 +- 8 files changed, 166 insertions(+), 107 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 87dcd69e..c4e24c99 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -28,10 +28,14 @@ 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("adjust_running_sys", false); setParam("max_sys_num", 1000000); // 允许运行的最大系统实例数 setParam("weight_unit", 0.0001); // 最小权重单位 setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 + setParam("trace", false); // 打印跟踪 } AllocateFundsBase::AllocateFundsBase(const string& name) @@ -40,23 +44,25 @@ AllocateFundsBase::AllocateFundsBase(const string& name) setParam("max_sys_num", 100000); // 最大系统实例数 setParam("weight_unit", 0.0001); // 最小权重单位 setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 + setParam("trace", false); // 打印跟踪 } AllocateFundsBase::~AllocateFundsBase() {} void AllocateFundsBase::reset() { - m_reserve_percent = getParam("default_reserve_percent"); - _reset(); - // 参数检查 - HKU_ERROR_IF(getParam("max_sys_num") <= 0, R"(AF param["max_sys_num"]({}) need > 0!)", - getParam("max_sys_num")); - HKU_ERROR_IF( - getParam("default_reserve_percent") >= 1.0, - R"(AF param(default_reserve_percent)({}) >= 1.0, No asset adjustments will be made!)"); - HKU_CHECK(getParam("default_reserve_percent") >= 0.0, - R"(Invalid AF param["default_reserve_percent"] ({}))", - getParam("default_reserve_percent")); + HKU_CHECK(getParam("max_sys_num") > 0, R"(AF param["max_sys_num"]({}) need > 0!)", + getParam("max_sys_num")); + HKU_CHECK(getParam("weight_unit") > 0.0, R"(AF param[{}]) need > 0!)", + getParam("weight_unit")); + + double default_reserve_percent = getParam("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() { @@ -149,13 +155,25 @@ bool AllocateFundsBase::_returnAssets(const SYSPtr& sys, const Datetime& date) { return true; } +void AllocateFundsBase::_check_weight(const SystemWeightList& sw_list) { + 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); + } +} + // 调整运行中子系统持仓 void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemWeightList& se_list, const std::list& running_list, const SystemWeightList& ignore_list) { + bool trace = getParam("trace"); + // 计算当前选中系统列表的权重 SystemWeightList sw_list = _allocateWeight(date, se_list); HKU_IF_RETURN(sw_list.size() == 0, void()); + if (trace) { + _check_weight(sw_list); + } // 构建实际分配权重大于零的的系统集合,权重小于等于0的将被忽略 std::unordered_set selected_sets; @@ -392,9 +410,8 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, const std::list& running_list) { HKU_IF_RETURN(se_list.size() == 0, void()); - // 如果运行中的系统数已大于等于允许的最大系统数,直接返回 - int max_num = getParam("max_sys_num"); - HKU_IF_RETURN(running_list.size() >= max_num, void()); + bool trace = getParam("trace"); + HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date); // 计算选中系统中当前正在运行中的子系统 std::unordered_set hold_sets; @@ -413,6 +430,9 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 获取计划分配的资产权重 SystemWeightList sw_list = _allocateWeight(date, pure_se_list); HKU_IF_RETURN(sw_list.size() == 0, void()); + if (trace) { + _check_weight(sw_list); + } // 按权重升序排序(注意:无法保证等权重的相对顺序,即使用stable_sort也一样,后面要倒序遍历) std::sort( @@ -420,75 +440,101 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), std::bind(&SystemWeight::weight, std::placeholders::_2))); - // 检测是否有信号发生,过滤掉没有发生信号的系统 以及 权重为 0 的系统 + // 总账号资金精度 + int precision = m_shadow_tm->getParam("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 = roundDown(total_funds * m_reserve_percent, precision); + + // 检测是否有信号发生,过滤掉: + // 1. 没有发生信号的系统 + // 2. 权重小于等于 0 的系统 + // 2. 累积权重>可分配的总权重之后的系统 + price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; + price_t sum_weight = 0.0; SystemWeightList new_sw_list; auto sw_iter = sw_list.rbegin(); for (; sw_iter != sw_list.rend(); ++sw_iter) { - if (sw_iter->weight <= 0.0) + // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 + if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { break; + } + if (sw_iter->sys->getSG()->shouldBuy(date)) { + sum_weight += sw_iter->weight; + // 如果累积权重大于1,则调整最后的系统权重 + if (sum_weight > can_allocate_sum_weight) { + sw_iter->weight = sum_weight - can_allocate_sum_weight; + sum_weight = can_allocate_sum_weight; + } new_sw_list.emplace_back(*sw_iter); } } - // 总账号资金精度 - int precision = m_shadow_tm->getParam("precision"); + if (trace) { + for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) { + HKU_INFO("[AF] ({}, {}, weight: {:<.4f}) ", iter->sys->name(), + iter->sys->getStock().market_code(), iter->weight); + } + } - // 获取当前总资产市值 - 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 = 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); + // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 + price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); + price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision); if (can_allocate_cash <= 0.0) { + HKU_WARN_IF( + trace, + "Insufficient available cash! can_allocate_cash: {}, current_cash: {}, reserve_funds: {}", + can_allocate_cash, current_cash, reserve_funds); return; } - // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 double weight_unit = getParam("weight_unit"); - price_t per_cash = total_funds * weight_unit; // 每单位权重资金 + // 根据账户精度微调总资产,尽可能消除由于四舍五入后导致的精度问题 + total_funds = total_funds - std::pow(0.1, precision) * 0.5 * new_sw_list.size(); + + // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 + int max_num = getParam("max_sys_num"); 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) { + for (auto iter = new_sw_list.begin(), end_iter = new_sw_list.end(); iter != end_iter; ++iter) { // 该系统期望分配的资金 - price_t will_cash = roundUp(per_cash * (sw_iter->weight / weight_unit), precision); + price_t will_cash = roundUp(total_funds * iter->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->sys->getTO().getKRecord(date); - if (krecord.closePrice < need_cash) { - continue; - } - // 尝试从总账户中取出资金存入子账户 - TMPtr sub_tm = sw_iter->sys->getTM(); + TMPtr sub_tm = iter->sys->getTM(); if (m_shadow_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] {} fetched cash: {}", iter->sys->name(), need_cash); // 如果超出允许运行的最大系统数,跳出循环 can_run_count++; if (can_run_count > max_num) { break; } + + // 计算剩余的可用于分配的资金,如果小于1,退出循环 + // (不需要小于0,小于一个特定的值即可,这里用1) + can_allocate_cash = roundUp(can_allocate_cash - need_cash, precision); + if (can_allocate_cash <= 1.0) { + HKU_DEBUG_IF(trace, + "[AF] {} could't fetch need cash {}, current can_allocate_cash: {}!", + iter->sys->name(), need_cash, can_allocate_cash); + break; + } + + } else { + HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account!", + iter->sys->name()); } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index e313e398..6a5214ea 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -120,6 +120,9 @@ private: /* 回收系统资产 */ bool _returnAssets(const SYSPtr& sys, const Datetime& date); + /* 检查分配的权重是否在 0 和 1 之间,如果存在错误,抛出异常,仅在 trace 时生效*/ + void _check_weight(const SystemWeightList&); + private: string m_name; // 组件名称 KQuery m_query; // 查询条件 diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 2be01657..1737a899 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -144,7 +144,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!"); @@ -164,26 +164,22 @@ void Portfolio::_runMoment(const Datetime& date) { // 当前日期小于账户建立日期,直接忽略 HKU_IF_RETURN(date < m_shadow_tm->initDatetime(), void()); - _runMomentOnOpen(date); - _runMomentOnClose(date); + bool trace = getParam("trace"); + HKU_INFO_IF(trace, "{} ===========================================================", date); + HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_list.size()); - // 释放掉临时数据占用的内存 - m_running_sys_set = std::unordered_set(); - m_running_sys_list = std::list(); - m_tmp_selected_list_on_open = SystemWeightList(); - m_tmp_selected_list_on_close = SystemWeightList(); - m_tmp_will_remove_sys = SystemWeightList(); -} + // 在开盘时执行上一周期中所有运行中的延迟交易系统系统 + // 在不需要调整已持仓系统时,不需要执行,只要每天执行一次系统即可 + // for (auto& sub_sys : m_running_sys_list) { + // if (sub_sys->getParam("buy_delay")) { + // auto tr = sub_sys->runMoment(date); + // if (!tr.isNull()) { + // HKU_INFO_IF(trace, "[PF] on open: {}", tr); + // m_tm->addTradeRecord(tr); + // } + // } + // } -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("precision"); @@ -209,6 +205,42 @@ void Portfolio::_runMomentOnOpen(const Datetime& date) { m_running_sys_set.erase(sub_sys.sys.get()); } + _runMomentOnOpen(date); + _runMomentOnClose(date); + + // 收盘时执行所有运行中的系统,无论是延迟还是非延迟,当天运行中的系统都需要被执行一次 + for (auto& sub_sys : m_running_sys_list) { + auto tr = sub_sys->runMoment(date); + if (!tr.isNull()) { + HKU_INFO_IF(trace, "[PF] on close: {}", tr); + m_tm->addTradeRecord(tr); + } + } + + // 释放掉临时数据占用的内存 + // m_running_sys_set = std::unordered_set(); + // m_running_sys_list = std::list(); + m_tmp_selected_list_on_open = SystemWeightList(); + m_tmp_selected_list_on_close = SystemWeightList(); + m_tmp_will_remove_sys = SystemWeightList(); +} + +void Portfolio::_runMomentOnOpen(const Datetime& date) { + // 从选股策略获取当前选中的系统列表 + m_tmp_selected_list_on_open = m_se->getSelectedOnOpen(date); + + bool trace = getParam("trace"); + if (trace && !m_tmp_selected_list_on_open.empty()) { + for (auto& sys : m_tmp_selected_list_on_open) { + HKU_INFO("[PF] select on open: {}, score: {:<.4f}, cash: {}", sys.sys->name(), + sys.weight, sys.sys->getTM()->cash(date, m_query.kType())); + } + } + + // 资产分配算法调整各子系统资产分配,忽略上一周期收盘时选中的系统 + m_af->adjustFunds(date, m_tmp_selected_list_on_open, m_running_sys_list, + m_tmp_selected_list_on_close); + // 遍历本次选择的系统列表,如果存在分配资金且不在运行中列表内,则加入运行列表 for (auto& sub_sys : m_tmp_selected_list_on_open) { price_t cash = sub_sys.sys->getTM()->cash(date, m_query.kType()); @@ -217,37 +249,23 @@ void Portfolio::_runMomentOnOpen(const Datetime& date) { m_running_sys_set.insert(sub_sys.sys.get()); } } - - // 在开盘时执行所有运行中的非延迟交易系统系统 - for (auto& sub_sys : m_running_sys_list) { - if (!sub_sys->getParam("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); + bool trace = getParam("trace"); + if (trace && !m_tmp_selected_list_on_close.empty()) { + for (auto& sys : m_tmp_selected_list_on_close) { + HKU_INFO("[PF] select on close: {}, score: {:<.4f}, cash: {}", sys.sys->name(), + sys.weight, sys.sys->getTM()->cash(date, m_query.kType())); + } + } + // 资产分配算法调整各子系统资产分配,忽略开盘时选中的系统 m_af->adjustFunds(date, m_tmp_selected_list_on_close, m_running_sys_list, m_tmp_selected_list_on_open); - if (getParam("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.sys->getTO().getStock(), - sys.sys->getTM()->cash(date, m_query.kType())); - } - for (auto& sys : m_tmp_selected_list_on_close) { - HKU_INFO("select on close: {}, cash: {}", sys.sys->getTO().getStock(), - sys.sys->getTM()->cash(date, m_query.kType())); - } - } // 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行 for (auto& sys : m_tmp_selected_list_on_close) { @@ -259,16 +277,6 @@ void Portfolio::_runMomentOnClose(const Datetime& date) { } } } - - // 执行所有非延迟运行中系统 - for (auto& sub_sys : m_running_sys_list) { - if (sub_sys->getParam("buy_delay")) { - auto tr = sub_sys->runMoment(date); - if (!tr.isNull()) { - m_tm->addTradeRecord(tr); - } - } - } } void Portfolio::run(const KQuery& query, bool force) { diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 1d8a7864..5c60919b 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -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 diff --git a/hikyuu_cpp/unit_test/xmake.lua b/hikyuu_cpp/unit_test/xmake.lua index b324b54f..6d0b11b6 100644 --- a/hikyuu_cpp/unit_test/xmake.lua +++ b/hikyuu_cpp/unit_test/xmake.lua @@ -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 diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index 1923028d..81ce2a0b 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -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]) diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index a12ea0cf..c49e5230 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -50,6 +50,8 @@ void export_AllocateFunds(py::module& m) { .def_property("query", py::overload_cast<>(&AllocateFundsBase::getQuery, py::const_), py::overload_cast(&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, R"(get_param(self, name) diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index ba379b28..6d514baf 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -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") From a8bdb556aca7f55b09d20fa567fec116840530cb Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 19 Mar 2024 19:16:31 +0800 Subject: [PATCH 061/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20PF/AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_manage/TradeManager.cpp | 3 +- .../allocatefunds/AllocateFundsBase.cpp | 579 ++++++++---------- .../allocatefunds/AllocateFundsBase.h | 39 +- .../imp/EqualWeightAllocateFunds.cpp | 6 +- .../imp/FixedWeightAllocateFunds.cpp | 4 +- .../moneymanager/MoneyManagerBase.cpp | 3 +- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 250 ++++---- .../hikyuu/trade_sys/portfolio/Portfolio.h | 11 +- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 34 + hikyuu_cpp/hikyuu/trade_sys/system/System.h | 3 + hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 10 +- 11 files changed, 451 insertions(+), 491 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 1c38a298..37779d46 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -1831,7 +1831,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; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index c4e24c99..1018c550 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -98,337 +98,60 @@ void AllocateFundsBase::setReservePercent(double percent) { m_reserve_percent = percent; } -void AllocateFundsBase::adjustFunds(const Datetime& date, const SystemWeightList& se_list, - const std::list& running_list, - const SystemWeightList& ignore_list) { - if (getParam("adjust_running_sys")) { - _adjust_with_running(date, se_list, running_list, ignore_list); - } else { - _adjust_without_running(date, se_list, running_list); - } -} - -price_t AllocateFundsBase::_getTotalFunds(const Datetime& date, - const std::list& running_list) { - price_t total_value = 0; - - // 计算运行中的子系统总资产净值 - 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; - } - - // 加上当前总账户现金余额 - m_shadow_tm->updateWithWeight(date); - total_value = roundDown(total_value + m_shadow_tm->cash(date, m_query.kType()), - m_shadow_tm->getParam("precision")); - return total_value; -} - -bool AllocateFundsBase::_returnAssets(const SYSPtr& sys, const Datetime& date) { - KData kdata = sys->getTO(); - size_t pos = kdata.getPos(date); - HKU_IF_RETURN(pos == Null(), false); - - KRecord record = kdata.getKRecord(pos); - Stock stock = kdata.getStock(); - KRecord srcRecord = stock.getKRecord(kdata.startPos() + pos); - - // 如果存在持仓则卖出 - TMPtr tm = sys->getTM(); - if (tm->have(stock)) { - TradeRecord tr = sys->sell(record, srcRecord, PART_ALLOCATEFUNDS); - if (!tr.isNull()) { - m_tm->addTradeRecord(tr); - } - } - - // 回收当前子账号资金至总账户 - 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::_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); } -// 调整运行中子系统持仓 -void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemWeightList& se_list, - const std::list& running_list, - const SystemWeightList& ignore_list) { - bool trace = getParam("trace"); - - // 计算当前选中系统列表的权重 - SystemWeightList sw_list = _allocateWeight(date, se_list); - HKU_IF_RETURN(sw_list.size() == 0, void()); - if (trace) { - _check_weight(sw_list); - } - - // 构建实际分配权重大于零的的系统集合,权重小于等于0的将被忽略 - std::unordered_set selected_sets; - for (auto& sw : sw_list) { - if (sw.weight > 0.0) { - selected_sets.insert(sw.sys.get()); - } - } - - std::unordered_set ignore_sets; - for (auto& sys : ignore_list) { - ignore_sets.insert(sys.sys.get()); - } - - std::unordered_set 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(), std::bind(&SystemWeight::weight, std::placeholders::_1), - std::bind(&SystemWeight::weight, std::placeholders::_2))); - - // 过滤掉超出允许范围的系统 - // 按权重从大到小遍历,构建不超过最大允许的运行子系统数的新权重列表(此时按从大到小顺序存放) - // 同时,将超出最大允许的运行子系统数范围外的运行中子系统清仓回收资金 - int max_num = getParam("max_sys_num"); - std::list new_sw_list; // 存放新的权重列表 - size_t count = 0; - for (auto iter = sw_list.rbegin(); iter != sw_list.rend(); ++iter) { - // 忽略小于等于零的权重 - if (iter->weight <= 0.0) { - break; - } - - // 小于最大允许运行数,直接保存至新的权重列表 - if (count < max_num) { - new_sw_list.emplace_back(std::move(*iter)); - count++; - continue; - } - - // 处理超出允许的最大系统数范围外的系统,尝试强制清仓,但不在放入权重列表(即后续不参与资金分配) - if (selected_running_sets.find(iter->sys.get()) != selected_running_sets.end()) { - _returnAssets(iter->sys, date); - } - } - - // 账户资金精度 - int precision = m_shadow_tm->getParam("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("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> wait_list; - - // 按权重从大到小遍历 - // 1.如果子系统当前资产已经等于期望被分配的资产则不做处理 - // 2.如果子系统当前资产小于期望被分配的资产,则尝试补充资金,否则放入等待列表 - // 3.如果当前资产大于期望分配的资产,则子账户是否有现金可以取出抵扣,否则卖掉部分股票 - for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) { - // 选中系统的分配权重已经小于等于0,则退出 - if (iter->weight <= 0.0) { - break; - } - - auto& sys = iter->sys; - - // 如果在忽略列表中,则跳过 - 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->weight / 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()) { - continue; - } - KRecord k = kdata.getKRecord(pos); - KRecord srcK = stock.getKRecord(kdata.startPos() + pos); - TradeRecord tr; - double need_sell_num = sys->getParam("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; - } - } +SystemWeightList AllocateFundsBase::adjustFunds(const Datetime& date, + const SystemWeightList& se_list, + const std::unordered_set& running_list) { + SystemWeightList result; + if (getParam("adjust_running_sys")) { + result = _adjust_with_running(date, se_list, running_list); + } else { + _adjust_without_running(date, se_list, running_list); } + return result; } void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemWeightList& se_list, - const std::list& running_list) { + const std::unordered_set& running_set) { HKU_IF_RETURN(se_list.size() == 0, void()); bool trace = getParam("trace"); HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date); - // 计算选中系统中当前正在运行中的子系统 - std::unordered_set hold_sets; - for (auto& sys : running_list) { - hold_sets.insert(sys.get()); - } - // 计算不包含运行中系统的子系统列表 SystemWeightList pure_se_list; for (auto& sw : se_list) { - if (hold_sets.find(sw.sys.get()) == hold_sets.end()) { + if (running_set.find(sw.sys) == running_set.end()) { pure_se_list.push_back(sw); } } + // 获取当前总资产市值,计算需保留的资产与剩余可分配权重 + int precision = m_shadow_tm->getParam("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; + HKU_ERROR_IF(m_reserve_percent < 0.0 || m_reserve_percent >= 1.0, + "Invalid reserve_percent({}) in AF, Calculations that will cause errors!", + m_reserve_percent); + price_t reserve_funds = roundDown(total_funds * m_reserve_percent, precision); + double can_allocate_weight = funds.cash / total_funds; + HKU_INFO_IF("can_allocate_weight: {:<.4f}", can_allocate_weight); + HKU_IF_RETURN(can_allocate_weight <= 0., void()); + // 获取计划分配的资产权重 - SystemWeightList sw_list = _allocateWeight(date, pure_se_list); + SystemWeightList sw_list = + _allocateWeight(date, pure_se_list, running_set.size(), can_allocate_weight); HKU_IF_RETURN(sw_list.size() == 0, void()); if (trace) { _check_weight(sw_list); @@ -440,16 +163,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), std::bind(&SystemWeight::weight, std::placeholders::_2))); - // 总账号资金精度 - int precision = m_shadow_tm->getParam("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 = roundDown(total_funds * m_reserve_percent, precision); - // 检测是否有信号发生,过滤掉: // 1. 没有发生信号的系统 // 2. 权重小于等于 0 的系统 @@ -457,8 +170,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; price_t sum_weight = 0.0; SystemWeightList new_sw_list; - auto sw_iter = sw_list.rbegin(); - for (; sw_iter != sw_list.rend(); ++sw_iter) { + for (auto sw_iter = sw_list.rbegin(); sw_iter != sw_list.rend(); ++sw_iter) { // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { break; @@ -493,7 +205,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, return; } - double weight_unit = getParam("weight_unit"); // 根据账户精度微调总资产,尽可能消除由于四舍五入后导致的精度问题 total_funds = total_funds - std::pow(0.1, precision) * 0.5 * new_sw_list.size(); @@ -518,25 +229,231 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 如果超出允许运行的最大系统数,跳出循环 can_run_count++; - if (can_run_count > max_num) { - break; - } - // 计算剩余的可用于分配的资金,如果小于1,退出循环 - // (不需要小于0,小于一个特定的值即可,这里用1) - can_allocate_cash = roundUp(can_allocate_cash - need_cash, precision); - if (can_allocate_cash <= 1.0) { - HKU_DEBUG_IF(trace, - "[AF] {} could't fetch need cash {}, current can_allocate_cash: {}!", - iter->sys->name(), need_cash, can_allocate_cash); - break; - } + // 计算剩余的可用于分配的资金 + can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision); } else { HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account!", iter->sys->name()); } + + if (can_run_count >= max_num) { + break; + } + + // (不需要小于0,小于一个特定的值即可,这里用1) + if (can_allocate_cash <= 1.0) { + break; + } } } +SystemWeightList AllocateFundsBase::_adjust_with_running( + const Datetime& date, const SystemWeightList& se_list, + const std::unordered_set& running_set) { + SystemWeightList result; + HKU_IF_RETURN(se_list.size() == 0, result); + + bool trace = getParam("trace"); + HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date); + + // 剩余可用于分配的权重 + HKU_ERROR_IF(m_reserve_percent < 0.0 || m_reserve_percent >= 1.0, + "Invalid reserve_percent({}) in AF, Calculations that will cause errors!", + m_reserve_percent); + double can_allocate_weith = 1.0 - m_reserve_percent; + + // 获取计划分配的资产权重 + SystemWeightList sw_list = + _allocateWeight(date, se_list, running_set.size(), can_allocate_weith); + HKU_IF_RETURN(sw_list.size() == 0, result); + if (trace) { + _check_weight(sw_list); + } + + // 按权重升序排序(注意:无法保证等权重的相对顺序,即使用stable_sort也一样,后面要倒序遍历) + std::sort( + sw_list.begin(), sw_list.end(), + std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), + std::bind(&SystemWeight::weight, std::placeholders::_2))); + + // 获取当前总资产市值,计算需保留的资产 + int precision = m_shadow_tm->getParam("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 = roundDown(total_funds * m_reserve_percent, precision); + + // 检测是否有信号发生,过滤掉: + // 1. 不在当前运行系统集合中且没有发生信号的系统 + // 2. 权重小于等于 0 的系统,如果是运行中的系统,则强制清仓 + // 2. 累积权重>可分配的总权重之后的系统 + price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; + price_t sum_weight = 0.0; + SystemWeightList new_sw_list; + std::unordered_set new_sw_set; + for (auto sw_iter = sw_list.rbegin(); sw_iter != sw_list.rend(); ++sw_iter) { + // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 + if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { + break; + } + + // 如果系统在运行中,或者有信号发生 + if (running_set.count(sw_iter->sys) > 0 || sw_iter->sys->getSG()->shouldBuy(date)) { + sum_weight += sw_iter->weight; + // 如果累积权重大于1,则调整最后的系统权重 + if (sum_weight > can_allocate_sum_weight) { + sw_iter->weight = sum_weight - can_allocate_sum_weight; + sum_weight = can_allocate_sum_weight; + } + new_sw_list.emplace_back(*sw_iter); + new_sw_set.insert(sw_iter->sys.get()); + } + } + + // 如果调整运行中系统持仓,则: + // 对已不在新的实际分配权重的系统集合中的运行中的系统,进行清仓操作 + for (auto& running_sys : running_set) { + if (new_sw_set.count(running_sys.get()) == 0) { + // 如果是延迟买入的系统,由于可能是在当天开盘刚刚买入,此时不能立刻清仓卖出,放入延迟卖出清单中 + if (running_sys->getParam("buy_delay")) { + result.emplace_back(running_sys, MAX_DOUBLE); + } else { + auto tr = running_sys->sellForce(date, MAX_DOUBLE, PART_ALLOCATEFUNDS); + if (!tr.isNull()) { + m_tm->addTradeRecord(tr); + auto sub_tm = running_sys->getTM(); + auto sub_cash = sub_tm->currentCash(); + if (sub_tm->checkout(date, sub_cash)) { + m_shadow_tm->checkin(date, sub_cash); + } + } + } + } + } + + if (trace) { + for (auto iter = sw_list.begin(); iter != sw_list.end(); ++iter) { + HKU_INFO("[AF] ({}, {}, weight: {:<.4f}) ", iter->sys->name(), + iter->sys->getStock().market_code(), iter->weight); + } + } + + // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 + price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); + price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision); + if (can_allocate_cash <= 0.0) { + HKU_WARN_IF( + trace, + "Insufficient available cash! can_allocate_cash: {}, current_cash: {}, reserve_funds: {}", + can_allocate_cash, current_cash, reserve_funds); + return result; + } + + // 根据账户精度微调总资产,尽可能消除由于四舍五入后导致的精度问题 + total_funds = total_funds - std::pow(0.1, precision) * 0.5 * new_sw_list.size(); + + // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 + int max_num = getParam("max_sys_num"); + size_t can_run_count = 0; + for (auto iter = new_sw_list.begin(), end_iter = new_sw_list.end(); iter != end_iter; ++iter) { + // 系统预期的分配资产额 + price_t will_funds = roundUp(total_funds * iter->weight, precision); + + // 如果该系统是当前运行中系统 + if (running_set.count(iter->sys) != 0) { + can_run_count++; + + TMPtr sub_tm = iter->sys->getTM(); + const KQuery& query = iter->sys->getTO().getQuery(); + FundsRecord funds = sub_tm->getFunds(query.kType()); + price_t current_funds = + funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value; + + // 如果预期资产和当前资产相等,则转到下个系统 + if (current_funds == will_funds) { + continue; + } + + // 如果预期资产和当前资产之差不够买入或卖出最小交易数量的证券,则转至下一系统 + Stock stock = iter->sys->getStock(); + auto krecord = stock.getKRecord(date, query.kType()); + double min_num = stock.minTradeNumber(); + if (std::abs(current_funds < will_funds) < krecord.closePrice * min_num) { + continue; + } + + if (current_funds < will_funds) { + // 已有资产小于预期资产,则分配新的资金 + price_t diff_price = roundUp(will_funds - current_funds, precision); + if (can_allocate_cash > 0. && m_shadow_tm->checkout(date, diff_price)) { + sub_tm->checkin(date, diff_price); + can_allocate_cash = roundDown(can_allocate_cash - diff_price, precision); + } + + } else { + // 已有资产大于预期资产,尝试减仓 + price_t need_back_return = current_funds - will_funds; + double need_back_num = need_back_return / krecord.closePrice; + if (min_num > 1) { + need_back_num = static_cast(need_back_num / min_num) * min_num; + } + + // 如果是延迟买入的系统,则将减仓卖出记录加入返回的延迟清单 + if (iter->sys->getParam("buy_delay")) { + result.emplace_back(iter->sys, need_back_num); + can_allocate_cash = + roundDown(can_allocate_cash - need_back_num * krecord.closePrice, precision); + + } else { + auto tr = iter->sys->sellForce(date, need_back_num, PART_ALLOCATEFUNDS); + if (!tr.isNull()) { + m_tm->addTradeRecord(tr); + auto sub_cash = sub_tm->currentCash(); + if (sub_tm->checkout(date, sub_cash)) { + m_shadow_tm->checkin(date, sub_cash); + can_allocate_cash = roundDown(can_allocate_cash - sub_cash, precision); + } + } + } + } + + } else { + // 非运行中的系统 + + // 计算子账户实际可获取的的资金 + price_t need_cash = will_funds <= can_allocate_cash ? will_funds : can_allocate_cash; + + // 尝试从总账户中取出资金存入子账户 + TMPtr sub_tm = iter->sys->getTM(); + if (m_shadow_tm->checkout(date, need_cash)) { + sub_tm->checkin(date, need_cash); + HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(), need_cash); + + // 如果超出允许运行的最大系统数,跳出循环 + can_run_count++; + + // 计算剩余的可用于分配的资金,如果小于1,退出循环 + // (不需要小于0,小于一个特定的值即可,这里用1) + can_allocate_cash = roundUp(can_allocate_cash - need_cash, precision); + + } else { + HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account!", + iter->sys->name()); + } + } + + if (can_run_count >= max_num) { + break; + } + + if (can_allocate_cash <= 1.0) { + break; + } + } + + return result; +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 6a5214ea..893f0c3e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -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 SystemWeightList& se_list, - const std::list& running_list, const SystemWeightList& ignore_list); + SystemWeightList adjustFunds(const Datetime& date, const SystemWeightList& se_list, + const std::unordered_set& running_list); /** 获取交易账户 */ const TMPtr& getTM() const; @@ -101,24 +101,17 @@ public: * @param se_list 系统实例选择器选出的系统实例 * @return */ - virtual SystemWeightList _allocateWeight(const Datetime& date, - const SystemWeightList& se_list) = 0; + virtual SystemWeightList _allocateWeight(const Datetime& date, const SystemWeightList& se_list, + size_t running_count, double can_allocate_weight) = 0; private: /* 同时调整已运行中的子系统(已分配资金或已持仓) */ - void _adjust_with_running(const Datetime& date, const SystemWeightList& se_list, - const std::list& running_list, - const SystemWeightList& ignore_list); + SystemWeightList _adjust_with_running(const Datetime& date, const SystemWeightList& se_list, + const std::unordered_set& running_list); /* 不调整已在运行中的子系统 */ void _adjust_without_running(const Datetime& date, const SystemWeightList& se_list, - const std::list& running_list); - - /* 计算当前的资产总值 */ - price_t _getTotalFunds(const Datetime& date, const std::list& running_list); - - /* 回收系统资产 */ - bool _returnAssets(const SYSPtr& sys, const Datetime& date); + const std::unordered_set& running_list); /* 检查分配的权重是否在 0 和 1 之间,如果存在错误,抛出异常,仅在 trace 时生效*/ void _check_weight(const SystemWeightList&); @@ -187,12 +180,14 @@ private: \ #define ALLOCATEFUNDS_NO_PRIVATE_MEMBER_SERIALIZATION #endif -#define ALLOCATEFUNDS_IMP(classname) \ -public: \ - virtual AFPtr _clone() override { \ - return AFPtr(new classname()); \ - } \ - virtual SystemWeightList _allocateWeight(const Datetime&, const SystemWeightList&) override; +#define ALLOCATEFUNDS_IMP(classname) \ +public: \ + virtual AFPtr _clone() override { \ + return AFPtr(new classname()); \ + } \ + virtual SystemWeightList _allocateWeight(const Datetime&, const SystemWeightList&, \ + size_t running_count, double can_allocate_weight) \ + override; typedef shared_ptr AllocateFundsPtr; typedef shared_ptr AFPtr; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp index 0dfe7d87..bab705ee 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp @@ -18,9 +18,11 @@ EqualWeightAllocateFunds::EqualWeightAllocateFunds() : AllocateFundsBase("AF_Equ EqualWeightAllocateFunds::~EqualWeightAllocateFunds() {} SystemWeightList EqualWeightAllocateFunds ::_allocateWeight(const Datetime& date, - const SystemWeightList& se_list) { + const SystemWeightList& se_list, + size_t running_count, + double can_allocate_weight) { SystemWeightList result; - price_t weight = 1.0 / se_list.size(); + price_t weight = can_allocate_weight / se_list.size(); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { result.emplace_back(iter->sys, weight); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp index 40c4b945..33247ad3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp @@ -20,7 +20,9 @@ FixedWeightAllocateFunds::FixedWeightAllocateFunds() : AllocateFundsBase("AF_Fix FixedWeightAllocateFunds::~FixedWeightAllocateFunds() {} SystemWeightList FixedWeightAllocateFunds ::_allocateWeight(const Datetime& date, - const SystemWeightList& se_list) { + const SystemWeightList& se_list, + size_t running_count, + double can_allocate_weight) { SystemWeightList result; price_t weight = getParam("weight"); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp index 168497b8..abfbbbe7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp @@ -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; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 1737a899..34953e88 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -59,9 +59,8 @@ 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(); @@ -160,125 +159,6 @@ bool Portfolio::_readyForRun() { return true; } -void Portfolio::_runMoment(const Datetime& date) { - // 当前日期小于账户建立日期,直接忽略 - HKU_IF_RETURN(date < m_shadow_tm->initDatetime(), void()); - - bool trace = getParam("trace"); - HKU_INFO_IF(trace, "{} ===========================================================", date); - HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_list.size()); - - // 在开盘时执行上一周期中所有运行中的延迟交易系统系统 - // 在不需要调整已持仓系统时,不需要执行,只要每天执行一次系统即可 - // for (auto& sub_sys : m_running_sys_list) { - // if (sub_sys->getParam("buy_delay")) { - // auto tr = sub_sys->runMoment(date); - // if (!tr.isNull()) { - // HKU_INFO_IF(trace, "[PF] on open: {}", tr); - // m_tm->addTradeRecord(tr); - // } - // } - // } - - // 遍历当前运行中的子系统,如果已没有分配资金和持仓,则放入待移除列表 - m_tmp_will_remove_sys.clear(); - int precision = m_shadow_tm->getParam("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.emplace_back(running_sys, 0.); - } - } - - // 依据待移除列表将系统从运行中系统列表里删除 - for (auto& sub_sys : m_tmp_will_remove_sys) { - m_running_sys_list.remove(sub_sys.sys); - m_running_sys_set.erase(sub_sys.sys.get()); - } - - _runMomentOnOpen(date); - _runMomentOnClose(date); - - // 收盘时执行所有运行中的系统,无论是延迟还是非延迟,当天运行中的系统都需要被执行一次 - for (auto& sub_sys : m_running_sys_list) { - auto tr = sub_sys->runMoment(date); - if (!tr.isNull()) { - HKU_INFO_IF(trace, "[PF] on close: {}", tr); - m_tm->addTradeRecord(tr); - } - } - - // 释放掉临时数据占用的内存 - // m_running_sys_set = std::unordered_set(); - // m_running_sys_list = std::list(); - m_tmp_selected_list_on_open = SystemWeightList(); - m_tmp_selected_list_on_close = SystemWeightList(); - m_tmp_will_remove_sys = SystemWeightList(); -} - -void Portfolio::_runMomentOnOpen(const Datetime& date) { - // 从选股策略获取当前选中的系统列表 - m_tmp_selected_list_on_open = m_se->getSelectedOnOpen(date); - - bool trace = getParam("trace"); - if (trace && !m_tmp_selected_list_on_open.empty()) { - for (auto& sys : m_tmp_selected_list_on_open) { - HKU_INFO("[PF] select on open: {}, score: {:<.4f}, cash: {}", sys.sys->name(), - sys.weight, sys.sys->getTM()->cash(date, m_query.kType())); - } - } - - // 资产分配算法调整各子系统资产分配,忽略上一周期收盘时选中的系统 - m_af->adjustFunds(date, m_tmp_selected_list_on_open, m_running_sys_list, - m_tmp_selected_list_on_close); - - // 遍历本次选择的系统列表,如果存在分配资金且不在运行中列表内,则加入运行列表 - for (auto& sub_sys : m_tmp_selected_list_on_open) { - price_t cash = sub_sys.sys->getTM()->cash(date, m_query.kType()); - if (cash > 0.0 && m_running_sys_set.find(sub_sys.sys.get()) == m_running_sys_set.end()) { - m_running_sys_list.push_back(sub_sys.sys); - m_running_sys_set.insert(sub_sys.sys.get()); - } - } -} - -void Portfolio::_runMomentOnClose(const Datetime& date) { - // 从选股策略获取当前选中的系统列表 - m_tmp_selected_list_on_close = m_se->getSelectedOnClose(date); - - bool trace = getParam("trace"); - if (trace && !m_tmp_selected_list_on_close.empty()) { - for (auto& sys : m_tmp_selected_list_on_close) { - HKU_INFO("[PF] select on close: {}, score: {:<.4f}, cash: {}", sys.sys->name(), - sys.weight, sys.sys->getTM()->cash(date, m_query.kType())); - } - } - - // 资产分配算法调整各子系统资产分配,忽略开盘时选中的系统 - m_af->adjustFunds(date, m_tmp_selected_list_on_close, m_running_sys_list, - m_tmp_selected_list_on_open); - - // 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行 - for (auto& sys : m_tmp_selected_list_on_close) { - if (m_running_sys_set.find(sys.sys.get()) == m_running_sys_set.end()) { - TMPtr tm = sys.sys->getTM(); - if (tm->cash(date, m_query.kType()) > 0.0) { - m_running_sys_list.push_back(sys.sys); - m_running_sys_set.insert(sys.sys.get()); - } - } - } -} - void Portfolio::run(const KQuery& query, bool force) { setQuery(query); if (force) { @@ -294,6 +174,132 @@ void Portfolio::run(const KQuery& query, bool force) { _runMoment(date); } m_need_calculate = false; + + // 释放掉临时数据占用的内存 + m_tmp_selected_list = SystemWeightList(); + m_tmp_will_remove_sys = SystemWeightList(); +} + +void Portfolio::_runMoment(const Datetime& date) { + // 当前日期小于账户建立日期,直接忽略 + HKU_IF_RETURN(date < m_shadow_tm->initDatetime(), void()); + + bool trace = getParam("trace"); + HKU_INFO_IF(trace, "{} ===========================================================", date); + HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_set.size()); + + //--------------------------------------------------- + // 开盘前处理 + //--------------------------------------------------- + int precision = m_tm->getParam("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(); + } + + // 开盘前,调整账户权息,并进行轧差处理 + m_tm->updateWithWeight(date); + + HKU_INFO_IF(trace, "The sum cash of sub_tm: {}, cash tm: {}, tm cash: {}", sum_cash, + m_shadow_tm->currentCash(), m_tm->currentCash()); + sum_cash += m_shadow_tm->currentCash(); + + price_t limit = m_tm->currentCash() * precision; + price_t diff = roundEx(std::abs(m_tm->currentCash() - sum_cash), precision); + if (diff > limit) { + if (m_tm->currentCash() > sum_cash) { + m_shadow_tm->checkin(date, diff); + } else if (m_tm->currentCash() < sum_cash) { + if (!m_shadow_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_shadow_tm->currentCash(), m_tm->currentCash()); + } + + // 处理需延迟调仓卖出的系统,在开盘时先卖出调整 + for (auto& sys : m_delay_adjust_sys_list) { + auto tr = sys.sys->sellForce(date, sys.weight, PART_PORTFOLIO); + if (!tr.isNull()) { + 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_shadow_tm->checkin(date, sub_cash); + } + } + } + + // 遍历当前运行中的子系统,如果已没有分配资金和持仓,则放入待移除列表 + 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 (position.number == 0 && cash <= min_cash) { + if (cash != 0) { + sub_tm->checkout(date, cash); + m_shadow_tm->checkin(date, cash); + } + m_tmp_will_remove_sys.emplace_back(running_sys, 0.); + } + } + + // 依据待移除列表将系统从运行中系统列表里删除 + for (auto& sub_sys : m_tmp_will_remove_sys) { + m_running_sys_set.erase(sub_sys.sys); + } + + //--------------------------------------------------- + // 收盘时处理 + //--------------------------------------------------- + + // 从选股策略获取选中的系统列表 + m_tmp_selected_list = m_se->getSelectedOnClose(date); + + if (trace && !m_tmp_selected_list.empty()) { + for (auto& sys : m_tmp_selected_list) { + HKU_INFO("[PF] select: {}, score: {:<.4f}, cash: {}", sys.sys->name(), sys.weight, + sys.sys->getTM()->cash(date, m_query.kType())); + } + } + + // 资产分配算法调整各子系统资产分配 + 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); + } + } + } + + // 收盘时执行所有运行中的系统,无论是延迟还是非延迟,当天运行中的系统都需要被执行一次 + for (auto& sub_sys : m_running_sys_set) { + auto tr = sub_sys->runMoment(date); + if (!tr.isNull()) { + HKU_INFO_IF(trace, "[PF] on close: {}", tr); + m_tm->addTradeRecord(tr); + } + } } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index c4291675..e3c625ce 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -100,13 +100,11 @@ private: bool _readyForRun(); void _runMoment(const Datetime& datetime); - void _runMomentOnOpen(const Datetime& datetime); - void _runMomentOnClose(const Datetime& datetime); protected: string m_name; TMPtr m_tm; - TMPtr m_shadow_tm; + TMPtr m_shadow_tm; // 仅仅负责内部资金的管理(即只需要 checkout 到子账号, 从账户checkin现金) SEPtr m_se; AFPtr m_af; @@ -118,10 +116,9 @@ protected: SystemList m_real_sys_list; // 所有实际运行的子系统列表 // 用于中间计算的临时数据 - std::unordered_set m_running_sys_set; // 当前仍在运行的子系统集合 - std::list m_running_sys_list; // 当前仍在运行的子系统列表 - SystemWeightList m_tmp_selected_list_on_open; - SystemWeightList m_tmp_selected_list_on_close; + std::unordered_set m_running_sys_set; + SystemWeightList m_delay_adjust_sys_list; // 延迟调仓卖出的系统列表 + SystemWeightList m_tmp_selected_list; SystemWeightList m_tmp_will_remove_sys; //============================================ diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 4210b243..720cc35a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -710,6 +710,40 @@ TradeRecord System::sellForce(const KRecord& today, const KRecord& src_today, do } } +TradeRecord System::sellForce(const Datetime& date, double num, Part from) { + HKU_ASSERT(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO); + + TradeRecord record; + size_t pos = m_kdata.getPos(date); + HKU_IF_RETURN(pos == Null(), record); + + 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(date, m_stock); + price_t realPrice = _getRealSellPrice(krecord.datetime, src_krecord.openPrice); + + 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(num / min_num) * min_num; + } + // double real_sell_num = min_num > 1 ? static_cast(num / min_num) * min_num : num; + // if ((position.number - real_sell_num) <= min_num) { + // real_sell_num = position.number; + // } + + // 以开盘价卖出 + record = m_tm->sell(date, m_stock, realPrice, real_sell_num, position.stoploss, + position.goalPrice, src_krecord.openPrice, from); + m_trade_list.push_back(record); + _sellNotifyAll(record); + return record; +} + TradeRecord System::_sell(const KRecord& today, const KRecord& src_today, Part from) { TradeRecord result; if (getParam("sell_delay")) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index 13ff98ec..495c66ba 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -214,6 +214,9 @@ public: // 强制卖出,用于资金分配管理器和资产组合指示系统进行强制卖出操作 TradeRecord sellForce(const KRecord& today, const KRecord& src_today, double num, Part from); + // Portfolio 指示开盘时立即进行强制卖出,以便对 buy_delay 的系统进行资金调整 + TradeRecord sellForce(const Datetime& date, double num, Part from); + private: bool _environmentIsValid(const Datetime& datetime); bool _conditionIsValid(const Datetime& datetime); diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index c49e5230..f2c949be 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -25,10 +25,11 @@ public: PYBIND11_OVERLOAD(void, AllocateFundsBase, _reset, ); } - SystemWeightList _allocateWeight(const Datetime& date, - const SystemWeightList& se_list) override { + SystemWeightList _allocateWeight(const Datetime& date, const SystemWeightList& se_list, + size_t running_count, double can_allocate_weight) override { PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, AllocateFundsBase, "_allocate_weight", - _allocateWeight, date, se_list); + _allocateWeight, date, se_list, running_count, + can_allocate_weight); } }; @@ -75,7 +76,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"), py::arg("running_count"), py::arg("can_allocate_weight"), + R"(_allocate_weight(self, date, se_list) 【重载接口】子类分配权重接口,获取实际分配资产的系统实例及其权重 From 8aa4671856fae7c0e9df1e2272392d6b080193e9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 19 Mar 2024 22:05:13 +0800 Subject: [PATCH 062/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20selector?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 2 +- .../hikyuu/trade_sys/selector/SelectorBase.h | 20 +++++-------- .../trade_sys/selector/imp/FixedSelector.cpp | 16 ++-------- .../trade_sys/selector/imp/SignalSelector.cpp | 19 ++++-------- .../trade_sys/selector/imp/SignalSelector.h | 6 ++-- .../allocatefunds/test_AllocateFunds.cpp | 4 +-- .../trade_sys/selector/test_SE_Fixed.cpp | 30 +++++++++---------- hikyuu_pywrap/trade_sys/_Selector.cpp | 26 ++++------------ 8 files changed, 42 insertions(+), 81 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 34953e88..2591249a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -271,7 +271,7 @@ void Portfolio::_runMoment(const Datetime& date) { //--------------------------------------------------- // 从选股策略获取选中的系统列表 - m_tmp_selected_list = m_se->getSelectedOnClose(date); + m_tmp_selected_list = m_se->getSelected(date); if (trace && !m_tmp_selected_list.empty()) { for (auto& sys : m_tmp_selected_list) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 3d3bf21d..fb0d9b7c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -97,11 +97,8 @@ public: /** 子类计算接口 */ virtual void _calculate() = 0; - /** 子类获取指定时刻开盘时选中的标的 */ - virtual SystemWeightList getSelectedOnOpen(Datetime date) = 0; - /** 子类获取指定时刻收盘时选中的标的 */ - virtual SystemWeightList getSelectedOnClose(Datetime date) = 0; + virtual SystemWeightList getSelected(Datetime date) = 0; virtual bool isMatchAF(const AFPtr& af) = 0; @@ -167,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 SystemWeightList getSelectedOnOpen(Datetime date) override; \ - virtual SystemWeightList 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; /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp index 7f0c0308..9865670f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp @@ -21,22 +21,10 @@ bool FixedSelector::isMatchAF(const AFPtr& af) { return true; } -SystemWeightList FixedSelector::getSelectedOnOpen(Datetime date) { +SystemWeightList FixedSelector::getSelected(Datetime date) { SystemWeightList result; for (auto& sys : m_real_sys_list) { - if (!sys->getParam("buy_delay")) { - result.emplace_back(sys, 1.0); - } - } - return result; -} - -SystemWeightList FixedSelector::getSelectedOnClose(Datetime date) { - SystemWeightList result; - for (auto& sys : m_real_sys_list) { - if (sys->getParam("buy_delay")) { - result.emplace_back(sys, 1.0); - } + result.emplace_back(sys, 1.0); } return result; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp index f882e85a..2be613bd 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp @@ -24,14 +24,9 @@ bool SignalSelector::isMatchAF(const AFPtr& af) { return true; } -SystemWeightList SignalSelector::getSelectedOnOpen(Datetime date) { - auto iter = m_sys_dict_on_open.find(date); - return iter != m_sys_dict_on_open.end() ? iter->second : SystemWeightList(); -} - -SystemWeightList SignalSelector::getSelectedOnClose(Datetime date) { - auto iter = m_sys_dict_on_close.find(date); - return iter != m_sys_dict_on_close.end() ? iter->second : SystemWeightList(); +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* date_dict; - date_dict = sys->getParam("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()) { + auto iter = m_sys_dict.find(date); + if (iter != m_sys_dict.end()) { iter->second.emplace_back(sys, 1.0); } else { - (*date_dict)[date] = {SystemWeight(sys, 1.0)}; + m_sys_dict[date] = {SystemWeight(sys, 1.0)}; } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h index f2e63eb7..67c89f24 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.h @@ -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 m_sys_dict_on_open; - unordered_map m_sys_dict_on_close; + unordered_map m_sys_dict; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp index d94df149..5c402601 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp @@ -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 */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp index cbeee55a..e4d1b230 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp @@ -29,24 +29,24 @@ TEST_CASE("test_SE_Fixed") { SYSPtr sys = SYS_Simple(); SEPtr se = SE_Fixed(); - /** @arg 试图加入一个不存在的stock */ - se->addStock(Stock(), sys); - SystemWeightList 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 */ diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index ccd13aeb..0a82015a 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -25,14 +25,9 @@ public: PYBIND11_OVERLOAD_PURE(void, SelectorBase, _calculate, ); } - SystemWeightList getSelectedOnOpen(Datetime date) override { - PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected_on_open", - getSelectedOnOpen, date); - } - - SystemWeightList getSelectedOnClose(Datetime date) override { - PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, 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 { @@ -129,19 +124,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: 选取的系统实例列表 From 474a67a7073366484984cf5a74c678467c38653e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 20 Mar 2024 01:38:21 +0800 Subject: [PATCH 063/601] update PF/AF --- .../allocatefunds/AllocateFundsBase.cpp | 86 ++++++++++++------- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 15 ++-- 2 files changed, 62 insertions(+), 39 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 1018c550..99802027 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -146,7 +146,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, m_reserve_percent); price_t reserve_funds = roundDown(total_funds * m_reserve_percent, precision); double can_allocate_weight = funds.cash / total_funds; - HKU_INFO_IF("can_allocate_weight: {:<.4f}", can_allocate_weight); + HKU_INFO_IF(trace, "can_allocate_weight: {:<.4f}", can_allocate_weight); HKU_IF_RETURN(can_allocate_weight <= 0., void()); // 获取计划分配的资产权重 @@ -167,32 +167,32 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 1. 没有发生信号的系统 // 2. 权重小于等于 0 的系统 // 2. 累积权重>可分配的总权重之后的系统 - price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; - price_t sum_weight = 0.0; - SystemWeightList new_sw_list; - for (auto sw_iter = sw_list.rbegin(); sw_iter != sw_list.rend(); ++sw_iter) { - // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 - if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { - break; - } + // price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; + // price_t sum_weight = 0.0; + // SystemWeightList new_sw_list; + // for (auto sw_iter = sw_list.rbegin(); sw_iter != sw_list.rend(); ++sw_iter) { + // // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 + // if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { + // break; + // } - if (sw_iter->sys->getSG()->shouldBuy(date)) { - sum_weight += sw_iter->weight; - // 如果累积权重大于1,则调整最后的系统权重 - if (sum_weight > can_allocate_sum_weight) { - sw_iter->weight = sum_weight - can_allocate_sum_weight; - sum_weight = can_allocate_sum_weight; - } - new_sw_list.emplace_back(*sw_iter); - } - } + // if (sw_iter->sys->getSG()->shouldBuy(date)) { + // sum_weight += sw_iter->weight; + // // 如果累积权重大于1,则调整最后的系统权重 + // if (sum_weight > can_allocate_sum_weight) { + // sw_iter->weight = sum_weight - can_allocate_sum_weight; + // sum_weight = can_allocate_sum_weight; + // } + // new_sw_list.emplace_back(*sw_iter); + // } + // } - if (trace) { - for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) { - HKU_INFO("[AF] ({}, {}, weight: {:<.4f}) ", iter->sys->name(), - iter->sys->getStock().market_code(), iter->weight); - } - } + // if (trace) { + // for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) { + // HKU_INFO("[AF] ({}, {}, weight: {:<.4f}) ", iter->sys->name(), + // iter->sys->getStock().market_code(), iter->weight); + // } + // } // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); @@ -206,14 +206,27 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, } // 根据账户精度微调总资产,尽可能消除由于四舍五入后导致的精度问题 - total_funds = total_funds - std::pow(0.1, precision) * 0.5 * new_sw_list.size(); + total_funds = total_funds - std::pow(0.1, precision) * 0.5 * sw_list.size(); // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 int max_num = getParam("max_sys_num"); size_t can_run_count = 0; - for (auto iter = new_sw_list.begin(), end_iter = new_sw_list.end(); iter != end_iter; ++iter) { + price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; + price_t sum_weight = 0.0; + // for (auto iter = new_sw_list.begin(), end_iter = new_sw_list.end(); iter != end_iter; ++iter) + // { + for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { + if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_run_count >= max_num) { + break; + } + + // 计算实际可用的权重 + price_t try_weight = iter->weight + sum_weight > can_allocate_sum_weight + ? iter->weight + sum_weight - can_allocate_sum_weight + : iter->weight; + // 该系统期望分配的资金 - price_t will_cash = roundUp(total_funds * iter->weight, precision); + price_t will_cash = roundUp(total_funds * try_weight, precision); if (will_cash <= 0.0) { continue; } @@ -221,11 +234,22 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 计算子账户实际可获取的的资金 price_t need_cash = will_cash <= can_allocate_cash ? will_cash : can_allocate_cash; + // 如果剩余资金连一手都买不了,直接忽略 + const KRecord& krecord = iter->sys->getTO().getKRecord(date); + if (need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) { + continue; + } + // 尝试从总账户中取出资金存入子账户 TMPtr sub_tm = iter->sys->getTM(); if (m_shadow_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); - HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(), need_cash); + HKU_INFO_IF(trace, "[AF] ({}, {}, weight: {:<.4f}) fetched cash: {}", iter->sys->name(), + iter->sys->getStock().market_code(), iter->weight, need_cash); + // HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(), need_cash); + + // 当前累积权重 + sum_weight += iter->weight; // 如果超出允许运行的最大系统数,跳出循环 can_run_count++; @@ -238,10 +262,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, iter->sys->name()); } - if (can_run_count >= max_num) { - break; - } - // (不需要小于0,小于一个特定的值即可,这里用1) if (can_allocate_cash <= 1.0) { break; diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 2591249a..8031543f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -251,18 +251,21 @@ void Portfolio::_runMoment(const Datetime& date) { min_cash = krecord.openPrice * stock.minTradeNumber(); } - // 已没有持仓且没有现金(一手都买不起),则放入待移除列表 - if (position.number == 0 && cash <= min_cash) { - if (cash != 0) { - sub_tm->checkout(date, cash); - m_shadow_tm->checkin(date, cash); + // 如果系统的剩余资金小于交易一手的资金,则回收资金 + if (cash != 0 && cash <= min_cash) { + sub_tm->checkout(date, cash); + m_shadow_tm->checkin(date, cash); + HKU_INFO_IF(trace, "Collect the scraps cash ({:<.2f}) from {}", cash, + running_sys->name()); + if (position.number == 0) { + m_tmp_will_remove_sys.emplace_back(running_sys, 0.); } - m_tmp_will_remove_sys.emplace_back(running_sys, 0.); } } // 依据待移除列表将系统从运行中系统列表里删除 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); } From c2197db9e6c6c30dbfcfef520ec4c0d11621e946 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 20 Mar 2024 02:17:43 +0800 Subject: [PATCH 064/601] update --- .../allocatefunds/AllocateFundsBase.cpp | 34 ------------------- 1 file changed, 34 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 99802027..3c61fe39 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -163,37 +163,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), std::bind(&SystemWeight::weight, std::placeholders::_2))); - // 检测是否有信号发生,过滤掉: - // 1. 没有发生信号的系统 - // 2. 权重小于等于 0 的系统 - // 2. 累积权重>可分配的总权重之后的系统 - // price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; - // price_t sum_weight = 0.0; - // SystemWeightList new_sw_list; - // for (auto sw_iter = sw_list.rbegin(); sw_iter != sw_list.rend(); ++sw_iter) { - // // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 - // if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { - // break; - // } - - // if (sw_iter->sys->getSG()->shouldBuy(date)) { - // sum_weight += sw_iter->weight; - // // 如果累积权重大于1,则调整最后的系统权重 - // if (sum_weight > can_allocate_sum_weight) { - // sw_iter->weight = sum_weight - can_allocate_sum_weight; - // sum_weight = can_allocate_sum_weight; - // } - // new_sw_list.emplace_back(*sw_iter); - // } - // } - - // if (trace) { - // for (auto iter = new_sw_list.begin(); iter != new_sw_list.end(); ++iter) { - // HKU_INFO("[AF] ({}, {}, weight: {:<.4f}) ", iter->sys->name(), - // iter->sys->getStock().market_code(), iter->weight); - // } - // } - // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision); @@ -213,8 +182,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, size_t can_run_count = 0; price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; price_t sum_weight = 0.0; - // for (auto iter = new_sw_list.begin(), end_iter = new_sw_list.end(); iter != end_iter; ++iter) - // { for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_run_count >= max_num) { break; @@ -246,7 +213,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, sub_tm->checkin(date, need_cash); HKU_INFO_IF(trace, "[AF] ({}, {}, weight: {:<.4f}) fetched cash: {}", iter->sys->name(), iter->sys->getStock().market_code(), iter->weight, need_cash); - // HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(), need_cash); // 当前累积权重 sum_weight += iter->weight; From 112500aaa3904cf816972356ce456cff07483032 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 20 Mar 2024 18:22:30 +0800 Subject: [PATCH 065/601] update --- hikyuu/trade_sys/trade_sys.py | 5 +- .../allocatefunds/AllocateFundsBase.cpp | 214 ++++++++---------- hikyuu_pywrap/trade_sys/_Portfolio.cpp | 3 +- 3 files changed, 94 insertions(+), 128 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index bfd23923..c3c2ebd2 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -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) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 3c61fe39..2ccca28f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -32,8 +32,6 @@ AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase"), m_reserve_ // adjust_running_sys: True - 主动根据资产分配对已持仓策略进行增减仓 // adjust_running_sys: False - 不会根据当前分配权重对已持仓策略进行强制加减仓 setParam("adjust_running_sys", false); - setParam("max_sys_num", 1000000); // 允许运行的最大系统实例数 - setParam("weight_unit", 0.0001); // 最小权重单位 setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 setParam("trace", false); // 打印跟踪 } @@ -41,8 +39,6 @@ AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase"), m_reserve_ AllocateFundsBase::AllocateFundsBase(const string& name) : m_name("AllocateMoneyBase"), m_reserve_percent(0) { setParam("adjust_running_sys", false); - setParam("max_sys_num", 100000); // 最大系统实例数 - setParam("weight_unit", 0.0001); // 最小权重单位 setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 setParam("trace", false); // 打印跟踪 } @@ -51,11 +47,6 @@ AllocateFundsBase::~AllocateFundsBase() {} void AllocateFundsBase::reset() { // 参数检查 - HKU_CHECK(getParam("max_sys_num") > 0, R"(AF param["max_sys_num"]({}) need > 0!)", - getParam("max_sys_num")); - HKU_CHECK(getParam("weight_unit") > 0.0, R"(AF param[{}]) need > 0!)", - getParam("weight_unit")); - double default_reserve_percent = getParam("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!)", @@ -120,6 +111,20 @@ SystemWeightList AllocateFundsBase::adjustFunds(const Datetime& date, return result; } +// 降序排列 SystemWeightList,同时保证 nan 值处于末尾 +static void sortSystemWeightListAsDesc(SystemWeightList& sw_list) { + 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; + }); +} + void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemWeightList& se_list, const std::unordered_set& running_set) { @@ -157,11 +162,8 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, _check_weight(sw_list); } - // 按权重升序排序(注意:无法保证等权重的相对顺序,即使用stable_sort也一样,后面要倒序遍历) - std::sort( - sw_list.begin(), sw_list.end(), - std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), - std::bind(&SystemWeight::weight, std::placeholders::_2))); + // 按权重降序排列 + sortSystemWeightListAsDesc(sw_list); // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); @@ -178,12 +180,10 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, total_funds = total_funds - std::pow(0.1, precision) * 0.5 * sw_list.size(); // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 - int max_num = getParam("max_sys_num"); - size_t can_run_count = 0; price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; price_t sum_weight = 0.0; for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { - if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_run_count >= max_num) { + if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_allocate_cash <= 1.0) { break; } @@ -201,9 +201,10 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 计算子账户实际可获取的的资金 price_t need_cash = will_cash <= can_allocate_cash ? will_cash : can_allocate_cash; - // 如果剩余资金连一手都买不了,直接忽略 + // 如果需要的资金连一手都买不了,直接忽略 const KRecord& krecord = iter->sys->getTO().getKRecord(date); - if (need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) { + if (krecord.isValid() && + need_cash < (krecord.closePrice * iter->sys->getStock().minTradeNumber())) { continue; } @@ -217,9 +218,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 当前累积权重 sum_weight += iter->weight; - // 如果超出允许运行的最大系统数,跳出循环 - can_run_count++; - // 计算剩余的可用于分配的资金 can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision); @@ -227,11 +225,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account!", iter->sys->name()); } - - // (不需要小于0,小于一个特定的值即可,这里用1) - if (can_allocate_cash <= 1.0) { - break; - } } } @@ -248,83 +241,25 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( HKU_ERROR_IF(m_reserve_percent < 0.0 || m_reserve_percent >= 1.0, "Invalid reserve_percent({}) in AF, Calculations that will cause errors!", m_reserve_percent); - double can_allocate_weith = 1.0 - m_reserve_percent; + double can_allocate_weight = 1.0 - m_reserve_percent; // 获取计划分配的资产权重 SystemWeightList sw_list = - _allocateWeight(date, se_list, running_set.size(), can_allocate_weith); + _allocateWeight(date, se_list, running_set.size(), can_allocate_weight); HKU_IF_RETURN(sw_list.size() == 0, result); if (trace) { _check_weight(sw_list); } - // 按权重升序排序(注意:无法保证等权重的相对顺序,即使用stable_sort也一样,后面要倒序遍历) - std::sort( - sw_list.begin(), sw_list.end(), - std::bind(std::less(), std::bind(&SystemWeight::weight, std::placeholders::_1), - std::bind(&SystemWeight::weight, std::placeholders::_2))); + // 按权重降序排列 + sortSystemWeightListAsDesc(sw_list); // 获取当前总资产市值,计算需保留的资产 int precision = m_shadow_tm->getParam("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 = roundDown(total_funds * m_reserve_percent, precision); - - // 检测是否有信号发生,过滤掉: - // 1. 不在当前运行系统集合中且没有发生信号的系统 - // 2. 权重小于等于 0 的系统,如果是运行中的系统,则强制清仓 - // 2. 累积权重>可分配的总权重之后的系统 - price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; - price_t sum_weight = 0.0; - SystemWeightList new_sw_list; - std::unordered_set new_sw_set; - for (auto sw_iter = sw_list.rbegin(); sw_iter != sw_list.rend(); ++sw_iter) { - // 如果当前系统权重小于等于0 或者 累积权重以及大于等于1 终止循环 - if (sw_iter->weight <= 0.0 || sum_weight >= can_allocate_sum_weight) { - break; - } - - // 如果系统在运行中,或者有信号发生 - if (running_set.count(sw_iter->sys) > 0 || sw_iter->sys->getSG()->shouldBuy(date)) { - sum_weight += sw_iter->weight; - // 如果累积权重大于1,则调整最后的系统权重 - if (sum_weight > can_allocate_sum_weight) { - sw_iter->weight = sum_weight - can_allocate_sum_weight; - sum_weight = can_allocate_sum_weight; - } - new_sw_list.emplace_back(*sw_iter); - new_sw_set.insert(sw_iter->sys.get()); - } - } - - // 如果调整运行中系统持仓,则: - // 对已不在新的实际分配权重的系统集合中的运行中的系统,进行清仓操作 - for (auto& running_sys : running_set) { - if (new_sw_set.count(running_sys.get()) == 0) { - // 如果是延迟买入的系统,由于可能是在当天开盘刚刚买入,此时不能立刻清仓卖出,放入延迟卖出清单中 - if (running_sys->getParam("buy_delay")) { - result.emplace_back(running_sys, MAX_DOUBLE); - } else { - auto tr = running_sys->sellForce(date, MAX_DOUBLE, PART_ALLOCATEFUNDS); - if (!tr.isNull()) { - m_tm->addTradeRecord(tr); - auto sub_tm = running_sys->getTM(); - auto sub_cash = sub_tm->currentCash(); - if (sub_tm->checkout(date, sub_cash)) { - m_shadow_tm->checkin(date, sub_cash); - } - } - } - } - } - - if (trace) { - for (auto iter = sw_list.begin(); iter != sw_list.end(); ++iter) { - HKU_INFO("[AF] ({}, {}, weight: {:<.4f}) ", iter->sys->name(), - iter->sys->getStock().market_code(), iter->weight); - } - } + price_t reserve_funds = roundEx(total_funds * m_reserve_percent, precision); // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); @@ -337,45 +272,56 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( return result; } - // 根据账户精度微调总资产,尽可能消除由于四舍五入后导致的精度问题 - total_funds = total_funds - std::pow(0.1, precision) * 0.5 * new_sw_list.size(); + // 遍历选中子系统列表,并调整资产 + price_t sum_weight = 0.0; // 累积已分配权重 + for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { + if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_allocate_cash <= 1.) { + break; + } - // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 - int max_num = getParam("max_sys_num"); - size_t can_run_count = 0; - for (auto iter = new_sw_list.begin(), end_iter = new_sw_list.end(); iter != end_iter; ++iter) { - // 系统预期的分配资产额 - price_t will_funds = roundUp(total_funds * iter->weight, precision); + // 计算实际可用的权重 + 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.count(iter->sys) != 0) { - can_run_count++; - + // 获取当前系统已持有的资产 TMPtr sub_tm = iter->sys->getTM(); const KQuery& query = iter->sys->getTO().getQuery(); FundsRecord funds = sub_tm->getFunds(query.kType()); price_t current_funds = funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value; - // 如果预期资产和当前资产相等,则转到下个系统 + // 如果预期资产和当前已持有的资产相等,则转到下个系统 if (current_funds == will_funds) { + // can_allocate_cash 不需要补充资金,保持不变 + sum_weight += current_weight; continue; } // 如果预期资产和当前资产之差不够买入或卖出最小交易数量的证券,则转至下一系统 Stock stock = iter->sys->getStock(); - auto krecord = stock.getKRecord(date, query.kType()); + const auto& krecord = iter->sys->getTO().getKRecord(date); double min_num = stock.minTradeNumber(); - if (std::abs(current_funds < will_funds) < krecord.closePrice * min_num) { + if (krecord.isValid() && + (std::abs(will_funds - current_funds) < krecord.closePrice * min_num)) { + // can_allocate_cash 无需补充资金,保持不变 + sum_weight += current_funds / total_funds; continue; } if (current_funds < will_funds) { - // 已有资产小于预期资产,则分配新的资金 + // 已有资产小于预期资产,则尝试分配新的资金 price_t diff_price = roundUp(will_funds - current_funds, precision); - if (can_allocate_cash > 0. && m_shadow_tm->checkout(date, diff_price)) { + if (m_shadow_tm->checkout(date, diff_price)) { sub_tm->checkin(date, diff_price); + // 更新剩余可分配现金及已分配累积权重 can_allocate_cash = roundDown(can_allocate_cash - diff_price, precision); + sum_weight += (current_funds + diff_price) / total_funds; } } else { @@ -386,57 +332,77 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( need_back_num = static_cast(need_back_num / min_num) * min_num; } - // 如果是延迟买入的系统,则将减仓卖出记录加入返回的延迟清单 + // 如果是延迟买入的系统,则将减仓卖出记录加入返回的延迟清单,无法调仓 if (iter->sys->getParam("buy_delay")) { result.emplace_back(iter->sys, need_back_num); - can_allocate_cash = - roundDown(can_allocate_cash - need_back_num * krecord.closePrice, precision); + // can_allocate_cash 当前可分配现金保存不变 + sum_weight += current_funds / total_funds; } else { + // 非延迟卖出的系统,立即强制卖出并回收资金 auto tr = iter->sys->sellForce(date, need_back_num, PART_ALLOCATEFUNDS); if (!tr.isNull()) { - m_tm->addTradeRecord(tr); auto sub_cash = sub_tm->currentCash(); if (sub_tm->checkout(date, sub_cash)) { m_shadow_tm->checkin(date, sub_cash); - can_allocate_cash = roundDown(can_allocate_cash - sub_cash, precision); + m_tm->addTradeRecord(tr); // 向总账户加入交易记录 + + // 更新剩余可用资金 = 原有 + 回收的 + can_allocate_cash = roundDown(can_allocate_cash + sub_cash, precision); + + // 更新当前总资产及累积权重 + funds = m_tm->getFunds(date, m_query.kType()); + total_funds = funds.cash + funds.market_value + funds.borrow_asset - + funds.short_market_value; + + FundsRecord sub_funds = sub_tm->getFunds(query.kType()); + sum_weight += (sub_funds.cash + sub_funds.market_value + + sub_funds.borrow_asset - sub_funds.short_market_value) / + total_funds; + } else { + HKU_DEBUG_IF(trace, "Failed to checkout {} cash from {}!", sub_cash, + iter->sys->name()); } + } else { + HKU_DEBUG_IF("Failed to force sell num({}) from {}", iter->sys->name()); + // 强制卖出失败,更新当前累积权重 + FundsRecord sub_funds = sub_tm->getFunds(query.kType()); + sum_weight += (sub_funds.cash + sub_funds.market_value + + sub_funds.borrow_asset - sub_funds.short_market_value) / + total_funds; } } } } else { // 非运行中的系统 - // 计算子账户实际可获取的的资金 price_t need_cash = will_funds <= can_allocate_cash ? will_funds : can_allocate_cash; + // 如果期望的资金连一手都买不起,则跳过 + const auto& krecord = iter->sys->getTO().getKRecord(date); + if (krecord.isValid() && + need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) { + continue; + } + // 尝试从总账户中取出资金存入子账户 TMPtr sub_tm = iter->sys->getTM(); if (m_shadow_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); HKU_INFO_IF(trace, "[AF] {} fetched cash: {}", iter->sys->name(), need_cash); - // 如果超出允许运行的最大系统数,跳出循环 - can_run_count++; + // 更新剩余可分配资金 + can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision); - // 计算剩余的可用于分配的资金,如果小于1,退出循环 - // (不需要小于0,小于一个特定的值即可,这里用1) - can_allocate_cash = roundUp(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()); } } - - if (can_run_count >= max_num) { - break; - } - - if (can_allocate_cash <= 1.0) { - break; - } } return result; diff --git a/hikyuu_pywrap/trade_sys/_Portfolio.cpp b/hikyuu_pywrap/trade_sys/_Portfolio.cpp index 73190971..a9ece9fd 100644 --- a/hikyuu_pywrap/trade_sys/_Portfolio.cpp +++ b/hikyuu_pywrap/trade_sys/_Portfolio.cpp @@ -57,7 +57,8 @@ 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("force") = false, + R"(run(self, query, force) 运行投资组合策略。在查询条件及各组件没有变化时,PF在第二次执行时,默认不会实际进行计算。 但由于各个组件的参数可能改变,此种情况无法自动判断是否需要重计算,可以手工指定进行强制计算。 From 0531bc03af7ceee2afa3c28ac8990caf26fc4e0b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 20 Mar 2024 19:22:33 +0800 Subject: [PATCH 066/601] update --- hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h index 94eb9348..9bdf746c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h @@ -20,7 +20,7 @@ namespace hku { */ struct HKU_API SystemWeight { SystemPtr sys; - price_t weight; + price_t weight{1.0}; SystemWeight() = default; SystemWeight(const SystemPtr& sys_, double weight_) : sys(sys_), weight(weight_) {} From 526bc058935fb61ecc484967863588c7ba82b64f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 21 Mar 2024 18:01:45 +0800 Subject: [PATCH 067/601] update --- hikyuu_cpp/hikyuu/Log.h | 92 ++--- .../allocatefunds/AllocateFundsBase.cpp | 382 ++++++++++-------- .../allocatefunds/AllocateFundsBase.h | 25 +- .../imp/EqualWeightAllocateFunds.cpp | 6 +- .../imp/FixedWeightAllocateFunds.cpp | 4 +- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 78 ++-- .../hikyuu/trade_sys/portfolio/Portfolio.h | 4 +- hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 9 +- hikyuu_pywrap/trade_sys/_Portfolio.cpp | 6 +- hikyuu_pywrap/trade_sys/_Selector.cpp | 3 +- 10 files changed, 339 insertions(+), 270 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Log.h b/hikyuu_cpp/hikyuu/Log.h index 9171e209..3f016e28 100644 --- a/hikyuu_cpp/hikyuu/Log.h +++ b/hikyuu_cpp/hikyuu/Log.h @@ -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 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 2ccca28f..aef7f1a1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -29,9 +29,19 @@ 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("adjust_running_sys", false); + + // 是否过滤子类返回的比例权重列表中的 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("filter_zero_weight", false); + setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 setParam("trace", false); // 打印跟踪 } @@ -39,6 +49,7 @@ AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase"), m_reserve_ AllocateFundsBase::AllocateFundsBase(const string& name) : m_name("AllocateMoneyBase"), m_reserve_percent(0) { setParam("adjust_running_sys", false); + setParam("filter_zero_weight", false); setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 setParam("trace", false); // 打印跟踪 } @@ -111,8 +122,8 @@ SystemWeightList AllocateFundsBase::adjustFunds(const Datetime& date, return result; } -// 降序排列 SystemWeightList,同时保证 nan 值处于末尾 -static void sortSystemWeightListAsDesc(SystemWeightList& sw_list) { +// 降序排列 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; @@ -123,77 +134,98 @@ static void sortSystemWeightListAsDesc(SystemWeightList& sw_list) { } return a.weight > b.weight; }); + + 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); + } + + 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); } +// 只对现金余额进行分配,此时的权重是针对余额的 void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemWeightList& se_list, const std::unordered_set& running_set) { - HKU_IF_RETURN(se_list.size() == 0, void()); + 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); bool trace = getParam("trace"); HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date); - // 计算不包含运行中系统的子系统列表 - SystemWeightList pure_se_list; - for (auto& sw : se_list) { + // 获取计划分配的资产权重,因为不调整已运行系统实现占位,将所有运行中的系统以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()) { - pure_se_list.push_back(sw); + new_se_list.emplace_back(sw); } } - // 获取当前总资产市值,计算需保留的资产与剩余可分配权重 - int precision = m_shadow_tm->getParam("precision"); - FundsRecord funds = m_tm->getFunds(date, m_query.kType()); + 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()); + + // 获取当前总资产市值,计算剩余可分配权重与现金 + int precision = m_tm->getParam("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; - HKU_ERROR_IF(m_reserve_percent < 0.0 || m_reserve_percent >= 1.0, - "Invalid reserve_percent({}) in AF, Calculations that will cause errors!", - m_reserve_percent); - price_t reserve_funds = roundDown(total_funds * m_reserve_percent, precision); - double can_allocate_weight = funds.cash / total_funds; - HKU_INFO_IF(trace, "can_allocate_weight: {:<.4f}", can_allocate_weight); - HKU_IF_RETURN(can_allocate_weight <= 0., void()); - - // 获取计划分配的资产权重 - SystemWeightList sw_list = - _allocateWeight(date, pure_se_list, running_set.size(), can_allocate_weight); - HKU_IF_RETURN(sw_list.size() == 0, void()); - if (trace) { - _check_weight(sw_list); + price_t reserve_funds = total_funds * m_reserve_percent; + price_t can_allocate_cash = m_shadow_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()); - // 按权重降序排列 - sortSystemWeightListAsDesc(sw_list); + // 调整权重(累积权重和归一)并按降序排列, 并过滤掉 0 值和 Nan 值 + adjustWeight(sw_list, can_allocate_weight, getParam("filter_zero_weight")); - // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 - price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); - price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision); - if (can_allocate_cash <= 0.0) { - HKU_WARN_IF( - trace, - "Insufficient available cash! can_allocate_cash: {}, current_cash: {}, reserve_funds: {}", - can_allocate_cash, current_cash, reserve_funds); - return; - } - - // 根据账户精度微调总资产,尽可能消除由于四舍五入后导致的精度问题 - total_funds = total_funds - std::pow(0.1, precision) * 0.5 * sw_list.size(); - - // 再次遍历选中子系统列表,并将剩余现金按权重比例转入子账户 - price_t can_allocate_sum_weight = 1.0 - m_reserve_percent; - price_t sum_weight = 0.0; + // 遍历选中子系统列表,并将剩余现金按权重比例转入子账户 + double sum_weight = 0.0; // 由于不调整已运行系统,已运行系统实际占用比例可能和要求的比例不一致 for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { - if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_allocate_cash <= 1.0) { + 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(m_query.kType()); + price_t sub_total_funds = + funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value; + sum_weight += sub_total_funds / total_funds; + continue; + } + // 计算实际可用的权重 - price_t try_weight = iter->weight + sum_weight > can_allocate_sum_weight - ? iter->weight + sum_weight - can_allocate_sum_weight - : iter->weight; + price_t current_weight = + iter->weight + sum_weight > 1.0 ? iter->weight + sum_weight - 1.0 : iter->weight; // 该系统期望分配的资金 - price_t will_cash = roundUp(total_funds * try_weight, precision); + price_t will_cash = roundUp(total_funds * current_weight, precision); if (will_cash <= 0.0) { continue; } @@ -201,8 +233,9 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 计算子账户实际可获取的的资金 price_t need_cash = will_cash <= can_allocate_cash ? will_cash : can_allocate_cash; - // 如果需要的资金连一手都买不了,直接忽略 - const KRecord& krecord = iter->sys->getTO().getKRecord(date); + // 如果需要的资金连一手都买不了,直接忽略跳过 + KRecord krecord = + iter->sys->getStock().getKRecord(date, iter->sys->getTO().getQuery().kType()); if (krecord.isValid() && need_cash < (krecord.closePrice * iter->sys->getStock().minTradeNumber())) { continue; @@ -213,17 +246,15 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, if (m_shadow_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); HKU_INFO_IF(trace, "[AF] ({}, {}, weight: {:<.4f}) fetched cash: {}", iter->sys->name(), - iter->sys->getStock().market_code(), iter->weight, need_cash); - - // 当前累积权重 - sum_weight += iter->weight; + iter->sys->getStock().market_code(), current_weight, need_cash); // 计算剩余的可用于分配的资金 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()); + HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account ({})!", + iter->sys->name(), m_shadow_tm->currentCash()); } } } @@ -231,51 +262,123 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, SystemWeightList AllocateFundsBase::_adjust_with_running( const Datetime& date, const SystemWeightList& se_list, const std::unordered_set& running_set) { - SystemWeightList result; - HKU_IF_RETURN(se_list.size() == 0, result); + 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("trace"); - HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date); - - // 剩余可用于分配的权重 - HKU_ERROR_IF(m_reserve_percent < 0.0 || m_reserve_percent >= 1.0, - "Invalid reserve_percent({}) in AF, Calculations that will cause errors!", - m_reserve_percent); - double can_allocate_weight = 1.0 - m_reserve_percent; + 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, running_set.size(), can_allocate_weight); - HKU_IF_RETURN(sw_list.size() == 0, result); - if (trace) { - _check_weight(sw_list); - } + SystemWeightList sw_list = _allocateWeight(date, se_list); + HKU_IF_RETURN(sw_list.size() == 0, delay_list); // 按权重降序排列 - sortSystemWeightListAsDesc(sw_list); + double can_allocate_weight = 1.0 - m_reserve_percent; + adjustWeight(sw_list, can_allocate_weight, getParam("filter_zero_weight")); + //----------------------------------------------------------------- + // 先将已不在 sw_list 中的运行系统进行清仓,回收可分配资金 + //----------------------------------------------------------------- + std::unordered_set 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("buy_delay")) { + delay_list.emplace_back(sys, MAX_DOUBLE); + } else { + // 非延迟卖出的系统,立即强制卖出并回收资金 + auto tr = sys->sellForce(date, MAX_DOUBLE, PART_ALLOCATEFUNDS); + if (!tr.isNull()) { + auto sub_tm = sys->getTM(); + auto sub_cash = sub_tm->currentCash(); + if (sub_tm->checkout(date, sub_cash)) { + m_shadow_tm->checkin(date, sub_cash); + m_tm->addTradeRecord(tr); // 向总账户加入交易记录 + } + } + } + } + } + + //----------------------------------------------------------------- + // 对于仍在选中系统中的运行系统,根据其权重进行减仓处理,回收可分配资金 + //----------------------------------------------------------------- // 获取当前总资产市值,计算需保留的资产 int precision = m_shadow_tm->getParam("precision"); - FundsRecord funds = m_tm->getFunds(date, m_query.kType()); + FundsRecord funds = m_tm->getFunds(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); - // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 - price_t current_cash = m_shadow_tm->cash(date, m_query.kType()); - price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision); - if (can_allocate_cash <= 0.0) { - HKU_WARN_IF( - trace, - "Insufficient available cash! can_allocate_cash: {}, current_cash: {}, reserve_funds: {}", - can_allocate_cash, current_cash, reserve_funds); - return result; + std::unordered_set 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(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("buy_delay")) { + delay_list.emplace_back(iter->sys, need_back_num); + } else { + auto tr = iter->sys->sellForce(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_shadow_tm->checkin(date, sub_cash); + m_tm->addTradeRecord(tr); // 向总账户加入交易记录 + } + } + } + } + } + } } + //----------------------------------------------------------------- + // 遍历当前选中系统,按指定权重分配资金 + //----------------------------------------------------------------- + // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 + price_t current_cash = m_shadow_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; // 累积已分配权重 + price_t sum_weight = 0.0; for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { - if (iter->weight <= 0.0 || sum_weight >= can_allocate_weight || can_allocate_cash <= 1.) { + if (sum_weight >= can_allocate_weight || can_allocate_cash < 1.0) { break; } @@ -288,88 +391,40 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( price_t will_funds = roundUp(total_funds * current_weight, precision); // 如果该系统是当前运行中系统 - if (running_set.count(iter->sys) != 0) { - // 获取当前系统已持有的资产 - TMPtr sub_tm = iter->sys->getTM(); + if (running_set.find(iter->sys) != running_set.cend()) { + auto sub_tm = iter->sys->getTM(); const KQuery& query = iter->sys->getTO().getQuery(); - FundsRecord funds = sub_tm->getFunds(query.kType()); - price_t current_funds = - funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value; + FundsRecord sub_funds = sub_tm->getFunds(query.kType()); + price_t sub_total_funds = sub_funds.cash + sub_funds.market_value + + sub_funds.borrow_asset - sub_funds.short_market_value; - // 如果预期资产和当前已持有的资产相等,则转到下个系统 - if (current_funds == will_funds) { - // can_allocate_cash 不需要补充资金,保持不变 - sum_weight += current_weight; - continue; - } - - // 如果预期资产和当前资产之差不够买入或卖出最小交易数量的证券,则转至下一系统 - Stock stock = iter->sys->getStock(); - const auto& krecord = iter->sys->getTO().getKRecord(date); - double min_num = stock.minTradeNumber(); - if (krecord.isValid() && - (std::abs(will_funds - current_funds) < krecord.closePrice * min_num)) { - // can_allocate_cash 无需补充资金,保持不变 - sum_weight += current_funds / total_funds; - continue; - } - - if (current_funds < will_funds) { - // 已有资产小于预期资产,则尝试分配新的资金 - price_t diff_price = roundUp(will_funds - current_funds, precision); - if (m_shadow_tm->checkout(date, diff_price)) { - sub_tm->checkin(date, diff_price); - // 更新剩余可分配现金及已分配累积权重 - can_allocate_cash = roundDown(can_allocate_cash - diff_price, precision); - sum_weight += (current_funds + diff_price) / total_funds; - } + // 如果是已经执行过减仓的系统 + if (reduced_running_set.find(iter->sys) != reduced_running_set.cend()) { + // 剩余可分配资金不变,已占用权重按实际权重累积 + sum_weight += sub_total_funds / total_funds; } else { - // 已有资产大于预期资产,尝试减仓 - price_t need_back_return = current_funds - will_funds; - double need_back_num = need_back_return / krecord.closePrice; - if (min_num > 1) { - need_back_num = static_cast(need_back_num / min_num) * min_num; - } - - // 如果是延迟买入的系统,则将减仓卖出记录加入返回的延迟清单,无法调仓 - if (iter->sys->getParam("buy_delay")) { - result.emplace_back(iter->sys, need_back_num); - // can_allocate_cash 当前可分配现金保存不变 - sum_weight += current_funds / total_funds; - + // 未执行过减仓的系统,需要予以相应资金分配 + if (sub_total_funds >= will_funds) { + sum_weight += sub_total_funds / total_funds; } else { - // 非延迟卖出的系统,立即强制卖出并回收资金 - auto tr = iter->sys->sellForce(date, need_back_num, PART_ALLOCATEFUNDS); - if (!tr.isNull()) { - auto sub_cash = sub_tm->currentCash(); - if (sub_tm->checkout(date, sub_cash)) { - m_shadow_tm->checkin(date, sub_cash); - m_tm->addTradeRecord(tr); // 向总账户加入交易记录 + 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; + } - // 更新剩余可用资金 = 原有 + 回收的 - can_allocate_cash = roundDown(can_allocate_cash + sub_cash, precision); - - // 更新当前总资产及累积权重 - funds = m_tm->getFunds(date, m_query.kType()); - total_funds = funds.cash + funds.market_value + funds.borrow_asset - - funds.short_market_value; - - FundsRecord sub_funds = sub_tm->getFunds(query.kType()); - sum_weight += (sub_funds.cash + sub_funds.market_value + - sub_funds.borrow_asset - sub_funds.short_market_value) / - total_funds; - } else { - HKU_DEBUG_IF(trace, "Failed to checkout {} cash from {}!", sub_cash, - iter->sys->name()); - } - } else { - HKU_DEBUG_IF("Failed to force sell num({}) from {}", iter->sys->name()); - // 强制卖出失败,更新当前累积权重 - FundsRecord sub_funds = sub_tm->getFunds(query.kType()); - sum_weight += (sub_funds.cash + sub_funds.market_value + - sub_funds.borrow_asset - sub_funds.short_market_value) / - total_funds; + if (m_shadow_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; } } } @@ -380,13 +435,14 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( price_t need_cash = will_funds <= can_allocate_cash ? will_funds : can_allocate_cash; // 如果期望的资金连一手都买不起,则跳过 - const auto& krecord = iter->sys->getTO().getKRecord(date); + 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_shadow_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); @@ -405,7 +461,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( } } - return result; + return delay_list; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 893f0c3e..34038b6f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -95,14 +95,13 @@ public: /** * 子类分配权重接口,获取实际分配资产的系统实例及其权重 - * @details 实际调用子类接口 _allocateWeight,并根据允许的最大持仓系统数参数对子类返回的 - * 系统实例及权重列表进行了截断处理 + * @details 实际调用子类接口 _allocateWeight * @param date 指定日期 * @param se_list 系统实例选择器选出的系统实例 - * @return + * @return 子类只需要返回每个系统的相对比例即可 */ - virtual SystemWeightList _allocateWeight(const Datetime& date, const SystemWeightList& se_list, - size_t running_count, double can_allocate_weight) = 0; + virtual SystemWeightList _allocateWeight(const Datetime& date, + const SystemWeightList& se_list) = 0; private: /* 同时调整已运行中的子系统(已分配资金或已持仓) */ @@ -135,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 @@ -144,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() @@ -180,14 +177,12 @@ private: \ #define ALLOCATEFUNDS_NO_PRIVATE_MEMBER_SERIALIZATION #endif -#define ALLOCATEFUNDS_IMP(classname) \ -public: \ - virtual AFPtr _clone() override { \ - return AFPtr(new classname()); \ - } \ - virtual SystemWeightList _allocateWeight(const Datetime&, const SystemWeightList&, \ - size_t running_count, double can_allocate_weight) \ - override; +#define ALLOCATEFUNDS_IMP(classname) \ +public: \ + virtual AFPtr _clone() override { \ + return AFPtr(new classname()); \ + } \ + virtual SystemWeightList _allocateWeight(const Datetime&, const SystemWeightList&) override; typedef shared_ptr AllocateFundsPtr; typedef shared_ptr AFPtr; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp index bab705ee..819b40a1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp @@ -18,11 +18,9 @@ EqualWeightAllocateFunds::EqualWeightAllocateFunds() : AllocateFundsBase("AF_Equ EqualWeightAllocateFunds::~EqualWeightAllocateFunds() {} SystemWeightList EqualWeightAllocateFunds ::_allocateWeight(const Datetime& date, - const SystemWeightList& se_list, - size_t running_count, - double can_allocate_weight) { + const SystemWeightList& se_list) { SystemWeightList result; - price_t weight = can_allocate_weight / 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->sys, weight); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp index 33247ad3..40c4b945 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp @@ -20,9 +20,7 @@ FixedWeightAllocateFunds::FixedWeightAllocateFunds() : AllocateFundsBase("AF_Fix FixedWeightAllocateFunds::~FixedWeightAllocateFunds() {} SystemWeightList FixedWeightAllocateFunds ::_allocateWeight(const Datetime& date, - const SystemWeightList& se_list, - size_t running_count, - double can_allocate_weight) { + const SystemWeightList& se_list) { SystemWeightList result; price_t weight = getParam("weight"); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 8031543f..6c203740 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -33,11 +33,13 @@ HKU_API std::ostream& operator<<(std::ostream& os, const PortfolioPtr& pf) { Portfolio::Portfolio() : m_name("Portfolio"), m_query(Null()), m_is_ready(false), m_need_calculate(true) { - setParam("trace", false); // 打印跟踪 + setParam("adjust_cycle", 1); // 调仓周期 + setParam("trace", false); // 打印跟踪 } Portfolio::Portfolio(const string& name) : m_name(name), m_query(Null()), m_is_ready(false), m_need_calculate(true) { + setParam("adjust_cycle", 1); // 调仓周期 setParam("trace", false); } @@ -49,6 +51,7 @@ Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFP m_query(Null()), m_is_ready(false), m_need_calculate(true) { + setParam("adjust_cycle", 1); // 调仓周期 setParam("trace", false); } @@ -159,20 +162,34 @@ bool Portfolio::_readyForRun() { return true; } -void Portfolio::run(const KQuery& query, bool force) { +void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) { + HKU_CHECK(adjust_cycle > 0, "Invalid param adjust_cycle! {}", adjust_cycle); + setParam("adjust_cycle", adjust_cycle); + 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; // 释放掉临时数据占用的内存 @@ -180,12 +197,13 @@ void Portfolio::run(const KQuery& query, bool force) { m_tmp_will_remove_sys = SystemWeightList(); } -void Portfolio::_runMoment(const Datetime& date) { +void Portfolio::_runMoment(const Datetime& date, bool adjust) { // 当前日期小于账户建立日期,直接忽略 HKU_IF_RETURN(date < m_shadow_tm->initDatetime(), void()); bool trace = getParam("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()); //--------------------------------------------------- @@ -204,13 +222,12 @@ void Portfolio::_runMoment(const Datetime& date) { // 开盘前,调整账户权息,并进行轧差处理 m_tm->updateWithWeight(date); - HKU_INFO_IF(trace, "The sum cash of sub_tm: {}, cash tm: {}, tm cash: {}", sum_cash, + HKU_INFO_IF(trace, "[PF] The sum cash of sub_tm: {}, cash tm: {}, tm cash: {}", sum_cash, m_shadow_tm->currentCash(), m_tm->currentCash()); sum_cash += m_shadow_tm->currentCash(); - price_t limit = m_tm->currentCash() * precision; price_t diff = roundEx(std::abs(m_tm->currentCash() - sum_cash), precision); - if (diff > limit) { + if (diff > 0.) { if (m_tm->currentCash() > sum_cash) { m_shadow_tm->checkin(date, diff); } else if (m_tm->currentCash() < sum_cash) { @@ -237,6 +254,9 @@ void Portfolio::_runMoment(const Datetime& date) { } } + // 清空,避免非调仓日重复处理 + m_delay_adjust_sys_list.clear(); + // 遍历当前运行中的子系统,如果已没有分配资金和持仓,则放入待移除列表 m_tmp_will_remove_sys.clear(); for (auto& running_sys : m_running_sys_set) { @@ -255,8 +275,8 @@ void Portfolio::_runMoment(const Datetime& date) { if (cash != 0 && cash <= min_cash) { sub_tm->checkout(date, cash); m_shadow_tm->checkin(date, cash); - HKU_INFO_IF(trace, "Collect the scraps cash ({:<.2f}) from {}", cash, - running_sys->name()); + HKU_INFO_IF(trace, "Collect the scraps cash ({:<.2f}) from {}, current cash: {}", cash, + running_sys->name(), m_shadow_tm->currentCash()); if (position.number == 0) { m_tmp_will_remove_sys.emplace_back(running_sys, 0.); } @@ -272,25 +292,25 @@ void Portfolio::_runMoment(const Datetime& date) { //--------------------------------------------------- // 收盘时处理 //--------------------------------------------------- + if (adjust) { + // 从选股策略获取选中的系统列表 + m_tmp_selected_list = m_se->getSelected(date); - // 从选股策略获取选中的系统列表 - 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}, cash: {}", sys.sys->name(), sys.weight, - sys.sys->getTM()->cash(date, m_query.kType())); + 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); + // 资产分配算法调整各子系统资产分配 + 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); + // 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行 + 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); + } } } } @@ -299,7 +319,7 @@ void Portfolio::_runMoment(const Datetime& date) { for (auto& sub_sys : m_running_sys_set) { auto tr = sub_sys->runMoment(date); if (!tr.isNull()) { - HKU_INFO_IF(trace, "[PF] on close: {}", tr); + HKU_INFO_IF(trace, "[PF] {}", tr); m_tm->addTradeRecord(tr); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index e3c625ce..4a998d0f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -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,7 +99,7 @@ private: /** 运行前准备 */ bool _readyForRun(); - void _runMoment(const Datetime& datetime); + void _runMoment(const Datetime& date, bool adjust); protected: string m_name; diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index f2c949be..5d3372b8 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -25,11 +25,10 @@ public: PYBIND11_OVERLOAD(void, AllocateFundsBase, _reset, ); } - SystemWeightList _allocateWeight(const Datetime& date, const SystemWeightList& se_list, - size_t running_count, double can_allocate_weight) override { + SystemWeightList _allocateWeight(const Datetime& date, + const SystemWeightList& se_list) override { PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, AllocateFundsBase, "_allocate_weight", - _allocateWeight, date, se_list, running_count, - can_allocate_weight); + _allocateWeight, date, se_list); } }; @@ -76,7 +75,7 @@ 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"), py::arg("running_count"), py::arg("can_allocate_weight"), + py::arg("se_list"), R"(_allocate_weight(self, date, se_list) 【重载接口】子类分配权重接口,获取实际分配资产的系统实例及其权重 diff --git a/hikyuu_pywrap/trade_sys/_Portfolio.cpp b/hikyuu_pywrap/trade_sys/_Portfolio.cpp index a9ece9fd..abdde05e 100644 --- a/hikyuu_pywrap/trade_sys/_Portfolio.cpp +++ b/hikyuu_pywrap/trade_sys/_Portfolio.cpp @@ -57,13 +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, force) + .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); diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 0a82015a..64d9bbfc 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -51,8 +51,7 @@ void export_Selector(py::module& m) { m, "SelectorBase", R"(选择器策略基类,实现标的、系统策略的评估和选取算法,自定义选择器策略子类接口: - - get_selected_on_open - 【必须】获取指定时刻开盘时选择的系统实例列表 - - get_selected_on_close - 【必须】获取指定时刻收盘时选择的系统实例列表 + - get_selected - 【必须】获取指定时刻选择的系统实例列表 - _calculate - 【必须】计算接口 - _reset - 【可选】重置私有属性 - _clone - 【必须】克隆接口)") From bf83d17635d2e2293f42a6f4cbae1ac1937c91cc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 21 Mar 2024 22:13:41 +0800 Subject: [PATCH 068/601] update --- .../trade_sys/allocatefunds/AllocateFundsBase.cpp | 8 ++++---- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index aef7f1a1..a7659333 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -213,7 +213,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 如果是运行中系统,不使用计算的权重,更新累积权重和 if (running_set.find(iter->sys) != running_set.cend()) { - FundsRecord sub_funds = m_tm->getFunds(m_query.kType()); + FundsRecord sub_funds = m_tm->getFunds(date, m_query.kType()); price_t sub_total_funds = funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value; sum_weight += sub_total_funds / total_funds; @@ -314,7 +314,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( //----------------------------------------------------------------- // 获取当前总资产市值,计算需保留的资产 int precision = m_shadow_tm->getParam("precision"); - FundsRecord funds = m_tm->getFunds(m_query.kType()); + 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); @@ -325,7 +325,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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(query.kType()); + 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; @@ -394,7 +394,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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(query.kType()); + 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; diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 6c203740..d71952db 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -243,6 +243,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { for (auto& sys : m_delay_adjust_sys_list) { auto tr = sys.sys->sellForce(date, sys.weight, PART_PORTFOLIO); if (!tr.isNull()) { + HKU_INFO_IF(trace, "[PF] Delay adjust sell: {}", tr); m_tm->addTradeRecord(tr); // 卖出后,尝试将资金取出转移至影子总账户 @@ -277,10 +278,18 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { m_shadow_tm->checkin(date, cash); HKU_INFO_IF(trace, "Collect the scraps cash ({:<.2f}) from {}, current cash: {}", cash, running_sys->name(), m_shadow_tm->currentCash()); - if (position.number == 0) { - m_tmp_will_remove_sys.emplace_back(running_sys, 0.); - } } + + 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()); + } + } + + if (trace) { + auto funds = m_tm->getFunds(date, m_query.kType()); + HKU_INFO("[PF] total funds: {}, cash: {}, market_value: {}", + funds.cash + funds.market_value, funds.cash, funds.market_value); } // 依据待移除列表将系统从运行中系统列表里删除 From 3478df506190390b54119b2bc4871b4ef25ac9f1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 22 Mar 2024 04:35:37 +0800 Subject: [PATCH 069/601] update --- .../hikyuu/trade_manage/TradeRecord.cpp | 63 ++------ .../allocatefunds/AllocateFundsBase.cpp | 32 ++-- .../allocatefunds/AllocateFundsBase.h | 20 +-- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 142 +++++++++++++----- .../hikyuu/trade_sys/portfolio/Portfolio.h | 6 +- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 60 ++------ hikyuu_cpp/hikyuu/trade_sys/system/System.h | 17 ++- 7 files changed, 175 insertions(+), 165 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp index 8833d834..a162e20e 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp @@ -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()) { - 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 { diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index a7659333..c40e3655 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -86,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; } @@ -189,7 +189,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, 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; - price_t can_allocate_cash = m_shadow_tm->currentCash(); // 可分配资金从资金账户中获取 + 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); } @@ -243,7 +243,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 尝试从总账户中取出资金存入子账户 TMPtr sub_tm = iter->sys->getTM(); - if (m_shadow_tm->checkout(date, need_cash)) { + if (m_cash_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); HKU_INFO_IF(trace, "[AF] ({}, {}, weight: {:<.4f}) fetched cash: {}", iter->sys->name(), iter->sys->getStock().market_code(), current_weight, need_cash); @@ -254,7 +254,7 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, } else { HKU_DEBUG_IF(trace, "[AF] {} failed to fetch cash from total account ({})!", - iter->sys->name(), m_shadow_tm->currentCash()); + iter->sys->name(), m_cash_tm->currentCash()); } } } @@ -296,13 +296,16 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( delay_list.emplace_back(sys, MAX_DOUBLE); } else { // 非延迟卖出的系统,立即强制卖出并回收资金 - auto tr = sys->sellForce(date, MAX_DOUBLE, PART_ALLOCATEFUNDS); + 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_shadow_tm->checkin(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); } } } @@ -313,7 +316,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( // 对于仍在选中系统中的运行系统,根据其权重进行减仓处理,回收可分配资金 //----------------------------------------------------------------- // 获取当前总资产市值,计算需保留的资产 - int precision = m_shadow_tm->getParam("precision"); + int precision = m_cash_tm->getParam("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; @@ -345,12 +348,13 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( if (iter->sys->getParam("buy_delay")) { delay_list.emplace_back(iter->sys, need_back_num); } else { - auto tr = iter->sys->sellForce(date, need_back_num, PART_ALLOCATEFUNDS); + 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_shadow_tm->checkin(date, sub_cash); + m_cash_tm->checkin(date, sub_cash); m_tm->addTradeRecord(tr); // 向总账户加入交易记录 } } @@ -364,7 +368,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( // 遍历当前选中系统,按指定权重分配资金 //----------------------------------------------------------------- // 计算可用于分配的现金, 小于等于需保留的资产,则直接返回 - price_t current_cash = m_shadow_tm->currentCash(); + price_t current_cash = m_cash_tm->currentCash(); price_t can_allocate_cash = roundDown(current_cash - reserve_funds, precision); HKU_INFO_IF(trace, @@ -420,7 +424,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( continue; } - if (m_shadow_tm->checkout(date, need_cash)) { + if (m_cash_tm->checkout(date, need_cash)) { sub_tm->checkin(date, need_cash); can_allocate_cash = roundDown(can_allocate_cash - need_cash, precision); // 更新已分配的累积权重 @@ -444,7 +448,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( // 尝试从资金账户中取出资金存入子账户 TMPtr sub_tm = iter->sys->getTM(); - if (m_shadow_tm->checkout(date, need_cash)) { + 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); diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 34038b6f..60f3cb5b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -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; @@ -116,10 +116,10 @@ private: 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; // 保留资产比例,不参与资产分配 //============================================ @@ -206,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 { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index d71952db..09ae0020 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -67,8 +67,8 @@ void Portfolio::reset() { 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) @@ -90,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; } @@ -120,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); @@ -199,7 +199,7 @@ void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) { void Portfolio::_runMoment(const Datetime& date, bool adjust) { // 当前日期小于账户建立日期,直接忽略 - HKU_IF_RETURN(date < m_shadow_tm->initDatetime(), void()); + HKU_IF_RETURN(date < m_cash_tm->initDatetime(), void()); bool trace = getParam("trace"); HKU_INFO_IF(trace, "{} ===========================================================", date); @@ -207,7 +207,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_set.size()); //--------------------------------------------------- - // 开盘前处理 + // 开盘前处理各个子账户、资金账户、总账户之间可能的误差 //--------------------------------------------------- int precision = m_tm->getParam("precision"); @@ -219,29 +219,41 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { 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_shadow_tm->currentCash(), m_tm->currentCash()); - sum_cash += m_shadow_tm->currentCash(); + 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_shadow_tm->checkin(date, diff); + m_cash_tm->checkin(date, diff); } else if (m_tm->currentCash() < sum_cash) { - if (!m_shadow_tm->checkout(date, diff)) { + 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_shadow_tm->currentCash(), m_tm->currentCash()); + 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->sellForce(date, sys.weight, PART_PORTFOLIO); + 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); @@ -250,15 +262,17 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { TMPtr sub_tm = sys.sys->getTM(); auto sub_cash = sub_tm->currentCash(); if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) { - m_shadow_tm->checkin(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(); @@ -275,21 +289,15 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { // 如果系统的剩余资金小于交易一手的资金,则回收资金 if (cash != 0 && cash <= min_cash) { sub_tm->checkout(date, cash); - m_shadow_tm->checkin(date, cash); - HKU_INFO_IF(trace, "Collect the scraps cash ({:<.2f}) from {}, current cash: {}", cash, - running_sys->name(), m_shadow_tm->currentCash()); + 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()); + } } - - 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()); - } - } - - if (trace) { - auto funds = m_tm->getFunds(date, m_query.kType()); - HKU_INFO("[PF] total funds: {}, cash: {}, market_value: {}", - funds.cash + funds.market_value, funds.cash, funds.market_value); } // 依据待移除列表将系统从运行中系统列表里删除 @@ -299,7 +307,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { } //--------------------------------------------------- - // 收盘时处理 + // 调仓日,进行资金分配调整 //--------------------------------------------------- if (adjust) { // 从选股策略获取选中的系统列表 @@ -322,9 +330,34 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { } } } + + // 从已运行系统列表中立即移除已没有持仓且没有资金的非延迟买入的系统 + m_tmp_will_remove_sys.clear(); + for (auto& sys : m_running_sys_set) { + auto sub_tm = sys->getTM(); + if (!sys->getParam("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()) { @@ -332,6 +365,45 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { 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) { + HKU_INFO("+------------+------------+------------+--------------+--------------+"); + HKU_INFO("| code | name | position | market value | remain cash |"); + HKU_INFO("+------------+------------+------------+--------------+--------------+"); + size_t count = 0; + for (const auto& sys : m_running_sys_set) { + Stock stk = sys->getStock(); + 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(' '); + } + } + auto funds = sys->getTM()->getFunds(date, m_query.kType()); + size_t position = sys->getTM()->getHoldNumber(date, stk); + HKU_INFO("| {:<11}| {}| {:<11}| {:<13.2f}| {:<13.2f}|", stk.market_code(), stk_name, + position, funds.market_value, funds.cash); + HKU_INFO("+------------+------------+------------+--------------+--------------+"); + if (++count >= 10) { + break; + } + } + } } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index 4a998d0f..faa7bd48 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -104,7 +104,7 @@ private: protected: string m_name; TMPtr m_tm; - TMPtr m_shadow_tm; // 仅仅负责内部资金的管理(即只需要 checkout 到子账号, 从账户checkin现金) + TMPtr m_cash_tm; // 仅仅负责内部资金的管理(即只需要 checkout 到子账号, 从账户checkin现金) SEPtr m_se; AFPtr m_af; @@ -132,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); @@ -145,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); diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 720cc35a..d90c6f43 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -671,58 +671,20 @@ 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("sell_delay")) { - if (m_sellRequest.valid) { - if (m_sellRequest.count > getParam("max_delay_count")) { - // 超出最大延迟次数,清除买入请求 - m_sellRequest.clear(); - return result; - } - m_sellRequest.count++; - - } else { - m_sellRequest.valid = true; - m_sellRequest.business = BUSINESS_SELL; - m_sellRequest.count = 1; - } - - 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; - - } 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; - } -} - -TradeRecord System::sellForce(const Datetime& date, double num, Part from) { - HKU_ASSERT(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO); - +TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool on_open) { TradeRecord record; size_t pos = m_kdata.getPos(date); - HKU_IF_RETURN(pos == Null(), record); + HKU_TRACE_IF_RETURN(pos == Null(), record, + "Failed to sellForce {}, the day {} could'nt sell!", m_stock.market_code(), + date); 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(date, m_stock); - price_t realPrice = _getRealSellPrice(krecord.datetime, src_krecord.openPrice); + price_t realPrice = + _getRealSellPrice(krecord.datetime, on_open ? src_krecord.openPrice : src_krecord.closePrice); double min_num = m_stock.minTradeNumber(); double real_sell_num = num; @@ -731,14 +693,10 @@ TradeRecord System::sellForce(const Datetime& date, double num, Part from) { } else if (min_num > 1) { real_sell_num = static_cast(num / min_num) * min_num; } - // double real_sell_num = min_num > 1 ? static_cast(num / min_num) * min_num : num; - // if ((position.number - real_sell_num) <= min_num) { - // real_sell_num = position.number; - // } - // 以开盘价卖出 - record = m_tm->sell(date, m_stock, realPrice, real_sell_num, position.stoploss, - position.goalPrice, src_krecord.openPrice, from); + 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; diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index 495c66ba..ca3c8d57 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -211,11 +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); + } - // Portfolio 指示开盘时立即进行强制卖出,以便对 buy_delay 的系统进行资金调整 - TradeRecord sellForce(const Datetime& date, double num, Part from); + // 强制以收盘价卖出,仅供 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); @@ -267,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; From 651e517ee8f026170cf717eaf3cac31bc7fa111a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 22 Mar 2024 15:50:04 +0800 Subject: [PATCH 070/601] update --- hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp | 6 ++++-- .../hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp | 4 ++-- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 6 +++++- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 37779d46..35eb3a86 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -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 diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index c40e3655..a0dc5b1e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -214,8 +214,8 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, // 如果是运行中系统,不使用计算的权重,更新累积权重和 if (running_set.find(iter->sys) != running_set.cend()) { FundsRecord sub_funds = m_tm->getFunds(date, m_query.kType()); - price_t sub_total_funds = - funds.cash + funds.market_value + funds.borrow_asset - funds.short_market_value; + 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; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 09ae0020..19b65611 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -385,6 +385,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { size_t count = 0; for (const auto& sys : m_running_sys_set) { Stock stk = sys->getStock(); +#if HKU_OS_WINDOWS auto stk_name = StockManager::instance().runningInPython() && StockManager::instance().pythonInJupyter() ? stk.name() @@ -394,9 +395,12 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { stk_name.push_back(' '); } } +#else + auto stk_name = stk.name(); +#endif auto funds = sys->getTM()->getFunds(date, m_query.kType()); size_t position = sys->getTM()->getHoldNumber(date, stk); - HKU_INFO("| {:<11}| {}| {:<11}| {:<13.2f}| {:<13.2f}|", stk.market_code(), stk_name, + HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}|", stk.market_code(), stk_name, position, funds.market_value, funds.cash); HKU_INFO("+------------+------------+------------+--------------+--------------+"); if (++count >= 10) { From a6a0caaa23d3754618d64dfcb90f4959771e7e88 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 23 Mar 2024 03:32:19 +0800 Subject: [PATCH 071/601] =?UTF-8?q?=E8=B0=83=E6=95=B4MA=E5=A4=84=E7=90=86n?= =?UTF-8?q?an,=20=E4=BF=AE=E6=AD=A3IC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 6 --- hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp | 18 +++++--- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 45 ++++--------------- .../trade_sys/factor/imp/ICMultiFactor.cpp | 42 +++-------------- .../unit_test/hikyuu/indicator/test_IC.cpp | 3 +- .../trade_sys/factor/test_MF_ICIRWeight.cpp | 10 +++-- .../trade_sys/factor/test_MF_ICWeight.cpp | 9 ++-- 7 files changed, 39 insertions(+), 94 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 0bf1f1b7..4af2486e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -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; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp index d12558e3..30e1c9d4 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp @@ -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; + } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index cc776a26..da796983 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -45,50 +45,21 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i } } - // 计算 IC 权重 - vector sumByDate(days_total, 0.0); - vector 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() : sumByDate[di] / countByDate[di]; - } - - vector all_factors(stk_count); - PriceList new_values(days_total); + // 以 ICIR 为权重,计算加权后的合成因子 + IndicatorList all_factors(stk_count); + PriceList new_values(days_total, 0); for (size_t si = 0; si < stk_count; si++) { memset(new_values.data(), 0, sizeof(price_t) * days_total); for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - const auto& value = all_stk_inds[si][ii][di]; - new_values[di] += value * sumByDate[di]; + if (!std::isnan(all_stk_inds[si][ii][di]) && !std::isnan(icir[ii][di])) { + new_values[di] += all_stk_inds[si][ii][di] * icir[ii][di]; + } } } - for (size_t di = discard; di < days_total; di++) { - new_values[di] = - (countByDate[di] == 0) ? Null() : new_values[di] / countByDate[di]; - } all_factors[si] = PRICELIST(new_values); - all_factors[si].name("IC"); - - // 更新 discard - for (size_t di = discard; di < days_total; di++) { - if (!std::isnan(all_factors[si][di])) { - all_factors[si].setDiscard(di); - break; - } - if (di == days_total - 1 && std::isnan(all_factors[si][di])) { - all_factors[si].setDiscard(di); - } - } + all_factors[si].name("ICIR"); + all_factors[si].setDiscard(discard); } return all_factors; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index 0ec83a9d..5e5b3590 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -35,7 +35,7 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind int ic_n = getParam("ic_n"); int ic_rolling_n = getParam("ic_rolling_n"); - // 计算每个原始因子的IC值 + // 计算每个原始因子的滚动IC值 size_t discard = 0; IndicatorList ic(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { @@ -45,51 +45,21 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind } } - // 计算每个原始因子的滚动 IC 权重 - vector sumByDate(days_total, 0.0); // 累积合、权重均值 - vector 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() : sumByDate[di] / countByDate[di]; - } - - // 对每支证券计算根据滚动 IC 权重计算合成因子 + // 以滚动 IC 为权重,计算加权后的合成因子 IndicatorList all_factors(stk_count); PriceList new_values(days_total, 0); for (size_t si = 0; si < stk_count; si++) { memset(new_values.data(), 0, sizeof(price_t) * days_total); for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - const auto& value = all_stk_inds[si][ii][di]; - new_values[di] += value * sumByDate[di]; + if (!std::isnan(all_stk_inds[si][ii][di]) && !std::isnan(ic[ii][di])) { + new_values[di] += all_stk_inds[si][ii][di] * ic[ii][di]; + } } } - for (size_t di = discard; di < days_total; di++) { - new_values[di] = - (countByDate[di] == 0) ? Null() : new_values[di] / countByDate[di]; - } all_factors[si] = PRICELIST(new_values); all_factors[si].name("IC"); - - // 更新 discard - for (size_t di = discard; di < days_total; di++) { - if (!std::isnan(all_factors[si][di])) { - all_factors[si].setDiscard(di); - break; - } - if (di == days_total - 1 && std::isnan(all_factors[si][di])) { - all_factors[si].setDiscard(di); - } - } + all_factors[si].setDiscard(discard); } return all_factors; diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index 9b53fb51..7c84fd20 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -84,9 +84,8 @@ TEST_CASE("test_IC") { 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)); } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index 03d39b53..e2c86335 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -53,13 +53,15 @@ TEST_CASE("test_MF_ICIRWeight") { for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } - CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); - for (size_t i = 0; i < ind4.discard(); i++) { + CHECK_EQ(4, std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); + for (size_t i = 0; i < 4; i++) { CHECK_UNARY(std::isnan(ind4[i])); } for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { - Indicator::value_t w = (ic1[i] + ic2[i] + ic3[i]) / 3.0; - CHECK_EQ(ind4[i], doctest::Approx((ind1[i] * w + ind2[i] * w + ind3[i] * w) / 3.0)); + Indicator::value_t w = ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]; + if (!std::isnan(ind4[i]) && !std::isnan(w)) { + CHECK_EQ(ind4[i], doctest::Approx(w)); + } } } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp index d3dac126..65378632 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include using namespace hku; @@ -52,13 +53,15 @@ TEST_CASE("test_MF_ICWeight") { for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } - CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); + CHECK_EQ(ndays, std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { - Indicator::value_t w = (ic1[i] + ic2[i] + ic3[i]) / 3.0; - CHECK_EQ(ind4[i], doctest::Approx((ind1[i] * w + ind2[i] * w + ind3[i] * w) / 3.0)); + Indicator::value_t w = ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]; + if (!std::isnan(ind4[i]) && !std::isnan(w)) { + CHECK_EQ(ind4[i], doctest::Approx(w)); + } } } From 2754ea87eed012bf2bd7780418561a72e9485c45 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 23 Mar 2024 05:00:02 +0800 Subject: [PATCH 072/601] update --- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 7 +++ hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp | 52 +++++++++++++------ .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 19 ++++++- .../trade_sys/factor/imp/ICMultiFactor.cpp | 27 ++++++++-- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 28 ++++++---- .../trade_sys/factor/test_MF_ICIRWeight.cpp | 6 +++ .../trade_sys/factor/test_MF_ICWeight.cpp | 7 ++- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 4 +- 8 files changed, 115 insertions(+), 35 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 4af2486e..18a4d817 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -101,6 +101,13 @@ 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) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp index 73a8b21a..878fb0ec 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp @@ -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值 diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index da796983..20a04612 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -47,19 +47,34 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i // 以 ICIR 为权重,计算加权后的合成因子 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); for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { if (!std::isnan(all_stk_inds[si][ii][di]) && !std::isnan(icir[ii][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++) { + 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("ICIR"); - all_factors[si].setDiscard(discard); + + 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); + } + } } return all_factors; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index 5e5b3590..8c20bad6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -47,19 +47,36 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind // 以滚动 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(); + } for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - if (!std::isnan(all_stk_inds[si][ii][di]) && !std::isnan(ic[ii][di])) { - new_values[di] += all_stk_inds[si][ii][di] * ic[ii][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++) { + 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"); - all_factors[si].setDiscard(discard); + + 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); + } + } } return all_factors; diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 19b65611..6a8525ff 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -378,13 +378,19 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { //---------------------------------------------------------------------- // 跟踪打印持仓情况 //---------------------------------------------------------------------- - if (trace) { - HKU_INFO("+------------+------------+------------+--------------+--------------+"); - HKU_INFO("| code | name | position | market value | remain cash |"); - HKU_INFO("+------------+------------+------------+--------------+--------------+"); + 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() @@ -395,14 +401,18 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { 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 - auto funds = sys->getTM()->getFunds(date, m_query.kType()); - size_t position = sys->getTM()->getHoldNumber(date, stk); - HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}|", stk.market_code(), stk_name, - position, funds.market_value, funds.cash); - HKU_INFO("+------------+------------+------------+--------------+--------------+"); + HKU_INFO( + "+------------+------------+------------+--------------+--------------+-------------" + "+"); if (++count >= 10) { break; } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index e2c86335..905d88aa 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include using namespace hku; @@ -44,6 +45,8 @@ 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 ma_ic1 = MA(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); + auto stdev_ic1 = STDEV(ma_ic1, ic_rolling_n); auto ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays)); auto ic2 = ICIR(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays)); @@ -59,10 +62,13 @@ TEST_CASE("test_MF_ICIRWeight") { } for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { Indicator::value_t w = ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]; + HKU_INFO("{}: {}, {}, {}, {}, {}", i, w, ind1[i], ma_ic1[i], ma_ic1.discard(), + stdev_ic1[i]); if (!std::isnan(ind4[i]) && !std::isnan(w)) { CHECK_EQ(ind4[i], doctest::Approx(w)); } } + HKU_INFO("{}", ind4); } //----------------------------------------------------------------------------- diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp index 65378632..d8c6ec41 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -53,16 +53,19 @@ TEST_CASE("test_MF_ICWeight") { for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } - CHECK_EQ(ndays, std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); + CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { - Indicator::value_t w = ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]; + Indicator::value_t w = (ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]) / + std::abs(ic1[i] + ic2[i] + ic3[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); } //----------------------------------------------------------------------------- diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 0bfbde41..c3541da9 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -170,7 +170,7 @@ void export_MultiFactor(py::module& m) { // MF_EqualWeight IndicatorList c_inds = python_list_to_vector(inds); StockList c_stks = python_list_to_vector(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(inds); StockList c_stks = python_list_to_vector(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, From e9c3cdb5a9c74a98076ff928a1c5d19c3643b4fc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 23 Mar 2024 05:27:33 +0800 Subject: [PATCH 073/601] update --- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 10 ++++++---- .../trade_sys/factor/test_MF_ICIRWeight.cpp | 15 ++++++++------- .../hikyuu/trade_sys/factor/test_MF_ICWeight.cpp | 10 ++++------ 3 files changed, 18 insertions(+), 17 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index 20a04612..07026b5a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -51,12 +51,14 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i 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(); + } for (size_t di = discard; di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - if (!std::isnan(all_stk_inds[si][ii][di]) && !std::isnan(icir[ii][di])) { - new_values[di] += all_stk_inds[si][ii][di] * icir[ii][di]; - sum_weight[di] += std::abs(icir[ii][di]); - } + new_values[di] += all_stk_inds[si][ii][di] * icir[ii][di]; + sum_weight[di] += std::abs(icir[ii][di]); } } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index 905d88aa..6d76199b 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -46,7 +46,7 @@ TEST_CASE("test_MF_ICIRWeight") { 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 ma_ic1 = MA(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); - auto stdev_ic1 = STDEV(ma_ic1, ic_rolling_n); + auto stdev_ic1 = STDEV(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); auto ind2 = AMA(ROCR(CLOSE(stk.getKData(query)), ndays)); auto ic2 = ICIR(IC(AMA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); auto ind3 = EMA(ROCR(CLOSE(stk.getKData(query)), ndays)); @@ -56,19 +56,20 @@ TEST_CASE("test_MF_ICIRWeight") { for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } - CHECK_EQ(4, std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); - for (size_t i = 0; i < 4; i++) { + CHECK_EQ(ind4.discard(), std::max(ic1.discard(), std::max(ic2.discard(), ic3.discard()))); + for (size_t i = 0; i < ind4.discard(); i++) { CHECK_UNARY(std::isnan(ind4[i])); } for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { - Indicator::value_t w = ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]; - HKU_INFO("{}: {}, {}, {}, {}, {}", i, w, ind1[i], ma_ic1[i], ma_ic1.discard(), - stdev_ic1[i]); + 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); + // HKU_INFO("{}", ind4); } //----------------------------------------------------------------------------- diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp index d8c6ec41..5adf103d 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -59,13 +59,11 @@ TEST_CASE("test_MF_ICWeight") { } for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { Indicator::value_t w = (ind1[i] * ic1[i] + ind2[i] * ic2[i] + ind3[i] * ic3[i]) / - std::abs(ic1[i] + ic2[i] + ic3[i]); - HKU_INFO("{}: {}, {}", i, w, ind4[i]); - if (!std::isnan(ind4[i]) && !std::isnan(w)) { - CHECK_EQ(ind4[i], doctest::Approx(w)); - } + (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); + // HKU_INFO("{}", ind4); } //----------------------------------------------------------------------------- From cef961d1469f9a59d9b890bd931c5b9e26a4eec9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 23 Mar 2024 05:34:27 +0800 Subject: [PATCH 074/601] update --- .../unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index 6d76199b..fb45c641 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -63,7 +63,7 @@ TEST_CASE("test_MF_ICIRWeight") { for (size_t i = ind4.discard(), len = ref_dates.size(); i < len; i++) { 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], 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)); From 204ca376ced4a177c99e2f00ff988a83f7521dee Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 23 Mar 2024 16:09:38 +0800 Subject: [PATCH 075/601] update --- docs/source/indicator/indicator.rst | 15 ++++--- hikyuu_cpp/hikyuu/indicator/crt/IC.h | 22 ++++----- hikyuu_cpp/hikyuu/indicator/crt/ICIR.h | 28 +++++++++--- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 6 +-- .../trade_sys/factor/MultiFactorBase.cpp | 7 ++- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 2 +- .../trade_sys/factor/imp/ICMultiFactor.cpp | 2 +- .../unit_test/hikyuu/indicator/test_IC.cpp | 20 ++++----- .../trade_sys/factor/test_MF_EqualWeight.cpp | 2 +- .../trade_sys/factor/test_MF_ICIRWeight.cpp | 8 ++-- .../trade_sys/factor/test_MF_ICWeight.cpp | 6 +-- hikyuu_pywrap/indicator/_build_in.cpp | 45 ++++++++++++++----- 12 files changed, 104 insertions(+), 59 deletions(-) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 67109377..702112e0 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -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 diff --git a/hikyuu_cpp/hikyuu/indicator/crt/IC.h b/hikyuu_cpp/hikyuu/indicator/crt/IC.h index 3b0c7cae..5113e50f 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/IC.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/IC.h @@ -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 \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h index fb71d304..5055ff26 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h @@ -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("n", n); + x.setParam("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("n", n); + x.setParam("rolling_n", rolling_n); return x; } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 18a4d817..ea0c0f16 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -110,13 +110,13 @@ void IIc::_calculate(const Indicator& inputInd) { } } -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(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 \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index fd8e052f..4a39c3bf 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -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("n", ic_n); + x.setParam("rolling_n", ir_n); + return x; } IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index 07026b5a..ec05bba2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -39,7 +39,7 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i size_t discard = 0; vector 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(); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index 8c20bad6..e7b0b583 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -39,7 +39,7 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind 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(); } diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index 7c84fd20..12574db0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -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 长度为2,query 的长度为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,7 +80,7 @@ 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()); @@ -107,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); } } } @@ -127,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); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 6ab83539..6b137f9c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -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); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index fb45c641..1b8bcf2a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -44,13 +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 ma_ic1 = MA(IC(MA(ROCR(CLOSE(), ndays)), stks, query, ndays, ref_stk), ic_rolling_n); - auto stdev_ic1 = STDEV(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++) { diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp index 5adf103d..32da6b91 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -43,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++) { diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index c50233d7..b3d45763 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -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(stks)) { const auto& blk = stks.cast(); - return IC(ind, blk, query, n, ref_stk); + return IC(ind, blk, query, ref_stk, n); } if (py::isinstance(stks)) { StockList c_stks = python_list_to_vector(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(stks)) { + const auto& blk = stks.cast(); + return ICIR(ind, blk, query, ref_stk, n, rolling_n); + } + + if (py::isinstance(stks)) { + StockList c_stks = python_list_to_vector(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); From 255f13c0e96f70664f36526fb6c76129599eb7d1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 23 Mar 2024 18:23:52 +0800 Subject: [PATCH 076/601] =?UTF-8?q?MF=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp | 10 ++++++---- .../hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp | 10 ++++++---- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index ec05bba2..275b2063 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -55,10 +55,12 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i for (size_t di = 0; di < discard; di++) { new_values[di] = Null(); } - for (size_t di = discard; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - new_values[di] += all_stk_inds[si][ii][di] * icir[ii][di]; - sum_weight[di] += std::abs(icir[ii][di]); + for (size_t ii = 0; ii < ind_count; ii++) { + const auto *ind_data = all_stk_inds[si][ii].data(); + const auto *icir_data = icir[ii].data(); + for (size_t di = discard; di < days_total; di++) { + new_values[di] += ind_data[di] * icir_data[di]; + sum_weight[di] += std::abs(icir_data[di]); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index e7b0b583..a8f17195 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -55,10 +55,12 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind for (size_t di = 0; di < discard; di++) { new_values[di] = Null(); } - for (size_t di = discard; di < days_total; di++) { - for (size_t ii = 0; ii < ind_count; ii++) { - new_values[di] += all_stk_inds[si][ii][di] * ic[ii][di]; - sum_weight[di] += std::abs(ic[ii][di]); + for (size_t ii = 0; ii < ind_count; ii++) { + const auto* ind_data = all_stk_inds[si][ii].data(); + const auto* ic_data = ic[ii].data(); + for (size_t di = discard; di < days_total; di++) { + new_values[di] += ind_data[di] * ic_data[di]; + sum_weight[di] += std::abs(ic_data[di]); } } From 245b2520e4033e9b269c5850367e491af935b067 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 24 Mar 2024 02:23:21 +0800 Subject: [PATCH 077/601] fixed ZSCORE --- hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp | 59 +++++++++++++------ .../hikyuu/indicator/test_ZSCORE.cpp | 20 +++++++ 2 files changed, 61 insertions(+), 18 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp index 7f9a9b06..ddb417f6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp @@ -34,38 +34,54 @@ bool IZScore::check() { static void normalize(IndicatorImp::value_t *dst, Indicator::value_t const *src, size_t total, bool outExtreme, double nsigma, bool recursive) { IndicatorImp::value_t sum = 0.0; + size_t count = 0; for (size_t i = 0; i < total; i++) { - sum += src[i]; + if (!std::isnan(src[i])) { + sum += src[i]; + count++; + } } - IndicatorImp::value_t mean = sum / total; - vector tmp(total); + HKU_IF_RETURN(count <= 1, void()); + + IndicatorImp::value_t mean = sum / count; + + vector tmp(total, Null()); sum = 0.0; for (size_t i = 0; i < total; i++) { - tmp[i] = src[i] - mean; - sum += tmp[i] * tmp[i]; - } - IndicatorImp::value_t sigma = std::sqrt(sum / (total - 1)); - for (size_t i = 0; i < total; i++) { - dst[i] = (src[i] - mean) / sigma; + if (!std::isnan(src[i])) { + tmp[i] = src[i] - mean; + sum += tmp[i] * tmp[i]; + } } + IndicatorImp::value_t sigma = std::sqrt(sum / (count - 1)); + for (size_t i = 0; i < total; i++) { + if (!std::isnan(src[i])) { + dst[i] = (src[i] - mean) / sigma; + } + } + + if (outExtreme) { - IndicatorImp::value_t ulimit = mean + nsigma * sigma; - IndicatorImp::value_t llimit = mean - nsigma * sigma; + IndicatorImp::value_t ulimit = nsigma; + IndicatorImp::value_t llimit = -nsigma; + bool found = false; for (size_t i = 0; i < total; i++) { - if (dst[i] > ulimit) { - dst[i] = ulimit; - found = true; - } else if (dst[i] < llimit) { - dst[i] = llimit; - found = true; + if (!std::isnan(dst[i])) { + if (dst[i] > ulimit) { + dst[i] = ulimit; + found = true; + } else if (dst[i] < llimit) { + dst[i] = llimit; + found = true; + } } } if (found && recursive) { - normalize(dst, src, total, outExtreme, nsigma, recursive); + normalize(dst, dst, total, outExtreme, nsigma, recursive); } } } @@ -84,6 +100,13 @@ void IZScore::_calculate(const Indicator &data) { auto const *src = data.data() + m_discard; auto *dst = this->data() + m_discard; normalize(dst, src, total - m_discard, outExtreme, nsigma, recursive); + + for (size_t i = m_discard; i < total; i++) { + if (!std::isnan(dst[i])) { + m_discard = i; + break; + } + } } Indicator HKU_API ZSCORE(bool outExtreme, double nsigma, bool recursive) { diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp index d8267856..d6814b27 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp @@ -50,6 +50,26 @@ TEST_CASE("test_ZSCORE") { for (size_t i = result.discard(), total = result.size(); i < total; i++) { CHECK_EQ(result[i], doctest::Approx(expect[i])); } + + /** @arg 过滤异常值,不递归*/ + k = getKData("SH000001", KQuery(3600, 4000)); + Indicator c = k.close(); + result = ZSCORE(c, true, 3.0, false); + expect = {-0.87128, -0.90351, -0.87397, -0.87383}; + for (size_t i = 0; i < expect.size(); i++) { + CHECK_EQ(result[i], doctest::Approx(expect[i])); + } + + /** @arg 过滤异常值,递归*/ + k = getKData("SH000001", KQuery(3600, 4000)); + c = k.close(); + auto result2 = ZSCORE(c, true, 3.0, true); + expect = {-0.87128, -0.90354, -0.87399, -0.87383}; + for (size_t i = 0; i < expect.size(); i++) { + CHECK_EQ(result2[i], doctest::Approx(expect[i])); + } + CHECK_EQ(result[16], doctest::Approx(-0.95994)); + CHECK_EQ(result2[16], doctest::Approx(-0.95996)); } //----------------------------------------------------------------------------- From 1c29630d0fc72e23a5791660a5cb6d8bcc7888f4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 24 Mar 2024 03:21:48 +0800 Subject: [PATCH 078/601] =?UTF-8?q?MF=E5=A2=9E=E5=8A=A0=E5=BD=92=E4=B8=80?= =?UTF-8?q?=E4=B8=8E=E6=A0=87=E5=87=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/factor/MultiFactorBase.cpp | 87 +++++++++++++++++-- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 9 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 + 3 files changed, 91 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 4a39c3bf..54d5283f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -13,6 +13,7 @@ #include "hikyuu/indicator/crt/IC.h" #include "hikyuu/indicator/crt/ICIR.h" #include "hikyuu/indicator/crt/SPEARMAN.h" +#include "hikyuu/indicator/crt/ZSCORE.h" #include "MultiFactorBase.h" namespace hku { @@ -61,11 +62,21 @@ HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorPtr& mf) { MultiFactorBase::MultiFactorBase() { setParam("fill_null", true); setParam("ic_n", 1); + setParam("enable_min_max_normalize", false); + setParam("enable_zscore", false); + setParam("zscore_out_extreme", false); + setParam("zscore_recursive", false); + setParam("zscore_nsigma", 3.0); } MultiFactorBase::MultiFactorBase(const string& name) { setParam("fill_null", true); setParam("ic_n", 1); + setParam("enable_min_max_normalize", false); + setParam("enable_zscore", false); + setParam("zscore_out_extreme", false); + setParam("zscore_recursive", false); + setParam("zscore_nsigma", 3.0); } MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stks, @@ -74,6 +85,11 @@ MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stk : m_name(name), m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { setParam("fill_null", true); setParam("ic_n", ic_n); + setParam("enable_min_max_normalize", false); + setParam("enable_zscore", false); + setParam("zscore_out_extreme", false); + setParam("zscore_recursive", false); + setParam("zscore_nsigma", 3.0); HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); @@ -262,13 +278,14 @@ IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { return all_returns; } -vector MultiFactorBase::_alignAllInds() { - bool fill_null = getParam("fill_null"); - size_t stk_count = m_stks.size(); - size_t ind_count = m_inds.size(); - +vector MultiFactorBase::getAllSrcFactors() { vector all_stk_inds; + size_t stk_count = m_stks.size(); + HKU_IF_RETURN(stk_count == 0, all_stk_inds); all_stk_inds.resize(stk_count); + + bool fill_null = getParam("fill_null"); + size_t ind_count = m_inds.size(); for (size_t i = 0; i < stk_count; i++) { const auto& stk = m_stks[i]; auto kdata = stk.getKData(m_query); @@ -280,6 +297,64 @@ vector MultiFactorBase::_alignAllInds() { } } + // 每日截面归一化 + if (getParam("enable_min_max_normalize")) { + vector one_day(stk_count, Null()); + for (size_t di = 0, days_total = m_ref_dates.size(); di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto* one_day_data = one_day.data(); + Indicator::value_t min_value = std::numeric_limits::max(); + Indicator::value_t max_value = std::numeric_limits::min(); + for (size_t si = 0; si < stk_count; si++) { + auto value = all_stk_inds[si][ii][di]; + if (!std::isnan(value)) { + if (value > max_value) { + max_value = value; + } else if (value < min_value) { + min_value = value; + } + } + } + + if (max_value == min_value || + max_value == std::numeric_limits::max()) { + for (size_t si = 0; si < stk_count; si++) { + auto* dst = all_stk_inds[si][ii].data(); + dst[di] = Null(); + } + } else { + Indicator::value_t diff = max_value - min_value; + for (size_t si = 0; si < stk_count; si++) { + auto* dst = all_stk_inds[si][ii].data(); + dst[di] = (dst[di] - min_value) / diff; + } + } + } + } + } + + // 每日截面标准化 + if (getParam("enable_zscore")) { + Indicator one_day = PRICELIST(PriceList(stk_count, Null())); + for (size_t di = 0, days_total = m_ref_dates.size(); di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + auto* one_day_data = one_day.data(); + for (size_t si = 0; si < stk_count; si++) { + one_day_data[si] = all_stk_inds[si][ii][di]; + } + + auto new_value = + ZSCORE(one_day, getParam("zscore_out_extreme"), + getParam("zscore_nsigma"), getParam("zscore_recursive")); + + for (size_t si = 0; si < stk_count; si++) { + auto* dst = all_stk_inds[si][ii].data(); + dst[di] = new_value[si]; + } + } + } + } + return all_stk_inds; } @@ -320,7 +395,7 @@ void MultiFactorBase::calculate() { HKU_IF_RETURN(m_calculated, void()); // 获取所有证券所有对齐后的原始因子 - vector> all_stk_inds = _alignAllInds(); + vector> all_stk_inds = getAllSrcFactors(); try { if (m_inds.size() == 1) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 36030fbc..2eaebe5d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -101,6 +101,14 @@ public: */ Indicator getICIR(int ir_n, int ic_n = 0); + /** + * 获取所有处理过的原始因子值(归一化、标准化) + * @note 考虑到内存占用,该数据没有缓存,一般用与测试或者想查看处理过的原始因子值 + * @return vector stks x inds + */ + vector getAllSrcFactors(); + + typedef std::shared_ptr MultiFactorPtr; MultiFactorPtr clone(); @@ -112,7 +120,6 @@ private: void calculate(); protected: - vector _alignAllInds(); void _buildIndex(); // 计算完成后创建截面索引 IndicatorList _getAllReturns(int ndays) const; diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index c3541da9..ee03b302 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -141,6 +141,8 @@ void export_MultiFactor(py::module& m) { :return: 每日 ScoreRecordList 结果的 list)") + .def("get_all_src_factors", &MultiFactorBase::getAllSrcFactors) + DEF_PICKLE(MultiFactorPtr); m.def( From f63a55dc2315475f96a3b3306563f8016c21b459 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 24 Mar 2024 14:50:14 +0800 Subject: [PATCH 079/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20HKU=5FASSERT=20?= =?UTF-8?q?=E5=AE=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.h.in | 6 +-- hikyuu_cpp/hikyuu/Log.h | 47 +++++-------------- .../base_info/mysql/MySQLBaseInfoDriver.cpp | 2 +- .../kdata/mysql/MySQLKDataDriver.cpp | 2 +- .../kdata/sqlite/SQLiteKDataDriver.cpp | 2 +- xmake.lua | 8 +++- 6 files changed, 25 insertions(+), 42 deletions(-) diff --git a/config.h.in b/config.h.in index 9333abc6..a4ec7bef 100644 --- a/config.h.in +++ b/config.h.in @@ -4,6 +4,9 @@ // clang-format off +// Debug 模式 +#define HKU_DEBUG_MODE ${HKU_DEBUG_MODE} + // support serialization #define HKU_SUPPORT_SERIALIZATION ${SUPPORT_SERIALIZATION} @@ -28,9 +31,6 @@ // spdlog默认日志级别 #define SPDLOG_ACTIVE_LEVEL ${LOG_ACTIVE_LEVEL} -// 关闭 HKU_ASSERT -#define HKU_DISABLE_ASSERT ${HKU_DISABLE_ASSERT} - // 启用MSVC内存泄漏检查 #define ENABLE_MSVC_LEAK_DETECT ${ENABLE_MSVC_LEAK_DETECT} diff --git a/hikyuu_cpp/hikyuu/Log.h b/hikyuu_cpp/hikyuu/Log.h index 3f016e28..095c187d 100644 --- a/hikyuu_cpp/hikyuu/Log.h +++ b/hikyuu_cpp/hikyuu/Log.h @@ -218,12 +218,20 @@ std::string HKU_API getLocalTime(); } while (0) #endif // #ifndef HKU_ENABLE_STACK_TRACE -#if HKU_DISABLE_ASSERT -#define HKU_ASSERT(expr) -#define HKU_ASSERT_M(expr, ...) - -#else /* #if HKU_DISABLE_ASSERT */ +#if HKU_DEBUG_MODE +#define HKU_ASSERT_DEBUG(expr) +#else +/** 仅在 debug 模式下生效 */ +#define HKU_ASSERT_DEBUG(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) +#endif /* #if HKU_DEBUG_MODE */ #ifdef HKU_ENABLE_STACK_TRACE /** * 若表达式为 false,将抛出 hku::exception 异常 @@ -239,21 +247,6 @@ std::string HKU_API getLocalTime(); } \ } 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({}) {}\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 { \ @@ -264,21 +257,7 @@ std::string HKU_API getLocalTime(); } \ } 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 */ #ifndef HKU_ENABLE_STACK_TRACE /** 抛出 hku::exception 及传入信息 */ diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index c55ff519..36f27fe1 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -29,7 +29,7 @@ MySQLBaseInfoDriver::~MySQLBaseInfoDriver() { } bool MySQLBaseInfoDriver::_init() { - HKU_ASSERT_M(m_pool == nullptr, "Maybe repeat initialization!"); + HKU_CHECK(m_pool == nullptr, "Maybe repeat initialization!"); Parameter connect_param; connect_param.set("host", getParamFromOther(m_params, "host", "127.0.0.1")); connect_param.set("usr", getParamFromOther(m_params, "usr", "root")); diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp index 67dcd61b..d7bd927e 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp @@ -21,7 +21,7 @@ MySQLKDataDriver::~MySQLKDataDriver() { } bool MySQLKDataDriver::_init() { - HKU_ASSERT_M(m_connect == nullptr, "Maybe repeat initialization!"); + HKU_CHECK(m_connect == nullptr, "Maybe repeat initialization!"); Parameter connect_param; connect_param.set("db", ""); // 数据库名称须在SQL语句中明确指定 connect_param.set("host", getParamFromOther(m_params, "host", "127.0.0.1")); diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/sqlite/SQLiteKDataDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/sqlite/SQLiteKDataDriver.cpp index 35f6884e..577cb677 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/sqlite/SQLiteKDataDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/sqlite/SQLiteKDataDriver.cpp @@ -38,7 +38,7 @@ SQLiteKDataDriver::SQLiteKDataDriver() : KDataDriver("sqlite3") {} SQLiteKDataDriver::~SQLiteKDataDriver() {} bool SQLiteKDataDriver::_init() { - HKU_ASSERT_M(m_sqlite_connection_map.empty(), "Maybe repeat initialization!"); + HKU_CHECK(m_sqlite_connection_map.empty(), "Maybe repeat initialization!"); // read param from config StringList keys = m_params.getNameList(); string db_filename; diff --git a/xmake.lua b/xmake.lua index a139b2d5..fd7880b1 100644 --- a/xmake.lua +++ b/xmake.lua @@ -51,7 +51,7 @@ option("tdx") option_end() option("stacktrace") - set_default(true) + set_default(false) set_showmenu(true) set_category("hikyuu") set_description("Enable check/assert with stack trace info.") @@ -117,6 +117,11 @@ else set_configvar("LOG_ACTIVE_LEVEL", 6) end +if is_mode("debug") then + set_configvar("HKU_DEBUG_MODE", 1) +else + set_configvar("HKU_DEBUG_MODE", 0) +end set_configvar("USE_SPDLOG_LOGGER", 1) -- 是否使用spdlog作为日志输出 set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) @@ -128,7 +133,6 @@ end set_configvar("SUPPORT_TEXT_ARCHIVE", 0) set_configvar("SUPPORT_XML_ARCHIVE", 1) set_configvar("SUPPORT_BINARY_ARCHIVE", 1) -set_configvar("HKU_DISABLE_ASSERT", 0) set_configvar("ENABLE_MSVC_LEAK_DETECT", 0) set_configvar("HKU_ENABLE_SEND_FEEDBACK", get_config("feedback") and 1 or 0) From 5d22c382f04fc2f260387263f1c6eff849e0d897 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 24 Mar 2024 18:14:12 +0800 Subject: [PATCH 080/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Indicator=20operat?= =?UTF-8?q?or(),=20=E5=A6=82=E6=9E=9C=E7=AD=89=E6=95=88=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 7482fb42..6c1bbfde 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -99,6 +99,9 @@ Indicator Indicator::operator()(const Indicator& ind) { HKU_IF_RETURN(!m_imp, Indicator()); HKU_IF_RETURN(!ind.getImp(), Indicator(m_imp)); IndicatorImpPtr p = m_imp->clone(); + if (m_imp->alike(*ind.getImp())) { + return Indicator(p); + } p->add(IndicatorImp::OP, IndicatorImpPtr(), ind.getImp()); return p->calculate(); } From 0c7d78f01cc097043e66362cfe99c5a14a6bca12 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 00:55:35 +0800 Subject: [PATCH 081/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E6=84=8F?= =?UTF-8?q?=E4=BA=8B=E9=A1=B9=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xmake.lua | 1 + 1 file changed, 1 insertion(+) diff --git a/xmake.lua b/xmake.lua index fd7880b1..f1064cb8 100644 --- a/xmake.lua +++ b/xmake.lua @@ -50,6 +50,7 @@ option("tdx") set_description("Enable tdx kdata engine.") option_end() +-- 注意:stacktrace 在 windows 下会严重影响性能 option("stacktrace") set_default(false) set_showmenu(true) From 2b57049c43b10648fc825e451eae30a4dbf8dfbc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 00:58:47 +0800 Subject: [PATCH 082/601] =?UTF-8?q?fixed=20MF=E9=81=97=E6=BC=8F=E6=9E=90?= =?UTF-8?q?=E6=9E=84=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h | 2 +- hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h | 1 + hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h | 1 + hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 2eaebe5d..f1b8ab81 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -28,6 +28,7 @@ public: MultiFactorBase(const string& name); MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, const string& name, int ic_n); + virtual ~MultiFactorBase() = default; /** 获取名称 */ const string& name() const { @@ -108,7 +109,6 @@ public: */ vector getAllSrcFactors(); - typedef std::shared_ptr MultiFactorPtr; MultiFactorPtr clone(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h index 471f1649..d8b1a40f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h @@ -19,6 +19,7 @@ public: EqualWeightMultiFactor(); EqualWeightMultiFactor(const vector& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n); + virtual ~EqualWeightMultiFactor() = default; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h index 17ca3255..857742e1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h @@ -19,6 +19,7 @@ public: ICIRMultiFactor(); ICIRMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n, int ic_rolling_n); + virtual ~ICIRMultiFactor() = default; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h index ce435c9c..ee4db416 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h @@ -19,6 +19,7 @@ public: ICMultiFactor(); ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n, int ic_rolling_n); + virtual ~ICMultiFactor() = default; }; } // namespace hku \ No newline at end of file From 4a8e91b96daf9365e84df5717b7615298849d306 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 03:05:36 +0800 Subject: [PATCH 083/601] =?UTF-8?q?MF=E5=8F=82=E6=95=B0=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E5=8F=98=E6=9B=B4=E4=B8=8E=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/factor/MultiFactorBase.cpp | 47 +++++++------ .../hikyuu/trade_sys/factor/MultiFactorBase.h | 4 +- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 12 +++- .../trade_sys/factor/imp/ICIRMultiFactor.h | 2 + .../trade_sys/factor/imp/ICMultiFactor.cpp | 10 ++- .../trade_sys/factor/imp/ICMultiFactor.h | 2 + hikyuu_cpp/hikyuu/utilities/Parameter.h | 69 +++++++++++++++++-- .../trade_sys/factor/test_MF_EqualWeight.cpp | 3 + .../trade_sys/factor/test_MF_ICIRWeight.cpp | 10 +++ 9 files changed, 131 insertions(+), 28 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 54d5283f..ae3251f7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -60,36 +60,20 @@ HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorPtr& mf) { } MultiFactorBase::MultiFactorBase() { - setParam("fill_null", true); - setParam("ic_n", 1); - setParam("enable_min_max_normalize", false); - setParam("enable_zscore", false); - setParam("zscore_out_extreme", false); - setParam("zscore_recursive", false); - setParam("zscore_nsigma", 3.0); + initParam(); } MultiFactorBase::MultiFactorBase(const string& name) { - setParam("fill_null", true); - setParam("ic_n", 1); - setParam("enable_min_max_normalize", false); - setParam("enable_zscore", false); - setParam("zscore_out_extreme", false); - setParam("zscore_recursive", false); - setParam("zscore_nsigma", 3.0); + initParam(); } MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, const string& name, int ic_n) : m_name(name), m_inds(inds), m_stks(stks), m_ref_stk(ref_stk), m_query(query) { - setParam("fill_null", true); + initParam(); setParam("ic_n", ic_n); - setParam("enable_min_max_normalize", false); - setParam("enable_zscore", false); - setParam("zscore_out_extreme", false); - setParam("zscore_recursive", false); - setParam("zscore_nsigma", 3.0); + checkParam("ic_n"); HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); @@ -108,6 +92,29 @@ MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stk m_stks.size()); } +void MultiFactorBase::initParam() { + setParam("fill_null", true); + setParam("ic_n", 1); + setParam("enable_min_max_normalize", false); + setParam("enable_zscore", false); + setParam("zscore_out_extreme", false); + setParam("zscore_recursive", false); + setParam("zscore_nsigma", 3.0); +} + +void MultiFactorBase::checkParam(const string& name) const { + if ("ic_n" == name) { + HKU_ASSERT(getParam("ic_n") >= 1); + } else if ("zscore_nsigma" == name) { + HKU_ASSERT(getParam("zscore_nsigma") > 0.0); + } +} + +void MultiFactorBase::paramChanged() { + std::lock_guard lock(m_mutex); + m_calculated = false; +} + MultiFactorPtr MultiFactorBase::clone() { std::lock_guard lock(m_mutex); MultiFactorPtr p; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index f1b8ab81..8d0dfe73 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -17,7 +17,7 @@ namespace hku { * @ingroup MultiFactor */ class HKU_API MultiFactorBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: typedef Indicator::value_t value_t; @@ -119,6 +119,8 @@ private: /** 执行计算 */ void calculate(); + void initParam(); + protected: void _buildIndex(); // 计算完成后创建截面索引 IndicatorList _getAllReturns(int ndays) const; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index 275b2063..b3fcdabe 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -26,6 +26,14 @@ ICIRMultiFactor::ICIRMultiFactor(const vector& inds, const StockList& int ic_rolling_n) : MultiFactorBase(inds, stks, query, ref_stk, "MF_ICIRWeight", ic_n) { setParam("ic_rolling_n", ic_rolling_n); + checkParam("ic_rolling_n"); +} + +void ICIRMultiFactor::checkParam(const string& name) const { + MultiFactorBase::checkParam(name); + if ("ic_rolling_n" == name) { + HKU_ASSERT(getParam("ic_rolling_n") >= 1); + } } IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_inds) { @@ -56,8 +64,8 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i new_values[di] = Null(); } for (size_t ii = 0; ii < ind_count; ii++) { - const auto *ind_data = all_stk_inds[si][ii].data(); - const auto *icir_data = icir[ii].data(); + const auto* ind_data = all_stk_inds[si][ii].data(); + const auto* icir_data = icir[ii].data(); for (size_t di = discard; di < days_total; di++) { new_values[di] += ind_data[di] * icir_data[di]; sum_weight[di] += std::abs(icir_data[di]); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h index 857742e1..ed08adcb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h @@ -20,6 +20,8 @@ public: ICIRMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n, int ic_rolling_n); virtual ~ICIRMultiFactor() = default; + + virtual void checkParam(const string& name) const override; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index a8f17195..5b772d63 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -25,6 +25,14 @@ ICMultiFactor::ICMultiFactor(const IndicatorList& inds, const StockList& stks, c const Stock& ref_stk, int ic_n, int ic_rolling_n) : MultiFactorBase(inds, stks, query, ref_stk, "MF_ICWeight", ic_n) { setParam("ic_rolling_n", ic_rolling_n); + checkParam("ic_rolling_n"); +} + +void ICMultiFactor::checkParam(const string& name) const { + MultiFactorBase::checkParam(name); + if ("ic_rolling_n" == name) { + HKU_ASSERT(getParam("ic_rolling_n") >= 1); + } } IndicatorList ICMultiFactor::_calculate(const vector& all_stk_inds) { @@ -56,7 +64,7 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind new_values[di] = Null(); } for (size_t ii = 0; ii < ind_count; ii++) { - const auto* ind_data = all_stk_inds[si][ii].data(); + const auto* ind_data = all_stk_inds[si][ii].data(); const auto* ic_data = ic[ii].data(); for (size_t di = discard; di < days_total; di++) { new_values[di] += ind_data[di] * ic_data[di]; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h index ee4db416..7fdf8fe9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h @@ -20,6 +20,8 @@ public: ICMultiFactor(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n, int ic_rolling_n); virtual ~ICMultiFactor() = default; + + virtual void checkParam(const string& name) const override; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index de45ab9c..8705fc97 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -126,7 +126,9 @@ public: } /** 获取参数个数 */ - size_t size() const; + size_t size() const { + return m_params.size(); + } /** * 获取指定参数的实际类型 @@ -348,9 +350,68 @@ public: \ return getParam(name); \ } -inline size_t Parameter::size() const { - return m_params.size(); -} +/** + * 支持自定义类参数检查及变化通知 + * 需要实现重载以下虚函数接口: + * virtual void checkParam(const string& name) -- 内部实现对应参数的检查,如果不合法需抛出异常 + * virtual void paramChanged() -- 参数变化时调用该函数 + * 注意:由于默认参数一般在类的构造函数中设置,此时由于 checkParam 和 paramChanged + * 均为虚函数,实际上是调用的是基类的虚函数。一般构造函数设置初始的默认参数时, + * 子类的 checkParam 和 paramChanged 即使没有被调用也没有影响。但如果需要用到 + * 构造函数中带入的参数值,则需要在 setParam 执行后,自行调用 checkParam, 或者 + * 在 setParam 执行之前,直接对构造函数传入的参数进行检查(子类和基类均是如此) + * 另:python 中一般不用引出这里个函数,python 类继承时可以自己进行检查 + */ +#define PARAMETER_SUPPORT_WITH_CHECK \ +protected: \ + Parameter m_params; \ + virtual void checkParam(const string& name) const; \ + virtual void paramChanged(); \ + \ +public: \ + const Parameter& getParameter() const { \ + return m_params; \ + } \ + \ + void setParameter(const Parameter& param) { \ + m_params = param; \ + for (auto iter = m_params.begin(); iter != m_params.end(); ++iter) { \ + checkParam(iter->first); \ + } \ + paramChanged(); \ + } \ + \ + bool haveParam(const string& name) const noexcept { \ + return m_params.have(name); \ + } \ + \ + template \ + void setParam(const string& name, const ValueType& value) { \ + m_params.set(name, value); \ + checkParam(name); \ + paramChanged(); \ + } \ + \ + template \ + ValueType getParam(const string& name) const { \ + return m_params.get(name); \ + } \ + \ + template \ + ValueType tryGetParam(const string& name, const ValueType& val) const { \ + return m_params.tryGet(name, val); \ + } \ + \ + template \ + ValueType getParamFromOther(const Parameter& other, const string& name, \ + const ValueType& default_value) { \ + if (other.have(name)) { \ + setParam(name, other.get(name)); \ + } else { \ + setParam(name, default_value); \ + } \ + return getParam(name); \ + } template ValueType Parameter::get(const string& name) const { diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index 6b137f9c..c0b09b37 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -78,6 +78,9 @@ TEST_CASE("test_MF_EqualWeight") { CHECK_THROWS_AS(MF_EqualWeight(src_inds, {sm["sh600004"]}, KQuery(-2), ref_stk), std::exception); + /** @arg 输入非法 ic_n */ + CHECK_THROWS_AS(MF_EqualWeight(src_inds, stks, KQuery(-2), ref_stk, 0), std::exception); + /** @arg 临界状态, 原始因子数量为1, 证券数量2, 数据长度为2 */ src_inds = {MA(CLOSE())}; stks = {sm["sh600005"], sm["sh600004"]}; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index 1b8bcf2a..2cabe06a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -38,9 +38,19 @@ TEST_CASE("test_MF_ICIRWeight") { KQuery query = KQuery(-20); KData ref_k = ref_stk.getKData(query); DatetimeList ref_dates = ref_k.getDatetimeList(); + + /** @arg 输入非法 ic_n, ic_rolling_n */ + CHECK_THROWS_AS(MF_ICIRWeight(src_inds, stks, query, ref_stk, 0), std::exception); + CHECK_THROWS_AS(MF_ICIRWeight(src_inds, stks, query, ref_stk, 1, -1), std::exception); + + /** @arg 正常计算 */ auto mf = MF_ICIRWeight(src_inds, stks, query, ref_stk, ndays, ic_rolling_n); CHECK_EQ(mf->name(), "MF_ICIRWeight"); CHECK_THROWS_AS(mf->getFactor(sm["sh600000"]), std::exception); + CHECK_THROWS_AS(mf->setParam("ic_n", -1), std::exception); + mf->setParam("ic_n", ndays); + CHECK_THROWS_AS(mf->setParam("ic_rolling_n", -1), std::exception); + mf->setParam("ic_rolling_n", ic_rolling_n); auto stk = sm["sh600004"]; auto ind1 = MA(ROCR(CLOSE(stk.getKData(query)), ndays)); From 66789e15467a08e21ab8c623f825ddf8198d2054 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 15:42:36 +0800 Subject: [PATCH 084/601] =?UTF-8?q?System=E6=94=AF=E6=8C=81=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=8F=98=E5=8A=A8=E5=90=8E=E9=87=8D=E7=AE=97=E5=8F=8A?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E6=AD=A2=E7=9B=88=E5=BB=B6=E8=BF=9F=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=9C=AA=E7=94=9F=E6=95=88=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 59 ++++++++++++------- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 31 +++++----- 2 files changed, 56 insertions(+), 34 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index d90c6f43..1132ff31 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -33,7 +33,7 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SystemPtr& sys) { System::System() : m_name("SYS_Simple"), - m_part_changed(true), + m_calculated(false), m_pre_ev_valid(true), // must true m_pre_cn_valid(true), // must true m_buy_days(0), @@ -45,7 +45,7 @@ System::System() System::System(const string& name) : m_name(name), - m_part_changed(true), + m_calculated(false), m_pre_ev_valid(true), m_pre_cn_valid(true), m_buy_days(0), @@ -69,7 +69,7 @@ System::System(const TradeManagerPtr& tm, const MoneyManagerPtr& mm, const Envir m_pg(pg), m_sp(sp), m_name(name), - m_part_changed(true), + m_calculated(false), m_pre_ev_valid(true), m_pre_cn_valid(true), m_buy_days(0), @@ -123,6 +123,18 @@ void System::initParam() { setParam("shared_sp", false); } +void System::checkParam(const string& name) const { + if ("max_delay_count" == name) { + HKU_ASSERT(getParam("max_delay_count") >= 0); + } else if ("tp_delay_n" == name) { + HKU_ASSERT(getParam("tp_delay_n") >= 0); + } +} + +void System::paramChanged() { + m_calculated = false; +} + void System::reset() { if (m_tm && !getParam("shared_tm")) m_tm->reset(); @@ -148,7 +160,7 @@ void System::reset() { // 一个sys实例绑定stock后,除非主动改变,否则不应该被reset // m_stock - m_part_changed = true; + m_calculated = false; m_pre_ev_valid = false; // true; m_pre_cn_valid = false; // true; @@ -189,7 +201,7 @@ void System::forceResetAll() { m_src_kdata = Null(); m_kdata = Null(); - m_part_changed = true; + m_calculated = false; m_pre_ev_valid = false; // true; m_pre_cn_valid = false; // true; @@ -206,10 +218,14 @@ void System::forceResetAll() { } void System::setTO(const KData& kdata) { - HKU_TRACE_IF_RETURN(!m_part_changed && m_kdata == kdata, void(), "No need to calcule!"); - m_kdata = kdata; - m_stock = kdata.getStock(); + if (m_kdata != kdata) { + m_calculated = false; + m_kdata = kdata; + } + HKU_TRACE_IF_RETURN(m_calculated, void(), "No need to calcule!"); + + m_stock = kdata.getStock(); KQuery query = kdata.getQuery(); if (m_stock.isNull() || query.recoverType() == KQuery::NO_RECOVER) { m_src_kdata = m_kdata; @@ -270,7 +286,7 @@ SystemPtr System::clone() { p->m_kdata = m_kdata; p->m_src_kdata = m_src_kdata; - p->m_part_changed = m_part_changed; + p->m_calculated = m_calculated; p->m_pre_ev_valid = m_pre_ev_valid; p->m_pre_cn_valid = m_pre_cn_valid; @@ -334,7 +350,7 @@ bool System::readyForRun() { } void System::run(const KQuery& query, bool reset, bool resetAll) { - HKU_ERROR_IF_RETURN(m_stock.isNull(), void(), "m_stock is NULL!"); + HKU_CHECK(!m_stock.isNull(), "m_stock is NULL!"); // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 if (resetAll) { @@ -343,11 +359,10 @@ void System::run(const KQuery& query, bool reset, bool resetAll) { this->reset(); } - HKU_IF_RETURN(!readyForRun(), void()); - KData kdata = m_stock.getKData(query); - HKU_IF_RETURN(kdata.empty(), void()); - HKU_DEBUG_IF_RETURN(!m_part_changed && m_kdata == kdata, void(), "Not need calculate."); + HKU_DEBUG_IF_RETURN(m_calculated && m_kdata == kdata, void(), "Not need calculate."); + + HKU_IF_RETURN(!readyForRun(), void()); setTO(kdata); size_t total = kdata.size(); @@ -358,7 +373,7 @@ void System::run(const KQuery& query, bool reset, bool resetAll) { _runMoment(ks[i], src_ks[i]); } } - m_part_changed = false; + m_calculated = true; } void System::run(const Stock& stock, const KQuery& query, bool reset, bool resetAll) { @@ -367,9 +382,6 @@ void System::run(const Stock& stock, const KQuery& query, bool reset, bool reset } void System::run(const KData& kdata, bool reset, bool resetAll) { - HKU_INFO_IF_RETURN(kdata.empty(), void(), "Input kdata is empty!"); - HKU_DEBUG_IF_RETURN(!m_part_changed && m_kdata == kdata, void(), "Not need calculate."); - // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 if (resetAll) { this->forceResetAll(); @@ -378,6 +390,8 @@ void System::run(const KData& kdata, bool reset, bool resetAll) { this->reset(); } + HKU_DEBUG_IF_RETURN(m_calculated && m_kdata == kdata, void(), "Not need calculate."); + HKU_IF_RETURN(!readyForRun(), void()); setTO(kdata); @@ -389,7 +403,7 @@ void System::run(const KData& kdata, bool reset, bool resetAll) { _runMoment(ks[i], src_ks[i]); } } - m_part_changed = false; + m_calculated = true; } void System::clearDelayRequest() { @@ -530,7 +544,12 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { } else { m_lastTakeProfit = current_take_profile; } - if (current_price <= current_take_profile) { + + int tp_delay_n = getParam("tp_delay_n"); + size_t pos = m_kdata.getPos(today.datetime); + size_t position_pos = m_kdata.getPos(position.takeDatetime); + // 如果当前价格小于等于止盈价,且满足止盈延迟条件则卖出 + if (pos - position_pos >= tp_delay_n && current_price <= current_take_profile) { tr = _sell(today, src_today, PART_TAKEPROFIT); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index ca3c8d57..32a968bc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -30,7 +30,7 @@ namespace hku { * @ingroup System */ class HKU_API System { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: /** 默认构造函数 */ @@ -292,7 +292,7 @@ protected: KData m_kdata; KData m_src_kdata; // 未复权的原始 K 线数据 - bool m_part_changed; // 记录部件是否发生变化,控制是否需要重新计算 + bool m_calculated; // 控制是否需要重新计算 bool m_pre_ev_valid; bool m_pre_cn_valid; @@ -334,7 +334,7 @@ private: // m_kdata中包含了stock和query的信息,不用保存m_stock ar& BOOST_SERIALIZATION_NVP(m_kdata); - ar& BOOST_SERIALIZATION_NVP(m_part_changed); + ar& BOOST_SERIALIZATION_NVP(m_calculated); ar& BOOST_SERIALIZATION_NVP(m_pre_ev_valid); ar& BOOST_SERIALIZATION_NVP(m_pre_cn_valid); @@ -369,7 +369,7 @@ private: ar& BOOST_SERIALIZATION_NVP(m_kdata); m_stock = m_kdata.getStock(); - ar& BOOST_SERIALIZATION_NVP(m_part_changed); + ar& BOOST_SERIALIZATION_NVP(m_calculated); ar& BOOST_SERIALIZATION_NVP(m_pre_ev_valid); ar& BOOST_SERIALIZATION_NVP(m_pre_cn_valid); @@ -451,63 +451,63 @@ inline SlippagePtr System::getSP() const { inline void System::setTM(const TradeManagerPtr& tm) { if (m_tm != tm) { m_tm = tm; - m_part_changed = true; + m_calculated = false; } } inline void System::setMM(const MoneyManagerPtr& mm) { if (m_mm != mm) { m_mm = mm; - m_part_changed = true; + m_calculated = false; } } inline void System::setEV(const EnvironmentPtr& ev) { if (m_ev != ev) { m_ev = ev; - m_part_changed = true; + m_calculated = false; } } inline void System::setCN(const ConditionPtr& cn) { if (m_cn != cn) { m_cn = cn; - m_part_changed = true; + m_calculated = false; } } inline void System::setSG(const SignalPtr& sg) { if (m_sg != sg) { m_sg = sg; - m_part_changed = true; + m_calculated = false; } } inline void System::setST(const StoplossPtr& st) { if (m_st != st) { m_st = st; - m_part_changed = true; + m_calculated = false; } } inline void System::setTP(const StoplossPtr& tp) { if (m_tp != tp) { m_tp = tp; - m_part_changed = true; + m_calculated = false; } } inline void System::setPG(const ProfitGoalPtr& pg) { if (m_pg != pg) { m_pg = pg; - m_part_changed = true; + m_calculated = false; } } inline void System::setSP(const SlippagePtr& sp) { if (m_sp != sp) { m_sp = sp; - m_part_changed = true; + m_calculated = false; } } @@ -516,7 +516,10 @@ inline Stock System::getStock() const { } inline void System::setStock(const Stock& stk) { - m_stock = stk; + if (m_stock != stk) { + m_stock = stk; + m_calculated = false; + } } inline const TradeRecordList& System::getTradeRecordList() const { From fafa9d3a7758874a9fd60b71fe7cf87bf81d3887 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 16:12:04 +0800 Subject: [PATCH 085/601] =?UTF-8?q?TradeCost=20=E6=94=AF=E6=8C=81=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E5=8F=98=E6=9B=B4=E4=B8=8E=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp | 4 ++++ hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.h | 2 +- .../trade_manage/imp/FixedA2015TradeCost.cpp | 12 ++++++++++++ .../hikyuu/trade_manage/imp/FixedA2015TradeCost.h | 2 ++ .../trade_manage/imp/FixedA2017TradeCost.cpp | 12 ++++++++++++ .../hikyuu/trade_manage/imp/FixedA2017TradeCost.h | 2 ++ .../hikyuu/trade_manage/imp/FixedATradeCost.cpp | 14 ++++++++++++++ .../hikyuu/trade_manage/imp/FixedATradeCost.h | 2 ++ 8 files changed, 49 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp index 97c1d0d3..220bf4c9 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp @@ -31,6 +31,10 @@ TradeCostBase::TradeCostBase(const string& name) : m_name(name) {} TradeCostBase::~TradeCostBase() {} +void TradeCostBase::checkParam(const string& name) const {} + +void TradeCostBase::paramChanged() {} + TradeCostPtr TradeCostBase::clone() { TradeCostPtr result = _clone(); TradeCostBase* p = result.get(); diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.h index 56ec9466..0ace1f0e 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.h @@ -20,7 +20,7 @@ namespace hku { * @ingroup TradeCost */ class HKU_API TradeCostBase { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: TradeCostBase(const string& name); diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp index beed6765..f569ecdb 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp @@ -23,6 +23,18 @@ FixedA2015TradeCost::FixedA2015TradeCost() : TradeCostBase("TC_FixedA2015") { FixedA2015TradeCost::~FixedA2015TradeCost() {} +void FixedA2015TradeCost::checkParam(const string& name) const { + if ("commission" == name) { + HKU_ASSERT(getParam("commission") >= 0.0); + } else if ("lowest_commission" == name) { + HKU_ASSERT(getParam("lowest_commission") >= 0.0); + } else if ("stamptax" == name) { + HKU_ASSERT(getParam("stamptax") >= 0.0); + } else if ("transferfee" == name) { + HKU_ASSERT(getParam("transferfee") >= 0.0); + } +} + CostRecord FixedA2015TradeCost::getBuyCost(const Datetime& datetime, const Stock& stock, price_t price, double num) const { CostRecord result; diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h index b4bebdb0..3031e467 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h @@ -20,6 +20,8 @@ public: FixedA2015TradeCost(); virtual ~FixedA2015TradeCost(); + virtual void checkParam(const string& name) const override; + /** * 计算买入成本 * @param datetime 交易日期 diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp index 1222ec5f..87aee1fa 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp @@ -23,6 +23,18 @@ FixedA2017TradeCost::FixedA2017TradeCost() : TradeCostBase("TC_FixedA2017") { FixedA2017TradeCost::~FixedA2017TradeCost() {} +void FixedA2017TradeCost::checkParam(const string& name) const { + if ("commission" == name) { + HKU_ASSERT(getParam("commission") >= 0.0); + } else if ("lowest_commission" == name) { + HKU_ASSERT(getParam("lowest_commission") >= 0.0); + } else if ("stamptax" == name) { + HKU_ASSERT(getParam("stamptax") >= 0.0); + } else if ("transferfee" == name) { + HKU_ASSERT(getParam("transferfee") >= 0.0); + } +} + CostRecord FixedA2017TradeCost::getBuyCost(const Datetime& datetime, const Stock& stock, price_t price, double num) const { CostRecord result; diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h index 4a3dd337..c3f475fd 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h @@ -23,6 +23,8 @@ public: FixedA2017TradeCost(); virtual ~FixedA2017TradeCost(); + virtual void checkParam(const string& name) const override; + /** * 计算买入成本 * @param datetime 交易日期 diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp index d6068613..6fc45df8 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp @@ -35,6 +35,20 @@ FixedATradeCost::FixedATradeCost(price_t commission, price_t lowestCommission, p FixedATradeCost::~FixedATradeCost() {} +void FixedATradeCost::checkParam(const string& name) const { + if ("commission" == name) { + HKU_ASSERT(getParam("commission") >= 0.0); + } else if ("lowest_commission" == name) { + HKU_ASSERT(getParam("lowest_commission") >= 0.0); + } else if ("stamptax" == name) { + HKU_ASSERT(getParam("stamptax") >= 0.0); + } else if ("transferfee" == name) { + HKU_ASSERT(getParam("transferfee") >= 0.0); + } else if ("lowest_transferfee" == name) { + HKU_ASSERT(getParam("lowest_transferfee") >= 0.0); + } +} + CostRecord FixedATradeCost::getBuyCost(const Datetime& datetime, const Stock& stock, price_t price, double num) const { CostRecord result; diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h index 3a7f2a3b..963cf701 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h @@ -56,6 +56,8 @@ public: price_t transferfee, price_t lowestTransferfee); virtual ~FixedATradeCost(); + virtual void checkParam(const string& name) const override; + /** * 计算买入成本 * @param datetime 交易日期 From 145a4608710b692a9362d2a5e63f70ca75fc37d8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 18:26:38 +0800 Subject: [PATCH 086/601] =?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=8F=82=E6=95=B0?= =?UTF-8?q?=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp | 4 ++++ .../hikyuu/trade_sys/profitgoal/ProfitGoalBase.h | 2 +- .../trade_sys/profitgoal/imp/FixedHoldDays.cpp | 9 +++++++-- .../hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h | 2 ++ .../profitgoal/imp/FixedPercentProfitGoal.cpp | 7 +++++++ .../profitgoal/imp/FixedPercentProfitGoal.h | 1 + .../hikyuu/trade_sys/selector/SelectorBase.cpp | 4 ++++ hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h | 2 +- hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp | 4 ++++ hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h | 2 +- .../hikyuu/trade_sys/signal/imp/BandSignal.cpp | 2 +- .../trade_sys/signal/imp/SingleFactorSignal.h | 12 ------------ .../hikyuu/trade_sys/signal/imp/SingleSignal.cpp | 9 +++++++++ .../hikyuu/trade_sys/signal/imp/SingleSignal.h | 1 + .../hikyuu/trade_sys/signal/imp/SingleSignal2.cpp | 9 +++++++++ .../hikyuu/trade_sys/signal/imp/SingleSignal2.h | 1 + .../hikyuu/trade_sys/slippage/SlippageBase.cpp | 4 ++++ hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h | 2 +- .../trade_sys/slippage/imp/FixedPercentSlippage.cpp | 7 +++++++ .../trade_sys/slippage/imp/FixedPercentSlippage.h | 1 + .../trade_sys/slippage/imp/FixedValueSlippage.cpp | 6 ++++++ .../trade_sys/slippage/imp/FixedValueSlippage.h | 1 + .../hikyuu/trade_sys/stoploss/StoplossBase.cpp | 4 ++++ hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h | 2 +- .../trade_sys/stoploss/imp/FixedPercentStoploss.cpp | 7 +++++++ .../trade_sys/stoploss/imp/FixedPercentStoploss.h | 1 + .../trade_sys/stoploss/imp/IndicatorStoploss.cpp | 9 +++++++++ .../trade_sys/stoploss/imp/IndicatorStoploss.h | 5 +++-- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 5 +++++ .../trade_sys/profitgoal/test_PG_FixedHoldDays.cpp | 9 +++------ .../trade_sys/stoploss/test_ST_FixedPercent.cpp | 13 +++++++++---- 31 files changed, 115 insertions(+), 32 deletions(-) delete mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp index c6093724..14eacc05 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp @@ -29,6 +29,10 @@ ProfitGoalBase::ProfitGoalBase(const string& name) : m_name(name) {} ProfitGoalBase::~ProfitGoalBase() {} +void ProfitGoalBase::checkParam(const string& name) const {} + +void ProfitGoalBase::paramChanged() {} + void ProfitGoalBase::reset() { m_kdata = Null(); m_tm.reset(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h index 90d22986..3f677cc3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h @@ -21,7 +21,7 @@ namespace hku { * @ingroup ProfitGoal */ class HKU_API ProfitGoalBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: ProfitGoalBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp index b6383267..e22a5283 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp @@ -19,11 +19,16 @@ FixedHoldDays::FixedHoldDays() : ProfitGoalBase("PG_FixedHoldDays") { FixedHoldDays::~FixedHoldDays() {} +void FixedHoldDays::checkParam(const string& name) const { + if ("days" == name) { + int days = getParam(name); + HKU_ASSERT(days > 0); + } +} + void FixedHoldDays::_calculate() {} price_t FixedHoldDays::getGoal(const Datetime& datetime, price_t price) { - HKU_WARN_IF_RETURN(getParam("days") <= 0, 0.0, "param days <= 0! Are you sure?"); - Stock stk = m_kdata.getStock(); PositionRecord position = m_tm->getPosition(datetime, stk); Datetime take_date = position.takeDatetime; diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h index f7556f65..68f8a0b4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h @@ -20,6 +20,8 @@ class FixedHoldDays : public ProfitGoalBase { public: FixedHoldDays(); virtual ~FixedHoldDays(); + + virtual void checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp index ffae7a9c..6710b3cb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp @@ -21,6 +21,13 @@ FixedPercentProfitGoal::~FixedPercentProfitGoal() {} void FixedPercentProfitGoal::_calculate() {} +void FixedPercentProfitGoal::checkParam(const string& name) const { + if ("p" == name) { + double p = getParam(name); + HKU_ASSERT(p > 0.0); + } +} + price_t FixedPercentProfitGoal::getGoal(const Datetime& datetime, price_t price) { Stock stock = getTO().getStock(); PositionRecord position = getTM()->getPosition(datetime, stock); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h index f5abb670..14af29e0 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h @@ -20,6 +20,7 @@ class FixedPercentProfitGoal : public ProfitGoalBase { public: FixedPercentProfitGoal(); virtual ~FixedPercentProfitGoal(); + virtual void checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 89dcd5f3..49896134 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -36,6 +36,10 @@ SelectorBase::SelectorBase(const string& name) : m_name(name) { SelectorBase::~SelectorBase() {} +void SelectorBase::checkParam(const string& name) const {} + +void SelectorBase::paramChanged() {} + void SelectorBase::removeAll() { m_pro_sys_list = SystemList(); m_real_sys_list = SystemList(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index fb0d9b7c..eec2e23a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -24,7 +24,7 @@ class HKU_API Portfolio; * @ingroup Selector */ class HKU_API SelectorBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: /** 默认构造函数 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 35b0742f..b44a2e2e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -36,6 +36,10 @@ SignalBase::SignalBase(const string& name) : m_name(name), m_hold_long(false), m SignalBase::~SignalBase() {} +void SignalBase::checkParam(const string& name) const {} + +void SignalBase::paramChanged() {} + SignalPtr SignalBase::clone() { SignalPtr p; try { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index 4721b07c..d80509f4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -22,7 +22,7 @@ namespace hku { * @ingroup Signal */ class HKU_API SignalBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: SignalBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp index 4bf98ccb..2131acc5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp @@ -17,7 +17,7 @@ BandSignal::BandSignal() : SignalBase("SG_Band") {} BandSignal::BandSignal(const Indicator& ind, price_t lower, price_t upper) : SignalBase("SG_Band"), m_ind(ind), m_lower(lower), m_upper(upper) { - HKU_ERROR_IF(lower > upper, "BandSignal: lower track is greater than upper track"); + HKU_CHECK(lower > upper, "BandSignal: lower track is greater than upper track"); } BandSignal::~BandSignal() {} diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h deleted file mode 100644 index 56669f67..00000000 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleFactorSignal.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-12 - * Author: fasiondog - */ - -#pragma once - -#include "../SignalBase.h" - -namespace hku {} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp index d40f1f96..bbf48f01 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp @@ -28,6 +28,15 @@ SingleSignal::SingleSignal(const Indicator& ind) : SignalBase("SG_Single"), m_in SingleSignal::~SingleSignal() {} +void SingleSignal::checkParam(const string& name) const { + if ("filter_n" == name) { + HKU_ASSERT(getParam("filter_n") >= 3); + } else if ("filter_p" == name) { + double filter_p = getParam(name); + HKU_ASSERT(filter_p > 0.0 && filter_p < 1.0); + } +} + SignalPtr SingleSignal::_clone() { SingleSignal* p = new SingleSignal(); p->m_ind = m_ind.clone(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h index 14a21ba1..36ba4186 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h @@ -20,6 +20,7 @@ public: explicit SingleSignal(const Indicator& ind); virtual ~SingleSignal(); + virtual void checkParam(const string& name) const override; virtual SignalPtr _clone() override; virtual void _calculate() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp index 51e30a15..f30bd821 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp @@ -31,6 +31,15 @@ SingleSignal2::SingleSignal2(const Indicator& ind) : SignalBase("SG_Single2"), m SingleSignal2::~SingleSignal2() {} +void SingleSignal2::checkParam(const string& name) const { + if ("filter_n" == name) { + HKU_ASSERT(getParam("filter_n") >= 3); + } else if ("filter_p" == name) { + double filter_p = getParam(name); + HKU_ASSERT(filter_p > 0.0 && filter_p < 1.0); + } +} + SignalPtr SingleSignal2::_clone() { SingleSignal2* p = new SingleSignal2(); p->m_ind = m_ind.clone(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h index 82e36885..f0295650 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h @@ -20,6 +20,7 @@ public: explicit SingleSignal2(const Indicator&); virtual ~SingleSignal2(); + virtual void checkParam(const string& name) const override; virtual SignalPtr _clone() override; virtual void _calculate() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp index e392fc8f..bed90712 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp @@ -27,6 +27,10 @@ SlippageBase::SlippageBase() : m_name("SlippageBase") {} SlippageBase::SlippageBase(const string& name) : m_name(name) {} +void SlippageBase::checkParam(const string& name) const {} + +void SlippageBase::paramChanged() {} + void SlippageBase::reset() { m_kdata = Null(); _reset(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h index a4933f30..cf7f02fc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h @@ -19,7 +19,7 @@ namespace hku { * @ingroup Slippage */ class HKU_API SlippageBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: SlippageBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp index fe2f8096..0e7128df 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp @@ -19,6 +19,13 @@ FixedPercentSlippage::FixedPercentSlippage() : SlippageBase("FixedPercent") { FixedPercentSlippage::~FixedPercentSlippage() {} +void FixedPercentSlippage::checkParam(const string& name) const { + if ("p" == name) { + double p = getParam(name); + HKU_ASSERT(p >= 0.0 && p < 1.0); + } +} + price_t FixedPercentSlippage ::getRealBuyPrice(const Datetime& datetime, price_t price) { return price * (1 + getParam("p")); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h index acbe4cf5..149c2d8c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h @@ -20,6 +20,7 @@ class FixedPercentSlippage : public SlippageBase { public: FixedPercentSlippage(); virtual ~FixedPercentSlippage(); + virtual void checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp index b5cedf0b..bd54dd7e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp @@ -19,6 +19,12 @@ FixedValueSlippage::FixedValueSlippage() { FixedValueSlippage::~FixedValueSlippage() {} +void FixedValueSlippage::checkParam(const string& name) const { + if ("p" == name) { + HKU_ASSERT(getParam(name) >= 0.0); + } +} + price_t FixedValueSlippage ::getRealBuyPrice(const Datetime& datetime, price_t price) { return price + getParam("value"); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h index a395918a..69c40841 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h @@ -20,6 +20,7 @@ class FixedValueSlippage : public SlippageBase { public: FixedValueSlippage(); virtual ~FixedValueSlippage(); + virtual void checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp index bf346491..ba49e4bb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp @@ -29,6 +29,10 @@ StoplossBase::StoplossBase(const string& name) : m_name(name) {} StoplossBase::~StoplossBase() {} +void StoplossBase::checkParam(const string& name) const {} + +void StoplossBase::paramChanged() {} + void StoplossBase::reset() { m_kdata = Null(); m_tm.reset(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h index ba57e9eb..356b2610 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h @@ -21,7 +21,7 @@ namespace hku { * @ingroup Stoploss */ class HKU_API StoplossBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: StoplossBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp index c3882fcf..30dba219 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp @@ -19,6 +19,13 @@ FixedPercentStoploss::FixedPercentStoploss() : StoplossBase("ST_FixedPercent") { FixedPercentStoploss::~FixedPercentStoploss() {} +void FixedPercentStoploss::checkParam(const string& name) const { + if ("p" == name) { + double p = getParam("p"); + HKU_ASSERT(p > 0.0 && p <= 1.0); + } +} + price_t FixedPercentStoploss ::getPrice(const Datetime& datetime, price_t price) { Stock stock = m_kdata.getStock(); int precision = stock.isNull() ? 2 : stock.precision(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h index d6e7320f..6d885d53 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h @@ -23,6 +23,7 @@ class FixedPercentStoploss : public StoplossBase { public: FixedPercentStoploss(); virtual ~FixedPercentStoploss(); + virtual void checkParam(const string& name) const; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp index b7150d05..d247c5ad 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp @@ -21,10 +21,19 @@ IndicatorStoploss::IndicatorStoploss() : StoplossBase("IndicatorStoploss") { IndicatorStoploss::IndicatorStoploss(const Indicator& op, const string& kdata_part) : StoplossBase("IndicatorStoploss"), m_op(op) { setParam("kpart", "CLOSE"); + checkParam("kpart"); } IndicatorStoploss::~IndicatorStoploss() {} +void IndicatorStoploss::checkParam(const string& name) const { + if ("kpart" == name) { + string kpart = getParam("kpart"); + HKU_ASSERT("CLOSE" == kpart || "OPEN" == kpart || "HIGH" == kpart || "LOW" == kpart || + "AMO" == kpart || "VOL" == kpart); + } +} + price_t IndicatorStoploss::getPrice(const Datetime& datetime, price_t price) { return m_result.count(datetime) ? m_result[datetime] : 0.0; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h index 1b933831..c4e506e1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h @@ -16,9 +16,10 @@ namespace hku { class IndicatorStoploss : public StoplossBase { public: - IndicatorStoploss(); //仅用于序列化默认构造函数 + IndicatorStoploss(); // 仅用于序列化默认构造函数 IndicatorStoploss(const Indicator& op, const string& kdata_part); virtual ~IndicatorStoploss(); + virtual void checkParam(const string& name) const; virtual price_t getPrice(const Datetime& datetime, price_t price) override; virtual void _reset() override; @@ -30,7 +31,7 @@ private: map m_result; //======================================== -//序列化支持 +// 序列化支持 //======================================== #if HKU_SUPPORT_SERIALIZATION private: diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index 32a968bc..bf44bcf9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -223,6 +223,11 @@ public: return _sellForce(date, num, from, false); } + // 由各个相关组件调用,用于组件参数变化时通知 sys,以便重算 + void partChangedNotify() { + m_calculated = false; + } + private: bool _environmentIsValid(const Datetime& datetime); bool _conditionIsValid(const Datetime& datetime); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/profitgoal/test_PG_FixedHoldDays.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/profitgoal/test_PG_FixedHoldDays.cpp index 6c628220..27947130 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/profitgoal/test_PG_FixedHoldDays.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/profitgoal/test_PG_FixedHoldDays.cpp @@ -23,8 +23,8 @@ TEST_CASE("test_PG_FixedHoldDays") { StockManager& sm = StockManager::instance(); TMPtr tm = crtTM(Datetime(199001010000LL), 100000); - Datetime start_date(199911100000LL); //测试起始日期 - Datetime end_date(200002250000LL); //测试结束日期 + Datetime start_date(199911100000LL); // 测试起始日期 + Datetime end_date(200002250000LL); // 测试结束日期 KQuery query = KQueryByDate(start_date, end_date, KQuery::DAY); Stock stk = sm.getStock("sh600000"); @@ -40,10 +40,7 @@ TEST_CASE("test_PG_FixedHoldDays") { CHECK_EQ(pg->getParam("days"), 5); /** @arg days = 0 */ - pg->setParam("days", 0); - CHECK_EQ(pg->getGoal(Datetime(199911100000LL), 0.0), 0.0); - CHECK_EQ(pg->getGoal(Datetime(199911110000LL), 0.0), 0.0); - CHECK_EQ(pg->getGoal(Datetime(199911120000LL), 0.0), 0.0); + CHECK_THROWS_AS(pg->setParam("days", 0), std::exception); /** @arg days = 1 */ pg->setParam("days", 1); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/stoploss/test_ST_FixedPercent.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/stoploss/test_ST_FixedPercent.cpp index 3f2b1500..5588f2f4 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/stoploss/test_ST_FixedPercent.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/stoploss/test_ST_FixedPercent.cpp @@ -32,15 +32,20 @@ TEST_CASE("test_FixedPercent_SL") { result = sp->getPrice(Datetime(199101030000), 66.4); CHECK_LT(std::fabs(result - 53.12), 0.01); - /** @arg p = 0.0 */ - sp = ST_FixedPercent(0.0); + /** @arg p = 0.99 */ + sp = ST_FixedPercent(0.99); result = sp->getPrice(Datetime(199101030000), 66.397); - CHECK_LT(std::fabs(result - 66.4), 0.01); + CHECK_EQ(result, doctest::Approx(roundEx(66.397 * 0.01, 2))); /** @arg p = 1.0 */ sp = ST_FixedPercent(1.0); result = sp->getPrice(Datetime(199101030000), 66.397); - CHECK_LT(std::fabs(result - 0), 0.01); + CHECK_EQ(result, doctest::Approx(0.0)); + + /** @arg 非法参数 */ + CHECK_THROWS_AS(ST_FixedPercent(0.0), std::exception); + CHECK_THROWS_AS(ST_FixedPercent(1.01), std::exception); + CHECK_THROWS_AS(ST_FixedPercent(-1.0), std::exception); } /** @} */ From 1a1962e766ab7d0ad113e8a7d4476108d8773327 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 20:43:06 +0800 Subject: [PATCH 087/601] =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E4=B8=8E=E6=9B=B4=E6=96=B0=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/trade_sys/money_manager.rst | 2 +- .../hikyuu/trade_manage/TradeCostBase.cpp | 3 +-- .../trade_manage/imp/FixedA2015TradeCost.cpp | 2 +- .../trade_manage/imp/FixedA2015TradeCost.h | 2 +- .../trade_manage/imp/FixedA2017TradeCost.cpp | 2 +- .../trade_manage/imp/FixedA2017TradeCost.h | 2 +- .../trade_manage/imp/FixedATradeCost.cpp | 2 +- .../hikyuu/trade_manage/imp/FixedATradeCost.h | 2 +- .../allocatefunds/AllocateFundsBase.cpp | 3 +++ .../allocatefunds/AllocateFundsBase.h | 2 +- .../imp/FixedWeightAllocateFunds.cpp | 7 +++++ .../imp/FixedWeightAllocateFunds.h | 1 + .../trade_sys/condition/ConditionBase.cpp | 3 +++ .../trade_sys/condition/ConditionBase.h | 2 +- .../trade_sys/environment/EnvironmentBase.cpp | 3 +++ .../trade_sys/environment/EnvironmentBase.h | 2 +- .../environment/imp/BoolEnvironment.cpp | 8 ++++++ .../environment/imp/BoolEnvironment.h | 1 + .../environment/imp/TwoLineEnvironment.cpp | 8 ++++++ .../environment/imp/TwoLineEnvironment.h | 1 + .../trade_sys/factor/MultiFactorBase.cpp | 2 +- .../trade_sys/factor/imp/ICIRMultiFactor.cpp | 3 +-- .../trade_sys/factor/imp/ICIRMultiFactor.h | 2 +- .../trade_sys/factor/imp/ICMultiFactor.cpp | 3 +-- .../trade_sys/factor/imp/ICMultiFactor.h | 2 +- .../moneymanager/MoneyManagerBase.cpp | 27 ++++++++++--------- .../trade_sys/moneymanager/MoneyManagerBase.h | 2 +- .../imp/FixedCapitalMoneyManager.cpp | 7 +++++ .../imp/FixedCapitalMoneyManager.h | 1 + .../imp/FixedCountMoneyManager.cpp | 7 +++++ .../moneymanager/imp/FixedCountMoneyManager.h | 1 + .../imp/FixedPercentMoneyManager.cpp | 9 +++++-- .../imp/FixedPercentMoneyManager.h | 1 + .../imp/FixedRatioMoneyManager.cpp | 7 +++++ .../moneymanager/imp/FixedRatioMoneyManager.h | 1 + .../imp/FixedRiskMoneyManager.cpp | 7 +++++ .../moneymanager/imp/FixedRiskMoneyManager.h | 1 + .../imp/FixedUnitsMoneyManager.cpp | 7 +++++ .../moneymanager/imp/FixedUnitsMoneyManager.h | 1 + .../imp/WilliamsFixedRiskMoneyManager.cpp | 14 ++++++++-- .../imp/WilliamsFixedRiskMoneyManager.h | 1 + .../trade_sys/profitgoal/ProfitGoalBase.cpp | 3 +-- .../profitgoal/imp/FixedHoldDays.cpp | 2 +- .../trade_sys/profitgoal/imp/FixedHoldDays.h | 2 +- .../profitgoal/imp/FixedPercentProfitGoal.cpp | 2 +- .../profitgoal/imp/FixedPercentProfitGoal.h | 2 +- .../trade_sys/selector/SelectorBase.cpp | 3 +-- .../hikyuu/trade_sys/signal/SignalBase.cpp | 3 +-- .../trade_sys/signal/imp/SingleSignal.cpp | 2 +- .../trade_sys/signal/imp/SingleSignal.h | 2 +- .../trade_sys/signal/imp/SingleSignal2.cpp | 2 +- .../trade_sys/signal/imp/SingleSignal2.h | 2 +- .../trade_sys/slippage/SlippageBase.cpp | 3 +-- .../slippage/imp/FixedPercentSlippage.cpp | 2 +- .../slippage/imp/FixedPercentSlippage.h | 2 +- .../slippage/imp/FixedValueSlippage.cpp | 2 +- .../slippage/imp/FixedValueSlippage.h | 2 +- .../trade_sys/stoploss/StoplossBase.cpp | 3 +-- .../stoploss/imp/FixedPercentStoploss.cpp | 2 +- .../stoploss/imp/FixedPercentStoploss.h | 2 +- .../stoploss/imp/IndicatorStoploss.cpp | 2 +- .../stoploss/imp/IndicatorStoploss.h | 2 +- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 2 +- hikyuu_cpp/hikyuu/utilities/Parameter.h | 24 +++++++++-------- .../moneymanager/test_MM_FixedCount.cpp | 7 ++--- 65 files changed, 165 insertions(+), 79 deletions(-) diff --git a/docs/source/trade_sys/money_manager.rst b/docs/source/trade_sys/money_manager.rst index dbddd6c6..613db139 100644 --- a/docs/source/trade_sys/money_manager.rst +++ b/docs/source/trade_sys/money_manager.rst @@ -50,7 +50,7 @@ .. py:function:: MM_FixedCapital([capital = 10000.0]) - 固定资本资金管理策略 + 固定资本资金管理策略, 即控制每次买入投入的总资金 :param float capital: 固定资本单位 :return: 资金管理策略实例 diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp index 220bf4c9..6be3b406 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeCostBase.cpp @@ -31,8 +31,7 @@ TradeCostBase::TradeCostBase(const string& name) : m_name(name) {} TradeCostBase::~TradeCostBase() {} -void TradeCostBase::checkParam(const string& name) const {} - +void TradeCostBase::baseCheckParam(const string& name) const {} void TradeCostBase::paramChanged() {} TradeCostPtr TradeCostBase::clone() { diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp index f569ecdb..d997ef5d 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp @@ -23,7 +23,7 @@ FixedA2015TradeCost::FixedA2015TradeCost() : TradeCostBase("TC_FixedA2015") { FixedA2015TradeCost::~FixedA2015TradeCost() {} -void FixedA2015TradeCost::checkParam(const string& name) const { +void FixedA2015TradeCost::_checkParam(const string& name) const { if ("commission" == name) { HKU_ASSERT(getParam("commission") >= 0.0); } else if ("lowest_commission" == name) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h index 3031e467..05ded041 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.h @@ -20,7 +20,7 @@ public: FixedA2015TradeCost(); virtual ~FixedA2015TradeCost(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; /** * 计算买入成本 diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp index 87aee1fa..df347cf5 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp @@ -23,7 +23,7 @@ FixedA2017TradeCost::FixedA2017TradeCost() : TradeCostBase("TC_FixedA2017") { FixedA2017TradeCost::~FixedA2017TradeCost() {} -void FixedA2017TradeCost::checkParam(const string& name) const { +void FixedA2017TradeCost::_checkParam(const string& name) const { if ("commission" == name) { HKU_ASSERT(getParam("commission") >= 0.0); } else if ("lowest_commission" == name) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h index c3f475fd..ff2114c5 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.h @@ -23,7 +23,7 @@ public: FixedA2017TradeCost(); virtual ~FixedA2017TradeCost(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; /** * 计算买入成本 diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp index 6fc45df8..51212fb3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp @@ -35,7 +35,7 @@ FixedATradeCost::FixedATradeCost(price_t commission, price_t lowestCommission, p FixedATradeCost::~FixedATradeCost() {} -void FixedATradeCost::checkParam(const string& name) const { +void FixedATradeCost::_checkParam(const string& name) const { if ("commission" == name) { HKU_ASSERT(getParam("commission") >= 0.0); } else if ("lowest_commission" == name) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h index 963cf701..71d904b7 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.h @@ -56,7 +56,7 @@ public: price_t transferfee, price_t lowestTransferfee); virtual ~FixedATradeCost(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; /** * 计算买入成本 diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index a0dc5b1e..0793944b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -56,6 +56,9 @@ AllocateFundsBase::AllocateFundsBase(const string& name) AllocateFundsBase::~AllocateFundsBase() {} +void AllocateFundsBase::baseCheckParam(const string& name) const {} +void AllocateFundsBase::paramChanged() {} + void AllocateFundsBase::reset() { // 参数检查 double default_reserve_percent = getParam("default_reserve_percent"); diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 60f3cb5b..34e27c94 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -20,7 +20,7 @@ namespace hku { * @ingroup AllocateFunds */ class HKU_API AllocateFundsBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: /** 默认构造函数 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp index 40c4b945..05054e66 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp @@ -19,6 +19,13 @@ FixedWeightAllocateFunds::FixedWeightAllocateFunds() : AllocateFundsBase("AF_Fix FixedWeightAllocateFunds::~FixedWeightAllocateFunds() {} +void FixedWeightAllocateFunds::_checkParam(const string& name) const { + if ("weight" == name) { + double weight = getParam("weight"); + HKU_ASSERT(weight > 0.0 && weight <= 1.); + } +} + SystemWeightList FixedWeightAllocateFunds ::_allocateWeight(const Datetime& date, const SystemWeightList& se_list) { SystemWeightList result; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.h index bc028033..b2b65183 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.h @@ -20,6 +20,7 @@ class FixedWeightAllocateFunds : public AllocateFundsBase { public: FixedWeightAllocateFunds(); virtual ~FixedWeightAllocateFunds(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp index 9e1a9e61..3469e12e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.cpp @@ -30,6 +30,9 @@ ConditionBase::ConditionBase(const string& name) : m_name(name) {} ConditionBase::~ConditionBase() {} +void ConditionBase::baseCheckParam(const string& name) const {} +void ConditionBase::paramChanged() {} + void ConditionBase::reset() { m_kdata = Null(); m_tm.reset(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h index 09d53c63..6f076088 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h @@ -22,7 +22,7 @@ namespace hku { * @ingroup Condition */ class HKU_API ConditionBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: ConditionBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp index e1f91835..f808b9f6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp @@ -29,6 +29,9 @@ EnvironmentBase::EnvironmentBase(const string& name) : m_name(name) {} EnvironmentBase::~EnvironmentBase() {} +void EnvironmentBase::baseCheckParam(const string& name) const {} +void EnvironmentBase::paramChanged() {} + void EnvironmentBase::reset() { std::unique_lock lock(m_mutex); m_query = Null(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h index ecbfc51d..3f00a14a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h @@ -22,7 +22,7 @@ namespace hku { * @ingroup Environment */ class HKU_API EnvironmentBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: EnvironmentBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp index 9790d081..8a44b6ca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp @@ -24,6 +24,14 @@ BoolEnvironment::BoolEnvironment(const Indicator& ind) : EnvironmentBase("EV_Boo BoolEnvironment::~BoolEnvironment() {} +void BoolEnvironment::_checkParam(const string& name) const { + if ("market" == name) { + string market = getParam(name); + auto market_info = StockManager::instance().getMarketInfo(name); + HKU_CHECK(market_info == Null(), "Invalid market: {}", market); + } +} + EnvironmentPtr BoolEnvironment::_clone() { return make_shared(m_ind.clone()); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.h b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.h index acfb0f72..b58c387e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.h +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.h @@ -18,6 +18,7 @@ public: explicit BoolEnvironment(const Indicator& ind); virtual ~BoolEnvironment(); + virtual void _checkParam(const string& name) const override; virtual void _calculate() override; virtual EnvironmentPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp index f767434c..cb9bce81 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp @@ -26,6 +26,14 @@ TwoLineEnvironment::TwoLineEnvironment(const Indicator& fast, const Indicator& s TwoLineEnvironment::~TwoLineEnvironment() {} +void TwoLineEnvironment::_checkParam(const string& name) const { + if ("market" == name) { + string market = getParam(name); + auto market_info = StockManager::instance().getMarketInfo(name); + HKU_CHECK(market_info == Null(), "Invalid market: {}", market); + } +} + EnvironmentPtr TwoLineEnvironment::_clone() { TwoLineEnvironment* ptr = new TwoLineEnvironment; ptr->m_fast = m_fast.clone(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.h b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.h index ac6217a0..97200203 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.h +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.h @@ -20,6 +20,7 @@ public: TwoLineEnvironment(const Indicator& fast, const Indicator& slow); virtual ~TwoLineEnvironment(); + virtual void _checkParam(const string& name) const override; virtual void _calculate() override; virtual EnvironmentPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index ae3251f7..2c8f88cf 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -102,7 +102,7 @@ void MultiFactorBase::initParam() { setParam("zscore_nsigma", 3.0); } -void MultiFactorBase::checkParam(const string& name) const { +void MultiFactorBase::baseCheckParam(const string& name) const { if ("ic_n" == name) { HKU_ASSERT(getParam("ic_n") >= 1); } else if ("zscore_nsigma" == name) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp index b3fcdabe..72690844 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp @@ -29,8 +29,7 @@ ICIRMultiFactor::ICIRMultiFactor(const vector& inds, const StockList& checkParam("ic_rolling_n"); } -void ICIRMultiFactor::checkParam(const string& name) const { - MultiFactorBase::checkParam(name); +void ICIRMultiFactor::_checkParam(const string& name) const { if ("ic_rolling_n" == name) { HKU_ASSERT(getParam("ic_rolling_n") >= 1); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h index ed08adcb..896a3409 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h @@ -21,7 +21,7 @@ public: const Stock& ref_stk, int ic_n, int ic_rolling_n); virtual ~ICIRMultiFactor() = default; - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp index 5b772d63..4dce321e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp @@ -28,8 +28,7 @@ ICMultiFactor::ICMultiFactor(const IndicatorList& inds, const StockList& stks, c checkParam("ic_rolling_n"); } -void ICMultiFactor::checkParam(const string& name) const { - MultiFactorBase::checkParam(name); +void ICMultiFactor::_checkParam(const string& name) const { if ("ic_rolling_n" == name) { HKU_ASSERT(getParam("ic_rolling_n") >= 1); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h index 7fdf8fe9..40c1e6af 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h @@ -21,7 +21,7 @@ public: const Stock& ref_stk, int ic_n, int ic_rolling_n); virtual ~ICMultiFactor() = default; - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp index abfbbbe7..fb7c312f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp @@ -40,6 +40,14 @@ MoneyManagerBase::MoneyManagerBase(const string& name) : m_name(name) { MoneyManagerBase::~MoneyManagerBase() {} +void MoneyManagerBase::baseCheckParam(const string& name) const { + if ("max-stock" == name) { + HKU_ASSERT(getParam("max-stock") >= 1); + } +} + +void MoneyManagerBase::paramChanged() {} + void MoneyManagerBase::buyNotify(const TradeRecord&) {} void MoneyManagerBase::sellNotify(const TradeRecord&) {} @@ -110,21 +118,16 @@ double MoneyManagerBase::getBuyNumber(const Datetime& datetime, const Stock& sto double n = _getBuyNumber(datetime, stock, price, risk, from); double min_trade = stock.minTradeNumber(); - - if (n < min_trade) { - HKU_TRACE("Ignore! Is less than the minimum number of transactions({}<{}) {}", n, min_trade, - stock.market_code()); - return 0; - } + HKU_TRACE_IF_RETURN(n < min_trade, 0.0, + "Ignore! Is less than the minimum number of transactions({}<{}) {}", n, + min_trade, stock.market_code()); // 转换为最小交易量的整数倍 - n = long(n / min_trade) * min_trade; - double max_trade = stock.maxTradeNumber(); + n = int64_t(n / min_trade) * min_trade; - if (n > max_trade) { - n = max_trade; - HKU_INFO("Over stock.maxTradeNumber({})!", max_trade); - } + double max_trade = stock.maxTradeNumber(); + HKU_WARN_IF_RETURN(n > max_trade, max_trade, + "Over stock.maxTradeNumber({}), will use maxTradeNumber", max_trade); // 在现金不足时,自动补充存入现金 if (getParam("auto-checkin")) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h index e9744e29..713988e3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h @@ -20,7 +20,7 @@ namespace hku { * @ingroup MoneyManager */ class HKU_API MoneyManagerBase : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: MoneyManagerBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp index 9f314b5b..a87471ab 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp @@ -19,6 +19,13 @@ FixedCapitalMoneyManager::FixedCapitalMoneyManager() : MoneyManagerBase("MM_Fixe FixedCapitalMoneyManager::~FixedCapitalMoneyManager() {} +void FixedCapitalMoneyManager::_checkParam(const string& name) const { + if ("capital" == name) { + double capital = getParam("capital"); + HKU_ASSERT(capital > 0.0); + } +} + double FixedCapitalMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { double capital = getParam("capital"); diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.h index 01a3a936..196aafb1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.h @@ -20,6 +20,7 @@ class FixedCapitalMoneyManager : public MoneyManagerBase { public: FixedCapitalMoneyManager(); virtual ~FixedCapitalMoneyManager(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp index 51cf58ce..12d3dd57 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp @@ -19,6 +19,13 @@ FixedCountMoneyManager::FixedCountMoneyManager() : MoneyManagerBase("MM_FixedCou FixedCountMoneyManager::~FixedCountMoneyManager() {} +void FixedCountMoneyManager::_checkParam(const string& name) const { + if ("n" == name) { + double n = getParam("n"); + HKU_ASSERT(n > 0.0); + } +} + double FixedCountMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { return getParam("n"); diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.h index 68dd1b6f..62003c75 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.h @@ -31,6 +31,7 @@ class FixedCountMoneyManager : public MoneyManagerBase { public: FixedCountMoneyManager(); virtual ~FixedCountMoneyManager(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp index 5ce5cde7..eecc9fde 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp @@ -19,11 +19,16 @@ FixedPercentMoneyManager::FixedPercentMoneyManager() : MoneyManagerBase("MM_Fixe FixedPercentMoneyManager::~FixedPercentMoneyManager() {} +void FixedPercentMoneyManager::_checkParam(const string& name) const { + if ("p" == name) { + double p = getParam("p"); + HKU_ASSERT(p > 0 && p <= 1.0); + } +} + double FixedPercentMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { double p = getParam("p"); - HKU_ERROR_IF_RETURN(p <= 0.0 || p > 1.0, 0.0, "Error param (p = {:<.4f})", p); - HKU_ERROR_IF_RETURN(risk == 0.0, 0.0, "risk is zero!"); return m_tm->cash(datetime, m_query.kType()) * p / risk; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.h index 0e1e6fc6..b06e308b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.h @@ -26,6 +26,7 @@ class HKU_API FixedPercentMoneyManager : public MoneyManagerBase { public: FixedPercentMoneyManager(); virtual ~FixedPercentMoneyManager(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp index 06aaf8c2..083aa391 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp @@ -20,6 +20,13 @@ FixedRatioMoneyManager::FixedRatioMoneyManager() FixedRatioMoneyManager::~FixedRatioMoneyManager() {} +void FixedRatioMoneyManager::_checkParam(const string& name) const { + if ("delta" == name) { + double delta = getParam("delta"); + HKU_ASSERT(delta > 0.0); + } +} + void FixedRatioMoneyManager::_reset() { m_current_num = 1; m_pre_cash = 0.0; diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h index 407474bc..0db9ad2b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h @@ -18,6 +18,7 @@ public: FixedRatioMoneyManager(); virtual ~FixedRatioMoneyManager(); + virtual void _checkParam(const string& name) const override; virtual void _reset() override; virtual MoneyManagerPtr _clone() override; virtual double _getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp index a0a6f14e..385b6341 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp @@ -19,6 +19,13 @@ FixedRiskMoneyManager::FixedRiskMoneyManager() : MoneyManagerBase("MM_FixedRisk" FixedRiskMoneyManager::~FixedRiskMoneyManager() {} +void FixedRiskMoneyManager::_checkParam(const string& name) const { + if ("risk" == name) { + double risk = getParam("risk"); + HKU_ASSERT(risk > 0.0); + } +} + double FixedRiskMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { return getParam("risk") / risk; diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.h index bd280555..6aa9c539 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.h @@ -20,6 +20,7 @@ class FixedRiskMoneyManager : public MoneyManagerBase { public: FixedRiskMoneyManager(); virtual ~FixedRiskMoneyManager(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp index 581113b9..630e9520 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp @@ -19,6 +19,13 @@ FixedUnitsMoneyManager::FixedUnitsMoneyManager() : MoneyManagerBase("MM_FixedUni FixedUnitsMoneyManager::~FixedUnitsMoneyManager() {} +void FixedUnitsMoneyManager::_checkParam(const string& name) const { + if ("n" == name) { + int n = getParam("n"); + HKU_ASSERT(n > 0); + } +} + double FixedUnitsMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.h index 2fcd45b8..d1fd6a3e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.h @@ -20,6 +20,7 @@ class FixedUnitsMoneyManager : public MoneyManagerBase { public: FixedUnitsMoneyManager(); virtual ~FixedUnitsMoneyManager(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp index 518d97b6..fd514d14 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp @@ -21,8 +21,18 @@ WilliamsFixedRiskMoneyManager::WilliamsFixedRiskMoneyManager() WilliamsFixedRiskMoneyManager::~WilliamsFixedRiskMoneyManager() {} -double WilliamsFixedRiskMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, - price_t price, price_t risk, SystemPart from) { +void WilliamsFixedRiskMoneyManager::_checkParam(const string& name) const { + if ("p" == name) { + double p = getParam("p"); + HKU_ASSERT(p > 0.0); + } else if ("max_loss" == name) { + price_t max_loss = getParam("max_loss"); + HKU_ASSERT(max_loss > 0.0); + } +} + +double WilliamsFixedRiskMoneyManager::_getBuyNumber(const Datetime& datetime, const Stock& stock, + price_t price, price_t risk, SystemPart from) { price_t max_loss = getParam("max_loss"); HKU_WARN_IF_RETURN(max_loss <= 0.0, 0.0, "max_loss is zero!"); return m_tm->cash(datetime, m_query.kType()) * getParam("p") / max_loss; diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.h index 081ca814..4d8e5fed 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.h @@ -20,6 +20,7 @@ class WilliamsFixedRiskMoneyManager : public MoneyManagerBase { public: WilliamsFixedRiskMoneyManager(); virtual ~WilliamsFixedRiskMoneyManager(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp index 14eacc05..1153a259 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.cpp @@ -29,8 +29,7 @@ ProfitGoalBase::ProfitGoalBase(const string& name) : m_name(name) {} ProfitGoalBase::~ProfitGoalBase() {} -void ProfitGoalBase::checkParam(const string& name) const {} - +void ProfitGoalBase::baseCheckParam(const string& name) const {} void ProfitGoalBase::paramChanged() {} void ProfitGoalBase::reset() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp index e22a5283..f560e7b5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.cpp @@ -19,7 +19,7 @@ FixedHoldDays::FixedHoldDays() : ProfitGoalBase("PG_FixedHoldDays") { FixedHoldDays::~FixedHoldDays() {} -void FixedHoldDays::checkParam(const string& name) const { +void FixedHoldDays::_checkParam(const string& name) const { if ("days" == name) { int days = getParam(name); HKU_ASSERT(days > 0); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h index 68f8a0b4..2dbec4a7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedHoldDays.h @@ -21,7 +21,7 @@ public: FixedHoldDays(); virtual ~FixedHoldDays(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp index 6710b3cb..15d82ba9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.cpp @@ -21,7 +21,7 @@ FixedPercentProfitGoal::~FixedPercentProfitGoal() {} void FixedPercentProfitGoal::_calculate() {} -void FixedPercentProfitGoal::checkParam(const string& name) const { +void FixedPercentProfitGoal::_checkParam(const string& name) const { if ("p" == name) { double p = getParam(name); HKU_ASSERT(p > 0.0); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h index 14af29e0..adfcda6c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/FixedPercentProfitGoal.h @@ -20,7 +20,7 @@ class FixedPercentProfitGoal : public ProfitGoalBase { public: FixedPercentProfitGoal(); virtual ~FixedPercentProfitGoal(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 49896134..2970d079 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -36,8 +36,7 @@ SelectorBase::SelectorBase(const string& name) : m_name(name) { SelectorBase::~SelectorBase() {} -void SelectorBase::checkParam(const string& name) const {} - +void SelectorBase::baseCheckParam(const string& name) const {} void SelectorBase::paramChanged() {} void SelectorBase::removeAll() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index b44a2e2e..51ee60c2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -36,8 +36,7 @@ SignalBase::SignalBase(const string& name) : m_name(name), m_hold_long(false), m SignalBase::~SignalBase() {} -void SignalBase::checkParam(const string& name) const {} - +void SignalBase::baseCheckParam(const string& name) const {} void SignalBase::paramChanged() {} SignalPtr SignalBase::clone() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp index bbf48f01..5561318b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp @@ -28,7 +28,7 @@ SingleSignal::SingleSignal(const Indicator& ind) : SignalBase("SG_Single"), m_in SingleSignal::~SingleSignal() {} -void SingleSignal::checkParam(const string& name) const { +void SingleSignal::_checkParam(const string& name) const { if ("filter_n" == name) { HKU_ASSERT(getParam("filter_n") >= 3); } else if ("filter_p" == name) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h index 36ba4186..3de41838 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h @@ -20,7 +20,7 @@ public: explicit SingleSignal(const Indicator& ind); virtual ~SingleSignal(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; virtual SignalPtr _clone() override; virtual void _calculate() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp index f30bd821..053c9aba 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp @@ -31,7 +31,7 @@ SingleSignal2::SingleSignal2(const Indicator& ind) : SignalBase("SG_Single2"), m SingleSignal2::~SingleSignal2() {} -void SingleSignal2::checkParam(const string& name) const { +void SingleSignal2::_checkParam(const string& name) const { if ("filter_n" == name) { HKU_ASSERT(getParam("filter_n") >= 3); } else if ("filter_p" == name) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h index f0295650..22bd590f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h @@ -20,7 +20,7 @@ public: explicit SingleSignal2(const Indicator&); virtual ~SingleSignal2(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; virtual SignalPtr _clone() override; virtual void _calculate() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp index bed90712..bf93da90 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.cpp @@ -27,8 +27,7 @@ SlippageBase::SlippageBase() : m_name("SlippageBase") {} SlippageBase::SlippageBase(const string& name) : m_name(name) {} -void SlippageBase::checkParam(const string& name) const {} - +void SlippageBase::baseCheckParam(const string& name) const {} void SlippageBase::paramChanged() {} void SlippageBase::reset() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp index 0e7128df..7f597b53 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp @@ -19,7 +19,7 @@ FixedPercentSlippage::FixedPercentSlippage() : SlippageBase("FixedPercent") { FixedPercentSlippage::~FixedPercentSlippage() {} -void FixedPercentSlippage::checkParam(const string& name) const { +void FixedPercentSlippage::_checkParam(const string& name) const { if ("p" == name) { double p = getParam(name); HKU_ASSERT(p >= 0.0 && p < 1.0); diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h index 149c2d8c..bd872919 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.h @@ -20,7 +20,7 @@ class FixedPercentSlippage : public SlippageBase { public: FixedPercentSlippage(); virtual ~FixedPercentSlippage(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp index bd54dd7e..be1c6def 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp @@ -19,7 +19,7 @@ FixedValueSlippage::FixedValueSlippage() { FixedValueSlippage::~FixedValueSlippage() {} -void FixedValueSlippage::checkParam(const string& name) const { +void FixedValueSlippage::_checkParam(const string& name) const { if ("p" == name) { HKU_ASSERT(getParam(name) >= 0.0); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h index 69c40841..ed15bc3e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.h @@ -20,7 +20,7 @@ class FixedValueSlippage : public SlippageBase { public: FixedValueSlippage(); virtual ~FixedValueSlippage(); - virtual void checkParam(const string& name) const override; + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp index ba49e4bb..38e76d81 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.cpp @@ -29,8 +29,7 @@ StoplossBase::StoplossBase(const string& name) : m_name(name) {} StoplossBase::~StoplossBase() {} -void StoplossBase::checkParam(const string& name) const {} - +void StoplossBase::baseCheckParam(const string& name) const {} void StoplossBase::paramChanged() {} void StoplossBase::reset() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp index 30dba219..b372691d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp @@ -19,7 +19,7 @@ FixedPercentStoploss::FixedPercentStoploss() : StoplossBase("ST_FixedPercent") { FixedPercentStoploss::~FixedPercentStoploss() {} -void FixedPercentStoploss::checkParam(const string& name) const { +void FixedPercentStoploss::_checkParam(const string& name) const { if ("p" == name) { double p = getParam("p"); HKU_ASSERT(p > 0.0 && p <= 1.0); diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h index 6d885d53..60b57aa2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h @@ -23,7 +23,7 @@ class FixedPercentStoploss : public StoplossBase { public: FixedPercentStoploss(); virtual ~FixedPercentStoploss(); - virtual void checkParam(const string& name) const; + virtual void _checkParam(const string& name) const; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp index d247c5ad..fa1a1a80 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp @@ -26,7 +26,7 @@ IndicatorStoploss::IndicatorStoploss(const Indicator& op, const string& kdata_pa IndicatorStoploss::~IndicatorStoploss() {} -void IndicatorStoploss::checkParam(const string& name) const { +void IndicatorStoploss::_checkParam(const string& name) const { if ("kpart" == name) { string kpart = getParam("kpart"); HKU_ASSERT("CLOSE" == kpart || "OPEN" == kpart || "HIGH" == kpart || "LOW" == kpart || diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h index c4e506e1..fe41f6e3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h @@ -19,7 +19,7 @@ public: IndicatorStoploss(); // 仅用于序列化默认构造函数 IndicatorStoploss(const Indicator& op, const string& kdata_part); virtual ~IndicatorStoploss(); - virtual void checkParam(const string& name) const; + virtual void _checkParam(const string& name) const; virtual price_t getPrice(const Datetime& datetime, price_t price) override; virtual void _reset() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 1132ff31..571e8d72 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -123,7 +123,7 @@ void System::initParam() { setParam("shared_sp", false); } -void System::checkParam(const string& name) const { +void System::baseCheckParam(const string& name) const { if ("max_delay_count" == name) { HKU_ASSERT(getParam("max_delay_count") >= 0); } else if ("tp_delay_n" == name) { diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index 8705fc97..16d9d4f7 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -352,21 +352,23 @@ public: \ /** * 支持自定义类参数检查及变化通知 - * 需要实现重载以下虚函数接口: - * virtual void checkParam(const string& name) -- 内部实现对应参数的检查,如果不合法需抛出异常 - * virtual void paramChanged() -- 参数变化时调用该函数 - * 注意:由于默认参数一般在类的构造函数中设置,此时由于 checkParam 和 paramChanged - * 均为虚函数,实际上是调用的是基类的虚函数。一般构造函数设置初始的默认参数时, - * 子类的 checkParam 和 paramChanged 即使没有被调用也没有影响。但如果需要用到 - * 构造函数中带入的参数值,则需要在 setParam 执行后,自行调用 checkParam, 或者 - * 在 setParam 执行之前,直接对构造函数传入的参数进行检查(子类和基类均是如此) - * 另:python 中一般不用引出这里个函数,python 类继承时可以自己进行检查 + * 子类需要实现重载以下虚函数接口: + * virtual void _checkParam(const string& name) -- 内部实现对应参数的检查,如果不合法需抛出异常 + * 另:python 中一般不需要引出 paramChanged/checkParam/_checkParam,python + * 类继承时可以自己在初始化时进行检查 */ #define PARAMETER_SUPPORT_WITH_CHECK \ protected: \ Parameter m_params; \ - virtual void checkParam(const string& name) const; \ - virtual void paramChanged(); \ + void paramChanged(); \ + void checkParam(const string& name) const { \ + baseCheckParam(name); \ + _checkParam(name); \ + } \ + virtual void _checkParam(const string& name) const {} \ + \ +private: \ + void baseCheckParam(const string& name) const; \ \ public: \ const Parameter& getParameter() const { \ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/moneymanager/test_MM_FixedCount.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/moneymanager/test_MM_FixedCount.cpp index 984e64f3..b4320943 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/moneymanager/test_MM_FixedCount.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/moneymanager/test_MM_FixedCount.cpp @@ -26,15 +26,12 @@ TEST_CASE("test_MM_FixedCount") { TradeManagerPtr tm = crtTM(Datetime(199001010000LL), 0.0, TC_FixedA()); /** @arg n < 1 */ - MoneyManagerPtr mm = MM_FixedCount(0); - mm->setTM(tm); - int result = mm->getBuyNumber(Datetime(200101010000), stock, 10.0, 10.0, PART_SIGNAL); - CHECK_EQ(result, 0); + CHECK_THROWS_AS(MM_FixedCount(0), std::exception); /** @arg n = 100, 一个初始资金为0的交易账户,能够执行买入操作 */ tm = crtTM(Datetime(199001010000LL), 0.0, TC_FixedA()); CHECK_EQ(tm->initCash(), 0.0); - mm = MM_FixedCount(100); + auto mm = MM_FixedCount(100); mm->setTM(tm); mm->setParam("auto-checkin", true); mm->getBuyNumber(Datetime(200001200000), stock, 24.11, 24.11, PART_SIGNAL); From b999287081a8e2a96e9b2a251823e119fb9e9401 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 22:33:35 +0800 Subject: [PATCH 088/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20indicator=20?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=A3=80=E6=9F=A5=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/IndicatorImp.h | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp | 11 ++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IAdvance.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp | 10 ++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IAma.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IAtr.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IBackset.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp | 7 +++++++ hikyuu_cpp/hikyuu/indicator/imp/ICorr.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ICount.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ICval.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp | 11 ++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IDecline.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IDevsq.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IEma.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IEvery.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IExist.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IFilter.h | 2 ++ hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IHighLine.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IIc.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp | 8 ++++++++ hikyuu_cpp/hikyuu/indicator/imp/IKData.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ILowLine.h | 1 + .../hikyuu/indicator/imp/ILowLineBars.cpp | 6 ++++++ .../hikyuu/indicator/imp/ILowLineBars.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IMa.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp | 10 ++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IMacd.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp | 2 ++ hikyuu_cpp/hikyuu/indicator/imp/IPow.h | 1 + .../hikyuu/indicator/imp/IPriceList.cpp | 8 ++++++++ hikyuu_cpp/hikyuu/indicator/imp/IPriceList.h | 3 ++- hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp | 8 ++++++++ hikyuu_cpp/hikyuu/indicator/imp/IRecover.h | 2 ++ hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRef.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRoc.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRocp.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRocr.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRocr100.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRound.h | 1 + .../hikyuu/indicator/imp/IRoundDown.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.h | 1 + .../hikyuu/indicator/imp/ISaftyLoss.cpp | 8 ++++++++ hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ISlice.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ISlope.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ISma.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 7 +++++++ hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp | 7 +++++++ hikyuu_cpp/hikyuu/indicator/imp/IStdev.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp | 7 +++++++ hikyuu_cpp/hikyuu/indicator/imp/IStdp.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/ISum.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp | 12 ++++++++++- hikyuu_cpp/hikyuu/indicator/imp/ITime.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp | 7 +++++++ hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IVar.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IVarp.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp | 6 ++++++ hikyuu_cpp/hikyuu/indicator/imp/IVigor.h | 1 + hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp | 9 +++++++-- hikyuu_cpp/hikyuu/indicator/imp/IZScore.h | 1 + .../hikyuu/trade_manage/TradeManagerBase.h | 11 +++++++++- .../environment/imp/BoolEnvironment.cpp | 2 +- .../environment/imp/TwoLineEnvironment.cpp | 2 +- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 11 ++++++++++ .../hikyuu/trade_sys/portfolio/Portfolio.h | 2 +- hikyuu_cpp/hikyuu/utilities/Parameter.h | 5 ++++- .../unit_test/hikyuu/indicator/test_CORR.cpp | 6 ++---- .../unit_test/hikyuu/indicator/test_IC.cpp | 4 +--- .../hikyuu/indicator/test_SAFTYLOSS.cpp | 20 +++---------------- .../hikyuu/indicator/test_SPEARMAN.cpp | 6 ++---- .../unit_test/hikyuu/indicator/test_STDEV.cpp | 6 +----- .../unit_test/hikyuu/indicator/test_STDP.cpp | 7 +------ .../unit_test/hikyuu/indicator/test_VAR.cpp | 7 +------ .../unit_test/hikyuu/indicator/test_VARP.cpp | 7 +------ .../hikyuu/indicator/test_ZSCORE.cpp | 8 ++------ 109 files changed, 407 insertions(+), 67 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index c159ea88..e8039768 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -173,6 +173,12 @@ IndicatorImp::IndicatorImp(const string &name, size_t result_num) m_result_num = result_num < MAX_RESULT_NUM ? result_num : MAX_RESULT_NUM; } +void IndicatorImp::baseCheckParam(const string &name) const {} + +void IndicatorImp::paramChanged() { + m_need_calculate = true; +} + void IndicatorImp::setIndParam(const string &name, const Indicator &ind) { IndicatorImpPtr imp = ind.getImp(); HKU_CHECK(imp, "Invalid input ind, no concrete implementation!"); diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h index ea43e041..a4ca89d1 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -26,7 +26,7 @@ class HKU_API IndParam; * @ingroup Indicator */ class HKU_API IndicatorImp : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK friend HKU_API std::ostream& operator<<(std::ostream& os, const IndicatorImp& imp); public: diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp index 829861e9..13b47102 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp @@ -33,6 +33,17 @@ bool IAdvance::check() { return true; } +void IAdvance::_checkParam(const string& name) const { + if ("market" == name) { + string market = getParam(name); + auto market_info = StockManager::instance().getMarketInfo(market); + HKU_CHECK(market_info != Null(), "Invalid market: {}", market); + } else if ("stk_type" == name) { + int stk_type = getParam("stk_type"); + HKU_ASSERT(stk_type >= 0); + } +} + void IAdvance::_calculate(const Indicator& ind) { // ref_date_list 参数会影响 IndicatorImp 全局,勿随意修改 string market; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.h b/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.h index 47ec5797..d6d8bd6e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.h @@ -22,6 +22,7 @@ class IAdvance : public IndicatorImp { public: IAdvance(); virtual ~IAdvance(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp index 7d34c629..8b79b455 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp @@ -29,6 +29,16 @@ bool IAma::check() { return getParam("n") >= 1 && getParam("fast_n") >= 0 && getParam("slow_n") >= 0; } +void IAma::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 1); + } else if ("fast_n" == name) { + HKU_ASSERT(getParam("fast_n") >= 0); + } else if ("slow_n" == name) { + HKU_ASSERT(getParam("slow_n") >= 0); + } +} + void IAma::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAma.h b/hikyuu_cpp/hikyuu/indicator/imp/IAma.h index 90b7514f..4d47f867 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAma.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAma.h @@ -27,6 +27,7 @@ public: IAma(); virtual ~IAma(); + virtual void _checkParam(const string& name) const override; virtual void _dyn_calculate(const Indicator&) override; private: diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp index 6e7c0f27..0a714059 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp @@ -25,6 +25,12 @@ bool IAtr::check() { return getParam("n") >= 1; } +void IAtr::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 1); + } +} + void IAtr::_calculate(const Indicator& indicator) { size_t total = indicator.size(); m_discard = indicator.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAtr.h b/hikyuu_cpp/hikyuu/indicator/imp/IAtr.h index e452c4a5..92bfc3f8 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAtr.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAtr.h @@ -20,6 +20,7 @@ class IAtr : public IndicatorImp { public: IAtr(); virtual ~IAtr(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp index 268ab531..f8ae4e49 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp @@ -25,6 +25,12 @@ bool IBackset::check() { return getParam("n") >= 1; } +void IBackset::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 1); + } +} + void IBackset::_calculate(const Indicator& ind) { size_t total = ind.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBackset.h b/hikyuu_cpp/hikyuu/indicator/imp/IBackset.h index 7ac0ed25..caa61e7f 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBackset.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBackset.h @@ -23,6 +23,7 @@ public: IBackset(); virtual ~IBackset(); + virtual void _checkParam(const string& name) const override; virtual bool isSerial() const override { return true; } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp index 353d940e..8db08348 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp @@ -33,6 +33,13 @@ bool ICorr::check() { return n == 0 || n >= 2; } +void ICorr::_checkParam(const string& name) const { + if ("n" == name) { + int n = getParam("n"); + HKU_ASSERT(n == 0 || n >= 2); + } +} + IndicatorImpPtr ICorr::_clone() { ICorr* p = new ICorr(); p->m_ref_ind = m_ref_ind.clone(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h index 796f8b40..796b7dc0 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h @@ -19,6 +19,7 @@ public: virtual ~ICorr(); virtual bool check() override; + virtual void _checkParam(const string& name) const override; virtual void _calculate(const Indicator& data) override; virtual IndicatorImpPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp index 44523f6f..66e35b89 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp @@ -23,6 +23,12 @@ bool ICount::check() { return getParam("n") >= 0; } +void ICount::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void ICount::_calculate(const Indicator& data) { size_t total = data.size(); if (0 == total) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICount.h b/hikyuu_cpp/hikyuu/indicator/imp/ICount.h index 1c0d6080..a0ec291f 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICount.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICount.h @@ -25,6 +25,7 @@ class ICount : public IndicatorImp { public: ICount(); virtual ~ICount(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp index 81ff3fcc..9a798bd5 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp @@ -29,6 +29,12 @@ bool ICval::check() { return getParam("discard") < 0 ? false : true; } +void ICval::_checkParam(const string& name) const { + if ("discard" == name) { + HKU_ASSERT(getParam("discard") >= 0); + } +} + void ICval::_calculate(const Indicator& data) { double value = getParam("value"); int discard = getParam("discard"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICval.h b/hikyuu_cpp/hikyuu/indicator/imp/ICval.h index 90450771..e16ba50e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICval.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICval.h @@ -21,6 +21,7 @@ public: ICval(); ICval(double value, size_t discard); virtual ~ICval(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp index de933bdf..24ca3602 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp @@ -33,6 +33,17 @@ bool IDecline::check() { return true; } +void IDecline::_checkParam(const string& name) const { + if ("market" == name) { + string market = getParam(name); + auto market_info = StockManager::instance().getMarketInfo(market); + HKU_CHECK(market_info != Null(), "Invalid market: {}", market); + } else if ("stk_type" == name) { + int stk_type = getParam("stk_type"); + HKU_ASSERT(stk_type >= 0); + } +} + void IDecline::_calculate(const Indicator& ind) { // ref_date_list 参数会影响 IndicatorImp 全局,勿随意修改 string market; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDecline.h b/hikyuu_cpp/hikyuu/indicator/imp/IDecline.h index 40319ffe..b20c308c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDecline.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDecline.h @@ -22,6 +22,7 @@ class IDecline : public IndicatorImp { public: IDecline(); virtual ~IDecline(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp index b1da9acc..79d1bab7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp @@ -26,6 +26,12 @@ bool IDevsq::check() { return getParam("n") >= 2; } +void IDevsq::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 2); + } +} + void IDevsq::_calculate(const Indicator& data) { size_t total = data.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.h b/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.h index 04a01d5f..509e0ae0 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.h @@ -22,6 +22,7 @@ class IDevsq : public hku::IndicatorImp { public: IDevsq(); virtual ~IDevsq(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp index 2690ca2b..dd3c7d83 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp @@ -25,6 +25,12 @@ bool IEma::check() { return getParam("n") > 0; } +void IEma::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") > 0); + } +} + void IEma::_calculate(const Indicator& indicator) { size_t total = indicator.size(); m_discard = indicator.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEma.h b/hikyuu_cpp/hikyuu/indicator/imp/IEma.h index 3fc01c6c..e0193bc5 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEma.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEma.h @@ -23,6 +23,7 @@ class IEma : public IndicatorImp { public: IEma(); virtual ~IEma(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp index f0503126..fa5475f0 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp @@ -25,6 +25,12 @@ bool IEvery::check() { return getParam("n") >= 0; } +void IEvery::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IEvery::_calculate(const Indicator& ind) { size_t total = ind.size(); HKU_IF_RETURN(0 == total, void()); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEvery.h b/hikyuu_cpp/hikyuu/indicator/imp/IEvery.h index 41bc4621..f5acfc32 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEvery.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEvery.h @@ -25,6 +25,7 @@ class IEvery : public IndicatorImp { public: IEvery(); virtual ~IEvery(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp index aa6351ef..f05c7fbe 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp @@ -25,6 +25,12 @@ bool IExist::check() { return getParam("n") >= 0; } +void IExist::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IExist::_calculate(const Indicator& ind) { size_t total = ind.size(); HKU_IF_RETURN(total == 0, void()); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IExist.h b/hikyuu_cpp/hikyuu/indicator/imp/IExist.h index a62d082e..90d25d5d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IExist.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IExist.h @@ -25,6 +25,7 @@ class IExist : public IndicatorImp { public: IExist(); virtual ~IExist(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp index c6c97b95..e12f8e13 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp @@ -25,6 +25,12 @@ bool IFilter::check() { return getParam("n") >= 0; } +void IFilter::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IFilter::_calculate(const Indicator& ind) { size_t total = ind.size(); m_discard = ind.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFilter.h b/hikyuu_cpp/hikyuu/indicator/imp/IFilter.h index e797f2e9..f04c61d9 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFilter.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFilter.h @@ -23,6 +23,8 @@ public: IFilter(); virtual ~IFilter(); + virtual void _checkParam(const string& name) const override; + virtual bool isSerial() const override { return true; } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp index e6800bfe..1b8a88ad 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp @@ -25,6 +25,12 @@ bool IHhvbars::check() { return getParam("n") >= 0; } +void IHhvbars::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IHhvbars::_calculate(const Indicator& ind) { size_t total = ind.size(); if (0 == total) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.h b/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.h index 96b31970..ff0ec4ad 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.h @@ -22,6 +22,7 @@ class IHhvbars : public IndicatorImp { public: IHhvbars(); virtual ~IHhvbars(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp index 1e9038c0..86a6d9fa 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp @@ -25,6 +25,12 @@ bool IHighLine::check() { return getParam("n") >= 0; } +void IHighLine::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IHighLine::_calculate(const Indicator& ind) { size_t total = ind.size(); if (0 == total) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.h b/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.h index c993be9e..b6763bb5 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.h @@ -26,6 +26,7 @@ class IHighLine : public IndicatorImp { public: IHighLine(); virtual ~IHighLine(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index ea0c0f16..36c3287a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -37,6 +37,12 @@ bool IIc::check() { return getParam("n") >= 1; } +void IIc::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 1); + } +} + IndicatorImpPtr IIc::_clone() { IIc* p = new IIc(); p->m_stks = m_stks; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.h b/hikyuu_cpp/hikyuu/indicator/imp/IIc.h index a6316352..a755a32b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.h @@ -17,6 +17,7 @@ public: IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk); virtual ~IIc(); + virtual void _checkParam(const string& name) const override; virtual bool check() override; virtual void _calculate(const Indicator& data) override; virtual IndicatorImpPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp index 8166e132..8121c6e6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp @@ -34,6 +34,14 @@ bool IKData::check() { "CLOSE" == part || "AMO" == part || "VOL" == part); } +void IKData::_checkParam(const string& name) const { + if ("kpart" == name) { + string part = getParam("kpart"); + HKU_ASSERT("KDATA" == part || "OPEN" == part || "HIGH" == part || "LOW" == part || + "CLOSE" == part || "AMO" == part || "VOL" == part); + } +} + // 支持KDATA Indicator作为参数 void IKData::_calculate(const Indicator& ind) { HKU_WARN_IF(!isLeaf() && !ind.empty(), diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IKData.h b/hikyuu_cpp/hikyuu/indicator/imp/IKData.h index cb6e18f1..06c174ce 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IKData.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IKData.h @@ -22,6 +22,7 @@ public: IKData(); IKData(const KData&, const string&); virtual ~IKData(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp index 9bc6de01..84720e85 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp @@ -25,6 +25,12 @@ bool ILowLine::check() { return haveIndParam("n") || getParam("n") >= 0; } +void ILowLine::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void ILowLine::_calculate(const Indicator& ind) { size_t total = ind.size(); if (0 == total) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.h b/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.h index 9688ba10..3333f42c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.h @@ -22,6 +22,7 @@ class ILowLine : public IndicatorImp { public: ILowLine(); virtual ~ILowLine(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp index af5beeb0..8f97676c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp @@ -25,6 +25,12 @@ bool ILowLineBars::check() { return getParam("n") >= 0; } +void ILowLineBars::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void ILowLineBars::_calculate(const Indicator& ind) { size_t total = ind.size(); if (0 == total) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.h b/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.h index c1fe5d87..fce32200 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.h @@ -22,6 +22,7 @@ class ILowLineBars : public IndicatorImp { public: ILowLineBars(); virtual ~ILowLineBars(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp index 30e1c9d4..69a69fdf 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp @@ -23,6 +23,12 @@ bool IMa::check() { return getParam("n") >= 0; } +void IMa::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IMa::_calculate(const Indicator& indicator) { size_t total = indicator.size(); m_discard = indicator.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMa.h b/hikyuu_cpp/hikyuu/indicator/imp/IMa.h index b35a5cbf..f6bb2504 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMa.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMa.h @@ -20,6 +20,7 @@ class IMa : public IndicatorImp { public: IMa(); virtual ~IMa(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp index 4e838e55..89a653c3 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp @@ -29,6 +29,16 @@ bool IMacd::check() { return getParam("n1") > 0 && getParam("n2") > 0 && getParam("n3") > 0; } +void IMacd::_checkParam(const string& name) const { + if ("n1" == name) { + HKU_ASSERT(getParam("n1") >= 0); + } else if ("n2" == name) { + HKU_ASSERT(getParam("n2") >= 0); + } else if ("n3" == name) { + HKU_ASSERT(getParam("n3") >= 0); + } +} + void IMacd::_calculate(const Indicator& data) { size_t total = data.size(); HKU_IF_RETURN(total == 0, void()); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMacd.h b/hikyuu_cpp/hikyuu/indicator/imp/IMacd.h index 06c746c1..2ed24265 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMacd.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMacd.h @@ -30,6 +30,7 @@ public: IMacd(); virtual ~IMacd(); + virtual void _checkParam(const string& name) const override; virtual void _dyn_calculate(const Indicator&) override; private: diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp index a9b27a76..3669ff6b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp @@ -25,6 +25,8 @@ bool IPow::check() { return true; } +void IPow::_checkParam(const string& name) const {} + void IPow::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPow.h b/hikyuu_cpp/hikyuu/indicator/imp/IPow.h index 38059a80..6fb9d963 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPow.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPow.h @@ -25,6 +25,7 @@ class IPow : public IndicatorImp { public: IPow(); virtual ~IPow(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp index 49fd3f6a..edd00c9f 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp @@ -31,6 +31,14 @@ bool IPriceList::check() { return (getParam("discard") >= 0 && getParam("result_index") >= 0); } +void IPriceList::_checkParam(const string& name) const { + if ("discard" == name) { + HKU_ASSERT(getParam("discard") >= 0); + } else if ("result_index" == name) { + HKU_ASSERT(getParam("result_index") >= 0); + } +} + void IPriceList::_calculate(const Indicator& data) { // 如果在叶子节点,直接取自身的data参数 if (isLeaf()) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.h b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.h index 7c704c2e..c8ad5e99 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.h @@ -13,7 +13,7 @@ namespace hku { -//找到数组最后一个Null,并将之前的数据全部置为Null +// 找到数组最后一个Null,并将之前的数据全部置为Null class IPriceList : public IndicatorImp { INDICATOR_IMP(IPriceList) INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION @@ -22,6 +22,7 @@ public: IPriceList(); IPriceList(const PriceList&, int discard); virtual ~IPriceList(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp index c0b2b685..53c6665c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp @@ -34,6 +34,14 @@ bool IRecover::check() { return recover_type >= KQuery::NO_RECOVER && recover_type < KQuery::INVALID_RECOVER_TYPE; } +void IRecover::_checkParam(const string& name) const { + if ("recover_type" == name) { + int recover_type = getParam("recover_type"); + HKU_ASSERT(recover_type >= KQuery::NO_RECOVER && + recover_type < KQuery::INVALID_RECOVER_TYPE); + } +} + void IRecover::checkInputIndicator(const Indicator& ind) { HKU_CHECK(typeid(*(ind.getImp())) == typeid(IKData), "Only the following indicators are accepted: OPEN|HIGH|CLOSE|LOW"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h index 3610ceb4..5bbc2864 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h @@ -21,6 +21,8 @@ public: IRecover(const KData&, int recoverType); virtual ~IRecover(); + virtual void _checkParam(const string& name) const override; + static void checkInputIndicator(const Indicator& ind); }; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp index 9162e046..366229b1 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp @@ -23,6 +23,12 @@ bool IRef::check() { return getParam("n") >= 0; } +void IRef::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IRef::_calculate(const Indicator& data) { size_t total = data.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRef.h b/hikyuu_cpp/hikyuu/indicator/imp/IRef.h index 3b936cfd..f1e61c89 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRef.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRef.h @@ -26,6 +26,7 @@ class IRef : public IndicatorImp { public: IRef(); virtual ~IRef(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp index 3befc58a..be1ce1f6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp @@ -25,6 +25,12 @@ bool IRoc::check() { return getParam("n") >= 0; } +void IRoc::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IRoc::_calculate(const Indicator& ind) { size_t total = ind.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoc.h b/hikyuu_cpp/hikyuu/indicator/imp/IRoc.h index 1fd0ece8..878b3f43 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoc.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoc.h @@ -22,6 +22,7 @@ class IRoc : public hku::IndicatorImp { public: IRoc(); virtual ~IRoc(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp index 5fbd93a0..a2d59555 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp @@ -25,6 +25,12 @@ bool IRocp::check() { return getParam("n") >= 0; } +void IRocp::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IRocp::_calculate(const Indicator& ind) { size_t total = ind.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocp.h b/hikyuu_cpp/hikyuu/indicator/imp/IRocp.h index 7230b91c..393876d7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocp.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocp.h @@ -22,6 +22,7 @@ class IRocp : public hku::IndicatorImp { public: IRocp(); virtual ~IRocp(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp index 140c054d..d325d861 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp @@ -25,6 +25,12 @@ bool IRocr::check() { return getParam("n") >= 0; } +void IRocr::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IRocr::_calculate(const Indicator& ind) { size_t total = ind.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocr.h b/hikyuu_cpp/hikyuu/indicator/imp/IRocr.h index 91a3dade..076b75ef 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocr.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocr.h @@ -22,6 +22,7 @@ class IRocr : public hku::IndicatorImp { public: IRocr(); virtual ~IRocr(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp index 2ff794eb..7d533172 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp @@ -25,6 +25,12 @@ bool IRocr100::check() { return getParam("n") >= 0; } +void IRocr100::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void IRocr100::_calculate(const Indicator& ind) { size_t total = ind.size(); int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.h b/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.h index 7391a245..9b52181d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.h @@ -22,6 +22,7 @@ class IRocr100 : public hku::IndicatorImp { public: IRocr100(); virtual ~IRocr100(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp index 4770d415..0bfbf526 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp @@ -25,6 +25,12 @@ bool IRound::check() { return getParam("ndigits") >= 0; } +void IRound::_checkParam(const string& name) const { + if ("ndigits" == name) { + HKU_ASSERT(getParam("ndigits") >= 0); + } +} + void IRound::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRound.h b/hikyuu_cpp/hikyuu/indicator/imp/IRound.h index 50799430..a66346b7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRound.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRound.h @@ -25,6 +25,7 @@ class IRound : public IndicatorImp { public: IRound(); virtual ~IRound(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp index 179d6ce5..be7cfb01 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp @@ -25,6 +25,12 @@ bool IRoundDown::check() { return getParam("ndigits") >= 0; } +void IRoundDown::_checkParam(const string& name) const { + if ("ndigits" == name) { + HKU_ASSERT(getParam("ndigits") >= 0); + } +} + void IRoundDown::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.h b/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.h index e6d71b70..216a0602 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.h @@ -25,6 +25,7 @@ class IRoundDown : public IndicatorImp { public: IRoundDown(); virtual ~IRoundDown(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp index 659a7cdc..fcc00df9 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp @@ -25,6 +25,12 @@ bool IRoundUp::check() { return getParam("ndigits") >= 0; } +void IRoundUp::_checkParam(const string& name) const { + if ("ndigits" == name) { + HKU_ASSERT(getParam("ndigits") >= 0); + } +} + void IRoundUp::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.h b/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.h index 9bd3e31c..48eb2b5c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.h @@ -25,6 +25,7 @@ class IRoundUp : public IndicatorImp { public: IRoundUp(); virtual ~IRoundUp(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp index 4a3aefc5..985d20c1 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp @@ -28,6 +28,14 @@ bool ISaftyLoss::check() { return getParam("n1") >= 2 && getParam("n2") >= 1; } +void ISaftyLoss::_checkParam(const string& name) const { + if ("n1" == name) { + HKU_ASSERT(getParam("n1") >= 2); + } else if ("n2" == name) { + HKU_ASSERT(getParam("n2") >= 1); + } +} + void ISaftyLoss::_calculate(const Indicator& data) { size_t total = data.size(); HKU_IF_RETURN(total == 0, void()); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h index 34fc860c..e9dd8e8c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.h @@ -33,6 +33,7 @@ public: ISaftyLoss(); virtual ~ISaftyLoss(); + virtual void _checkParam(const string& name) const override; virtual void _dyn_calculate(const Indicator&) override; private: diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp index c429aa56..3c156e77 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp @@ -33,6 +33,12 @@ bool ISlice::check() { return getParam("result_index") >= 0; } +void ISlice::_checkParam(const string& name) const { + if ("result_index" == name) { + HKU_ASSERT(getParam("result_index") >= 0); + } +} + void ISlice::_calculate(const Indicator& data) { // 如果在叶子节点,直接取自身的data参数 if (isLeaf()) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISlice.h b/hikyuu_cpp/hikyuu/indicator/imp/ISlice.h index 3be9221f..0243306f 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISlice.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISlice.h @@ -19,6 +19,7 @@ public: ISlice(); ISlice(const PriceList&, int64_t start, int64_t end); virtual ~ISlice(); + virtual void _checkParam(const string& name) const override; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp index 9d71d7c5..fda31db9 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp @@ -23,6 +23,12 @@ bool ISlope::check() { return getParam("n") >= 0; } +void ISlope::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void ISlope::_calculate(const Indicator& ind) { size_t total = ind.size(); m_discard = ind.discard() + 1; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISlope.h b/hikyuu_cpp/hikyuu/indicator/imp/ISlope.h index 71b17c8c..def1d9c6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISlope.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISlope.h @@ -19,6 +19,7 @@ class ISlope : public IndicatorImp { public: ISlope(); virtual ~ISlope(); + virtual void _checkParam(const string& name) const override; }; } // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp index 48601eef..5c6a1e5e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp @@ -29,6 +29,12 @@ bool ISma::check() { return getParam("n") >= 1; } +void ISma::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 1); + } +} + void ISma::_calculate(const Indicator& ind) { size_t total = ind.size(); m_discard = ind.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISma.h b/hikyuu_cpp/hikyuu/indicator/imp/ISma.h index 52ee5abb..3a4e980f 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISma.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISma.h @@ -23,6 +23,7 @@ public: ISma(); virtual ~ISma(); + virtual void _checkParam(const string& name) const override; virtual void _dyn_calculate(const Indicator&) override; private: diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index f60c6320..4a56624b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -34,6 +34,13 @@ bool ISpearman::check() { return n == 0 || n >= 2; } +void ISpearman::_checkParam(const string &name) const { + if ("n" == name) { + int n = getParam("n"); + HKU_ASSERT(n == 0 || n >= 2); + } +} + IndicatorImpPtr ISpearman::_clone() { ISpearman *p = new ISpearman(); p->m_ref_ind = m_ref_ind.clone(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h index ac313e1d..0e338cca 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h @@ -18,6 +18,7 @@ public: ISpearman(const Indicator& ref_ind, int n); virtual ~ISpearman(); + virtual void _checkParam(const string& name) const override; virtual bool check() override; virtual void _calculate(const Indicator& data) override; virtual IndicatorImpPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp index 878fb0ec..7b045a17 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp @@ -25,6 +25,13 @@ bool IStdev::check() { return n == 0 || n >= 2; } +void IStdev::_checkParam(const string& name) const { + if ("n" == name) { + int n = getParam("n"); + HKU_ASSERT(n == 0 || n >= 2); + } +} + void IStdev::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.h b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.h index 05413e5f..6e7ca129 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.h @@ -25,6 +25,7 @@ class IStdev : public hku::IndicatorImp { public: IStdev(); virtual ~IStdev(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp index b3d25afd..9c29d7cc 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp @@ -27,6 +27,13 @@ bool IStdp::check() { return n == 0 || n >= 2; } +void IStdp::_checkParam(const string& name) const { + if ("n" == name) { + int n = getParam("n"); + HKU_ASSERT(n == 0 || n >= 2); + } +} + void IStdp::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.h b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.h index c13e04d5..97d93a5c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.h @@ -26,6 +26,7 @@ class IStdp : public hku::IndicatorImp { public: IStdp(); virtual ~IStdp(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp index fd19d44a..60247e50 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp @@ -23,6 +23,12 @@ bool ISum::check() { return getParam("n") >= 0; } +void ISum::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 0); + } +} + void ISum::_calculate(const Indicator& ind) { size_t total = ind.size(); if (0 == total || ind.discard() >= total) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISum.h b/hikyuu_cpp/hikyuu/indicator/imp/ISum.h index 0d106fde..f62806dd 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISum.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISum.h @@ -20,6 +20,7 @@ class ISum : public IndicatorImp { public: ISum(); virtual ~ISum(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp index a7ed1bf2..c7ffbeae 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp @@ -14,7 +14,7 @@ BOOST_CLASS_EXPORT(hku::ITime) namespace hku { ITime::ITime() : IndicatorImp("TIME") { - setParam("type", "time"); + setParam("type", "TIME"); } ITime::~ITime() {} @@ -34,6 +34,16 @@ bool ITime::check() { "WEEK" == part || "DAY" == part || "HOUR" == part || "MINUTE" == part; } +void ITime::_checkParam(const string& name) const { + if ("type" == name) { + string param_type = getParam("type"); + HKU_CHECK("TIME" == param_type || "DATE" == param_type || "YEAR" == param_type || + "MONTH" == param_type || "WEEK" == param_type || "DAY" == param_type || + "HOUR" == param_type || "MINUTE" == param_type, + "Invalid param type: {}", param_type); + } +} + void ITime::_calculate(const Indicator& data) { HKU_WARN_IF(!isLeaf() && !data.empty(), "The input is ignored because {} depends on the context!", m_name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITime.h b/hikyuu_cpp/hikyuu/indicator/imp/ITime.h index 85fa2de3..6c9043fa 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITime.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITime.h @@ -25,6 +25,7 @@ public: ITime(); ITime(const KData&, const string& type); virtual ~ITime(); + virtual void _checkParam(const string& name) const override; }; } // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp index 547a6a70..e7e69eeb 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp @@ -32,6 +32,13 @@ bool ITimeLine::check() { return part == "price" || part == "vol"; } +void ITimeLine::_checkParam(const string& name) const { + if ("part" == name) { + string part = getParam("part"); + HKU_ASSERT(part == "price" || part == "vol"); + } +} + void ITimeLine::_calculate(const Indicator& data) { HKU_WARN_IF(!isLeaf() && !data.empty(), "The input is ignored because {} depends on the context!", m_name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.h b/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.h index a14bdd39..62eb6b64 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.h @@ -24,6 +24,7 @@ public: ITimeLine(); explicit ITimeLine(const KData&); virtual ~ITimeLine(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp index 3dcf6f26..7ac6bc58 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp @@ -26,6 +26,12 @@ bool IVar::check() { return getParam("n") >= 2; } +void IVar::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 2); + } +} + void IVar::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVar.h b/hikyuu_cpp/hikyuu/indicator/imp/IVar.h index 69d7dec4..282aa94a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVar.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVar.h @@ -25,6 +25,7 @@ class IVar : public hku::IndicatorImp { public: IVar(); virtual ~IVar(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp index 4cf8c91f..a6c06333 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp @@ -26,6 +26,12 @@ bool IVarp::check() { return getParam("n") >= 2; } +void IVarp::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 2); + } +} + void IVarp::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVarp.h b/hikyuu_cpp/hikyuu/indicator/imp/IVarp.h index 7d8a5417..14d840b1 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVarp.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVarp.h @@ -25,6 +25,7 @@ class IVarp : public hku::IndicatorImp { public: IVarp(); virtual ~IVarp(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp index 8659f838..32b58bfe 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp @@ -31,6 +31,12 @@ bool IVigor::check() { return getParam("n") >= 1; } +void IVigor::_checkParam(const string& name) const { + if ("n" == name) { + HKU_ASSERT(getParam("n") >= 1); + } +} + void IVigor::_calculate(const Indicator& ind) { HKU_WARN_IF(!isLeaf() && !ind.empty(), "The input is ignored because {} depends on the context!", m_name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVigor.h b/hikyuu_cpp/hikyuu/indicator/imp/IVigor.h index db7ba8dd..fc7deabf 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVigor.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVigor.h @@ -28,6 +28,7 @@ public: IVigor(); explicit IVigor(int n); virtual ~IVigor(); + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp index ddb417f6..e752c91a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp @@ -31,6 +31,12 @@ bool IZScore::check() { return getParam("nsigma") > 0.; } +void IZScore::_checkParam(const string &name) const { + if ("nsigma" == name) { + HKU_ASSERT(getParam("nsigma") > 0); + } +} + static void normalize(IndicatorImp::value_t *dst, Indicator::value_t const *src, size_t total, bool outExtreme, double nsigma, bool recursive) { IndicatorImp::value_t sum = 0.0; @@ -62,7 +68,6 @@ static void normalize(IndicatorImp::value_t *dst, Indicator::value_t const *src, } } - if (outExtreme) { IndicatorImp::value_t ulimit = nsigma; IndicatorImp::value_t llimit = -nsigma; @@ -100,7 +105,7 @@ void IZScore::_calculate(const Indicator &data) { auto const *src = data.data() + m_discard; auto *dst = this->data() + m_discard; normalize(dst, src, total - m_discard, outExtreme, nsigma, recursive); - + for (size_t i = m_discard; i < total; i++) { if (!std::isnan(dst[i])) { m_discard = i; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h index 5a942552..f32939f4 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.h @@ -19,6 +19,7 @@ public: IZScore(); IZScore(bool outExtreme, double nsigma, bool recursive); virtual ~IZScore(); + virtual void _checkParam(const string& name) const override; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index 79247319..bf50a80f 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -29,7 +29,7 @@ namespace hku { * @ingroup TradeManagerClass */ class HKU_API TradeManagerBase { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: TradeManagerBase() : TradeManagerBase("", TC_Zero()) {} @@ -664,6 +664,15 @@ private: BOOST_SERIALIZATION_ASSUME_ABSTRACT(TradeManagerBase) #endif +inline void TradeManagerBase::baseCheckParam(const string& name) const { + if ("precision" == name) { + int precision = getParam("precision"); + HKU_ASSERT(precision > 0); + } +} + +inline void TradeManagerBase::paramChanged() {} + /** * 客户程序应使用此类型进行实际操作 * @ingroup TradeManagerClass diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp index 8a44b6ca..7da275bc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp @@ -28,7 +28,7 @@ void BoolEnvironment::_checkParam(const string& name) const { if ("market" == name) { string market = getParam(name); auto market_info = StockManager::instance().getMarketInfo(name); - HKU_CHECK(market_info == Null(), "Invalid market: {}", market); + HKU_CHECK(market_info != Null(), "Invalid market: {}", market); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp index cb9bce81..a7f67d00 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp @@ -30,7 +30,7 @@ void TwoLineEnvironment::_checkParam(const string& name) const { if ("market" == name) { string market = getParam(name); auto market_info = StockManager::instance().getMarketInfo(name); - HKU_CHECK(market_info == Null(), "Invalid market: {}", market); + HKU_CHECK(market_info != Null(), "Invalid market: {}", market); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 6a8525ff..87302369 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -57,6 +57,17 @@ Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFP Portfolio::~Portfolio() {} +void Portfolio::baseCheckParam(const string& name) const { + if ("adjust_cycle" == name) { + int adjust_cycle = getParam("adjust_cycle"); + HKU_ASSERT(adjust_cycle >= 1); + } +} + +void Portfolio::paramChanged() { + m_need_calculate = true; +} + void Portfolio::reset() { m_is_ready = false; m_pro_sys_list.clear(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index faa7bd48..dafd7b7b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -19,7 +19,7 @@ namespace hku { * @ingroup Portfolio */ class HKU_API Portfolio : public enable_shared_from_this { - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: /** 默认构造函数 */ diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index 16d9d4f7..0a0bbf04 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -353,7 +353,10 @@ public: \ /** * 支持自定义类参数检查及变化通知 * 子类需要实现重载以下虚函数接口: - * virtual void _checkParam(const string& name) -- 内部实现对应参数的检查,如果不合法需抛出异常 + * virtual void _checkParam(const string& name) const + * 基类需要实现以下接口: + * void baseCheckParam(const string& name) + * void paramChanged() * 另:python 中一般不需要引出 paramChanged/checkParam/_checkParam,python * 类继承时可以自己在初始化时进行检查 */ diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp index 26a7b592..0f0d0541 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_CORR.cpp @@ -42,10 +42,8 @@ TEST_CASE("test_CORR") { Indicator y = PRICELIST(b); // 非法参数 n - result = CORR(x, y, -1); - CHECK_UNARY(result.empty()); - result = CORR(x, y, 1); - CHECK_UNARY(result.empty()); + CHECK_THROWS_AS(CORR(x, y, -1), std::exception); + CHECK_THROWS_AS(CORR(x, y, 1), std::exception); // 正常情况 result = CORR(x, y, 0); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp index 12574db0..c3b69938 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IC.cpp @@ -31,9 +31,7 @@ TEST_CASE("test_IC") { Indicator result; /** @arg 传入非法 n */ - result = IC(MA(CLOSE()), stks, query, ref_stk, -1); - CHECK_EQ(result.name(), "IC"); - CHECK_UNARY(result.empty()); + CHECK_THROWS_AS(IC(MA(CLOSE()), stks, query, ref_stk, -1), std::exception); /** @arg 传入的 ref_stk 为 null */ result = IC(stks, query, Stock(), 1)(MA(CLOSE())); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp index 590f6dc2..0063c2b0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SAFTYLOSS.cpp @@ -38,23 +38,9 @@ TEST_CASE("test_SAFTYLOSS") { query = KQuery(0, 20); kdata = stock.getKData(query); close = CLOSE(kdata); - result = SAFTYLOSS(close, 1, 1); - CHECK_EQ(result.empty(), false); - CHECK_EQ(result.size(), close.size()); - CHECK_EQ(result.discard(), close.size()); - for (size_t i = 0; i < result.size(); ++i) { - CHECK_UNARY(std::isnan(result[i])); - } - - result = SAFTYLOSS(close, 0, 0); - CHECK_EQ(result.empty(), false); - CHECK_EQ(result.size(), close.size()); - CHECK_EQ(result.discard(), close.size()); - - result = SAFTYLOSS(close, 2, 0); - CHECK_EQ(result.empty(), false); - CHECK_EQ(result.size(), close.size()); - CHECK_EQ(result.discard(), close.size()); + CHECK_THROWS_AS(SAFTYLOSS(close, 1, 1), std::exception); + CHECK_THROWS_AS(SAFTYLOSS(close, 0, 0), std::exception); + CHECK_THROWS_AS(SAFTYLOSS(close, 2, 0), std::exception); /** @arg 正常参数 */ result = SAFTYLOSS(close, 2, 1, 1.0); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp index f2a26fb4..6535aabd 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_SPEARMAN.cpp @@ -137,10 +137,8 @@ TEST_CASE("test_SPEARMAN") { Indicator y = PRICELIST(b); /** @arg 非法参数 n */ - result = SPEARMAN(x, y, -1); - CHECK_UNARY(result.empty()); - result = SPEARMAN(x, y, 1); - CHECK_UNARY(result.empty()); + CHECK_THROWS_AS(SPEARMAN(x, y, -1), std::exception); + CHECK_THROWS_AS(SPEARMAN(x, y, 1), std::exception); /** @arg 正常情况 n */ PriceList expect{Null(), 1., 1., 0.95, 0.875}; diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp index 458ee196..c89ee580 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDEV.cpp @@ -46,11 +46,7 @@ TEST_CASE("test_STDEV") { } /** @arg n = 1时 */ - dev = STDEV(ind, 1); - CHECK_EQ(dev.size(), 15); - for (size_t i = 0; i < dev.size(); ++i) { - CHECK_UNARY(std::isnan(dev[i])); - } + CHECK_THROWS_AS(STDEV(ind, 1), std::exception); /** @arg operator() */ Indicator expect = STDEV(ind, 10); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDP.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDP.cpp index 0a025f35..0e4decc3 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDP.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_STDP.cpp @@ -44,12 +44,7 @@ TEST_CASE("test_STDP") { } /** @arg n = 1时 */ - dev = STDP(ind, 1); - CHECK_EQ(dev.name(), "STDP"); - CHECK_EQ(dev.size(), 15); - for (size_t i = 0; i < dev.size(); ++i) { - CHECK_UNARY(std::isnan(dev[i])); - } + CHECK_THROWS_AS(STDP(ind, 1), std::exception); /** @arg operator() */ Indicator expect = STDP(ind, 10); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_VAR.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_VAR.cpp index 59c36fe2..63ea2fd2 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_VAR.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_VAR.cpp @@ -46,12 +46,7 @@ TEST_CASE("test_VAR") { } /** @arg n = 1时 */ - dev = VAR(ind, 1); - CHECK_EQ(dev.name(), "VAR"); - CHECK_EQ(dev.size(), 15); - for (size_t i = 0; i < dev.size(); ++i) { - CHECK_UNARY(std::isnan(dev[i])); - } + CHECK_THROWS_AS(VAR(ind, 1), std::exception); /** @arg operator() */ Indicator expect = VAR(ind, 10); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_VARP.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_VARP.cpp index d12a78ff..f91eb415 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_VARP.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_VARP.cpp @@ -47,12 +47,7 @@ TEST_CASE("test_VARP") { } /** @arg n = 1时 */ - dev = VARP(ind, 1); - CHECK_EQ(dev.name(), "VARP"); - CHECK_EQ(dev.size(), 15); - for (size_t i = 0; i < dev.size(); ++i) { - CHECK_UNARY(std::isnan(dev[i])); - } + CHECK_THROWS_AS(VARP(ind, 1), std::exception); /** @arg operator() */ Indicator expect = VARP(ind, 10); diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp index d6814b27..7dad00f9 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_ZSCORE.cpp @@ -33,11 +33,7 @@ TEST_CASE("test_ZSCORE") { /** @arg 输入的 nsigma < 0 */ KData k = getKData("SH000001", KQuery(-5)); - result = ZSCORE(k.close(), true, -0.5); - CHECK_EQ(result.name(), "ZSCORE"); - CHECK_UNARY(!result.empty()); - CHECK_EQ(result.size(), k.size()); - CHECK_EQ(result.discard(), result.size()); + CHECK_THROWS_AS(ZSCORE(k.close(), true, -0.5), std::exception); /** @arg 正常计算,不剔除异常值 */ result = ZSCORE(k.close()); @@ -60,7 +56,7 @@ TEST_CASE("test_ZSCORE") { CHECK_EQ(result[i], doctest::Approx(expect[i])); } - /** @arg 过滤异常值,递归*/ + /** @arg 过滤异常值,递归*/ k = getKData("SH000001", KQuery(3600, 4000)); c = k.close(); auto result2 = ZSCORE(c, true, 3.0, true); From 2ea1e882034c7307bf8d7f1858895e3d0475739e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 25 Mar 2024 23:17:25 +0800 Subject: [PATCH 089/601] =?UTF-8?q?=E8=B0=83=E6=95=B4Indicator=E5=8F=82?= =?UTF-8?q?=E6=95=B0=E6=A3=80=E6=9F=A5=E4=B8=8E=E6=9B=B4=E6=96=B0=E6=9C=BA?= =?UTF-8?q?=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 21 ------------------- hikyuu_cpp/hikyuu/indicator/IndicatorImp.h | 6 ------ hikyuu_cpp/hikyuu/indicator/imp/IAbs.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAcos.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAd.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAsin.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAtan.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp | 4 ---- .../hikyuu/indicator/imp/IBarsCount.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IBarsLast.cpp | 4 ---- .../hikyuu/indicator/imp/IBarsSince.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ICeil.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp | 5 ----- hikyuu_cpp/hikyuu/indicator/imp/ICorr.h | 1 - hikyuu_cpp/hikyuu/indicator/imp/ICos.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IDiff.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IExp.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IFloor.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IIc.h | 1 - hikyuu_cpp/hikyuu/indicator/imp/IIntpart.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp | 6 ------ .../hikyuu/indicator/imp/ILiuTongPan.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ILn.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ILog.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp | 4 ---- .../hikyuu/indicator/imp/ILowLineBars.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IMdd.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IMrr.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/INot.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp | 4 ---- .../hikyuu/indicator/imp/IPriceList.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp | 5 ----- hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IReverse.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp | 4 ---- .../hikyuu/indicator/imp/IRoundDown.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp | 4 ---- .../hikyuu/indicator/imp/ISaftyLoss.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISign.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISin.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 5 ----- hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h | 1 - hikyuu_cpp/hikyuu/indicator/imp/ISqrt.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp | 5 ----- hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp | 5 ----- hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ISumBars.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ITan.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp | 6 ------ hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp | 5 ----- hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp | 4 ---- hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp | 6 +----- hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.cpp | 4 ---- hikyuu_pywrap/indicator/_IndicatorImp.cpp | 5 ----- 81 files changed, 1 insertion(+), 346 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index e8039768..408fd319 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -687,27 +687,6 @@ Indicator IndicatorImp::calculate() { return Indicator(result); } - if (!check()) { - HKU_ERROR("Invalid param! {} : {}", formula(), long_name()); - if (m_right) { - m_right->calculate(); - _readyBuffer(m_right->size(), m_result_num); - m_discard = m_right->size(); - try { - result = shared_from_this(); - } catch (...) { - // Python中继承的实现会出现bad_weak_ptr错误,通过此方式避免 - result = clone(); - } - } - - if (size() != 0) { - m_need_calculate = false; - } - - return Indicator(result); - } - switch (m_optype) { case LEAF: if (m_ind_params.empty()) { diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h index a4ca89d1..bd288cb7 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.h @@ -148,10 +148,6 @@ public: // =================== // 子类接口 // =================== - virtual bool check() { - return true; - } - virtual void _calculate(const Indicator&) {} virtual void _dyn_run_one_step(const Indicator& ind, size_t curPos, size_t step) {} @@ -329,7 +325,6 @@ private: \ #define INDICATOR_IMP(classname) \ public: \ - virtual bool check() override; \ virtual void _calculate(const Indicator& data) override; \ virtual IndicatorImpPtr _clone() override { \ return make_shared(); \ @@ -337,7 +332,6 @@ public: \ #define INDICATOR_IMP_SUPPORT_DYNAMIC_STEP(classname) \ public: \ - virtual bool check() override; \ virtual void _calculate(const Indicator& ind) override; \ virtual void _dyn_run_one_step(const Indicator& ind, size_t curPos, size_t step) override; \ virtual bool supportIndParam() const override { \ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAbs.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAbs.cpp index 78ad1fd1..5de9ce37 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAbs.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAbs.cpp @@ -17,10 +17,6 @@ IAbs::IAbs() : IndicatorImp("ABS", 1) {} IAbs::~IAbs() {} -bool IAbs::check() { - return true; -} - void IAbs::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAcos.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAcos.cpp index ca1c88e4..068c0a1e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAcos.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAcos.cpp @@ -19,10 +19,6 @@ IAcos::IAcos() : IndicatorImp("ACOS", 1) {} IAcos::~IAcos() {} -bool IAcos::check() { - return true; -} - void IAcos::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAd.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAd.cpp index 19224fd7..e09bcf23 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAd.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAd.cpp @@ -24,10 +24,6 @@ IAd::IAd(const KData& k) : IndicatorImp("AD", 1) { IAd::~IAd() {} -bool IAd::check() { - return true; -} - void IAd::_calculate(const Indicator& data) { HKU_WARN_IF(!isLeaf() && !data.empty(), "The input is ignored because {} depends on the context!", m_name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp index 13b47102..75fa6085 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAdvance.cpp @@ -29,10 +29,6 @@ IAdvance::IAdvance() : IndicatorImp("ADVANCE", 1) { IAdvance::~IAdvance() {} -bool IAdvance::check() { - return true; -} - void IAdvance::_checkParam(const string& name) const { if ("market" == name) { string market = getParam(name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp index ac0f6983..d3c4bdaf 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp @@ -22,10 +22,6 @@ IAlign::IAlign() : IndicatorImp("ALIGN") { IAlign::~IAlign() {} -bool IAlign::check() { - return true; -} - void IAlign::_calculate(const Indicator& ind) { // ref_date_list 参数会影响 IndicatorImp 全局,勿随意修改 DatetimeList dates = getParam("align_date_list"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp index 8b79b455..46c69a3e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp @@ -25,10 +25,6 @@ IAma::IAma() : IndicatorImp("AMA", 2) { IAma::~IAma() {} -bool IAma::check() { - return getParam("n") >= 1 && getParam("fast_n") >= 0 && getParam("slow_n") >= 0; -} - void IAma::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 1); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAsin.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAsin.cpp index 748e713f..b4e50664 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAsin.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAsin.cpp @@ -19,10 +19,6 @@ IAsin::IAsin() : IndicatorImp("ASIN", 1) {} IAsin::~IAsin() {} -bool IAsin::check() { - return true; -} - void IAsin::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAtan.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAtan.cpp index c8be0280..89e4e277 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAtan.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAtan.cpp @@ -19,10 +19,6 @@ IAtan::IAtan() : IndicatorImp("ATAN", 1) {} IAtan::~IAtan() {} -bool IAtan::check() { - return true; -} - void IAtan::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp index 0a714059..73979685 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAtr.cpp @@ -21,10 +21,6 @@ IAtr::IAtr() : IndicatorImp("ATR", 1) { IAtr::~IAtr() {} -bool IAtr::check() { - return getParam("n") >= 1; -} - void IAtr::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 1); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp index f8ae4e49..e86cef23 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBackset.cpp @@ -21,10 +21,6 @@ IBackset::IBackset() : IndicatorImp("BACKSET", 1) { IBackset::~IBackset() {} -bool IBackset::check() { - return getParam("n") >= 1; -} - void IBackset::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 1); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBarsCount.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBarsCount.cpp index fae1de3a..acb7af95 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBarsCount.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBarsCount.cpp @@ -19,10 +19,6 @@ IBarsCount::IBarsCount() : IndicatorImp("BARSCOUNT", 1) {} IBarsCount::~IBarsCount() {} -bool IBarsCount::check() { - return true; -} - void IBarsCount::_calculate(const Indicator& ind) { KData k = ind.getContext(); Stock stk = k.getStock(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBarsLast.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBarsLast.cpp index e8d7e420..b29456f0 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBarsLast.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBarsLast.cpp @@ -19,10 +19,6 @@ IBarsLast::IBarsLast() : IndicatorImp("BARSLAST", 1) {} IBarsLast::~IBarsLast() {} -bool IBarsLast::check() { - return true; -} - void IBarsLast::_calculate(const Indicator& ind) { size_t total = ind.size(); m_discard = ind.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBarsSince.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBarsSince.cpp index 9193b1a1..a2950c6e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBarsSince.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBarsSince.cpp @@ -19,10 +19,6 @@ IBarsSince::IBarsSince() : IndicatorImp("BARSSINCE", 1) {} IBarsSince::~IBarsSince() {} -bool IBarsSince::check() { - return true; -} - void IBarsSince::_calculate(const Indicator &ind) { size_t total = ind.size(); m_discard = ind.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICeil.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICeil.cpp index 726bceac..afc8bfea 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICeil.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICeil.cpp @@ -19,10 +19,6 @@ ICeil::ICeil() : IndicatorImp("CEILING", 1) {} ICeil::~ICeil() {} -bool ICeil::check() { - return true; -} - void ICeil::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp index 8db08348..f1b2c05d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp @@ -28,11 +28,6 @@ ICorr::ICorr(const Indicator& ref_ind, int n) : IndicatorImp("CORR"), m_ref_ind( ICorr::~ICorr() {} -bool ICorr::check() { - int n = getParam("n"); - return n == 0 || n >= 2; -} - void ICorr::_checkParam(const string& name) const { if ("n" == name) { int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h index 796b7dc0..88895b36 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h @@ -18,7 +18,6 @@ public: ICorr(const Indicator& ref_ind, int n); virtual ~ICorr(); - virtual bool check() override; virtual void _checkParam(const string& name) const override; virtual void _calculate(const Indicator& data) override; virtual IndicatorImpPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICos.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICos.cpp index 0c78dcad..45f03b76 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICos.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICos.cpp @@ -19,10 +19,6 @@ ICos::ICos() : IndicatorImp("COS", 1) {} ICos::~ICos() {} -bool ICos::check() { - return true; -} - void ICos::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp index 66e35b89..91106e56 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICount.cpp @@ -19,10 +19,6 @@ ICount::ICount() : IndicatorImp("COUNT", 1) { ICount::~ICount() {} -bool ICount::check() { - return getParam("n") >= 0; -} - void ICount::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp index 9a798bd5..c2b8ee20 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICval.cpp @@ -25,10 +25,6 @@ ICval::ICval(double value, size_t discard) : IndicatorImp("CVAL", 1) { ICval::~ICval() {} -bool ICval::check() { - return getParam("discard") < 0 ? false : true; -} - void ICval::_checkParam(const string& name) const { if ("discard" == name) { HKU_ASSERT(getParam("discard") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp index 24ca3602..b162f67a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDecline.cpp @@ -29,10 +29,6 @@ IDecline::IDecline() : IndicatorImp("DECLINE", 1) { IDecline::~IDecline() {} -bool IDecline::check() { - return true; -} - void IDecline::_checkParam(const string& name) const { if ("market" == name) { string market = getParam(name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp index 79d1bab7..4a58ab95 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDevsq.cpp @@ -22,10 +22,6 @@ IDevsq::IDevsq() : IndicatorImp("DEVSQ", 1) { IDevsq::~IDevsq() {} -bool IDevsq::check() { - return getParam("n") >= 2; -} - void IDevsq::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 2); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDiff.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDiff.cpp index 863c7ba3..c5e82ceb 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDiff.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDiff.cpp @@ -17,10 +17,6 @@ IDiff::IDiff() : IndicatorImp("DIFF", 1) {} IDiff::~IDiff() {} -bool IDiff::check() { - return true; -} - void IDiff::_calculate(const Indicator& data) { size_t total = data.size(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp index 3ceef5c8..f295b099 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp @@ -21,10 +21,6 @@ IDropna::IDropna() : IndicatorImp("DROPNA", 1) { IDropna::~IDropna() {} -bool IDropna::check() { - return true; -} - void IDropna::_calculate(const Indicator& ind) { // ref_date_list 参数会影响 IndicatorImp 全局,勿随意修改 size_t total = ind.size(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp index dd3c7d83..6e697773 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEma.cpp @@ -21,10 +21,6 @@ IEma::IEma() : IndicatorImp("EMA", 1) { IEma::~IEma() {} -bool IEma::check() { - return getParam("n") > 0; -} - void IEma::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") > 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp index fa5475f0..004fe347 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IEvery.cpp @@ -21,10 +21,6 @@ IEvery::IEvery() : IndicatorImp("EVERY", 1) { IEvery::~IEvery() {} -bool IEvery::check() { - return getParam("n") >= 0; -} - void IEvery::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp index f05c7fbe..32c60cd2 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IExist.cpp @@ -21,10 +21,6 @@ IExist::IExist() : IndicatorImp("EXIST", 1) { IExist::~IExist() {} -bool IExist::check() { - return getParam("n") >= 0; -} - void IExist::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IExp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IExp.cpp index 28e4ecca..877a0aba 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IExp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IExp.cpp @@ -17,10 +17,6 @@ IExp::IExp() : IndicatorImp("EXP", 1) {} IExp::~IExp() {} -bool IExp::check() { - return true; -} - void IExp::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp index e12f8e13..8d327065 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFilter.cpp @@ -21,10 +21,6 @@ IFilter::IFilter() : IndicatorImp("FILTER", 1) { IFilter::~IFilter() {} -bool IFilter::check() { - return getParam("n") >= 0; -} - void IFilter::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFloor.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFloor.cpp index cb0de238..b75a5730 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFloor.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFloor.cpp @@ -19,10 +19,6 @@ IFloor::IFloor() : IndicatorImp("FLOOR", 1) {} IFloor::~IFloor() {} -bool IFloor::check() { - return true; -} - void IFloor::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp index 1b8a88ad..61980722 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IHhvbars.cpp @@ -21,10 +21,6 @@ IHhvbars::IHhvbars() : IndicatorImp("HHVBARS", 1) { IHhvbars::~IHhvbars() {} -bool IHhvbars::check() { - return getParam("n") >= 0; -} - void IHhvbars::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp index 86a6d9fa..a034a4d9 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IHighLine.cpp @@ -21,10 +21,6 @@ IHighLine::IHighLine() : IndicatorImp("HHV", 1) { IHighLine::~IHighLine() {} -bool IHighLine::check() { - return getParam("n") >= 0; -} - void IHighLine::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index 36c3287a..f326ec81 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -33,10 +33,6 @@ IIc::IIc(const StockList& stks, const KQuery& query, int n, const Stock& ref_stk IIc::~IIc() {} -bool IIc::check() { - return getParam("n") >= 1; -} - void IIc::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 1); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.h b/hikyuu_cpp/hikyuu/indicator/imp/IIc.h index a755a32b..6036a9ab 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.h @@ -18,7 +18,6 @@ public: virtual ~IIc(); virtual void _checkParam(const string& name) const override; - virtual bool check() override; virtual void _calculate(const Indicator& data) override; virtual IndicatorImpPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIntpart.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIntpart.cpp index 7bb53b1a..70df1a75 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIntpart.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIntpart.cpp @@ -19,10 +19,6 @@ IIntpart::IIntpart() : IndicatorImp("INTPART", 1) {} IIntpart::~IIntpart() {} -bool IIntpart::check() { - return true; -} - void IIntpart::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp index 8121c6e6..cf428542 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IKData.cpp @@ -28,12 +28,6 @@ IKData::IKData(const KData& kdata, const string& part) : IndicatorImp() { IKData::~IKData() {} -bool IKData::check() { - string part = getParam("kpart"); - return ("KDATA" == part || "OPEN" == part || "HIGH" == part || "LOW" == part || - "CLOSE" == part || "AMO" == part || "VOL" == part); -} - void IKData::_checkParam(const string& name) const { if ("kpart" == name) { string part = getParam("kpart"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILiuTongPan.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILiuTongPan.cpp index 8823ca57..710c12a6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILiuTongPan.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILiuTongPan.cpp @@ -22,10 +22,6 @@ ILiuTongPan::ILiuTongPan(const KData& k) : IndicatorImp("LIUTONGPAN", 1) { ILiuTongPan::_calculate(Indicator()); } -bool ILiuTongPan::check() { - return true; -} - void ILiuTongPan::_calculate(const Indicator& data) { HKU_WARN_IF(!isLeaf() && !data.empty(), "The input is ignored because {} depends on the context!", m_name); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILn.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILn.cpp index 5aef5e20..68ff376a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILn.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILn.cpp @@ -17,10 +17,6 @@ ILn::ILn() : IndicatorImp("LN", 1) {} ILn::~ILn() {} -bool ILn::check() { - return true; -} - void ILn::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILog.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILog.cpp index 3364aa26..444e8abb 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILog.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILog.cpp @@ -17,10 +17,6 @@ ILog::ILog() : IndicatorImp("LOG", 1) {} ILog::~ILog() {} -bool ILog::check() { - return true; -} - void ILog::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp index 84720e85..b02cad4e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILowLine.cpp @@ -21,10 +21,6 @@ ILowLine::ILowLine() : IndicatorImp("LLV", 1) { ILowLine::~ILowLine() {} -bool ILowLine::check() { - return haveIndParam("n") || getParam("n") >= 0; -} - void ILowLine::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp index 8f97676c..479e8a3b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ILowLineBars.cpp @@ -21,10 +21,6 @@ ILowLineBars::ILowLineBars() : IndicatorImp("LLVBARS", 1) { ILowLineBars::~ILowLineBars() {} -bool ILowLineBars::check() { - return getParam("n") >= 0; -} - void ILowLineBars::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp index 69a69fdf..124dfdd7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMa.cpp @@ -19,10 +19,6 @@ IMa::IMa() : IndicatorImp("MA", 1) { IMa::~IMa() {} -bool IMa::check() { - return getParam("n") >= 0; -} - void IMa::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp index 89a653c3..cfcf51cd 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMacd.cpp @@ -25,10 +25,6 @@ IMacd::IMacd() : IndicatorImp("MACD", 3) { IMacd::~IMacd() {} -bool IMacd::check() { - return getParam("n1") > 0 && getParam("n2") > 0 && getParam("n3") > 0; -} - void IMacd::_checkParam(const string& name) const { if ("n1" == name) { HKU_ASSERT(getParam("n1") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMdd.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMdd.cpp index 08a1798c..f7042a92 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMdd.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMdd.cpp @@ -17,10 +17,6 @@ IMdd::IMdd() : IndicatorImp("MDD", 1) {} IMdd::~IMdd() {} -bool IMdd::check() { - return true; -} - void IMdd::_calculate(const Indicator &ind) { m_discard = 0; for (size_t i = 0, len = ind.discard(); i < len; i++) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IMrr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IMrr.cpp index 65548797..6a56667c 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IMrr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IMrr.cpp @@ -17,10 +17,6 @@ IMrr::IMrr() : IndicatorImp("MRR", 1) {} IMrr::~IMrr() {} -bool IMrr::check() { - return true; -} - void IMrr::_calculate(const Indicator& ind) { m_discard = 0; for (size_t i = 0, len = ind.discard(); i < len; i++) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/INot.cpp b/hikyuu_cpp/hikyuu/indicator/imp/INot.cpp index e6e1722a..6aae19e3 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/INot.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/INot.cpp @@ -17,10 +17,6 @@ INot::INot() : IndicatorImp("NOT", 1) {} INot::~INot() {} -bool INot::check() { - return true; -} - void INot::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp index 3669ff6b..65fbd8e7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPow.cpp @@ -21,10 +21,6 @@ IPow::IPow() : IndicatorImp("POW", 1) { IPow::~IPow() {} -bool IPow::check() { - return true; -} - void IPow::_checkParam(const string& name) const {} void IPow::_calculate(const Indicator& data) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp index edd00c9f..64e356c6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IPriceList.cpp @@ -27,10 +27,6 @@ IPriceList::IPriceList(const PriceList& data, int in_discard) : IndicatorImp("PR IPriceList::~IPriceList() {} -bool IPriceList::check() { - return (getParam("discard") >= 0 && getParam("result_index") >= 0); -} - void IPriceList::_checkParam(const string& name) const { if ("discard" == name) { HKU_ASSERT(getParam("discard") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp index 53c6665c..0e216517 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.cpp @@ -29,11 +29,6 @@ IRecover::IRecover(const KData& kdata, int recoverType) : IndicatorImp("RECOVER" IRecover::~IRecover() {} -bool IRecover::check() { - int recover_type = getParam("recover_type"); - return recover_type >= KQuery::NO_RECOVER && recover_type < KQuery::INVALID_RECOVER_TYPE; -} - void IRecover::_checkParam(const string& name) const { if ("recover_type" == name) { int recover_type = getParam("recover_type"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp index 366229b1..6f5b3e4d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRef.cpp @@ -19,10 +19,6 @@ IRef::IRef() : IndicatorImp("REF", 1) { IRef::~IRef() {} -bool IRef::check() { - return getParam("n") >= 0; -} - void IRef::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IReverse.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IReverse.cpp index 51fe61d7..40bec2a4 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IReverse.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IReverse.cpp @@ -19,10 +19,6 @@ IReverse::IReverse() : IndicatorImp("REVERSE", 1) {} IReverse::~IReverse() {} -bool IReverse::check() { - return true; -} - void IReverse::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp index be1ce1f6..a8fa7c21 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoc.cpp @@ -21,10 +21,6 @@ IRoc::IRoc() : IndicatorImp("ROC", 1) { IRoc::~IRoc() {} -bool IRoc::check() { - return getParam("n") >= 0; -} - void IRoc::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp index a2d59555..aed6fb62 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocp.cpp @@ -21,10 +21,6 @@ IRocp::IRocp() : IndicatorImp("ROCP", 1) { IRocp::~IRocp() {} -bool IRocp::check() { - return getParam("n") >= 0; -} - void IRocp::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp index d325d861..4dc98e50 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocr.cpp @@ -21,10 +21,6 @@ IRocr::IRocr() : IndicatorImp("ROCR", 1) { IRocr::~IRocr() {} -bool IRocr::check() { - return getParam("n") >= 0; -} - void IRocr::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp index 7d533172..76240724 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRocr100.cpp @@ -21,10 +21,6 @@ IRocr100::IRocr100() : IndicatorImp("ROCR100", 1) { IRocr100::~IRocr100() {} -bool IRocr100::check() { - return getParam("n") >= 0; -} - void IRocr100::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp index 0bfbf526..beb69d65 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRound.cpp @@ -21,10 +21,6 @@ IRound::IRound() : IndicatorImp("ROUND", 1) { IRound::~IRound() {} -bool IRound::check() { - return getParam("ndigits") >= 0; -} - void IRound::_checkParam(const string& name) const { if ("ndigits" == name) { HKU_ASSERT(getParam("ndigits") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp index be7cfb01..c2b07623 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoundDown.cpp @@ -21,10 +21,6 @@ IRoundDown::IRoundDown() : IndicatorImp("ROUNDDOWN", 1) { IRoundDown::~IRoundDown() {} -bool IRoundDown::check() { - return getParam("ndigits") >= 0; -} - void IRoundDown::_checkParam(const string& name) const { if ("ndigits" == name) { HKU_ASSERT(getParam("ndigits") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp index fcc00df9..9f96bb65 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRoundUp.cpp @@ -21,10 +21,6 @@ IRoundUp::IRoundUp() : IndicatorImp("ROUNDUP", 1) { IRoundUp::~IRoundUp() {} -bool IRoundUp::check() { - return getParam("ndigits") >= 0; -} - void IRoundUp::_checkParam(const string& name) const { if ("ndigits" == name) { HKU_ASSERT(getParam("ndigits") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp index 985d20c1..d353c2e3 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISaftyLoss.cpp @@ -24,10 +24,6 @@ ISaftyLoss::ISaftyLoss() : IndicatorImp("SAFTYLOSS", 1) { ISaftyLoss::~ISaftyLoss() {} -bool ISaftyLoss::check() { - return getParam("n1") >= 2 && getParam("n2") >= 1; -} - void ISaftyLoss::_checkParam(const string& name) const { if ("n1" == name) { HKU_ASSERT(getParam("n1") >= 2); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISign.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISign.cpp index 9fffe31f..9f2282a1 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISign.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISign.cpp @@ -19,10 +19,6 @@ ISign::ISign() : IndicatorImp("SGN", 1) {} ISign::~ISign() {} -bool ISign::check() { - return true; -} - void ISign::_calculate(const Indicator& ind) { size_t total = ind.size(); m_discard = ind.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISin.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISin.cpp index e0308cde..b0eeb61b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISin.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISin.cpp @@ -19,10 +19,6 @@ ISin::ISin() : IndicatorImp("SIN", 1) {} ISin::~ISin() {} -bool ISin::check() { - return true; -} - void ISin::_calculate(const Indicator& data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp index 3c156e77..916c50a7 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISlice.cpp @@ -29,10 +29,6 @@ ISlice::ISlice(const PriceList& data, int64_t start, int64_t end) : IndicatorImp ISlice::~ISlice() {} -bool ISlice::check() { - return getParam("result_index") >= 0; -} - void ISlice::_checkParam(const string& name) const { if ("result_index" == name) { HKU_ASSERT(getParam("result_index") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp index fda31db9..de23f61b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISlope.cpp @@ -19,10 +19,6 @@ ISlope::ISlope() : IndicatorImp("SLOPE", 1) { ISlope::~ISlope() {} -bool ISlope::check() { - return getParam("n") >= 0; -} - void ISlope::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp index 5c6a1e5e..bb3a10c5 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISma.cpp @@ -25,10 +25,6 @@ ISma::ISma() : IndicatorImp("SMA", 1) { ISma::~ISma() {} -bool ISma::check() { - return getParam("n") >= 1; -} - void ISma::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 1); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index 4a56624b..28a044e5 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -29,11 +29,6 @@ ISpearman::ISpearman(const Indicator &ref_ind, int n) ISpearman::~ISpearman() {} -bool ISpearman::check() { - int n = getParam("n"); - return n == 0 || n >= 2; -} - void ISpearman::_checkParam(const string &name) const { if ("n" == name) { int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h index 0e338cca..307ec4e4 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h @@ -19,7 +19,6 @@ public: virtual ~ISpearman(); virtual void _checkParam(const string& name) const override; - virtual bool check() override; virtual void _calculate(const Indicator& data) override; virtual IndicatorImpPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISqrt.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISqrt.cpp index e5340c22..ed07de1d 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISqrt.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISqrt.cpp @@ -19,10 +19,6 @@ ISqrt::ISqrt() : IndicatorImp("SQRT", 1) {} ISqrt::~ISqrt() {} -bool ISqrt::check() { - return true; -} - void ISqrt::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp index 7b045a17..23609bd3 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp @@ -20,11 +20,6 @@ IStdev::IStdev() : IndicatorImp("STDEV", 1) { IStdev::~IStdev() {} -bool IStdev::check() { - int n = getParam("n"); - return n == 0 || n >= 2; -} - void IStdev::_checkParam(const string& name) const { if ("n" == name) { int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp index 9c29d7cc..5e98ec81 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdp.cpp @@ -22,11 +22,6 @@ IStdp::IStdp() : IndicatorImp("STDP", 1) { IStdp::~IStdp() {} -bool IStdp::check() { - int n = getParam("n"); - return n == 0 || n >= 2; -} - void IStdp::_checkParam(const string& name) const { if ("n" == name) { int n = getParam("n"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp index 60247e50..3bab45eb 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp @@ -19,10 +19,6 @@ ISum::ISum() : IndicatorImp("SUM", 1) { ISum::~ISum() {} -bool ISum::check() { - return getParam("n") >= 0; -} - void ISum::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 0); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISumBars.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISumBars.cpp index a20d5da8..763ce0de 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISumBars.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISumBars.cpp @@ -21,10 +21,6 @@ ISumBars::ISumBars() : IndicatorImp("SUMBARS", 1) { ISumBars::~ISumBars() {} -bool ISumBars::check() { - return true; -} - void ISumBars::_calculate(const Indicator& ind) { size_t total = ind.size(); m_discard = ind.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITan.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ITan.cpp index 8344a916..a7e45745 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITan.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITan.cpp @@ -19,10 +19,6 @@ ITan::ITan() : IndicatorImp("TAN", 1) {} ITan::~ITan() {} -bool ITan::check() { - return true; -} - void ITan::_calculate(const Indicator &data) { size_t total = data.size(); m_discard = data.discard(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp index c7ffbeae..1f657cc1 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITime.cpp @@ -28,12 +28,6 @@ ITime::ITime(const KData& k, const string& type) : IndicatorImp() { ITime::_calculate(Indicator()); } -bool ITime::check() { - string part = getParam("type"); - return "TIME" == part || "DATE" == part || "YEAR" == part || "MONTH" == part || - "WEEK" == part || "DAY" == part || "HOUR" == part || "MINUTE" == part; -} - void ITime::_checkParam(const string& name) const { if ("type" == name) { string param_type = getParam("type"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp index e7e69eeb..35a346b5 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ITimeLine.cpp @@ -27,11 +27,6 @@ ITimeLine::ITimeLine(const KData& k) : IndicatorImp("TIMELINE", 1) { ITimeLine::_calculate(Indicator()); } -bool ITimeLine::check() { - string part = getParam("part"); - return part == "price" || part == "vol"; -} - void ITimeLine::_checkParam(const string& name) const { if ("part" == name) { string part = getParam("part"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp index 7ac6bc58..d8039422 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVar.cpp @@ -22,10 +22,6 @@ IVar::IVar() : IndicatorImp("VAR", 1) { IVar::~IVar() {} -bool IVar::check() { - return getParam("n") >= 2; -} - void IVar::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 2); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp index a6c06333..685fdd69 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVarp.cpp @@ -22,10 +22,6 @@ IVarp::IVarp() : IndicatorImp("VARP", 1) { IVarp::~IVarp() {} -bool IVarp::check() { - return getParam("n") >= 2; -} - void IVarp::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 2); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp index 32b58bfe..25beeea2 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IVigor.cpp @@ -27,10 +27,6 @@ IVigor::IVigor(int n) : IndicatorImp("VIGOR") { IVigor::~IVigor() {} -bool IVigor::check() { - return getParam("n") >= 1; -} - void IVigor::_checkParam(const string& name) const { if ("n" == name) { HKU_ASSERT(getParam("n") >= 1); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp index e752c91a..d58a4457 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZScore.cpp @@ -27,13 +27,9 @@ IZScore::IZScore(bool outExtreme, double nsigma, bool recursive) : IndicatorImp( IZScore::~IZScore() {} -bool IZScore::check() { - return getParam("nsigma") > 0.; -} - void IZScore::_checkParam(const string &name) const { if ("nsigma" == name) { - HKU_ASSERT(getParam("nsigma") > 0); + HKU_ASSERT(getParam("nsigma") > 0.); } } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.cpp index 555b4b03..c8296c7b 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.cpp @@ -27,10 +27,6 @@ IZhBond10::IZhBond10(const DatetimeList& dates, double default_val) : IndicatorI IZhBond10::~IZhBond10() {} -bool IZhBond10::check() { - return true; -} - void IZhBond10::_calculate(const Indicator& data) { DatetimeList dates; auto k = data.getContext(); diff --git a/hikyuu_pywrap/indicator/_IndicatorImp.cpp b/hikyuu_pywrap/indicator/_IndicatorImp.cpp index b5691e8c..650a07ea 100644 --- a/hikyuu_pywrap/indicator/_IndicatorImp.cpp +++ b/hikyuu_pywrap/indicator/_IndicatorImp.cpp @@ -17,10 +17,6 @@ class PyIndicatorImp : public IndicatorImp { public: using IndicatorImp::IndicatorImp; - bool check() override { - PYBIND11_OVERLOAD(bool, IndicatorImp, check, ); - } - void _calculate(const Indicator& ind) override { PYBIND11_OVERLOAD(void, IndicatorImp, _calculate, ind); } @@ -93,7 +89,6 @@ void export_IndicatorImp(py::module& m) { .def("get_result_num", &IndicatorImp::getResultNumber) .def("get_result_as_price_list", &IndicatorImp::getResultAsPriceList) .def("calculate", &IndicatorImp::calculate) - .def("check", &IndicatorImp::check) .def("clone", &IndicatorImp::clone) .def("_calculate", &IndicatorImp::_calculate) .def("_dyn_run_one_step", &IndicatorImp::_dyn_run_one_step) From 1b811ca84094a297e8650aafdd2cdb6001dd9ddb Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 01:46:26 +0800 Subject: [PATCH 090/601] update --- hikyuu/__init__.py | 15 +++++++++++---- hikyuu/interactive.py | 8 -------- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/hikyuu/__init__.py b/hikyuu/__init__.py index a570865a..1aa71c05 100644 --- a/hikyuu/__init__.py +++ b/hikyuu/__init__.py @@ -69,6 +69,17 @@ except Exception as e: 上提交 issue,同时附上 "用户目录/.hikyuu" 下的 hikyuu_py.log 和 hikyuu.log 日志文件 """) raise e +__version__ = get_version() + +sm = StockManager.instance() + +# 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python +if in_ipython_frontend(): + sm.python_in_jupyter = True + hku_info("hikyuu version: {}", get_version_with_build()) + iodog = OstreamRedirect() + iodog.open() + # ============================================================================== # @@ -84,7 +95,3 @@ V = VOL() D = Datetime K = None Q = Query - -sm = StockManager.instance() - -__version__ = get_version() diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 80251404..d5ba30ed 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -58,14 +58,6 @@ import configparser from hikyuu.data.hku_config_template import generate_default_config from hikyuu import * - -# 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python -if in_ipython_frontend(): - sm.python_in_jupyter = True - hku_info("hikyuu version: {}", get_version_with_build()) - iodog = OstreamRedirect() - iodog.open() - # ============================================================================== # 引入扯线木偶 # ============================================================================== From 54a1261ff2429704a3d283a1c30ad15c00a18bb1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 02:12:02 +0800 Subject: [PATCH 091/601] =?UTF-8?q?=E6=B8=85=E7=90=86=20checkcpp=20?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/data_driver/base_info/table/ZhBond10Table.h | 4 ++-- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 4 ++-- hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp | 6 +++--- hikyuu_cpp/hikyuu/indicator/imp/ICorr.h | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IRecover.h | 2 +- hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.h | 2 +- hikyuu_cpp/hikyuu/trade_manage/Performance.cpp | 8 -------- hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp | 7 +++---- .../hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp | 2 -- .../hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp | 2 +- .../trade_sys/environment/imp/TwoLineEnvironment.cpp | 2 +- hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp | 4 +--- hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h | 2 +- .../hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h | 2 +- .../hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h | 2 +- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 3 +-- 19 files changed, 23 insertions(+), 37 deletions(-) diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/ZhBond10Table.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/ZhBond10Table.h index d403d249..f1b292bf 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/table/ZhBond10Table.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/ZhBond10Table.h @@ -13,8 +13,8 @@ namespace hku { struct ZhBond10Table { TABLE_BIND2(ZhBond10Table, zh_bond10, date, value) - int64_t date; - int64_t value; + int64_t date{0}; + int64_t value{0}; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index 408fd319..61398ae5 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -1514,8 +1514,8 @@ bool IndicatorImp::alike(const IndicatorImp &other) const { m_ind_params.size() != other.m_ind_params.size() || m_params != other.m_params, false); - auto &self_id = typeid(*this); - auto &cval_id = typeid(ICval); + const auto &self_id = typeid(*this); + const auto &cval_id = typeid(ICval); if (self_id == cval_id) { HKU_IF_RETURN(isLeaf() && other.isLeaf(), true); return m_right && m_right->alike(*other.m_right); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp index d3c4bdaf..d209944a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAlign.cpp @@ -131,7 +131,7 @@ void IAlign::_calculate(const Indicator& ind) { for (size_t r = 0; r < m_result_num; r++) { _set(ind.get(j, r), i, r); } - } else if (!fill_null && j < ind_total) { + } else if (!fill_null) { for (size_t r = 0; r < m_result_num; r++) { _set(ind.get(j - 1, r), i, r); } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp index 46c69a3e..3c399b55 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IAma.cpp @@ -57,7 +57,7 @@ void IAma::_calculate(const Indicator& data) { price_t slowest = 2.0 / (slow_n + 1); price_t delta = fastest - slowest; - price_t prevol = 0.0, vol = 0.0, er = 1.0, c = 0.0; + price_t prevol = 0.0, vol = 0.0, er = 1.0; price_t ama = src[start]; size_t first_end = start + n + 1 >= total ? total : start + n + 1; _set(ama, start, 0); @@ -67,7 +67,7 @@ void IAma::_calculate(const Indicator& data) { er = (vol == 0.0) ? 1.0 : (src[i] - src[start]) / vol; if (er > 1.0) er = 1.0; - c = std::pow((std::fabs(er) * delta + slowest), 2); + price_t c = std::pow((std::fabs(er) * delta + slowest), 2); ama += c * (src[i] - ama); dst0[i] = ama; dst1[i] = er; @@ -81,7 +81,7 @@ void IAma::_calculate(const Indicator& data) { er = 1.0; if (er < -1.0) er = -1.0; - c = std::pow((std::fabs(er) * delta + slowest), 2); + price_t c = std::pow((std::fabs(er) * delta + slowest), 2); ama += c * (src[i] - ama); prevol = vol; dst0[i] = ama; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h index 88895b36..d94c15e1 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.h @@ -14,7 +14,7 @@ namespace hku { class ICorr : public IndicatorImp { public: ICorr(); - ICorr(int n); + explicit ICorr(int n); ICorr(const Indicator& ref_ind, int n); virtual ~ICorr(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h index 5bbc2864..09ef0f08 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IRecover.h @@ -17,7 +17,7 @@ class IRecover : public IndicatorImp { public: IRecover(); - IRecover(int recoverType); + explicit IRecover(int recoverType); IRecover(const KData&, int recoverType); virtual ~IRecover(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h index 307ec4e4..3c446ec6 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.h @@ -14,7 +14,7 @@ namespace hku { class ISpearman : public IndicatorImp { public: ISpearman(); - ISpearman(int n); + explicit ISpearman(int n); ISpearman(const Indicator& ref_ind, int n); virtual ~ISpearman(); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp index 23609bd3..1975327e 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IStdev.cpp @@ -84,7 +84,7 @@ void IStdev::_calculate(const Indicator& data) { price_t d_pow = std::pow(d, 2); pow_buf[i] = d_pow; ex2 += d_pow; - size_t num = i - j; + num = i - j; if (num != 1) { dst[i] = std::sqrt((ex2 - std::pow(ex, 2) / num) / (num - 1)); } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.h b/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.h index 3a3fc2ac..3ac6e463 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZhBond10.h @@ -23,7 +23,7 @@ class IZhBond10 : public IndicatorImp { public: IZhBond10(); - IZhBond10(const DatetimeList& dates, double default_val = 4.0); + explicit IZhBond10(const DatetimeList& dates, double default_val = 4.0); virtual ~IZhBond10(); }; diff --git a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp index db87d0a2..6ae45c5c 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp @@ -432,14 +432,6 @@ void Performance ::statistics(const TradeManagerPtr& tm, const Datetime& datetim continue; } - cur_iter = cur_position.begin(); - for (; cur_iter != cur_position.end(); ++cur_iter) { - if (cur_iter->takeDatetime <= *day_iter) { - hold = false; - break; - } - } - // 当前是空仓 total_short_days++; if (pre_short) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 35eb3a86..8be35f2b 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -1187,12 +1187,11 @@ FundsRecord TradeManager::getFunds(KQuery::KType inktype) const { string ktype(inktype); to_upper(ktype); - price_t price(0.0); - price_t value(0.0); // 当前市值 + price_t value{0.0}; // 当前市值 position_map_type::const_iterator iter = m_position.begin(); for (; iter != m_position.end(); ++iter) { const PositionRecord& record = iter->second; - price = record.stock.getMarketValue(lastDatetime(), ktype); + auto price = record.stock.getMarketValue(lastDatetime(), ktype); value = roundEx((value + record.number * price * record.stock.unit()), precision); } @@ -1200,7 +1199,7 @@ FundsRecord TradeManager::getFunds(KQuery::KType inktype) const { iter = m_short_position.begin(); for (; iter != m_short_position.end(); ++iter) { const PositionRecord& record = iter->second; - price = record.stock.getMarketValue(lastDatetime(), ktype); + auto price = record.stock.getMarketValue(lastDatetime(), ktype); short_value = roundEx((short_value + record.number * price * record.stock.unit()), precision); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 0793944b..d0be8058 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -354,7 +354,6 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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); @@ -420,7 +419,6 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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()) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp index 7da275bc..24061570 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp @@ -27,7 +27,7 @@ BoolEnvironment::~BoolEnvironment() {} void BoolEnvironment::_checkParam(const string& name) const { if ("market" == name) { string market = getParam(name); - auto market_info = StockManager::instance().getMarketInfo(name); + auto market_info = StockManager::instance().getMarketInfo(market); HKU_CHECK(market_info != Null(), "Invalid market: {}", market); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp index a7f67d00..e0364a7c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp @@ -29,7 +29,7 @@ TwoLineEnvironment::~TwoLineEnvironment() {} void TwoLineEnvironment::_checkParam(const string& name) const { if ("market" == name) { string market = getParam(name); - auto market_info = StockManager::instance().getMarketInfo(name); + auto market_info = StockManager::instance().getMarketInfo(market); HKU_CHECK(market_info != Null(), "Invalid market: {}", market); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 2c8f88cf..83016e25 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -265,7 +265,7 @@ Indicator MultiFactorBase::getIC(int ndays) { return result; } -Indicator MultiFactorBase::getICIR(int ic_n, int ir_n) { +Indicator MultiFactorBase::getICIR(int ir_n, int ic_n) { Indicator ic = getIC(ic_n); Indicator x = MA(ic, ir_n) / STDEV(ic, ir_n); x.name("ICIR"); @@ -306,10 +306,8 @@ vector MultiFactorBase::getAllSrcFactors() { // 每日截面归一化 if (getParam("enable_min_max_normalize")) { - vector one_day(stk_count, Null()); for (size_t di = 0, days_total = m_ref_dates.size(); di < days_total; di++) { for (size_t ii = 0; ii < ind_count; ii++) { - auto* one_day_data = one_day.data(); Indicator::value_t min_value = std::numeric_limits::max(); Indicator::value_t max_value = std::numeric_limits::min(); for (size_t si = 0; si < stk_count; si++) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h index 5420a519..23b01ea8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h @@ -16,7 +16,7 @@ struct HKU_API ScoreRecord { typedef Indicator::value_t value_t; Stock stock; - value_t value; + value_t value{0.0}; ScoreRecord() = default; ScoreRecord(const Stock& stock_, value_t value_); diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h index 60b57aa2..7535c7fb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.h @@ -23,7 +23,7 @@ class FixedPercentStoploss : public StoplossBase { public: FixedPercentStoploss(); virtual ~FixedPercentStoploss(); - virtual void _checkParam(const string& name) const; + virtual void _checkParam(const string& name) const override; }; } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h index fe41f6e3..f854d09a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.h @@ -19,8 +19,8 @@ public: IndicatorStoploss(); // 仅用于序列化默认构造函数 IndicatorStoploss(const Indicator& op, const string& kdata_part); virtual ~IndicatorStoploss(); - virtual void _checkParam(const string& name) const; + virtual void _checkParam(const string& name) const override; virtual price_t getPrice(const Datetime& datetime, price_t price) override; virtual void _reset() override; virtual StoplossPtr _clone() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 571e8d72..fa8305de 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -334,8 +334,7 @@ bool System::readyForRun() { m_pre_cn_valid = false; // 默认的前一日市场有效标志置为false } - if (m_mm) - m_mm->setTM(m_tm); + m_mm->setTM(m_tm); if (m_pg) m_pg->setTM(m_tm); if (m_st) From a4876c0b971c25c99949ad104f8c9d1d8fae3b69 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 02:58:44 +0800 Subject: [PATCH 092/601] update Log.cpp --- hikyuu_cpp/hikyuu/Log.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu_cpp/hikyuu/Log.cpp b/hikyuu_cpp/hikyuu/Log.cpp index 01d6b956..28ee2142 100644 --- a/hikyuu_cpp/hikyuu/Log.cpp +++ b/hikyuu_cpp/hikyuu/Log.cpp @@ -63,6 +63,7 @@ std::shared_ptr getHikyuuLogger() { #if HKU_USE_SPDLOG_ASYNC_LOGGER void initLogger(bool inJupyter) { std::string logname = "hikyuu"; + spdlog::drop(logname); std::shared_ptr logger = spdlog::get(logname); if (logger) { spdlog::drop(logname); @@ -91,6 +92,7 @@ void initLogger(bool inJupyter) { void initLogger(bool inJupyter) { std::string logname = "hikyuu"; + spdlog::drop(logname); std::shared_ptr logger = spdlog::get(logname); if (logger) { spdlog::drop(logname); From 01279141aebf84ffc360d39d047b7c41a34d9944 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 03:08:19 +0800 Subject: [PATCH 093/601] update --- hikyuu_cpp/hikyuu/analysis/analysis_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h index b6f25779..31e756b9 100644 --- a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h +++ b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h @@ -46,7 +46,7 @@ inline vector analysisSystemListWith(const Container vector result; HKU_IF_RETURN(blk.size() == 0 || !sys_proto, result); - sys_proto->reset(); + sys_proto->forceReset(); SystemList sys_list; StockList stk_list; for (const auto& stk : blk) { From ff9494f6e779993a9d62b594514c4ffcf7687129 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 15:43:30 +0800 Subject: [PATCH 094/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20ostream=20?= =?UTF-8?q?=E9=87=8D=E5=AE=9A=E5=90=91=E8=87=B3=20python=EF=BC=8CC++?= =?UTF-8?q?=E5=A4=9A=E7=BA=BF=E7=A8=8B=E5=8F=AF=E8=83=BD=E5=BC=95=E8=B5=B7?= =?UTF-8?q?=20jupyter=20=E6=89=A7=E8=A1=8C=E5=B4=A9=E6=BA=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/__init__.py | 13 ++- hikyuu_cpp/hikyuu/Log.cpp | 13 --- hikyuu_cpp/hikyuu/Log.h | 4 - hikyuu_pywrap/_analysis.cpp | 18 +++- hikyuu_pywrap/ioredirect.cpp | 36 +++++-- hikyuu_pywrap/ioredirect.h | 184 ++++++----------------------------- hikyuu_pywrap/main.cpp | 4 +- hikyuu_pywrap/pybind_utils.h | 1 + xmake.lua | 2 +- 9 files changed, 89 insertions(+), 186 deletions(-) diff --git a/hikyuu/__init__.py b/hikyuu/__init__.py index 1aa71c05..e30e6de9 100644 --- a/hikyuu/__init__.py +++ b/hikyuu/__init__.py @@ -73,11 +73,22 @@ __version__ = get_version() sm = StockManager.instance() + +class iodog: + # Only for compatibility with old code + @staticmethod + def open(): + open_ostream_to_python() + + @staticmethod + def close(): + close_ostream_to_python() + + # 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python if in_ipython_frontend(): sm.python_in_jupyter = True hku_info("hikyuu version: {}", get_version_with_build()) - iodog = OstreamRedirect() iodog.open() diff --git a/hikyuu_cpp/hikyuu/Log.cpp b/hikyuu_cpp/hikyuu/Log.cpp index 28ee2142..78cb743d 100644 --- a/hikyuu_cpp/hikyuu/Log.cpp +++ b/hikyuu_cpp/hikyuu/Log.cpp @@ -26,24 +26,11 @@ namespace hku { static std::thread::id g_main_thread_id = std::this_thread::get_id(); -static std::atomic g_ioredirect_to_python_count = 0; bool isLogInMainThread() { return std::this_thread::get_id() == g_main_thread_id; } -int getIORedirectToPythonCount() { - return g_ioredirect_to_python_count; -} - -void increaseIORedicrectToPythonCount() { - g_ioredirect_to_python_count++; -} - -void decreaseIORedicrectToPythonCount() { - g_ioredirect_to_python_count--; -} - static LOG_LEVEL g_log_level = LOG_LEVEL::LOG_TRACE; std::string g_unknown_error_msg{"Unknown error!"}; diff --git a/hikyuu_cpp/hikyuu/Log.h b/hikyuu_cpp/hikyuu/Log.h index 095c187d..e3989287 100644 --- a/hikyuu_cpp/hikyuu/Log.h +++ b/hikyuu_cpp/hikyuu/Log.h @@ -50,10 +50,6 @@ namespace hku { bool HKU_API isLogInMainThread(); -void HKU_API increaseIORedicrectToPythonCount(); -void HKU_API decreaseIORedicrectToPythonCount(); -int HKU_API getIORedirectToPythonCount(); - /********************************************** * Use SPDLOG for logging *********************************************/ diff --git a/hikyuu_pywrap/_analysis.cpp b/hikyuu_pywrap/_analysis.cpp index 3816c4a7..8b9203ac 100644 --- a/hikyuu_pywrap/_analysis.cpp +++ b/hikyuu_pywrap/_analysis.cpp @@ -52,8 +52,14 @@ static py::dict combinate_ind_analysis(const Stock& stk, const KQuery& query, Tr c_sell_inds.emplace_back(sell_inds[i].cast()); } + std::map pers; + { + OStreamToPython guard(false); + py::gil_scoped_release release; + pers = combinateIndicatorAnalysis(stk, query, tm, sys, c_buy_inds, c_sell_inds, n); + } + py::dict result; - auto pers = combinateIndicatorAnalysis(stk, query, tm, sys, c_buy_inds, c_sell_inds, n); for (auto iter = pers.begin(); iter != pers.end(); ++iter) { result[iter->first.c_str()] = std::move(iter->second); } @@ -74,8 +80,13 @@ static py::dict combinate_ind_analysis_with_block(const Block& blk, const KQuery c_sell_inds.emplace_back(sell_inds[i].cast()); } - auto records = - combinateIndicatorAnalysisWithBlock(blk, query, tm, sys, c_buy_inds, c_sell_inds, n); + vector records; + { + OStreamToPython guard(false); + py::gil_scoped_release release; + records = + combinateIndicatorAnalysisWithBlock(blk, query, tm, sys, c_buy_inds, c_sell_inds, n); + } std::vector tmp; @@ -140,6 +151,7 @@ static py::dict analysis_sys_list(const py::object& pystk_list, const KQuery& qu vector records; { + OStreamToPython guard(false); py::gil_scoped_release release; records = analysisSystemList(sys_list, stk_list, query); } diff --git a/hikyuu_pywrap/ioredirect.cpp b/hikyuu_pywrap/ioredirect.cpp index 59a963fd..737a7081 100644 --- a/hikyuu_pywrap/ioredirect.cpp +++ b/hikyuu_pywrap/ioredirect.cpp @@ -5,16 +5,34 @@ * Author: fasiondog */ -#include +#include #include "ioredirect.h" -namespace py = pybind11; +namespace hku { -void export_io_redirect(py::module& m) { - py::class_(m, "OstreamRedirect") - .def(py::init(), py::arg("stdout") = true, py::arg("stderr") = true) - .def("__enter__", &OstreamRedirect::enter) - .def("__exit__", &OstreamRedirect::exit) - .def("open", &OstreamRedirect::enter) - .def("close", &OstreamRedirect::exit); +pybind11::detail::OstreamRedirect OStreamToPython::ms_io_redirect(true, true); +bool OStreamToPython::ms_opened{false}; + +void open_ostream_to_python() { + if (!OStreamToPython::ms_opened) { + OStreamToPython::ms_io_redirect.enter(); + OStreamToPython::ms_opened = true; + } +} + +void close_ostream_to_python() { + if (OStreamToPython::ms_opened) { + OStreamToPython::ms_io_redirect.exit(); + OStreamToPython::ms_opened = false; + } +} + +} // namespace hku + +namespace py = pybind11; +using namespace hku; + +void export_io_redirect(py::module &m) { + m.def("open_ostream_to_python", open_ostream_to_python); + m.def("close_ostream_to_python", close_ostream_to_python); } diff --git a/hikyuu_pywrap/ioredirect.h b/hikyuu_pywrap/ioredirect.h index 66785e3c..b43e0146 100644 --- a/hikyuu_pywrap/ioredirect.h +++ b/hikyuu_pywrap/ioredirect.h @@ -12,164 +12,42 @@ */ #pragma once -#ifndef IOREDIRECT_H_ -#define IOREDIRECT_H_ -#include -#include -#include -#include -#include -#include +#include +#include -using namespace pybind11; +namespace hku { -#ifdef __GNUC__ -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wattributes" -#endif +void open_ostream_to_python(); +void close_ostream_to_python(); + +class OStreamToPython final { + friend void open_ostream_to_python(); + friend void close_ostream_to_python(); + +public: + explicit OStreamToPython(bool open) : m_old_opened(open) { + if (open && !ms_opened) { + ms_io_redirect.enter(); + } else if (!open && ms_opened) { + ms_io_redirect.exit(); + } + } + + ~OStreamToPython() { + if (m_old_opened && !ms_opened) { + ms_io_redirect.enter(); + } else if (!m_old_opened && ms_opened) { + ms_io_redirect.exit(); + } + } -// Buffer that writes to Python instead of C++ -class pythonbuf : public std::streambuf { private: - using traits_type = std::streambuf::traits_type; + bool m_old_opened; - char d_buffer[1024]; - object pywrite; - object pyflush; - - int overflow(int c) { - if (!traits_type::eq_int_type(c, traits_type::eof())) { - *pptr() = traits_type::to_char_type(c); - pbump(1); - } - return sync() ? traits_type::not_eof(c) : traits_type::eof(); - } - - int sync() { - if (pbase() != pptr()) { - // This subtraction cannot be negative, so dropping the sign - str line(pbase(), static_cast(pptr() - pbase())); - - pywrite(line); - pyflush(); - - setp(pbase(), epptr()); - } - return 0; - } - -public: - pythonbuf(object pyostream) - : pywrite(pyostream.attr("write")), pyflush(pyostream.attr("flush")) { - setp(d_buffer, d_buffer + sizeof(d_buffer) - 1); - } - - /// Sync before destroy - ~pythonbuf() { - sync(); - } +private: + static pybind11::detail::OstreamRedirect ms_io_redirect; + static bool ms_opened; }; -class scoped_ostream_redirect { -protected: - std::streambuf *old; - std::ostream &costream; - pythonbuf buffer; - -public: - scoped_ostream_redirect(std::ostream &costream = std::cout, - object pyostream = module::import("sys").attr("stdout")) - : costream(costream), buffer(pyostream) { - old = costream.rdbuf(&buffer); - } - - ~scoped_ostream_redirect() { - costream.rdbuf(old); - } - - scoped_ostream_redirect(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect(scoped_ostream_redirect &&other) = default; - scoped_ostream_redirect &operator=(const scoped_ostream_redirect &) = delete; - scoped_ostream_redirect &operator=(scoped_ostream_redirect &&) = delete; -}; - -class scoped_estream_redirect : public scoped_ostream_redirect { -public: - scoped_estream_redirect(std::ostream &costream = std::cerr, - object pyostream = module::import("sys").attr("stderr")) - : scoped_ostream_redirect(costream, pyostream) {} -}; - -class OstreamRedirect { - bool do_stdout_; - bool do_stderr_; - bool had_stdout_; // prevent the reentrant - bool had_stderr_; // prevent the reentrant - std::unique_ptr redirect_stdout; - std::unique_ptr redirect_stderr; - -public: - // OstreamRedirect() - // : do_stdout_(false), do_stderr_(false) {} - OstreamRedirect(bool do_stdout = true, bool do_stderr = true) - : do_stdout_(do_stdout), do_stderr_(do_stderr), had_stdout_(false), had_stderr_(false) {} - - OstreamRedirect(const OstreamRedirect &src) { - do_stdout_ = src.do_stdout_; - do_stderr_ = src.do_stderr_; - had_stdout_ = src.had_stdout_; - had_stderr_ = src.had_stderr_; - } - - ~OstreamRedirect() { - if (had_stdout_ && do_stdout_) { - hku::decreaseIORedicrectToPythonCount(); - redirect_stdout.reset(); - had_stdout_ = false; - std::cout << "redirected std::cout has been returned" << std::endl; - } - if (had_stderr_ && do_stderr_) { - hku::decreaseIORedicrectToPythonCount(); - redirect_stderr.reset(); - had_stderr_ = false; - std::cout << "redirected std::cerr has been returned" << std::endl; - } - } - - void enter() { - if (!had_stdout_ && do_stdout_) { - hku::increaseIORedicrectToPythonCount(); - redirect_stdout.reset(new scoped_ostream_redirect()); - had_stdout_ = true; - std::cout << "std::cout are redirected to python::stdout" << std::endl; - } - if (!had_stderr_ && do_stderr_) { - hku::increaseIORedicrectToPythonCount(); - redirect_stderr.reset(new scoped_estream_redirect()); - had_stderr_ = true; - std::cout << "std::cerr are redirected to python::stderr" << std::endl; - } - } - - void exit() { - if (had_stdout_ && do_stdout_) { - hku::decreaseIORedicrectToPythonCount(); - redirect_stdout.reset(); - had_stdout_ = false; - std::cout << "redirected std::cout has been returned" << std::endl; - } - if (had_stderr_ && do_stderr_) { - hku::decreaseIORedicrectToPythonCount(); - redirect_stderr.reset(); - had_stderr_ = false; - std::cout << "redirected std::cerr has been returned" << std::endl; - } - } -}; - -#ifdef __GNUC__ -#pragma GCC diagnostic pop -#endif - -#endif /** IOREDIRECT_H_ **/ +} // namespace hku diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index 1ad080b0..313ff58f 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -62,14 +62,14 @@ PYBIND11_MODULE(core312, m) { PYBIND11_MODULE(core, m) { #endif + py::register_exception(m, "HKUException"); + StockManager::instance().runningInPython(true); #if HKU_ENABLE_SEND_FEEDBACK sendPythonVersionFeedBack(PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); #endif - py::register_exception(m, "HKUException"); - export_bind_stl(m); export_DataType(m); export_Constant(m); diff --git a/hikyuu_pywrap/pybind_utils.h b/hikyuu_pywrap/pybind_utils.h index b6ff9b94..364c9c3c 100644 --- a/hikyuu_pywrap/pybind_utils.h +++ b/hikyuu_pywrap/pybind_utils.h @@ -18,6 +18,7 @@ #include #include "convert_any.h" #include "pickle_support.h" +#include "ioredirect.h" namespace py = pybind11; diff --git a/xmake.lua b/xmake.lua index f1064cb8..48e3ac10 100644 --- a/xmake.lua +++ b/xmake.lua @@ -124,7 +124,7 @@ else set_configvar("HKU_DEBUG_MODE", 0) end set_configvar("USE_SPDLOG_LOGGER", 1) -- 是否使用spdlog作为日志输出 -set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog +set_configvar("USE_SPDLOG_ASYNC_LOGGER", 1) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) if is_plat("macosx") then set_configvar("SUPPORT_SERIALIZATION", 0) From 213aa1e8b02fc59135b5fa8541e872ee0d0c198f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 15:50:43 +0800 Subject: [PATCH 095/601] fixed compile error on ubuntu --- hikyuu_cpp/hikyuu/analysis/analysis_sys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h index 31e756b9..a3e99452 100644 --- a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h +++ b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h @@ -46,7 +46,7 @@ inline vector analysisSystemListWith(const Container vector result; HKU_IF_RETURN(blk.size() == 0 || !sys_proto, result); - sys_proto->forceReset(); + sys_proto->forceResetAll(); SystemList sys_list; StockList stk_list; for (const auto& stk : blk) { From 4a4d20237c9d173af990ad525cbc834125e5ddbe Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 18:07:39 +0800 Subject: [PATCH 096/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20combinateIndicator?= =?UTF-8?q?AnalysisWithBlock=EF=BC=8C=E4=B9=8B=E5=89=8D=20ioredirect=20?= =?UTF-8?q?=E4=BC=9A=E9=98=BB=E5=A1=9E=E5=B7=B2=E8=A7=A3=E5=86=B3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/analysis/combinate.cpp | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/hikyuu_cpp/hikyuu/analysis/combinate.cpp b/hikyuu_cpp/hikyuu/analysis/combinate.cpp index cd79d5e0..3e6febaa 100644 --- a/hikyuu_cpp/hikyuu/analysis/combinate.cpp +++ b/hikyuu_cpp/hikyuu/analysis/combinate.cpp @@ -5,6 +5,9 @@ * Author: fasiondog */ +#include +#include + #include "hikyuu/utilities/osdef.h" #include "hikyuu/utilities/thread/thread.h" #include "hikyuu/indicator/crt/EXIST.h" @@ -72,22 +75,14 @@ vector HKU_API combinateIndicatorAnalysisWithBlock( } vector result; - auto cpu_num = std::thread::hardware_concurrency(); -#if HKU_OS_WINDOWS - size_t work_num = 5; - if (cpu_num < work_num) { - work_num = cpu_num; - } -#else - size_t work_num = cpu_num; -#endif - MQStealThreadPool tg(work_num); - vector>> tasks; - auto stocks = blk.getAllStocks(); size_t total = stocks.size(); HKU_IF_RETURN(total == 0, result); + auto work_num = std::thread::hardware_concurrency(); + MQStealThreadPool tg(work_num); + vector>> tasks; + size_t per_num = total > work_num ? total / (work_num * 10) : 1; size_t count = total % per_num == 0 ? total / per_num : total / per_num + 1; @@ -131,13 +126,11 @@ vector HKU_API combinateIndicatorAnalysisWithBlock( } for (auto& task : tasks) { - // result.emplace_back(std::move(task.get())); auto records = task.get(); for (auto& record : records) { result.emplace_back(record); } } - return result; } From 75532dd23f71638155e42164783c974b118d14ec Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 26 Mar 2024 19:02:50 +0800 Subject: [PATCH 097/601] update --- hikyuu_cpp/hikyuu/analysis/combinate.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/analysis/combinate.cpp b/hikyuu_cpp/hikyuu/analysis/combinate.cpp index 3e6febaa..ebfddde1 100644 --- a/hikyuu_cpp/hikyuu/analysis/combinate.cpp +++ b/hikyuu_cpp/hikyuu/analysis/combinate.cpp @@ -5,9 +5,6 @@ * Author: fasiondog */ -#include -#include - #include "hikyuu/utilities/osdef.h" #include "hikyuu/utilities/thread/thread.h" #include "hikyuu/indicator/crt/EXIST.h" @@ -125,10 +122,11 @@ vector HKU_API combinateIndicatorAnalysisWithBlock( })); } + result.reserve(sgs.size() * stocks.size()); for (auto& task : tasks) { auto records = task.get(); for (auto& record : records) { - result.emplace_back(record); + result.emplace_back(std::move(record)); } } return result; From fb197bd7af5dcddafb64672d6ec3b6492c9e63f0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 27 Mar 2024 02:06:02 +0800 Subject: [PATCH 098/601] =?UTF-8?q?AF=E6=B5=8B=E8=AF=95=E5=8F=8A=E8=B0=83?= =?UTF-8?q?=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allocatefunds/AllocateFundsBase.cpp | 119 +++++++++--------- .../allocatefunds/AllocateFundsBase.h | 27 ++-- .../imp/EqualWeightAllocateFunds.cpp | 3 +- .../imp/FixedWeightAllocateFunds.cpp | 8 +- .../trade_sys/selector/SelectorBase.cpp | 2 +- .../allocatefunds/test_AF_EqualWeight.cpp | 34 ++++- .../allocatefunds/test_AF_FixedWeight.cpp | 21 ++++ .../allocatefunds/test_AllocateFunds.cpp | 53 ++++++-- xmake.lua | 2 +- 9 files changed, 170 insertions(+), 99 deletions(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index d0be8058..ad789ac1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -26,7 +26,17 @@ HKU_API std::ostream& operator<<(std::ostream& os, const AFPtr& af) { return os; } -AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase"), m_reserve_percent(0) { +AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase") { + initParam(); +} + +AllocateFundsBase::AllocateFundsBase(const string& name) : m_name("AllocateMoneyBase") { + initParam(); +} + +AllocateFundsBase::~AllocateFundsBase() {} + +void AllocateFundsBase::initParam() { // 是否调整之前已经持仓策略的持仓。不调整时,仅使用总账户当前剩余资金进行分配,否则将使用总市值进行分配 // 注意:无论是否调整已持仓策略,权重比例都是相对于总资产,不是针对剩余现金余额 // 仅针对剩余现金比例调整没有意义,即使分配由于交易成本原因可能也无法完成实际交易 @@ -34,39 +44,33 @@ AllocateFundsBase::AllocateFundsBase() : m_name("AllocateMoneyBase"), m_reserve_ // adjust_running_sys: False - 不会根据当前分配权重对已持仓策略进行强制加减仓 setParam("adjust_running_sys", false); + // 自动调整权重,此时认为传入的权重为各证券的相互比例(详见ignore_zero_weight说明) + // 否则,以传入的权重为指定权重不做调整(此时传入的各个权重需要小于1) + setParam("auto_adjust_weight", true); + + // 该参数在 auto_adjust_weight 时生效 // 是否过滤子类返回的比例权重列表中的 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("filter_zero_weight", false); + setParam("ignore_zero_weight", false); - setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 - setParam("trace", false); // 打印跟踪 + setParam("reserve_percent", 0.0); // 保留不参与重分配的资产比例 + setParam("trace", false); // 打印跟踪 } -AllocateFundsBase::AllocateFundsBase(const string& name) -: m_name("AllocateMoneyBase"), m_reserve_percent(0) { - setParam("adjust_running_sys", false); - setParam("filter_zero_weight", false); - setParam("default_reserve_percent", 0.0); // 默认保留不参与重分配的资产比例 - setParam("trace", false); // 打印跟踪 +void AllocateFundsBase::baseCheckParam(const string& name) const { + if ("reserve_percent" == name) { + double reserve_percent = getParam(name); + HKU_ASSERT(reserve_percent >= 0.0 && reserve_percent < 1.0); + } } -AllocateFundsBase::~AllocateFundsBase() {} - -void AllocateFundsBase::baseCheckParam(const string& name) const {} void AllocateFundsBase::paramChanged() {} void AllocateFundsBase::reset() { - // 参数检查 - double default_reserve_percent = getParam("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(); } @@ -87,7 +91,6 @@ AFPtr AllocateFundsBase::clone() { p->m_params = m_params; p->m_name = m_name; p->m_query = m_query; - p->m_reserve_percent = m_reserve_percent; /* m_tm, m_cash_tm 由 PF 运行时指定,不需要 clone if (m_tm) @@ -97,12 +100,6 @@ AFPtr AllocateFundsBase::clone() { return p; } -void AllocateFundsBase::setReservePercent(double percent) { - HKU_CHECK_THROW(percent >= 0.0 && percent <= 1.0, std::out_of_range, - "percent ({}) is out of range [0, 1]!"); - m_reserve_percent = percent; -} - void AllocateFundsBase::_check_weight(const SystemWeightList& sw_list) { price_t sum_weight = 0.0; for (const auto& sw : sw_list) { @@ -125,8 +122,14 @@ SystemWeightList AllocateFundsBase::adjustFunds(const Datetime& date, return result; } -// 降序排列 SystemWeightList,并将权重调整为总权重为 1 -static void adjustWeight(SystemWeightList& sw_list, double can_allocate_weight, bool filter_zero) { +/* + * 降序排列 SystemWeightList + * can_allocate_weight - 剩余可用于分配的总权重 + * auto_adjust - 是否自动按比例调整,根据权重调整为总权重为 1 + */ +void AllocateFundsBase::adjustWeight(SystemWeightList& sw_list, double can_allocate_weight, + bool auto_adjust, bool ignore_zero) { + // 降序排列,同时保证 nan 排在最后 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; @@ -150,40 +153,32 @@ static void adjustWeight(SystemWeightList& sw_list, double can_allocate_weight, new_list.emplace_back(item); } - 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; + if (auto_adjust) { + // 过滤 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份中保持相对比例 + double per_weight = ignore_zero + ? 1.0 / sum * can_allocate_weight + : (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); } -// 只对现金余额进行分配,此时的权重是针对余额的 +// 所有的权重分配都是针对总资产的,不针对剩余现金 +// 不会根据当前分配权重对已持仓策略进行强制加减仓 void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemWeightList& se_list, const std::unordered_set& 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); - bool trace = getParam("trace"); HKU_INFO_IF(trace, "[AF] {} _adjust_without_running", date); - // 获取计划分配的资产权重,因为不调整已运行系统实现占位,将所有运行中的系统以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); - } - } - - HKU_IF_RETURN(new_se_list.size() == 0, void()); - SystemWeightList sw_list = _allocateWeight(date, new_se_list); + // 从分配算法获取计划的资产分配权重 + SystemWeightList sw_list = _allocateWeight(date, se_list); HKU_IF_RETURN(sw_list.size() == 0, void()); // 获取当前总资产市值,计算剩余可分配权重与现金 @@ -191,12 +186,13 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, 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; + double reserve_percent = getParam("reserve_percent"); + price_t reserve_funds = total_funds * reserve_percent; 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; + double can_allocate_weight = 1.0 - reserve_percent; HKU_INFO_IF(trace, "can_allocate_weight: {:<.4f}, can_allocate_cash: {:<.2f}, current cash: {:<.2f}, " "total funds: {:<.2f}, " @@ -205,7 +201,8 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, HKU_IF_RETURN(can_allocate_cash <= 1.0, void()); // 调整权重(累积权重和归一)并按降序排列, 并过滤掉 0 值和 Nan 值 - adjustWeight(sw_list, can_allocate_weight, getParam("filter_zero_weight")); + adjustWeight(sw_list, can_allocate_weight, getParam("auto_adjust_weight"), + getParam("ignore_zero_weight")); // 遍历选中子系统列表,并将剩余现金按权重比例转入子账户 double sum_weight = 0.0; // 由于不调整已运行系统,已运行系统实际占用比例可能和要求的比例不一致 @@ -265,10 +262,6 @@ void AllocateFundsBase::_adjust_without_running(const Datetime& date, SystemWeightList AllocateFundsBase::_adjust_with_running( const Datetime& date, const SystemWeightList& se_list, const std::unordered_set& 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("trace"); @@ -280,8 +273,10 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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("filter_zero_weight")); + double reserve_percent = getParam("reserve_percent"); + double can_allocate_weight = 1.0 - reserve_percent; + adjustWeight(sw_list, can_allocate_weight, getParam("auto_adjust_weight"), + getParam("ignore_zero_weight")); //----------------------------------------------------------------- // 先将已不在 sw_list 中的运行系统进行清仓,回收可分配资金 @@ -323,7 +318,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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); + price_t reserve_funds = roundEx(total_funds * reserve_percent, precision); std::unordered_set reduced_running_set; // 缓存已执行过减仓的运行中系统 for (auto iter = sw_list.begin(), end_iter = sw_list.end(); iter != end_iter; ++iter) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 34e27c94..42074f38 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -69,16 +69,6 @@ public: /** 设置查询条件, 由 PF 设定 */ void setQuery(const KQuery& query); - /** 获取当前不参与资产分配的保留比例 */ - double getReservePercent(); - - /** - * 设置不参与资产分配的保留比例,该比例在执行reset时会被置为参数 default_reserve_percent 的值 - * @note 主要用分配算法动态控制不参与分配的资产比例 - * @param p 取值范围[0,1],小于0将被强制置为0, 大于1将被置为1 - */ - void setReservePercent(double p); - /** 复位 */ void reset(); @@ -103,7 +93,17 @@ public: virtual SystemWeightList _allocateWeight(const Datetime& date, const SystemWeightList& se_list) = 0; +public: + /* + * 内部函数,仅为测试需要设置为 public。 + * 对由子类分配的计划权重根据内部参数设置进行调整 + */ + static void adjustWeight(SystemWeightList& sw_list, double can_allocate_weight, + bool auto_adjust, bool ignore_zero); + private: + void initParam(); + /* 同时调整已运行中的子系统(已分配资金或已持仓) */ SystemWeightList _adjust_with_running(const Datetime& date, const SystemWeightList& se_list, const std::unordered_set& running_list); @@ -120,7 +120,6 @@ private: KQuery m_query; // 查询条件 TMPtr m_tm; // 运行期由PF设定,PF的实际账户 TMPtr m_cash_tm; // 运行期由PF设定,tm 的影子账户,由于协调分配资金 - double m_reserve_percent; // 保留资产比例,不参与资产分配 //============================================ // 序列化支持 @@ -133,7 +132,6 @@ private: ar& BOOST_SERIALIZATION_NVP(m_name); ar& BOOST_SERIALIZATION_NVP(m_params); ar& BOOST_SERIALIZATION_NVP(m_query); - ar& BOOST_SERIALIZATION_NVP(m_reserve_percent); } template @@ -141,7 +139,6 @@ private: ar& BOOST_SERIALIZATION_NVP(m_name); ar& BOOST_SERIALIZATION_NVP(m_params); ar& BOOST_SERIALIZATION_NVP(m_query); - ar& BOOST_SERIALIZATION_NVP(m_reserve_percent); } BOOST_SERIALIZATION_SPLIT_MEMBER() @@ -222,10 +219,6 @@ inline void AllocateFundsBase::setQuery(const KQuery& query) { m_query = query; } -inline double AllocateFundsBase::getReservePercent() { - return m_reserve_percent; -} - } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp index 819b40a1..5b1941b1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/EqualWeightAllocateFunds.cpp @@ -20,9 +20,8 @@ EqualWeightAllocateFunds::~EqualWeightAllocateFunds() {} SystemWeightList EqualWeightAllocateFunds ::_allocateWeight(const Datetime& date, const SystemWeightList& se_list) { SystemWeightList result; - price_t weight = 1 / se_list.size(); for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) { - result.emplace_back(iter->sys, weight); + result.emplace_back(iter->sys, 1.0); } return result; diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp index 05054e66..383f6db9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/FixedWeightAllocateFunds.cpp @@ -15,6 +15,9 @@ namespace hku { FixedWeightAllocateFunds::FixedWeightAllocateFunds() : AllocateFundsBase("AF_FixedWeight") { setParam("weight", 0.1); + + // 公共参数必须设置为 false,禁止自动调整权重 + setParam("auto_adjust_weight", false); } FixedWeightAllocateFunds::~FixedWeightAllocateFunds() {} @@ -23,6 +26,9 @@ void FixedWeightAllocateFunds::_checkParam(const string& name) const { if ("weight" == name) { double weight = getParam("weight"); HKU_ASSERT(weight > 0.0 && weight <= 1.); + } else if ("auto_adjust_weight" == name) { + bool auto_adjust_weight = getParam("auto_adjust_weight"); + HKU_CHECK(!auto_adjust_weight, R"(param "auto_adjust_weight" must be false!)"); } } @@ -38,8 +44,6 @@ SystemWeightList FixedWeightAllocateFunds ::_allocateWeight(const Datetime& date } AFPtr HKU_API AF_FixedWeight(double weight) { - HKU_CHECK_THROW(weight > 0.0 && weight <= 1.0, std::out_of_range, - "input weigth ({}) is out of range [0, 1]!", weight); auto p = make_shared(); p->setParam("weight", weight); return p; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 2970d079..08f64c7a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -25,7 +25,7 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SelectorPtr& st) { } SelectorBase::SelectorBase() : m_name("SelectorBase") { - // 是否单独执行原型系统 + // 是否单独执行原型系统,仅限用于测试目的 setParam("run_proto_sys", false); } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_EqualWeight.cpp index 968afbe5..5c42f72b 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_EqualWeight.cpp @@ -5,7 +5,7 @@ * Author: fasiondog */ -#include "doctest/doctest.h" +#include "../../test_config.h" #include #include #include @@ -66,9 +66,33 @@ TEST_CASE("test_AF_EqualWeight_not_adjust_hold") { */ } -/** @par 检测点,自动调仓 */ -// TEST_CASE( test_AF_EqualWeight_adjust_hold) { -// TODO test_AF_EqualWeight_adjust_hold -//} +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_AF_EqualWeight_export") { + StockManager& sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/AF_EQUALWEIGHT.xml"; + + AFPtr af1 = AF_EqualWeight(); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(af1); + } + + AFPtr af2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(af2); + } + + CHECK_EQ(af1->name(), af2->name()); +} +#endif /* HKU_SUPPORT_SERIALIZATION */ /** @} */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp new file mode 100644 index 00000000..2830c962 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-27 + * Author: fasiondog + */ + +#include "../../test_config.h" + +using namespace hku; + +/** + * @defgroup test_AF_FixedWeight test_AF_FixedWeight + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_AF_FixedWeight") {} + +/** @} */ \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp index 5c402601..f50d2929 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AllocateFunds.cpp @@ -45,17 +45,52 @@ TEST_CASE("test_AllocateFunds") { CHECK_NE(af2.get(), af.get()); CHECK_EQ(af2->name(), af->name()); - /** @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); - // ac_list = af->getAllocatedSystemList(Datetime(201802100000L), se_list, hold_list); - // CHECK_EQ(sw_list.size(), 0); - // CHECK_EQ(ac_list.size(), 0); + //---------------------------------------------------- + // 测试对计划权重的调整 + //---------------------------------------------------- + /** @arg 不自动调整 auto_adjust = false */ + SystemWeightList sw(10); + SystemWeightList expect_sw(10); + for (size_t i = 0; i < 10; i++) { + sw[i].weight = i; + expect_sw[i].weight = 9 - i; + } - /** @arg 最大持仓系统数小于0 */ + AllocateFundsBase::adjustWeight(sw, 1.0, false, false); + CHECK_EQ(sw.size(), 9); + for (size_t i = 0; i < 9; i++) { + CHECK_EQ(sw[i].weight, expect_sw[i].weight); + } - /** @arg 最大持仓系统数为0 */ + /** @arg auto_adjust = true, ignore_zero = true*/ + sw.clear(); + sw.emplace_back(SYSPtr(), 0.0); + sw.emplace_back(SYSPtr(), 2.0); + sw.emplace_back(SYSPtr(), Null()); + sw.emplace_back(SYSPtr(), 3.0); + expect_sw.clear(); + expect_sw.emplace_back(SYSPtr(), 3.0 / 5.0 * 0.8); + expect_sw.emplace_back(SYSPtr(), 2.0 / 5.0 * 0.8); + + AllocateFundsBase::adjustWeight(sw, 0.8, true, true); + CHECK_EQ(sw.size(), expect_sw.size()); + CHECK_EQ(sw[0].weight, doctest::Approx(expect_sw[0].weight)); + CHECK_EQ(sw[1].weight, doctest::Approx(expect_sw[1].weight)); + + /** @arg auto_adjust = true, ignore_zero = false*/ + sw.clear(); + sw.emplace_back(SYSPtr(), 0.0); + sw.emplace_back(SYSPtr(), 2.0); + sw.emplace_back(SYSPtr(), Null()); + sw.emplace_back(SYSPtr(), 3.0); + expect_sw.clear(); + expect_sw.emplace_back(SYSPtr(), 3.0 / 5.0 * (2.0 / 4.0) * 0.8); + expect_sw.emplace_back(SYSPtr(), 2.0 / 5.0 * (2.0 / 4.0) * 0.8); + + AllocateFundsBase::adjustWeight(sw, 0.8, true, false); + CHECK_EQ(sw.size(), expect_sw.size()); + CHECK_EQ(sw[0].weight, doctest::Approx(expect_sw[0].weight)); + CHECK_EQ(sw[1].weight, doctest::Approx(expect_sw[1].weight)); } /** @} */ diff --git a/xmake.lua b/xmake.lua index 48e3ac10..f1064cb8 100644 --- a/xmake.lua +++ b/xmake.lua @@ -124,7 +124,7 @@ else set_configvar("HKU_DEBUG_MODE", 0) end set_configvar("USE_SPDLOG_LOGGER", 1) -- 是否使用spdlog作为日志输出 -set_configvar("USE_SPDLOG_ASYNC_LOGGER", 1) -- 使用异步的spdlog +set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) if is_plat("macosx") then set_configvar("SUPPORT_SERIALIZATION", 0) From 2834ba913734e8a6432d4e0005714b636de031ae Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 27 Mar 2024 02:29:32 +0800 Subject: [PATCH 099/601] add test case --- .../trade_sys/allocatefunds/crt/AF_FixedWeight.h | 2 +- .../trade_sys/allocatefunds/test_AF_FixedWeight.cpp | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_FixedWeight.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_FixedWeight.h index 019b22e5..5902e640 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_FixedWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_FixedWeight.h @@ -15,7 +15,7 @@ namespace hku { /** * @brief 固定比例资产分配,每个选中的资产都只占总资产固定的比例 - * @param weight 指定的资产比例 [0, 1] + * @param weight 指定的资产比例 (0, 1] * @return AFPtr * @ingroup AllocateFunds */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp index 2830c962..48ff949c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/allocatefunds/test_AF_FixedWeight.cpp @@ -6,6 +6,7 @@ */ #include "../../test_config.h" +#include "hikyuu/trade_sys/allocatefunds/crt/AF_FixedWeight.h" using namespace hku; @@ -16,6 +17,15 @@ using namespace hku; */ /** @par 检测点 */ -TEST_CASE("test_AF_FixedWeight") {} +TEST_CASE("test_AF_FixedWeight") { + /** @arg 非法参数 */ + CHECK_THROWS_AS(AF_FixedWeight(0.0), std::exception); + CHECK_THROWS_AS(AF_FixedWeight(-0.1), std::exception); + CHECK_THROWS_AS(AF_FixedWeight(1.001), std::exception); + + /** @arg 尝试变更非法的公共参数 */ + auto af = AF_FixedWeight(0.1); + CHECK_THROWS_AS(af->setParam("auto_adjust_weight", true), std::exception); +} /** @} */ \ No newline at end of file From c6bd2e5e7c2c31e696fbdbd5707ad711dcc7dbf7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 27 Mar 2024 16:18:41 +0800 Subject: [PATCH 100/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20python=20=E8=BF=90?= =?UTF-8?q?=E8=A1=8C=E7=8A=B6=E6=80=81=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/__init__.py | 2 +- hikyuu_cpp/hikyuu/StockManager.cpp | 8 +------- hikyuu_cpp/hikyuu/StockManager.h | 16 ---------------- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 19 +++++++++++++++++++ hikyuu_cpp/hikyuu/global/sysinfo.h | 11 +++++++++++ .../hikyuu/trade_manage/TradeRecord.cpp | 13 ++++++------- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 7 +++---- hikyuu_pywrap/main.cpp | 5 ++++- 8 files changed, 45 insertions(+), 36 deletions(-) diff --git a/hikyuu/__init__.py b/hikyuu/__init__.py index e30e6de9..ae6052c4 100644 --- a/hikyuu/__init__.py +++ b/hikyuu/__init__.py @@ -87,7 +87,7 @@ class iodog: # 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python if in_ipython_frontend(): - sm.python_in_jupyter = True + set_python_in_jupyter(True) hku_info("hikyuu version: {}", get_version_with_build()) iodog.open() diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index f9b30e04..84904bb3 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -33,8 +33,7 @@ void StockManager::quit() { } } -StockManager::StockManager() -: m_initializing(false), m_runningInPython(false), m_pythonInJupyter(false) { +StockManager::StockManager() : m_initializing(false) { m_stockDict_mutex = new std::mutex; m_marketInfoDict_mutex = new std::mutex; m_stockTypeInfo_mutex = new std::mutex; @@ -56,11 +55,6 @@ StockManager& StockManager::instance() { return (*m_sm); } -void StockManager::pythonInJupyter(bool inJupyter) { - m_pythonInJupyter = inJupyter; - initLogger(inJupyter); -} - Parameter default_preload_param() { Parameter param; param.set("day", true); diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index a9f322ed..bd7d6182 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -196,20 +196,6 @@ public: return m_thread_id; } - bool runningInPython() const { - return m_runningInPython; - } - - void runningInPython(bool inpython) { - m_runningInPython = inpython; - } - - bool pythonInJupyter() const { - return m_pythonInJupyter; - } - - void pythonInJupyter(bool inJupyter); - public: typedef StockMapIterator const_iterator; const_iterator begin() const { @@ -250,8 +236,6 @@ private: private: static StockManager* m_sm; std::atomic_bool m_initializing; - std::atomic_bool m_runningInPython; // 是否是在 python 中运行 - std::atomic_bool m_pythonInJupyter; // python 是否为交互模式 std::thread::id m_thread_id; // 记录线程id,用于判断Stratege是以独立进程方式还是线程方式运行 string m_tmpdir; string m_datadir; diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index bf4e995e..cec0b18d 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -25,6 +25,25 @@ using json = nlohmann::json; namespace hku { std::atomic g_latest_version{0}; +std::atomic_bool g_runningInPython{false}; // 是否是在 python 中运行 +std::atomic_bool g_pythonInJupyter{false}; // python 是否为交互模式 + +bool HKU_API runningInPython() { + return g_runningInPython; +} + +void HKU_API setRunningInPython(bool inpython) { + g_runningInPython = inpython; +} + +bool HKU_API pythonInJupyter() { + return g_pythonInJupyter; +} + +void HKU_API setPythonInJupyter(bool injupyter) { + g_pythonInJupyter = injupyter; + initLogger(injupyter); +} bool HKU_API CanUpgrade() { int current_version = diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.h b/hikyuu_cpp/hikyuu/global/sysinfo.h index a5fbf803..097e0b5d 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.h +++ b/hikyuu_cpp/hikyuu/global/sysinfo.h @@ -46,4 +46,15 @@ void HKU_API sendFeedback(); /** 用于发送 python 版本信息 */ void HKU_API sendPythonVersionFeedBack(int major, int minor, int micro); +/** 当前是否运行在 python 环境中 */ +bool HKU_API runningInPython(); + +/** 当前是否运行在 Jupyter 环境中 */ +bool HKU_API pythonInJupyter(); + +void HKU_API setRunningInPython(bool inpython); + +/** 当前是否运行在 Jupyter 环境中 */ +void HKU_API setPythonInJupyter(bool injupyter); + } // namespace hku diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp index a162e20e..ec530af8 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp @@ -6,6 +6,7 @@ */ #include +#include "hikyuu/global/sysinfo.h" #include "TradeRecord.h" namespace hku { @@ -125,13 +126,11 @@ string TradeRecord::toString() const { } #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)); + return fmt::format("Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime, + market_code, runningInPython() && 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, diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 87302369..bc90dfef 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include "hikyuu/global/sysinfo.h" #include "../../trade_manage/crt/crtTM.h" #include "Portfolio.h" @@ -403,10 +404,8 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { 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()); + auto stk_name = + runningInPython() && 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(' '); diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index 313ff58f..7825c27b 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -64,7 +64,8 @@ PYBIND11_MODULE(core, m) { py::register_exception(m, "HKUException"); - StockManager::instance().runningInPython(true); + // 设置系统运行状态 + setRunningInPython(true); #if HKU_ENABLE_SEND_FEEDBACK sendPythonVersionFeedBack(PY_MAJOR_VERSION, PY_MINOR_VERSION, PY_MICRO_VERSION); @@ -106,6 +107,8 @@ PYBIND11_MODULE(core, m) { export_global_main(m); export_io_redirect(m); + m.def("set_python_in_jupyter", setPythonInJupyter); + m.def("close_spend_time", close_spend_time, "全局关闭 c++ 部分耗时打印"); m.def("open_spend_time", close_spend_time, "全局开启 c++ 部分耗时打印"); From 81f11ae2e7d071108136b716bcb0c07de1d4ac43 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 27 Mar 2024 17:48:06 +0800 Subject: [PATCH 101/601] update --- hikyuu_pywrap/_StockManager.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 0ed40065..594e8704 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -31,13 +31,6 @@ void export_StockManager(py::module& m) { param hikyuu_param 其他参数 param StrategyContext context 策略上下文, 默认加载全部证券)") - .def_property("running_in_python", - py::overload_cast<>(&StockManager::runningInPython, py::const_), - py::overload_cast(&StockManager::runningInPython)) - .def_property("python_in_jupyter", - py::overload_cast<>(&StockManager::pythonInJupyter, py::const_), - py::overload_cast(&StockManager::pythonInJupyter)) - .def("reload", &StockManager::reload, "重新加载所有证券数据") .def("tmpdir", &StockManager::tmpdir, R"(tmpdir(self) -> str From 2dad10e217d9e5057a077903448c76bd342be81a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 27 Mar 2024 17:48:21 +0800 Subject: [PATCH 102/601] fixed meta part clone --- hikyuu/test/Signal.py | 25 +++++++++++++++++++++++-- hikyuu/test/test.py | 8 +++++--- hikyuu/trade_sys/trade_sys.py | 21 ++++++++++----------- 3 files changed, 38 insertions(+), 16 deletions(-) diff --git a/hikyuu/test/Signal.py b/hikyuu/test/Signal.py index 3e3c2fe9..1568933d 100644 --- a/hikyuu/test/Signal.py +++ b/hikyuu/test/Signal.py @@ -2,10 +2,10 @@ # -*- coding: utf8 -*- # gb18030 -#=============================================================================== +# =============================================================================== # 作者:fasiondog # 历史:1)20130321, Added by fasiondog -#=============================================================================== +# =============================================================================== import unittest @@ -128,6 +128,7 @@ class TestCrtSG(unittest.TestCase): self.assertIn(d[i], [Datetime(201201300000), Datetime(200101030000)]) p_clone = p.clone() + self.assertFalse(p_clone is p) d = p_clone.get_buy_signal() for i in range(len(d)): self.assertIn(d[i], [Datetime(201201210000), Datetime(200101010000)]) @@ -137,9 +138,29 @@ class TestCrtSG(unittest.TestCase): self.assertIn(d[i], [Datetime(201201300000), Datetime(200101030000)]) +def testSignalClone(self): + new_object = crtSG(testSignal, self._params, self._name) + new_object.x = self.x + return new_object + + +class TestCrtSGWithClone(unittest.TestCase): + def test_crtSG(self): + p = crtSG(testSignal, params={'test': 30}, name='SG_TEST', clone=testSignalClone) + p.x = 10 + p2 = p.clone() + self.assertFalse(p2 is p) + self.assertEqual(p2.name, "SG_TEST") + self.assertEqual(p2.x, p.x) + + def suite(): return unittest.TestLoader().loadTestsFromTestCase(SignalTest) def suiteTestCrtSG(): return unittest.TestLoader().loadTestsFromTestCase(TestCrtSG) + + +def suiteTestCrtSGWithClone(): + return unittest.TestLoader().loadTestsFromTestCase(TestCrtSGWithClone) diff --git a/hikyuu/test/test.py b/hikyuu/test/test.py index af6f5f9b..cf1d6d73 100644 --- a/hikyuu/test/test.py +++ b/hikyuu/test/test.py @@ -2,10 +2,10 @@ # -*- coding: utf8 -*- # cp936 -#=============================================================================== +# =============================================================================== # 作者:fasiondog # 历史:1)20120927, Added by fasiondog -#=============================================================================== +# =============================================================================== import unittest @@ -48,6 +48,8 @@ if __name__ == "__main__": suite.addTest(MoneyManager.suiteTestCrtMM()) suite.addTest(Signal.suite()) suite.addTest(Signal.suiteTestCrtSG()) + suite.addTest(Signal.suiteTestCrtSGWithClone()) + suite.addTest(Stoploss.suite()) suite.addTest(Stoploss.suiteTestCrtST()) suite.addTest(ProfitGoal.suite()) @@ -58,4 +60,4 @@ if __name__ == "__main__": suite.addTest(AllocateFunds.suite()) unittest.TextTestRunner(verbosity=2).run(suite) - #unittest.main() + # unittest.main() diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index c3c2ebd2..6a1e0d42 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -1,6 +1,5 @@ # -*- coding: utf8 -*- -from hikyuu.util.slice import list_getitem from hikyuu.core import ( System, SystemPart, ConditionBase, EnvironmentBase, MoneyManagerBase, ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase, AllocateFundsBase @@ -52,7 +51,7 @@ def crtCN(func, params={}, name='crtCN', clone=None): :return: 自定义系统有效条件实例 """ meta_x = type(name, (ConditionBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -71,7 +70,7 @@ def crtEV(func, params={}, name='crtEV', clone=None): :return: 自定义市场环境判断策略实例 """ meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -90,7 +89,7 @@ def crtMM(func, params={}, name='crtMM', clone=None): :return: 自定义资金管理策略实例 """ meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -109,7 +108,7 @@ def crtPG(func, params={}, name='crtPG', clone=None): :return: 盈利目标策略实例 """ meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -128,7 +127,7 @@ def crtSG(func, params={}, name='crtSG', clone=None): :return: 自定义信号指示器实例 """ meta_x = type(name, (SignalBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -164,8 +163,8 @@ def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE', cl meta_x = type(name, (SelectorBase, ), {'__init__': part_init}) meta_x._calculate = calculate 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 + 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) @@ -183,7 +182,7 @@ def crtAF(allocate_weight_func, params={}, name='crtAF', clone=None): :return: 自定义资产分配算法实例 """ meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._allocate_weight = allocate_weight_func return meta_x(name, params) @@ -202,7 +201,7 @@ def crtSL(func, params={}, name='crtSL', clone=None): :return: 移滑价差算法实例 """ meta_x = type(name, (SlippageBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) @@ -221,6 +220,6 @@ def crtST(func, params={}, name='crtST', clone=None): :return: 止损/止盈策略实例 """ meta_x = type(name, (StoplossBase, ), {'__init__': part_init}) - meta_x._clone = lambda self: meta_x(self._name, self._params) if clone is None else clone + meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone meta_x._calculate = func return meta_x(name, params) From 8b14868199fcdb2974191a7a84af124dd8826a3f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 27 Mar 2024 22:39:38 +0800 Subject: [PATCH 103/601] =?UTF-8?q?pf=20=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h | 25 +++++ .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 93 +++++++------------ .../hikyuu/trade_sys/portfolio/Portfolio.h | 14 +-- .../hikyuu/trade_sys/selector/SelectorBase.h | 3 - hikyuu_pywrap/trade_sys/_Portfolio.cpp | 2 - 5 files changed, 65 insertions(+), 72 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h index bc3fd574..1f05c470 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h +++ b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h @@ -43,6 +43,31 @@ public: // 当前收益 = 当前净资产 - 当前投入本值资产 // = cash + market_value - short_market_value - borrow_cash - base_cash - base_asset + // 当前总资产 + price_t total_assets() const { + return cash + market_value + borrow_asset - short_market_value; + } + + // 当前净资产 + price_t net_assets() const { + return cash + market_value - short_market_value - borrow_cash; + } + + // 总负债 + price_t total_borrow() const { + return borrow_cash + borrow_asset; + } + + // 当前投入本值资产 + price_t total_base() const { + return base_cash + base_asset; + } + + // 当前收益 + price_t profit() const { + return cash + market_value - short_market_value - borrow_cash - base_cash - base_asset; + } + FundsRecord operator+(const FundsRecord& other) const; FundsRecord& operator+=(const FundsRecord& other); diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index bc90dfef..ebd7d37b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -32,16 +32,13 @@ HKU_API std::ostream& operator<<(std::ostream& os, const PortfolioPtr& pf) { return os; } -Portfolio::Portfolio() -: m_name("Portfolio"), m_query(Null()), m_is_ready(false), m_need_calculate(true) { - setParam("adjust_cycle", 1); // 调仓周期 - setParam("trace", false); // 打印跟踪 +Portfolio::Portfolio() : m_name("Portfolio"), m_query(Null()), m_need_calculate(true) { + initParam(); } Portfolio::Portfolio(const string& name) -: m_name(name), m_query(Null()), m_is_ready(false), m_need_calculate(true) { - setParam("adjust_cycle", 1); // 调仓周期 - setParam("trace", false); +: m_name(name), m_query(Null()), m_need_calculate(true) { + initParam(); } Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFPtr& af) @@ -50,14 +47,17 @@ Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFP m_se(se), m_af(af), m_query(Null()), - m_is_ready(false), m_need_calculate(true) { - setParam("adjust_cycle", 1); // 调仓周期 - setParam("trace", false); + initParam(); } Portfolio::~Portfolio() {} +void Portfolio::initParam() { + setParam("adjust_cycle", 1); // 调仓周期 + setParam("trace", false); // 打印跟踪 +} + void Portfolio::baseCheckParam(const string& name) const { if ("adjust_cycle" == name) { int adjust_cycle = getParam("adjust_cycle"); @@ -70,8 +70,6 @@ void Portfolio::paramChanged() { } void Portfolio::reset() { - m_is_ready = false; - m_pro_sys_list.clear(); m_real_sys_list.clear(); m_running_sys_set.clear(); m_delay_adjust_sys_list.clear(); @@ -85,6 +83,7 @@ void Portfolio::reset() { m_se->reset(); if (m_af) m_af->reset(); + m_need_calculate = true; } PortfolioPtr Portfolio::clone() { @@ -92,9 +91,7 @@ PortfolioPtr Portfolio::clone() { p->m_params = m_params; p->m_name = m_name; p->m_query = m_query; - p->m_pro_sys_list = m_pro_sys_list; p->m_real_sys_list = m_real_sys_list; - p->m_is_ready = m_is_ready; p->m_need_calculate = m_need_calculate; if (m_se) p->m_se = m_se->clone(); @@ -107,85 +104,67 @@ PortfolioPtr Portfolio::clone() { return p; } -bool Portfolio::_readyForRun() { - if (!m_se) { - HKU_WARN("m_se is null!"); - m_is_ready = false; - return false; - } +void Portfolio::_readyForRun() { + SPEND_TIME(Portfolio_readyForRun); + HKU_CHECK(m_se, "m_se is null!"); + HKU_CHECK(m_tm, "m_tm is null!"); + HKU_CHECK(m_af, "m_am is null!"); - if (!m_tm) { - HKU_WARN("m_tm is null!"); - m_is_ready = false; - return false; - } + // se算法和af算法不匹配 + HKU_CHECK(m_se->isMatchAF(m_af), "The current SE and AF do not match!"); - if (!m_af) { - HKU_WARN("m_am is null!"); - m_is_ready = false; - return false; - } - - // se算法和af算法不匹配时,给出告警 - HKU_WARN_IF(!m_se->isMatchAF(m_af), "The current SE and AF do not match!"); + // 检查账户是否存在初始资产 + FundsRecord funds = m_tm->getFunds(); + HKU_CHECK(funds.total_assets() > 0.0, "The current tm is zero assets!"); reset(); - // 将影子账户指定给资产分配器 + // 生成资金账户 m_cash_tm = m_tm->clone(); + + // 配置资产分配器 m_af->setTM(m_tm); m_af->setCashTM(m_cash_tm); - - // 为资金分配器设置关联查询条件 m_af->setQuery(m_query); // 从 se 获取原型系统列表 - m_pro_sys_list = m_se->getProtoSystemList(); + auto pro_sys_list = m_se->getProtoSystemList(); // 获取所有备选子系统,为无关联账户的子系统分配子账号,对所有子系统做好启动准备 TMPtr pro_tm = crtTM(m_tm->initDatetime(), 0.0, m_tm->costFunc(), "TM_SUB"); - size_t total = m_pro_sys_list.size(); + size_t total = pro_sys_list.size(); + m_real_sys_list.reserve(total); for (size_t i = 0; i < total; i++) { - SystemPtr& pro_sys = m_pro_sys_list[i]; + SystemPtr& pro_sys = pro_sys_list[i]; SystemPtr sys = pro_sys->clone(); m_real_sys_list.emplace_back(sys); - // 如果原型子系统没有关联账户,则为其创建一个和总资金金额相同的账户,以便能够运行 - if (!pro_sys->getTM()) { - pro_sys->setTM(m_tm->clone()); - } - // 为内部实际执行的系统创建初始资金为0的子账户 sys->setTM(pro_tm->clone()); - sys->getTM()->name(fmt::format("TM_SUB{}", i)); - sys->name(fmt::format("PF_Real_{}_{}_{}", i, sys->name(), sys->getStock().market_code())); + string sys_name = fmt::format("{}_{}", sys->name(), sys->getStock().market_code()); + sys->getTM()->name(fmt::format("TM_SUB_{}", sys_name)); + sys->name(fmt::format("PF_{}", sys_name)); - HKU_CHECK(sys->readyForRun() && pro_sys->readyForRun(), - "Exist invalid system, it could not ready for run!"); + HKU_CHECK(sys->readyForRun(), "Exist invalid system, it could not ready for run!"); KData k = sys->getStock().getKData(m_query); sys->setTO(k); - pro_sys->setTO(k); } // 告知 se 当前实际运行的系统列表 m_se->calculate(m_real_sys_list, m_query); - - m_is_ready = true; - return true; } void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) { - HKU_CHECK(adjust_cycle > 0, "Invalid param adjust_cycle! {}", adjust_cycle); + SPEND_TIME(Portfolio_run); setParam("adjust_cycle", adjust_cycle); - setQuery(query); + if (force) { m_need_calculate = true; } HKU_IF_RETURN(!m_need_calculate, void()); - HKU_CHECK(_readyForRun(), - "readyForRun fails, check to see if a valid TradeManager, Selector, or " - "AllocateFunds instance have been specified."); + + _readyForRun(); DatetimeList datelist = StockManager::instance().getTradingCalendar(query); HKU_IF_RETURN(datelist.empty(), void()); diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index dafd7b7b..c53db206 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -90,14 +90,16 @@ public: PortfolioPtr clone(); /** 获取所有原型系统列表,与 SE 同 */ - const SystemList& getProtoSystemList() const; + const SystemList& getSystemList() const; /** 获取所有实际运行的系统列表,与 SE 同 */ const SystemList& getRealSystemList() const; private: + void initParam(); + /** 运行前准备 */ - bool _readyForRun(); + void _readyForRun(); void _runMoment(const Datetime& date, bool adjust); @@ -109,10 +111,8 @@ protected: AFPtr m_af; KQuery m_query; // 关联的查询条件 - bool m_is_ready; // 是否已做好运行准备 bool m_need_calculate; // 是否需要计算标志 - SystemList m_pro_sys_list; // 所有原型系统列表,来自 SE SystemList m_real_sys_list; // 所有实际运行的子系统列表 // 用于中间计算的临时数据 @@ -136,7 +136,6 @@ private: ar& BOOST_SERIALIZATION_NVP(m_se); ar& BOOST_SERIALIZATION_NVP(m_af); ar& BOOST_SERIALIZATION_NVP(m_query); - ar& BOOST_SERIALIZATION_NVP(m_is_ready); ar& BOOST_SERIALIZATION_NVP(m_need_calculate); } @@ -149,7 +148,6 @@ private: ar& BOOST_SERIALIZATION_NVP(m_se); ar& BOOST_SERIALIZATION_NVP(m_af); ar& BOOST_SERIALIZATION_NVP(m_query); - ar& BOOST_SERIALIZATION_NVP(m_is_ready); ar& BOOST_SERIALIZATION_NVP(m_need_calculate); } @@ -219,10 +217,6 @@ inline void Portfolio::setAF(const AFPtr& af) { } } -inline const SystemList& Portfolio::getProtoSystemList() const { - return m_pro_sys_list; -} - inline const SystemList& Portfolio::getRealSystemList() const { return m_real_sys_list; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index eec2e23a..99a42d0b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -17,8 +17,6 @@ namespace hku { -class HKU_API Portfolio; - /** * 交易对象选择模块 * @ingroup Selector @@ -102,7 +100,6 @@ public: virtual bool isMatchAF(const AFPtr& af) = 0; - friend class HKU_API Portfolio; /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ void calculate(const SystemList& sysList, const KQuery& query); diff --git a/hikyuu_pywrap/trade_sys/_Portfolio.cpp b/hikyuu_pywrap/trade_sys/_Portfolio.cpp index abdde05e..88dc9bcd 100644 --- a/hikyuu_pywrap/trade_sys/_Portfolio.cpp +++ b/hikyuu_pywrap/trade_sys/_Portfolio.cpp @@ -31,8 +31,6 @@ void export_Portfolio(py::module& m) { .def_property("tm", &Portfolio::getTM, &Portfolio::setTM, "设置或获取交易管理对象") .def_property("se", &Portfolio::getSE, &Portfolio::setSE, "设置或获取交易对象选择算法") .def_property("af", &Portfolio::getAF, &Portfolio::setAF, "设置或获取资产分配算法") - .def_property_readonly("proto_sys_list", &Portfolio::getProtoSystemList, - py::return_value_policy::copy, "获取原型系统列") .def_property_readonly("real_sys_list", &Portfolio::getRealSystemList, py::return_value_policy::copy, "由 PF 运行时设定的实际运行系统列表") From f3624aa0a1253f3c8f10cb2806c39a7d4a9494a9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 28 Mar 2024 03:14:31 +0800 Subject: [PATCH 104/601] =?UTF-8?q?=E5=B9=B6=E8=A1=8C=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp | 43 ++++----- hikyuu_cpp/hikyuu/indicator/crt/POS.cpp | 14 ++- .../hikyuu/utilities/thread/algorithm.h | 87 +++++++++++++++++++ hikyuu_cpp/hikyuu/utilities/thread/thread.h | 5 +- .../{ => thread}/test_ThreadPool.cpp | 0 .../utilities/thread/test_algorithm.cpp | 65 ++++++++++++++ 6 files changed, 177 insertions(+), 37 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/utilities/thread/algorithm.h rename hikyuu_cpp/unit_test/hikyuu/utilities/{ => thread}/test_ThreadPool.cpp (100%) create mode 100644 hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp diff --git a/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp b/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp index 897133e3..fc89e6ea 100644 --- a/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp +++ b/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp @@ -19,37 +19,30 @@ vector HKU_API analysisSystemList(const SystemList& size_t total = sys_list.size(); HKU_IF_RETURN(0 == total, result); - MQStealThreadPool tg; - vector> tasks; - - for (size_t i = 0; i < total; i++) { + result = parallel_for_index(0, total, [&](size_t i) { const auto& sys = sys_list[i]; const auto& stk = stk_list[i]; + + AnalysisSystemWithBlockOut ret; if (!sys || stk.isNull()) { - continue; + return ret; } - tasks.emplace_back(tg.submit([&sys, &stk, &query]() { - AnalysisSystemWithBlockOut ret; - try { - sys->run(stk, query); - Performance per; - per.statistics(sys->getTM()); - ret.market_code = stk.market_code(); - ret.name = stk.name(); - ret.values = per.values(); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR("Unknown error!"); - } - return ret; - })); - } + try { + sys->run(stk, query); + Performance per; + per.statistics(sys->getTM()); + ret.market_code = stk.market_code(); + ret.name = stk.name(); + ret.values = per.values(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + return ret; + }); - for (auto& task : tasks) { - result.emplace_back(task.get()); - } return result; } diff --git a/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp b/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp index a5feb7c8..84d616f4 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp +++ b/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp @@ -36,15 +36,13 @@ Indicator HKU_API POS(const Block& block, KQuery query, SignalPtr sg) { } } - ThreadPool tg; - vector sgs; - for (auto stk_iter = block.begin(); stk_iter != block.end(); ++stk_iter) { + auto stks = block.getAllStocks(); + vector sgs = parallel_for_index(0, stks.size(), [&](size_t i) { auto tmpsg = sg->clone(); - sgs.push_back(tmpsg); - KData kdata = stk_iter->getKData(query); - tg.submit([tmpsg, k = std::move(kdata)]() { tmpsg->setTO(k); }); - } - tg.join(); + auto kdata = stks[i].getKData(query); + tmpsg->setTO(kdata); + return tmpsg; + }); // 计算每日持仓的股票数 vector position(dayTotal); diff --git a/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h b/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h new file mode 100644 index 00000000..3269ba7b --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-27 + * Author: fasiondog + */ + +#pragma once + +#include +#include +#include +#include "ThreadPool.h" +#include "StealThreadPool.h" +#include "MQThreadPool.h" +#include "MQStealThreadPool.h" + +namespace hku { + +inline std::vector> parallelIndexRange(size_t start, size_t end) { + std::vector> ret; + if (start >= end) { + return ret; + } + + size_t total = end - start; + size_t cpu_num = std::thread::hardware_concurrency(); + if (total <= cpu_num) { + ret.emplace_back(start, end); + return ret; + } + + size_t per_num = total / cpu_num; + for (size_t i = start; i < per_num; i++) { + size_t first = i * cpu_num; + ret.emplace_back(first, first + cpu_num); + } + + for (size_t i = per_num * cpu_num; i < total; i++) { + ret.emplace_back(i, i + 1); + } + + return ret; +} + +template +void parallel_for_index(size_t start, size_t end, FunctionType f) { + auto ranges = parallelIndexRange(start, end); + TaskGroup tg; + for (size_t i = 0, total = ranges.size(); i < total; i++) { + tg.submit([=, range = ranges[i]]() { + for (size_t ix = range.first; ix < range.second; ix++) { + f(ix); + } + }); + } + tg.join(); + return; +} + +template +auto parallel_for_index(size_t start, size_t end, FunctionType f) { + auto ranges = parallelIndexRange(start, end); + TaskGroup tg; + std::vector::type>>> + tasks; + for (size_t i = 0, total = ranges.size(); i < total; i++) { + tasks.emplace_back(tg.submit([&, range = ranges[i]]() { + std::vector::type> one_ret; + for (size_t ix = range.first; ix < range.second; ix++) { + one_ret.emplace_back(f(ix)); + } + return one_ret; + })); + } + std::vector::type> ret; + for (auto& task : tasks) { + auto one = task.get(); + for (auto&& value : one) { + ret.emplace_back(std::move(value)); + } + } + + return ret; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/thread/thread.h b/hikyuu_cpp/hikyuu/utilities/thread/thread.h index e4fe7107..cf87351d 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/thread.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/thread.h @@ -7,7 +7,4 @@ #pragma once -#include "ThreadPool.h" -#include "StealThreadPool.h" -#include "MQThreadPool.h" -#include "MQStealThreadPool.h" \ No newline at end of file +#include "algorithm.h" diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_ThreadPool.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_ThreadPool.cpp similarity index 100% rename from hikyuu_cpp/unit_test/hikyuu/utilities/test_ThreadPool.cpp rename to hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_ThreadPool.cpp diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp new file mode 100644 index 00000000..3966c91d --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-27 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include + +using namespace hku; + +TEST_CASE("test_parallelIndexRange") { + std::vector> expect{{0, 2}}; + auto result = parallelIndexRange(0, 2); + CHECK_EQ(result.size(), 1); + for (size_t i = 0, len = expect.size(); i < len; i++) { + CHECK_EQ(result[i].first, expect[i].first); + CHECK_EQ(result[i].second, expect[i].second); + } + + size_t cpu_num = std::thread::hardware_concurrency(); + if (cpu_num == 32) { + result = parallelIndexRange(0, 100); + expect = {{0, 32}, {32, 64}, {64, 96}, {96, 97}, {97, 98}, {98, 99}, {99, 100}}; + CHECK_EQ(result.size(), expect.size()); + for (size_t i = 0, len = expect.size(); i < len; i++) { + CHECK_EQ(result[i].first, expect[i].first); + CHECK_EQ(result[i].second, expect[i].second); + } + + } else if (cpu_num == 8) { + result = parallelIndexRange(0, 35); + expect = {{0, 4}, {4, 8}, {8, 12}, {12, 16}, {16, 20}, {20, 24}, + {24, 28}, {28, 32}, {32, 33}, {33, 34}, {34, 35}}; + CHECK_EQ(result.size(), expect.size()); + for (size_t i = 0, len = expect.size(); i < len; i++) { + CHECK_EQ(result[i].first, expect[i].first); + CHECK_EQ(result[i].second, expect[i].second); + } + } +} + +TEST_CASE("test_parallel_for_index") { + std::vector values(100); + for (size_t i = 0, len = values.size(); i < len; i++) { + values[i] = i; + } + + auto result = parallel_for_index(0, values.size(), [](size_t i) { + // HKU_INFO("i: {}", i); + return i + 1; + }); + + std::vector expect(100); + for (size_t i = 0, len = expect.size(); i < len; i++) { + expect[i] = i + 1; + } + CHECK_EQ(result.size(), expect.size()); + for (size_t i = 0, len = expect.size(); i < len; i++) { + CHECK_EQ(result[i], expect[i]); + } + + // parallel_for_index(0, values.size(), [](size_t i) { HKU_INFO("i: {}", i); }); +} From bb861105f21831021760bd6f053f85eef591c261 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 28 Mar 2024 14:09:48 +0800 Subject: [PATCH 105/601] fixed compile errors with gcc --- hikyuu_cpp/hikyuu/utilities/thread/algorithm.h | 4 ++-- .../unit_test/hikyuu/utilities/thread/test_algorithm.cpp | 5 +---- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h b/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h index 3269ba7b..71be9ed2 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h @@ -43,8 +43,8 @@ inline std::vector> parallelIndexRange(size_t start, s return ret; } -template -void parallel_for_index(size_t start, size_t end, FunctionType f) { +template +void parallel_for_index_void(size_t start, size_t end, FunctionType f) { auto ranges = parallelIndexRange(start, end); TaskGroup tg; for (size_t i = 0, total = ranges.size(); i < total; i++) { diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp index 3966c91d..f02acfcf 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp @@ -47,10 +47,7 @@ TEST_CASE("test_parallel_for_index") { values[i] = i; } - auto result = parallel_for_index(0, values.size(), [](size_t i) { - // HKU_INFO("i: {}", i); - return i + 1; - }); + auto result = parallel_for_index(0, values.size(), [](size_t i) { return i + 1; }); std::vector expect(100); for (size_t i = 0, len = expect.size(); i < len; i++) { From efc50e635027be4d1fb654d916dbcc93a57f0ef1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 28 Mar 2024 14:28:08 +0800 Subject: [PATCH 106/601] fixed test_parallelIndexRange --- .../unit_test/hikyuu/utilities/thread/test_algorithm.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp index f02acfcf..a98a056f 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp @@ -31,8 +31,7 @@ TEST_CASE("test_parallelIndexRange") { } else if (cpu_num == 8) { result = parallelIndexRange(0, 35); - expect = {{0, 4}, {4, 8}, {8, 12}, {12, 16}, {16, 20}, {20, 24}, - {24, 28}, {28, 32}, {32, 33}, {33, 34}, {34, 35}}; + expect = {{0, 8}, {8, 16}, {16, 24}, {24, 32}, {32, 33}, {33, 34}, {34, 35}}; CHECK_EQ(result.size(), expect.size()); for (size_t i = 0, len = expect.size(); i < len; i++) { CHECK_EQ(result[i].first, expect[i].first); From 0e419ddebadaa188a4102dbf930fe08c9fb83332 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 28 Mar 2024 18:43:23 +0800 Subject: [PATCH 107/601] remove MYSQL_OPT_RECONNECT --- hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp index d0f34a11..aa09155c 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp @@ -51,9 +51,13 @@ void MySQLConnect::connect() { // HKU_TRACE("MYSQL port: {}", port); // HKU_TRACE("MYSQL database: {}", database); +#if MYSQL_VERSION_ID < 80034 + // mysql 后续不再支持自动重连选项 + // see: https://dev.mysql.com/doc/c-api/8.2/en/c-api-auto-reconnect.html my_bool reconnect = 1; SQL_CHECK(mysql_options(m_mysql, MYSQL_OPT_RECONNECT, &reconnect) == 0, mysql_errno(m_mysql), "Failed set reconnect options, {}", mysql_error(m_mysql)); +#endif // 20220314: 新版 mysqlclient 默认 ssl 可能被开启,这里强制设为关闭 unsigned int ssl_mode = SSL_MODE_DISABLED; From cca73f1bff21c75aa250c97d3ab9a57ee1d1da8f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 29 Mar 2024 01:10:05 +0800 Subject: [PATCH 108/601] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20python=20crt?= =?UTF-8?q?=E7=B3=BB=E5=88=97=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=84=E4=BB=B6?= =?UTF-8?q?=20clone?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/test/Signal.py | 8 +-- hikyuu/trade_sys/trade_sys.py | 63 ++++++++----------- .../allocatefunds/AllocateFundsBase.h | 3 +- .../trade_sys/condition/ConditionBase.h | 3 +- .../trade_sys/environment/EnvironmentBase.cpp | 3 + .../trade_sys/environment/EnvironmentBase.h | 5 +- .../trade_sys/factor/MultiFactorBase.cpp | 8 +++ .../hikyuu/trade_sys/factor/MultiFactorBase.h | 3 +- .../trade_sys/moneymanager/MoneyManagerBase.h | 3 +- .../trade_sys/profitgoal/ProfitGoalBase.h | 3 +- .../hikyuu/trade_sys/selector/SelectorBase.h | 3 +- .../hikyuu/trade_sys/signal/SignalBase.h | 3 +- .../hikyuu/trade_sys/slippage/SlippageBase.h | 3 +- .../hikyuu/trade_sys/stoploss/StoplossBase.h | 3 +- hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 2 + hikyuu_pywrap/trade_sys/_Condition.cpp | 2 + hikyuu_pywrap/trade_sys/_Environment.cpp | 2 + hikyuu_pywrap/trade_sys/_MoneyManager.cpp | 2 + hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 + hikyuu_pywrap/trade_sys/_ProfitGoal.cpp | 2 + hikyuu_pywrap/trade_sys/_Signal.cpp | 2 + hikyuu_pywrap/trade_sys/_Slippage.cpp | 2 + hikyuu_pywrap/trade_sys/_Stoploss.cpp | 2 + 23 files changed, 78 insertions(+), 54 deletions(-) diff --git a/hikyuu/test/Signal.py b/hikyuu/test/Signal.py index 1568933d..1d1e8d4d 100644 --- a/hikyuu/test/Signal.py +++ b/hikyuu/test/Signal.py @@ -138,15 +138,9 @@ class TestCrtSG(unittest.TestCase): self.assertIn(d[i], [Datetime(201201300000), Datetime(200101030000)]) -def testSignalClone(self): - new_object = crtSG(testSignal, self._params, self._name) - new_object.x = self.x - return new_object - - class TestCrtSGWithClone(unittest.TestCase): def test_crtSG(self): - p = crtSG(testSignal, params={'test': 30}, name='SG_TEST', clone=testSignalClone) + p = crtSG(testSignal, params={'test': 30}, name='SG_TEST') p.x = 10 p2 = p.clone() self.assertFalse(p2 is p) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index 6a1e0d42..e43d5604 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -14,7 +14,7 @@ def part_iter(self): ConditionBase.__iter__ = part_iter -def part_init(self, name, params): +def part_init(self, name='', params={}): super(self.__class__, self).__init__(name) self._name = name self._params = params @@ -22,6 +22,13 @@ def part_init(self, name, params): self.set_param(k, v) +def part_clone(self): + cloned = self.__class__.__new__(self.__class__) + self.__class__.__init__(cloned, self) + cloned.__dict__.update(self.__dict__) + return cloned + + # ------------------------------------------------------------------ # System # ------------------------------------------------------------------ @@ -40,18 +47,16 @@ System.INVALID = System.Part.INVALID # ------------------------------------------------------------------ # condition # ------------------------------------------------------------------ -def crtCN(func, params={}, name='crtCN', clone=None): +def crtCN(func, params={}, name='crtCN'): """ 快速创建系统有效条件 :param func: 系统有效条件函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 自定义系统有效条件实例 """ - meta_x = type(name, (ConditionBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (ConditionBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) @@ -59,18 +64,16 @@ def crtCN(func, params={}, name='crtCN', clone=None): # ------------------------------------------------------------------ # environment # ------------------------------------------------------------------ -def crtEV(func, params={}, name='crtEV', clone=None): +def crtEV(func, params={}, name='crtEV'): """ 快速创建市场环境判断策略 :param func: 市场环境判断策略函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 自定义市场环境判断策略实例 """ - meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) @@ -78,18 +81,16 @@ def crtEV(func, params={}, name='crtEV', clone=None): # ------------------------------------------------------------------ # moneymanager # ------------------------------------------------------------------ -def crtMM(func, params={}, name='crtMM', clone=None): +def crtMM(func, params={}, name='crtMM'): """ 快速创建资金管理策略 :param func: 资金管理策略计算函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 自定义资金管理策略实例 """ - meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) @@ -97,18 +98,16 @@ def crtMM(func, params={}, name='crtMM', clone=None): # ------------------------------------------------------------------ # profitgoal # ------------------------------------------------------------------ -def crtPG(func, params={}, name='crtPG', clone=None): +def crtPG(func, params={}, name='crtPG'): """ 快速创建盈利目标策略 :param func: 盈利目标策略函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 盈利目标策略实例 """ - meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) @@ -116,18 +115,16 @@ def crtPG(func, params={}, name='crtPG', clone=None): # ------------------------------------------------------------------ # signal # ------------------------------------------------------------------ -def crtSG(func, params={}, name='crtSG', clone=None): +def crtSG(func, params={}, name='crtSG'): """ 快速创建信号指示器 :param func: 信号策略函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 自定义信号指示器实例 """ - meta_x = type(name, (SignalBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (SignalBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) @@ -148,7 +145,7 @@ def se_add_stock_list(self, stk_list, proto_sys): SelectorBase.add_stock_list = se_add_stock_list -def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE', clone=None): +def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE'): """ 快速创建交易对象选择算法 @@ -157,32 +154,28 @@ def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE', cl :param get_selected_on_open function: 开盘时刻选择算法 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 自定义交易对象选择算法实例 """ - meta_x = type(name, (SelectorBase, ), {'__init__': part_init}) + meta_x = type(name, (SelectorBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = calculate 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) # ------------------------------------------------------------------ # allocatefunds # ------------------------------------------------------------------ -def crtAF(allocate_weight_func, params={}, name='crtAF', clone=None): +def crtAF(allocate_weight_func, params={}, name='crtAF'): """ 快速创建资产分配算法 :param allocate_weight_func: 资产分配算法 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 自定义资产分配算法实例 """ - meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._allocate_weight = allocate_weight_func return meta_x(name, params) @@ -190,18 +183,16 @@ def crtAF(allocate_weight_func, params={}, name='crtAF', clone=None): # ------------------------------------------------------------------ # slippage # ------------------------------------------------------------------ -def crtSL(func, params={}, name='crtSL', clone=None): +def crtSL(func, params={}, name='crtSL'): """ 快速创建移滑价差算法 :param func: 移滑价差算法函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 移滑价差算法实例 """ - meta_x = type(name, (SlippageBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (SlippageBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) @@ -209,17 +200,15 @@ def crtSL(func, params={}, name='crtSL', clone=None): # ------------------------------------------------------------------ # stoploss # ------------------------------------------------------------------ -def crtST(func, params={}, name='crtST', clone=None): +def crtST(func, params={}, name='crtST'): """ 快速创建止损/止盈策略 :param func: 止损/止盈策略函数 :param {} params: 参数字典 :param str name: 自定义名称 - :param clone: 带有私有属性时自定义的clone函数 :return: 止损/止盈策略实例 """ - meta_x = type(name, (StoplossBase, ), {'__init__': part_init}) - meta_x._clone = (lambda self: meta_x(self._name, self._params)) if clone is None else clone + meta_x = type(name, (StoplossBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func return meta_x(name, params) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index 42074f38..bf93d129 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -25,12 +25,13 @@ class HKU_API AllocateFundsBase : public enable_shared_from_this { public: ConditionBase(); - ConditionBase(const string& name); + ConditionBase(const ConditionBase&) = default; + explicit ConditionBase(const string& name); virtual ~ConditionBase(); /** 获取名称 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp index f808b9f6..426545c9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.cpp @@ -25,6 +25,9 @@ HKU_API std::ostream& operator<<(std::ostream& os, const EnvironmentPtr& en) { EnvironmentBase::EnvironmentBase() : m_name("EnvironmentBase") {} +EnvironmentBase::EnvironmentBase(const EnvironmentBase& base) +: m_params(base.m_params), m_name(base.m_name), m_valid(base.m_valid) {} + EnvironmentBase::EnvironmentBase(const string& name) : m_name(name) {} EnvironmentBase::~EnvironmentBase() {} diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h index 3f00a14a..62e88855 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h @@ -26,9 +26,12 @@ class HKU_API EnvironmentBase : public enable_shared_from_this public: EnvironmentBase(); - EnvironmentBase(const string& name); + explicit EnvironmentBase(const string& name); virtual ~EnvironmentBase(); + // 用于 python clone, 但由于 mutex, 是非线程安全的 + EnvironmentBase(const EnvironmentBase&); + /** 获取名称 */ const string& name() const { return m_name; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 83016e25..bcae8c82 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -67,6 +67,14 @@ MultiFactorBase::MultiFactorBase(const string& name) { initParam(); } +MultiFactorBase::MultiFactorBase(const MultiFactorBase& base) +: m_params(base.m_params), + m_name(base.m_name), + m_inds(base.m_inds), + m_stks(base.m_stks), + m_ref_stk(base.m_ref_stk), + m_query(base.m_query) {} + MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, const string& name, int ic_n) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 8d0dfe73..ab53b7cc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -25,9 +25,10 @@ public: public: MultiFactorBase(); - MultiFactorBase(const string& name); + explicit MultiFactorBase(const string& name); MultiFactorBase(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, const string& name, int ic_n); + MultiFactorBase(const MultiFactorBase&); virtual ~MultiFactorBase() = default; /** 获取名称 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h index 713988e3..2b31b7dd 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h @@ -24,7 +24,8 @@ class HKU_API MoneyManagerBase : public enable_shared_from_this { public: ProfitGoalBase(); - ProfitGoalBase(const string& name); + explicit ProfitGoalBase(const string& name); + ProfitGoalBase(const ProfitGoalBase&) = default; virtual ~ProfitGoalBase(); /** 设置账户 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 99a42d0b..267a7992 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -27,12 +27,13 @@ class HKU_API SelectorBase : public enable_shared_from_this { public: /** 默认构造函数 */ SelectorBase(); + SelectorBase(const SelectorBase&) = default; /** * 构造函数,同时指定算法名称 * @param name 指定名称 */ - SelectorBase(const string& name); + explicit SelectorBase(const string& name); /** 析构函数 */ virtual ~SelectorBase(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index d80509f4..f7b38925 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -26,7 +26,8 @@ class HKU_API SignalBase : public enable_shared_from_this { public: SignalBase(); - SignalBase(const string& name); + explicit SignalBase(const string& name); + SignalBase(const SignalBase&) = default; virtual ~SignalBase(); /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h index cf7f02fc..8ea3aea2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h @@ -23,7 +23,8 @@ class HKU_API SlippageBase : public enable_shared_from_this { public: SlippageBase(); - SlippageBase(const string& name); + explicit SlippageBase(const string& name); + SlippageBase(const SlippageBase&) = default; virtual ~SlippageBase() {} /** 设置交易对象 */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h index 356b2610..cf8f1b69 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h @@ -25,7 +25,8 @@ class HKU_API StoplossBase : public enable_shared_from_this { public: StoplossBase(); - StoplossBase(const string& name); + explicit StoplossBase(const string& name); + StoplossBase(const StoplossBase&) = default; virtual ~StoplossBase(); /** 获取名称 */ diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index 5d3372b8..88bf00c3 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -20,6 +20,7 @@ class PyAllocateFundsBase : public AllocateFundsBase { public: using AllocateFundsBase::AllocateFundsBase; + PyAllocateFundsBase(const AllocateFundsBase& base) : AllocateFundsBase(base) {} void _reset() override { PYBIND11_OVERLOAD(void, AllocateFundsBase, _reset, ); @@ -42,6 +43,7 @@ void export_AllocateFunds(py::module& m) { - _reset : 【可选】重载私有变量)") .def(py::init<>()) .def(py::init()) + .def(py::init()) .def("__str__", to_py_str) .def("__repr__", to_py_str) .def_property("name", py::overload_cast<>(&AllocateFundsBase::name, py::const_), diff --git a/hikyuu_pywrap/trade_sys/_Condition.cpp b/hikyuu_pywrap/trade_sys/_Condition.cpp index 99fedabf..37e7cfa6 100644 --- a/hikyuu_pywrap/trade_sys/_Condition.cpp +++ b/hikyuu_pywrap/trade_sys/_Condition.cpp @@ -17,6 +17,7 @@ class PyConditionBase : public ConditionBase { public: using ConditionBase::ConditionBase; + PyConditionBase(const ConditionBase& base) : ConditionBase(base) {} void _calculate() override { PYBIND11_OVERLOAD_PURE(void, ConditionBase, _calculate, ); @@ -36,6 +37,7 @@ void export_Condition(py::module& m) { - _clone : 【必须】克隆接口 - _reset : 【可选】重载私有变量)") .def(py::init<>()) + .def(py::init()) .def(py::init(), R"(初始化构造函数 :param str name: 名称)") diff --git a/hikyuu_pywrap/trade_sys/_Environment.cpp b/hikyuu_pywrap/trade_sys/_Environment.cpp index 11d99aba..88d31ddc 100644 --- a/hikyuu_pywrap/trade_sys/_Environment.cpp +++ b/hikyuu_pywrap/trade_sys/_Environment.cpp @@ -24,6 +24,7 @@ class PyEnvironmentBase : public EnvironmentBase { public: using EnvironmentBase::EnvironmentBase; + PyEnvironmentBase(const EnvironmentBase& base) : EnvironmentBase(base) {} void _calculate() override { PYBIND11_OVERLOAD_PURE(void, EnvironmentBase, _calculate, ); @@ -45,6 +46,7 @@ void export_Environment(py::module& m) { - _clone : 【必须】克隆接口 - _reset : 【可选】重载私有变量)") .def(py::init<>()) + .def(py::init()) .def(py::init()) .def("__str__", to_py_str) diff --git a/hikyuu_pywrap/trade_sys/_MoneyManager.cpp b/hikyuu_pywrap/trade_sys/_MoneyManager.cpp index e3bdb4f2..f015cadc 100644 --- a/hikyuu_pywrap/trade_sys/_MoneyManager.cpp +++ b/hikyuu_pywrap/trade_sys/_MoneyManager.cpp @@ -16,6 +16,7 @@ class PyMoneyManagerBase : public MoneyManagerBase { public: using MoneyManagerBase::MoneyManagerBase; + PyMoneyManagerBase(const MoneyManagerBase& base) : MoneyManagerBase(base) {} void _reset() override { PYBIND11_OVERLOAD(void, MoneyManagerBase, _reset, ); @@ -74,6 +75,7 @@ void export_MoneyManager(py::module& m) { - _reset : 【可选】重置私有属性 - _clone : 【必须】克隆接口)") .def(py::init<>()) + .def(py::init()) .def(py::init(), R"(初始化构造函数 :param str name: 名称)") diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index ee03b302..4d01da67 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -16,6 +16,7 @@ class PyMultiFactor : public MultiFactorBase { public: using MultiFactorBase::MultiFactorBase; + PyMultiFactor(const MultiFactorBase& base) : MultiFactorBase(base) {} IndicatorList _calculate(const vector& all_stk_inds) { // PYBIND11_OVERLOAD_PURE_NAME(IndicatorList, MultiFactorBase, "_calculate", _calculate, @@ -47,6 +48,7 @@ void export_MultiFactor(py::module& m) { - _clone : 【必须】克隆接口 - _reset : 【可选】重载私有变量)") .def(py::init<>()) + .def(py::init()) .def("__str__", to_py_str) .def("__repr__", to_py_str) diff --git a/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp b/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp index 47c48868..628c57da 100644 --- a/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp +++ b/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp @@ -16,6 +16,7 @@ class PyProfitGoalBase : public ProfitGoalBase { public: using ProfitGoalBase::ProfitGoalBase; + PyProfitGoalBase(const ProfitGoalBase& base) : ProfitGoalBase(base) {} void buyNotify(const TradeRecord& tr) override { PYBIND11_OVERLOAD_NAME(void, ProfitGoalBase, "buy_notify", buyNotify, tr); @@ -57,6 +58,7 @@ void export_ProfitGoal(py::module& m) { - sellNotify : 【可选】接收实际卖出通知,预留用于多次增减仓处理)") .def(py::init<>()) + .def(py::init()) .def(py::init(), R"(初始化构造函数 :param str name: 名称)") diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 6576ba94..74930f57 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -16,6 +16,7 @@ class PySignalBase : public SignalBase { public: using SignalBase::SignalBase; + PySignalBase(const SignalBase& base) : SignalBase(base) {} void _calculate() override { PYBIND11_OVERLOAD_PURE(void, SignalBase, _calculate, ); @@ -42,6 +43,7 @@ void export_Signal(py::module& m) { .def(py::init<>()) .def(py::init()) + .def(py::init()) .def("__str__", to_py_str) .def("__repr__", to_py_str) diff --git a/hikyuu_pywrap/trade_sys/_Slippage.cpp b/hikyuu_pywrap/trade_sys/_Slippage.cpp index 8656f3b6..42ca4f27 100644 --- a/hikyuu_pywrap/trade_sys/_Slippage.cpp +++ b/hikyuu_pywrap/trade_sys/_Slippage.cpp @@ -17,6 +17,7 @@ class PySlippageBase : public SlippageBase { public: using SlippageBase::SlippageBase; + PySlippageBase(const SlippageBase& base) : SlippageBase(base) {} void _calculate() override { PYBIND11_OVERLOAD_PURE(void, SlippageBase, _calculate, ); @@ -49,6 +50,7 @@ void export_Slippage(py::module& m) { - _reset : 【可选】重载私有变量)") .def(py::init<>()) + .def(py::init()) .def(py::init(), R"(初始化构造函数 :param str name: 名称)") diff --git a/hikyuu_pywrap/trade_sys/_Stoploss.cpp b/hikyuu_pywrap/trade_sys/_Stoploss.cpp index 7cf8c673..dc67da79 100644 --- a/hikyuu_pywrap/trade_sys/_Stoploss.cpp +++ b/hikyuu_pywrap/trade_sys/_Stoploss.cpp @@ -16,6 +16,7 @@ class PyStoplossBase : public StoplossBase { public: using StoplossBase::StoplossBase; + PyStoplossBase(const StoplossBase& base) : StoplossBase(base) {} void _calculate() override { PYBIND11_OVERLOAD_PURE(void, StoplossBase, _calculate, ); @@ -44,6 +45,7 @@ void export_Stoploss(py::module& m) { - _clone : 【必须】克隆接口 - _reset : 【可选】重载私有变量)") .def(py::init<>()) + .def(py::init()) .def(py::init(), R"(初始化构造函数 :param str name: 名称)") From 4e615480202dd160eaef944eb49bab3cd2471539 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 29 Mar 2024 02:49:38 +0800 Subject: [PATCH 109/601] =?UTF-8?q?=E5=B9=B6=E8=A1=8C=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp | 27 +++++++++++++++++ hikyuu_cpp/hikyuu/analysis/analysis_sys.h | 23 ++------------- hikyuu_cpp/hikyuu/analysis/combinate.cpp | 16 ++++------ .../hikyuu/utilities/thread/algorithm.h | 29 +++++++++++++++++-- hikyuu_pywrap/_analysis.cpp | 1 + 5 files changed, 62 insertions(+), 34 deletions(-) diff --git a/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp b/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp index fc89e6ea..1d91f660 100644 --- a/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp +++ b/hikyuu_cpp/hikyuu/analysis/analysis_sys.cpp @@ -46,4 +46,31 @@ vector HKU_API analysisSystemList(const SystemList& return result; } +vector HKU_API analysisSystemList(const StockList& stk_list, + const KQuery& query, + const SystemPtr& pro_sys) { + HKU_CHECK(pro_sys, "pro_sys is null!"); + + return parallel_for_range(0, stk_list.size(), [=](const range_t& range) { + vector ret; + auto sys = pro_sys->clone(); + Performance per; + AnalysisSystemWithBlockOut out; + for (size_t i = range.first; i < range.second; i++) { + try { + auto stk = stk_list[i]; + sys->run(stk, query); + per.statistics(sys->getTM()); + out.market_code = stk.market_code(); + out.name = stk.name(); + out.values = per.values(); + ret.emplace_back(std::move(out)); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } + } + return ret; + }); +} + } // namespace hku diff --git a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h index a3e99452..6f8a605d 100644 --- a/hikyuu_cpp/hikyuu/analysis/analysis_sys.h +++ b/hikyuu_cpp/hikyuu/analysis/analysis_sys.h @@ -39,25 +39,8 @@ vector HKU_API analysisSystemList(const SystemList& const StockList& stk_list, const KQuery& query); -template -inline vector analysisSystemListWith(const Container& blk, - const KQuery& query, - const SystemPtr sys_proto) { - vector result; - HKU_IF_RETURN(blk.size() == 0 || !sys_proto, result); - - sys_proto->forceResetAll(); - SystemList sys_list; - StockList stk_list; - for (const auto& stk : blk) { - if (!stk.isNull()) { - sys_list.emplace_back(std::move(sys_proto->clone())); - stk_list.emplace_back(stk); - } - } - - result = analysisSystemList(sys_list, stk_list, query); - return result; -} +vector HKU_API analysisSystemList(const StockList& stk_list, + const KQuery& query, + const SystemPtr& pro_sys); } // namespace hku diff --git a/hikyuu_cpp/hikyuu/analysis/combinate.cpp b/hikyuu_cpp/hikyuu/analysis/combinate.cpp index ebfddde1..99653af5 100644 --- a/hikyuu_cpp/hikyuu/analysis/combinate.cpp +++ b/hikyuu_cpp/hikyuu/analysis/combinate.cpp @@ -9,7 +9,6 @@ #include "hikyuu/utilities/thread/thread.h" #include "hikyuu/indicator/crt/EXIST.h" #include "hikyuu/trade_sys/signal/crt/SG_Bool.h" -#include "hikyuu/global/GlobalTaskGroup.h" #include "combinate.h" namespace hku { @@ -80,22 +79,18 @@ vector HKU_API combinateIndicatorAnalysisWithBlock( MQStealThreadPool tg(work_num); vector>> tasks; - size_t per_num = total > work_num ? total / (work_num * 10) : 1; - size_t count = total % per_num == 0 ? total / per_num : total / per_num + 1; - vector buf; - for (size_t i = 0; i < count; i++) { + auto ranges = parallelIndexRange(0, total); + for (const auto& range : ranges) { buf.clear(); - for (size_t j = i * per_num, end = (i + 1) * per_num; j < end; j++) { - if (j >= stocks.size()) { - break; - } - buf.emplace_back(stocks[j]); + for (size_t i = range.first; i < range.second; i++) { + buf.emplace_back(stocks[i]); } tasks.emplace_back(tg.submit([sgs, stks = std::move(buf), n_query = query, n_tm = tm->clone(), n_sys = sys->clone()]() { vector ret; Performance per; + CombinateAnalysisOutput out; for (size_t i = 0, len = stks.size(); i < len; i++) { const Stock& n_stk = stks[i]; for (const auto& sg : sgs) { @@ -105,7 +100,6 @@ vector HKU_API combinateIndicatorAnalysisWithBlock( n_sys->setTM(n_tm); n_sys->run(n_stk, n_query); per.statistics(n_tm, Datetime::now()); - CombinateAnalysisOutput out; out.combinateName = n_sg->name(); out.market_code = n_stk.market_code(); out.name = n_stk.name(); diff --git a/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h b/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h index 71be9ed2..85cc5c3c 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/algorithm.h @@ -17,7 +17,9 @@ namespace hku { -inline std::vector> parallelIndexRange(size_t start, size_t end) { +typedef std::pair range_t; + +inline std::vector parallelIndexRange(size_t start, size_t end) { std::vector> ret; if (start >= end) { return ret; @@ -65,14 +67,15 @@ auto parallel_for_index(size_t start, size_t end, FunctionType f) { std::vector::type>>> tasks; for (size_t i = 0, total = ranges.size(); i < total; i++) { - tasks.emplace_back(tg.submit([&, range = ranges[i]]() { + tasks.emplace_back(tg.submit([func = f, range = ranges[i]]() { std::vector::type> one_ret; for (size_t ix = range.first; ix < range.second; ix++) { - one_ret.emplace_back(f(ix)); + one_ret.emplace_back(func(ix)); } return one_ret; })); } + std::vector::type> ret; for (auto& task : tasks) { auto one = task.get(); @@ -84,4 +87,24 @@ auto parallel_for_index(size_t start, size_t end, FunctionType f) { return ret; } +template +auto parallel_for_range(size_t start, size_t end, FunctionType f) { + auto ranges = parallelIndexRange(start, end); + TaskGroup tg; + std::vector::type>> tasks; + for (size_t i = 0, total = ranges.size(); i < total; i++) { + tasks.emplace_back(tg.submit([func = f, range = ranges[i]]() { return func(range); })); + } + + typename std::result_of::type ret; + for (auto& task : tasks) { + auto one = task.get(); + for (auto&& value : one) { + ret.emplace_back(std::move(value)); + } + } + + return ret; +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/_analysis.cpp b/hikyuu_pywrap/_analysis.cpp index 8b9203ac..c67b309c 100644 --- a/hikyuu_pywrap/_analysis.cpp +++ b/hikyuu_pywrap/_analysis.cpp @@ -154,6 +154,7 @@ static py::dict analysis_sys_list(const py::object& pystk_list, const KQuery& qu OStreamToPython guard(false); py::gil_scoped_release release; records = analysisSystemList(sys_list, stk_list, query); + // records = analysisSystemList(stk_list, query, sys_proto); } Performance per; From 1a35e137b9dbe724268e50c67f05b0b45e8262b6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 29 Mar 2024 03:02:24 +0800 Subject: [PATCH 110/601] =?UTF-8?q?SpendTimer=20=E6=94=B9=E8=BE=93?= =?UTF-8?q?=E5=87=BA=E5=88=B0=20std::cout=20=EF=BC=8C=E4=BB=A5=E4=BE=BF=20?= =?UTF-8?q?jupyter=20=E5=8F=AF=E4=BB=A5=E6=8D=95=E8=8E=B7=E8=BE=93?= =?UTF-8?q?=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp b/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp index e638c1bb..1596a5cd 100644 --- a/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp +++ b/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp @@ -66,7 +66,10 @@ SpendTimer::~SpendTimer() { #endif #else - printf("%5zu keep: %7.3f %s - %s\n", i, duration, unit.c_str(), m_keep_desc[i].c_str()); + // printf("%5zu keep: %7.3f %s - %s\n", i, duration, unit.c_str(), + // m_keep_desc[i].c_str()); + std::cout << std::setw(5) << " keep: " << i << std::setw(7) << std::setprecision(3) + << duration << " " << unit << " - " << m_keep_desc[i] << std::endl; #endif /* __ANDROID__ */ } } @@ -113,7 +116,8 @@ void SpendTimer::show() const { #endif #else - printf("%s", outmsg); + // printf("%s", outmsg); + std::cout << outmsg; #endif /* __ANDROID__ */ } From 03fb9548c08dac0ed9c30852c21a2691b270e172 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 29 Mar 2024 13:00:37 +0800 Subject: [PATCH 111/601] update requirements, add h5py --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index e0969b9c..c4326cb4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -15,4 +15,5 @@ flatbuffers>=23.5.6 pynng akshare pyecharts -pipdeptree \ No newline at end of file +pipdeptree +h5py \ No newline at end of file From 7b0dadb4548dd044e7ed05f79b07bdfbf52d064a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 29 Mar 2024 17:57:06 +0800 Subject: [PATCH 112/601] =?UTF-8?q?StockManager/Block/MF=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E8=BF=87=E6=BB=A4=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Block.cpp | 16 +++++++++++---- hikyuu_cpp/hikyuu/Block.h | 3 ++- hikyuu_cpp/hikyuu/StockManager.cpp | 19 ++++++++++++++++++ hikyuu_cpp/hikyuu/StockManager.h | 3 +++ hikyuu_cpp/hikyuu/indicator/crt/POS.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 2 +- .../trade_sys/factor/MultiFactorBase.cpp | 12 +++++++++++ .../hikyuu/trade_sys/factor/MultiFactorBase.h | 5 ++++- .../hikyuu/trade_sys/factor/ScoreRecord.h | 1 + hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 4 +++- hikyuu_pywrap/_Block.cpp | 20 +++++++++++++++++++ hikyuu_pywrap/_StockManager.cpp | 20 +++++++++++++++++++ hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 8 ++++++++ 13 files changed, 106 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Block.cpp b/hikyuu_cpp/hikyuu/Block.cpp index dab5b597..35afec71 100644 --- a/hikyuu_cpp/hikyuu/Block.cpp +++ b/hikyuu_cpp/hikyuu/Block.cpp @@ -62,12 +62,20 @@ Stock Block::get(const string& market_code) const { return result; } -vector Block::getAllStocks() const { - vector ret; +StockList Block::getStockList(std::function&& filter) const { + StockList ret; ret.reserve(size()); auto iter = m_data->m_stockDict.begin(); - for (; iter != m_data->m_stockDict.end(); ++iter) { - ret.emplace_back(iter->second); + if (filter) { + for (; iter != m_data->m_stockDict.end(); ++iter) { + if (filter(iter->second)) { + ret.emplace_back(iter->second); + } + } + } else { + for (; iter != m_data->m_stockDict.end(); ++iter) { + ret.emplace_back(iter->second); + } } return ret; } diff --git a/hikyuu_cpp/hikyuu/Block.h b/hikyuu_cpp/hikyuu/Block.h index eacb80ee..2c780364 100644 --- a/hikyuu_cpp/hikyuu/Block.h +++ b/hikyuu_cpp/hikyuu/Block.h @@ -87,7 +87,8 @@ public: } /** 获取板块下所有证券 */ - vector getAllStocks() const; + StockList getStockList( + std::function&& filter = std::function()) const; /** 加入指定证券 */ bool add(const Stock& stock); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 84904bb3..284da471 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -327,6 +327,25 @@ Stock StockManager::getStock(const string& querystr) const { return (iter != m_stockDict.end()) ? iter->second : result; } +StockList StockManager::getStockList(std::function&& filter) const { + StockList ret; + ret.reserve(m_stockDict.size()); + std::lock_guard lock(*m_stockDict_mutex); + auto iter = m_stockDict.begin(); + if (filter) { + for (; iter != m_stockDict.end(); ++iter) { + if (filter(iter->second)) { + ret.emplace_back(iter->second); + } + } + } else { + for (; iter != m_stockDict.end(); ++iter) { + ret.emplace_back(iter->second); + } + } + return ret; +} + MarketInfo StockManager::getMarketInfo(const string& market) const { MarketInfo result; string market_tmp = market; diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index bd7d6182..39ae4f28 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -100,6 +100,9 @@ public: /** 同 getStock @see getStock */ Stock operator[](const string&) const; + StockList getStockList( + std::function&& filter = std::function()) const; + /** * 获取相应的市场信息 * @param market 指定的市场标识 diff --git a/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp b/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp index 84d616f4..701dee07 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp +++ b/hikyuu_cpp/hikyuu/indicator/crt/POS.cpp @@ -36,7 +36,7 @@ Indicator HKU_API POS(const Block& block, KQuery query, SignalPtr sg) { } } - auto stks = block.getAllStocks(); + auto stks = block.getStockList(); vector sgs = parallel_for_index(0, stks.size(), [&](size_t i) { auto tmpsg = sg->clone(); auto kdata = stks[i].getKData(query); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index f326ec81..aba8a0e3 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -117,7 +117,7 @@ Indicator HKU_API IC(const StockList& stks, const KQuery& query, const Stock& re } Indicator HKU_API IC(const Block& blk, const KQuery& query, const Stock& ref_stk, int n) { - StockList stks = blk.getAllStocks(); + StockList stks = blk.getStockList(); return IC(stks, query, ref_stk, n); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index bcae8c82..78921f6f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -198,6 +198,18 @@ ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, si return ret; } +ScoreRecordList MultiFactorBase::getScore(const Datetime& date, + std::function filter) { + ScoreRecordList ret; + const auto& all_scores = getScore(date); + for (const auto& score : all_scores) { + if (filter(score)) { + ret.emplace_back(score); + } + } + return ret; +} + const vector& MultiFactorBase::getAllScores() { calculate(); return m_stk_factor_by_date; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index ab53b7cc..b7cae809 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -13,7 +13,7 @@ namespace hku { /** - * 合成多因子 + * 合成多因子,当只有一个因子时相当于简易的评分板 * @ingroup MultiFactor */ class HKU_API MultiFactorBase : public enable_shared_from_this { @@ -84,6 +84,9 @@ public: ScoreRecordList getScore(const Datetime& date, size_t start, size_t end = Null()); + /** 获取指定日期截面的所有因子值, 并通过指定的filer进行过滤 */ + ScoreRecordList getScore(const Datetime& date, std::function filter); + /** 获取所有截面数据,已按降序排列 */ const vector& getAllScores(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h index 23b01ea8..7baf7a4e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h @@ -29,6 +29,7 @@ struct HKU_API ScoreRecord { }; typedef vector ScoreRecordList; +typedef vector ScoreList; HKU_API std::ostream& operator<<(std::ostream& out, const ScoreRecord& td); diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index fa8305de..1482880c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -696,11 +696,13 @@ TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool "Failed to sellForce {}, the day {} could'nt sell!", m_stock.market_code(), date); + PositionRecord position = m_tm->getPosition(date, m_stock); + HKU_IF_RETURN(position.number <= 0.0, record); + 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(date, m_stock); price_t realPrice = _getRealSellPrice(krecord.datetime, on_open ? src_krecord.openPrice : src_krecord.closePrice); diff --git a/hikyuu_pywrap/_Block.cpp b/hikyuu_pywrap/_Block.cpp index 152cb579..cff69d3d 100644 --- a/hikyuu_pywrap/_Block.cpp +++ b/hikyuu_pywrap/_Block.cpp @@ -92,5 +92,25 @@ void export_Block(py::module& m) { }, py::keep_alive<0, 1>()) + .def( + "get_stock_list", + [](const Block& self, py::object filter) { + StockList ret; + if (filter.is_none()) { + ret = self.getStockList(); + } else { + HKU_CHECK(py::hasattr(filter, "__call__"), "filter not callable!"); + py::object filter_func = filter.attr("__call__"); + ret = self.getStockList( + [&](const Stock& stk) { return filter_func(stk).cast(); }); + } + return ret; + }, + py::arg("filter") = py::none(), R"(get_stock_list(self[, filter=None]) + + 获取证券列表 + + :param func filter: 输入参数为 stock, 返回 True | False 的过滤函数)") + DEF_PICKLE(Block); } diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 594e8704..03923d39 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -83,6 +83,26 @@ void export_StockManager(py::module& m) { :return: 对应的证券实例,如果实例不存在,则Null(),不抛出异常 :rtype: Stock)") + .def( + "get_stock_list", + [](const StockManager& self, py::object filter) { + StockList ret; + if (filter.is_none()) { + ret = self.getStockList(); + } else { + HKU_CHECK(py::hasattr(filter, "__call__"), "filter not callable!"); + py::object filter_func = filter.attr("__call__"); + ret = self.getStockList( + [&](const Stock& stk) { return filter_func(stk).cast(); }); + } + return ret; + }, + py::arg("filter") = py::none(), R"(get_stock_list(self[, filter=None]) + + 获取证券列表 + + :param func filter: 输入参数为 stock, 返回 True | False 的过滤函数)") + .def("get_block", &StockManager::getBlock, R"(get_block(self, category, name) 获取预定义的板块 diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 4d01da67..718d8f23 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -136,6 +136,14 @@ void export_MultiFactor(py::module& m) { :param int end: 取当日排名结束(不包含本身) :rtype: ScoreRecordList)") + .def("get_score", + [](MultiFactorBase& self, const Datetime& date, py::object filter) { + HKU_CHECK(py::hasattr(filter, "__call__"), "filter not callable!"); + py::object filter_func = filter.attr("__call__"); + return self.getScore( + date, [&](const ScoreRecord& score) { return filter_func(score).cast(); }); + }) + .def("get_all_scores", &MultiFactorBase::getAllScores, py::return_value_policy::copy, R"(get_all_scores(self) From 2f46872a435274582e9419033879fb3f3bf56c78 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 29 Mar 2024 18:05:00 +0800 Subject: [PATCH 113/601] update --- hikyuu_cpp/hikyuu/analysis/combinate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/analysis/combinate.cpp b/hikyuu_cpp/hikyuu/analysis/combinate.cpp index 99653af5..00116e64 100644 --- a/hikyuu_cpp/hikyuu/analysis/combinate.cpp +++ b/hikyuu_cpp/hikyuu/analysis/combinate.cpp @@ -71,7 +71,7 @@ vector HKU_API combinateIndicatorAnalysisWithBlock( } vector result; - auto stocks = blk.getAllStocks(); + auto stocks = blk.getStockList(); size_t total = stocks.size(); HKU_IF_RETURN(total == 0, result); From e72540e63ef4adc2785c091bc3a380823a3b2901 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 03:17:15 +0800 Subject: [PATCH 114/601] update get_part add *args --- hikyuu/hub.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hikyuu/hub.py b/hikyuu/hub.py index c29879de..4fc7f02d 100644 --- a/hikyuu/hub.py +++ b/hikyuu/hub.py @@ -545,13 +545,14 @@ def remove_hub(name): HubManager().remove_hub(name) -def get_part(name, **kwargs): +def get_part(name, *args, **kwargs): """获取指定策略部件 :param str name: 策略部件名称 + :param args: 其他部件相关参数 :param kwargs: 其他部件相关参数 """ - return HubManager().get_part(name, **kwargs) + return HubManager().get_part(name, *args, **kwargs) def get_hub_path(name): From 4f3aa965c715fbca6d3c1acc5d4a9bd5251ca591 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 03:17:42 +0800 Subject: [PATCH 115/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20MF=20get=5Fscore?= =?UTF-8?q?=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/factor/MultiFactorBase.cpp | 42 +++++++++++++++---- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 16 +++++-- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 34 ++++++++++----- 3 files changed, 69 insertions(+), 23 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 78921f6f..0805f468 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -181,7 +181,8 @@ const ScoreRecordList& MultiFactorBase::getScore(const Datetime& d) { return m_stk_factor_by_date[iter->second]; } -ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, size_t end) { +ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, size_t end, + std::function&& filter) { ScoreRecordList ret; HKU_IF_RETURN(start >= end, ret); @@ -191,22 +192,45 @@ ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, si } ret.resize(end - start); - for (size_t i = start; i < end; i++) { - ret[i] = cross[i]; + if (filter) { + for (size_t i = start; i < end; i++) { + if (filter(cross[i])) { + ret[i] = cross[i]; + } + } + } else { + for (size_t i = start; i < end; i++) { + ret[i] = cross[i]; + } } return ret; } -ScoreRecordList MultiFactorBase::getScore(const Datetime& date, - std::function filter) { +ScoreRecordList MultiFactorBase::getScore( + const Datetime& date, size_t start, size_t end, + std::function&& filter) { ScoreRecordList ret; - const auto& all_scores = getScore(date); - for (const auto& score : all_scores) { - if (filter(score)) { - ret.emplace_back(score); + HKU_IF_RETURN(start >= end, ret); + + const auto& cross = getScore(date); + if (end == Null() || end > cross.size()) { + end = cross.size(); + } + + ret.resize(end - start); + if (filter) { + for (size_t i = start; i < end; i++) { + if (filter(date, cross[i])) { + ret[i] = cross[i]; + } + } + } else { + for (size_t i = start; i < end; i++) { + ret[i] = cross[i]; } } + return ret; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index b7cae809..53ff372f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -82,10 +82,20 @@ public: /** 获取指定日期截面的所有因子值,已经降序排列 */ const ScoreRecordList& getScore(const Datetime&); - ScoreRecordList getScore(const Datetime& date, size_t start, size_t end = Null()); + /** + * 获取指定日期截面 [start, end] 范围内的因子值(评分), 并通过filer进行过滤 + * @param date 指定日期 + * @param start 排序起始点 + * @param end 排序起始点(不含该点) + * @param filter 过滤函数 + */ + ScoreRecordList getScore( + const Datetime& date, size_t start, size_t end = Null(), + std::function&& filter = std::function()); - /** 获取指定日期截面的所有因子值, 并通过指定的filer进行过滤 */ - ScoreRecordList getScore(const Datetime& date, std::function filter); + ScoreRecordList getScore(const Datetime& date, size_t start, size_t end = Null(), + std::function&& filter = + std::function()); /** 获取所有截面数据,已按降序排列 */ const vector& getAllScores(); diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 718d8f23..69811d5e 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -123,10 +123,29 @@ void export_MultiFactor(py::module& m) { .def( "get_score", - [](MultiFactorBase& self, const Datetime& date, size_t start, size_t end) { - return self.getScore(date, start, end); + [](MultiFactorBase& self, const Datetime& date, size_t start, size_t end, + py::object filter) { + if (filter.is_none()) { + return self.getScore(date, start, end, std::function()); + } + HKU_CHECK(py::hasattr(filter, "__call__"), "filter not callable!"); + py::object filter_func = filter.attr("__call__"); + ScoreRecord sc; + try { + filter_func(sc); + return self.getScore(date, start, end, [&](const ScoreRecord& score_) { + return filter_func(score_).cast(); + }); + } catch (...) { + filter_func(date, sc); + return self.getScore(date, start, end, + [&](const Datetime& date_, const ScoreRecord& score_) { + return filter_func(date_, score_).cast(); + }); + } }, py::arg("datet"), py::arg("start") = 0, py::arg("end") = null_size, + py::arg("filter") = py::none(), R"(get_score(self, date[, start=0, end=Null]) 获取指定日期截面的所有因子值,已经降序排列,相当于各证券日期截面评分。 @@ -134,22 +153,15 @@ void export_MultiFactor(py::module& m) { :param Datetime date: 指定日期 :param int start: 取当日排名开始 :param int end: 取当日排名结束(不包含本身) + :param function func: (ScoreRecord)->bool 或 (Datetime, ScoreRecord)->bool 为原型的可调用对象 :rtype: ScoreRecordList)") - .def("get_score", - [](MultiFactorBase& self, const Datetime& date, py::object filter) { - HKU_CHECK(py::hasattr(filter, "__call__"), "filter not callable!"); - py::object filter_func = filter.attr("__call__"); - return self.getScore( - date, [&](const ScoreRecord& score) { return filter_func(score).cast(); }); - }) - .def("get_all_scores", &MultiFactorBase::getAllScores, py::return_value_policy::copy, R"(get_all_scores(self) 获取所有日期的所有评分,长度与参考日期相同 - :return: 每日 ScoreRecordList 结果的 list)") + :return: ScoreRecordList)") .def("get_all_src_factors", &MultiFactorBase::getAllSrcFactors) From d545a6b1fac98f1cb34190031a5c62e07be7ee62 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 03:22:58 +0800 Subject: [PATCH 116/601] =?UTF-8?q?SE/PF/AF=20=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../allocatefunds/AllocateFundsBase.cpp | 2 +- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 2 +- .../trade_sys/selector/SelectorBase.cpp | 82 ++++++++--------- .../hikyuu/trade_sys/selector/SelectorBase.h | 10 ++- .../trade_sys/selector/test_SE_Fixed.cpp | 87 +++++++++---------- .../hikyuu/trade_sys/selector/test_export.cpp | 11 +++ 6 files changed, 101 insertions(+), 93 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index ad789ac1..2b715bb1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -295,7 +295,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( } else { // 非延迟卖出的系统,立即强制卖出并回收资金 auto tr = sys->sellForceOnClose(date, MAX_DOUBLE, PART_ALLOCATEFUNDS); - HKU_DEBUG_IF(trace && tr.isNull(), "[AF] failed to sell: {}", sys->name()); + // 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(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index ebd7d37b..3ca54f0f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -244,7 +244,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { //---------------------------------------------------------------------- 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()); + // 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); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 08f64c7a..db7ba1bb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -25,26 +25,37 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SelectorPtr& st) { } SelectorBase::SelectorBase() : m_name("SelectorBase") { - // 是否单独执行原型系统,仅限用于测试目的 - setParam("run_proto_sys", false); + initParam(); } SelectorBase::SelectorBase(const string& name) : m_name(name) { - // 是否单独执行原型系统 - setParam("run_proto_sys", false); + initParam(); } SelectorBase::~SelectorBase() {} +void SelectorBase::initParam() { + // 通常原型系统不参与计算,但某些特殊的场景,需要依赖于伴生系统策略, + // 此时可以认为实际执行的系统行为跟随伴生系统的买卖交易,如依赖于SG进行选择 + // (不过由于仅依赖SG的场景不严谨,因为原型和实际系统的SG是一样的) + // 此时,需要在自身计算之前执行原型系统,然后SE自行时可以使用。 + // 而对于实际系统和被跟随的系统完全不一样的情况,可以自行设计特殊的SE。 + setParam("depend_on_proto_sys", false); // 此种情况,需要原型系统可独立运行 +} + void SelectorBase::baseCheckParam(const string& name) const {} -void SelectorBase::paramChanged() {} + +void SelectorBase::paramChanged() { + m_calculated = false; +} void SelectorBase::removeAll() { - m_pro_sys_list = SystemList(); - m_real_sys_list = SystemList(); + m_pro_sys_list.swap(SystemList()); + m_real_sys_list.swap(SystemList()); } void SelectorBase::reset() { + m_calculated = false; SystemList::const_iterator iter = m_pro_sys_list.begin(); for (; iter != m_pro_sys_list.end(); ++iter) { (*iter)->reset(); @@ -83,52 +94,43 @@ SelectorPtr SelectorBase::clone() { return p; } -void SelectorBase::calculate(const SystemList& sysList, const KQuery& query) { - m_real_sys_list = sysList; - if (getParam("run_proto_sys")) { - // 用于手工测试 +void SelectorBase::calculate(const SystemList& pf_realSysList, const KQuery& query) { + m_real_sys_list = pf_realSysList; + + // 需要依赖于运行系统,在自身运算之前完成计算 + if (getParam("depend_on_proto_sys")) { for (auto& sys : m_pro_sys_list) { sys->run(query); } } + _calculate(); + m_calculated = true; } -bool SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { - HKU_ERROR_IF_RETURN(stock.isNull(), false, "Try add Null stock, will be discard!"); - HKU_ERROR_IF_RETURN(!protoSys, false, "Try add Null protoSys, will be discard!"); - HKU_ERROR_IF_RETURN(!protoSys->getMM(), false, "protoSys has not MoneyManager!"); - HKU_ERROR_IF_RETURN(!protoSys->getSG(), false, "protoSys has not Siganl!"); - SYSPtr sys = protoSys->clone(); - // 每个系统独立,不共享 tm - sys->setParam("shared_tm", false); +void SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { + HKU_CHECK(!stock.isNull(), "The input stock is null!"); + HKU_CHECK(protoSys, "The input stock is null!"); + HKU_CHECK(protoSys->getMM(), "protoSys missing MoneyManager!"); + HKU_CHECK(protoSys->getSG(), "protoSys missing Siganl!"); + HKU_CHECK(!protoSys->getParam("shared_tm"), "Unsupport shared TM for protoSys!"); + if (getParam("depend_on_proto_sys")) { + HKU_CHECK(protoSys->getTM(), + "Scenarios that depend on prototype systems need to specify a TM!"); + } + + auto proto = protoSys; + proto->forceResetAll(); + SYSPtr sys = proto->clone(); sys->reset(); sys->setStock(stock); m_pro_sys_list.emplace_back(sys); - return true; } -bool SelectorBase::addStockList(const StockList& stkList, const SystemPtr& protoSys) { - HKU_ERROR_IF_RETURN(!protoSys, false, "Try add Null protoSys, will be discard!"); - HKU_ERROR_IF_RETURN(!protoSys->getMM(), false, "protoSys has not MoneyManager!"); - HKU_ERROR_IF_RETURN(!protoSys->getSG(), false, "protoSys has not Signal!"); - SYSPtr newProtoSys = protoSys->clone(); - // 复位清除之前的数据,避免因原有数据过多导致下面循环时速度过慢 - // 每个系统独立,不共享 tm - newProtoSys->setParam("shared_tm", false); - newProtoSys->reset(); - StockList::const_iterator iter = stkList.begin(); - for (; iter != stkList.end(); ++iter) { - if (iter->isNull()) { - HKU_WARN("Try add Null stock, will be discard!"); - continue; - } - - SYSPtr sys = newProtoSys->clone(); - sys->setStock(*iter); - m_pro_sys_list.emplace_back(sys); +void SelectorBase::addStockList(const StockList& stkList, const SystemPtr& protoSys) { + for (const auto& stk : stkList) { + addStock(stk, protoSys); } - return true; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 267a7992..b852bbbb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -50,7 +50,7 @@ public: * @param protoSys 交易系统策略原型 * @return 如果 protoSys 无效 或 stock 无效,则返回 false, 否则返回 true */ - bool addStock(const Stock& stock, const SystemPtr& protoSys); + void addStock(const Stock& stock, const SystemPtr& protoSys); /** * 加入一组相同交易策略的股票 @@ -59,7 +59,7 @@ public: * @param protoSys 交易系统策略原型 * @return 如果 protoSys 无效则返回false,否则返回 true */ - bool addStockList(const StockList& stkList, const SystemPtr& protoSys); + void addStockList(const StockList& stkList, const SystemPtr& protoSys); /** * @brief 获取原型系统列表 @@ -102,10 +102,14 @@ public: virtual bool isMatchAF(const AFPtr& af) = 0; /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ - void calculate(const SystemList& sysList, const KQuery& query); + void calculate(const SystemList& pf_realSysList, const KQuery& query); + +private: + void initParam(); protected: string m_name; + bool m_calculated{false}; // 是否已计算过 SystemList m_pro_sys_list; // 原型系统列表 SystemList m_real_sys_list; // PF组合中实际运行的系统,有PF执行时设定,顺序与原型列表一一对应 diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp index e4d1b230..a29a478a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp @@ -29,64 +29,55 @@ TEST_CASE("test_SE_Fixed") { SYSPtr sys = SYS_Simple(); SEPtr se = SE_Fixed(); - // /** @arg 试图加入一个不存在的stock */ - // se->addStock(Stock(), sys); - // SystemWeightList result = se->getSelectedOnOpen(Datetime(200001010000L)); - // CHECK_EQ(result.size(), 0); + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); - // /** @arg 试图加入一个空的系统策略原型 */ - // se->addStock(sm["sh600000"], SYSPtr()); - // result = se->getSelectedOnOpen(Datetime(200001010000L)); - // CHECK_EQ(result.size(), 0); + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); - // /** @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 | SG的系统策略原型 */ + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + sys->setSG(sg); + CHECK_THROWS_AS(se->addStock(sm["sh600000"], sys), std::exception); - // /* @arg 试图加入一个未指定SG的系统原型 */ - // sys->setMM(mm); - // CHECK_UNARY(!se->addStock(sm["sh600000"], sys)); + sys->setSG(SGPtr()); + sys->setMM(mm); + CHECK_THROWS_AS(se->addStock(sm["sh600000"], sys), std::exception); // 目前必须有PF指定实际执行的子系统,下面代码无法执行 // /** @arg getSelectedSystemList */ - // sys->setSG(sg); - // se->addStock(sm["sh600000"], sys); - // se->addStock(sm["sz000001"], sys); - // se->addStock(sm["sz000002"], sys); + sys->setSG(sg); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); - // se->reset(); - // result = se->getSelectedSystemList(Datetime(200001010000L)); - // CHECK_EQ(result.size(), 3); - // CHECK_EQ(sm["sh600000"], result[0]->getStock()); - // CHECK_EQ(sm["sz000001"], result[1]->getStock()); - // CHECK_EQ(sm["sz000002"], result[2]->getStock()); + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); - // /** @arg clear */ - // se->clear(); - // result = se->getSelectedSystemList(Datetime(200001010000L)); - // CHECK_EQ(result.size(), 0); + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); - // /** @arg reset */ - // se->addStock(sm["sh600000"], sys); - // se->addStock(sm["sz000001"], sys); - // se->addStock(sm["sz000002"], sys); - // se->reset(); - // result = se->getSelectedSystemList(Datetime(200001010000L)); - // CHECK_EQ(result.size(), 3); - // CHECK_EQ(sm["sh600000"], result[0]->getStock()); - // CHECK_EQ(sm["sz000001"], result[1]->getStock()); - // CHECK_EQ(sm["sz000002"], result[2]->getStock()); + /** @arg reset */ + se->reset(); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 0); - // /** @arg 克隆操作 */ - // SEPtr se2; - // se2 = se->clone(); - // CHECK_NE(se2.get(), se.get()); - // result = se2->getSelectedSystemList(Datetime(200001010000L)); - // CHECK_EQ(result.size(), 3); - // CHECK_EQ(sm["sh600000"], result[0]->getStock()); - // CHECK_EQ(sm["sz000001"], result[1]->getStock()); - // CHECK_EQ(sm["sz000002"], result[2]->getStock()); + /** @arg 克隆操作 */ + proto_sys_list = se->getProtoSystemList(); + se->calculate(proto_sys_list, KQuery(-20)); + SEPtr se2; + se2 = se->clone(); + CHECK_NE(se2.get(), se.get()); + result = se2->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); } /** @} */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp index 10d3f2b4..f2287fc4 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp @@ -13,6 +13,11 @@ #include #include #include +#include +#include +#include +#include +#include #include #include @@ -30,7 +35,13 @@ TEST_CASE("test_SE_FIXED_export") { string filename(sm.tmpdir()); filename += "/SE_FIXED.xml"; + TMPtr tm = crtTM(Datetime(20010101), 100000); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); SYSPtr sys = SYS_Simple(); + sys->setTM(tm); + sys->setSG(sg); + sys->setMM(mm); StockList stkList; stkList.push_back(sm["sh600000"]); stkList.push_back(sm["sz000001"]); From 173ddbffeb22b94a0dbaa9d1a6fcb21b5c60ac48 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 12:38:40 +0800 Subject: [PATCH 117/601] update se --- .../trade_sys/selector/SelectorBase.cpp | 35 +++++++++++++++---- .../hikyuu/trade_sys/selector/SelectorBase.h | 8 ++++- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index db7ba1bb..58b99da0 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -47,15 +47,17 @@ void SelectorBase::baseCheckParam(const string& name) const {} void SelectorBase::paramChanged() { m_calculated = false; + m_proto_calculated = false; } void SelectorBase::removeAll() { - m_pro_sys_list.swap(SystemList()); - m_real_sys_list.swap(SystemList()); + m_pro_sys_list.clear(); + m_real_sys_list.clear(); + m_calculated = false; + m_proto_calculated = false; } void SelectorBase::reset() { - m_calculated = false; SystemList::const_iterator iter = m_pro_sys_list.begin(); for (; iter != m_pro_sys_list.end(); ++iter) { (*iter)->reset(); @@ -63,6 +65,9 @@ void SelectorBase::reset() { m_real_sys_list.clear(); _reset(); + + m_calculated = false; + m_proto_calculated = false; } SelectorPtr SelectorBase::clone() { @@ -81,6 +86,10 @@ SelectorPtr SelectorBase::clone() { p->m_params = m_params; p->m_name = m_name; + p->m_query = m_query; + p->m_proto_query = m_proto_query; + p->m_calculated = m_calculated; + p->m_proto_calculated = m_proto_calculated; p->m_real_sys_list.reserve(m_real_sys_list.size()); for (const auto& sys : m_real_sys_list) { @@ -95,19 +104,30 @@ SelectorPtr SelectorBase::clone() { } void SelectorBase::calculate(const SystemList& pf_realSysList, const KQuery& query) { + HKU_IF_RETURN(m_calculated && m_query == query, void()); + + m_query = query; m_real_sys_list = pf_realSysList; // 需要依赖于运行系统,在自身运算之前完成计算 if (getParam("depend_on_proto_sys")) { - for (auto& sys : m_pro_sys_list) { - sys->run(query); - } + calculate_proto(query); } _calculate(); m_calculated = true; } +void SelectorBase::calculate_proto(const KQuery& query) { + if (m_proto_query != query && !m_proto_calculated) { + for (auto& sys : m_pro_sys_list) { + sys->run(query); + } + m_proto_calculated = true; + m_proto_query = query; + } +} + void SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { HKU_CHECK(!stock.isNull(), "The input stock is null!"); HKU_CHECK(protoSys, "The input stock is null!"); @@ -125,6 +145,9 @@ void SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { sys->reset(); sys->setStock(stock); m_pro_sys_list.emplace_back(sys); + + m_calculated = false; + m_proto_calculated = false; } void SelectorBase::addStockList(const StockList& stkList, const SystemPtr& protoSys) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index b852bbbb..1531359c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -104,12 +104,18 @@ public: /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ void calculate(const SystemList& pf_realSysList, const KQuery& query); + void calculate_proto(const KQuery& query); + private: void initParam(); protected: string m_name; - bool m_calculated{false}; // 是否已计算过 + bool m_calculated{false}; // 是否已计算过 + bool m_proto_calculated{false}; + KQuery m_query; + KQuery m_proto_query; + SystemList m_pro_sys_list; // 原型系统列表 SystemList m_real_sys_list; // PF组合中实际运行的系统,有PF执行时设定,顺序与原型列表一一对应 From 4c6035ecb65577f1b494a97c97122d4fcc23bef5 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 13:42:42 +0800 Subject: [PATCH 118/601] fixed MF get_scores --- .../trade_sys/factor/MultiFactorBase.cpp | 16 +++++++-------- .../hikyuu/trade_sys/factor/MultiFactorBase.h | 8 ++++---- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 20 +++++++++---------- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp index 0805f468..7c7bf1db 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp @@ -181,8 +181,8 @@ const ScoreRecordList& MultiFactorBase::getScore(const Datetime& d) { return m_stk_factor_by_date[iter->second]; } -ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, size_t end, - std::function&& filter) { +ScoreRecordList MultiFactorBase::getScores(const Datetime& date, size_t start, size_t end, + std::function&& filter) { ScoreRecordList ret; HKU_IF_RETURN(start >= end, ret); @@ -191,23 +191,22 @@ ScoreRecordList MultiFactorBase::getScore(const Datetime& date, size_t start, si end = cross.size(); } - ret.resize(end - start); if (filter) { for (size_t i = start; i < end; i++) { if (filter(cross[i])) { - ret[i] = cross[i]; + ret.emplace_back(cross[i]); } } } else { for (size_t i = start; i < end; i++) { - ret[i] = cross[i]; + ret.emplace_back(cross[i]); } } return ret; } -ScoreRecordList MultiFactorBase::getScore( +ScoreRecordList MultiFactorBase::getScores( const Datetime& date, size_t start, size_t end, std::function&& filter) { ScoreRecordList ret; @@ -218,16 +217,15 @@ ScoreRecordList MultiFactorBase::getScore( end = cross.size(); } - ret.resize(end - start); if (filter) { for (size_t i = start; i < end; i++) { if (filter(date, cross[i])) { - ret[i] = cross[i]; + ret.emplace_back(cross[i]); } } } else { for (size_t i = start; i < end; i++) { - ret[i] = cross[i]; + ret.emplace_back(cross[i]); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h index 53ff372f..3dcf7fca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h @@ -89,13 +89,13 @@ public: * @param end 排序起始点(不含该点) * @param filter 过滤函数 */ - ScoreRecordList getScore( + ScoreRecordList getScores( const Datetime& date, size_t start, size_t end = Null(), std::function&& filter = std::function()); - ScoreRecordList getScore(const Datetime& date, size_t start, size_t end = Null(), - std::function&& filter = - std::function()); + ScoreRecordList getScores(const Datetime& date, size_t start, size_t end = Null(), + std::function&& filter = + std::function()); /** 获取所有截面数据,已按降序排列 */ const vector& getAllScores(); diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 69811d5e..104464c6 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -38,7 +38,6 @@ void export_MultiFactor(py::module& m) { .def_readwrite("stock", &ScoreRecord::stock, "时间") .def_readwrite("value", &ScoreRecord::value, "时间"); - size_t null_size = Null(); py::class_(m, "MultiFactorBase", R"(市场环境判定策略基类 @@ -122,29 +121,30 @@ void export_MultiFactor(py::module& m) { .def("clone", &MultiFactorBase::clone, "克隆操作") .def( - "get_score", - [](MultiFactorBase& self, const Datetime& date, size_t start, size_t end, + "get_scores", + [](MultiFactorBase& self, const Datetime& date, size_t start, py::object end, py::object filter) { + size_t cend = end.is_none() ? Null() : end.cast(); if (filter.is_none()) { - return self.getScore(date, start, end, std::function()); + return self.getScores(date, start, cend, std::function()); } HKU_CHECK(py::hasattr(filter, "__call__"), "filter not callable!"); py::object filter_func = filter.attr("__call__"); ScoreRecord sc; try { filter_func(sc); - return self.getScore(date, start, end, [&](const ScoreRecord& score_) { + return self.getScores(date, start, cend, [&](const ScoreRecord& score_) { return filter_func(score_).cast(); }); } catch (...) { filter_func(date, sc); - return self.getScore(date, start, end, - [&](const Datetime& date_, const ScoreRecord& score_) { - return filter_func(date_, score_).cast(); - }); + return self.getScores(date, start, cend, + [&](const Datetime& date_, const ScoreRecord& score_) { + return filter_func(date_, score_).cast(); + }); } }, - py::arg("datet"), py::arg("start") = 0, py::arg("end") = null_size, + py::arg("datet"), py::arg("start") = 0, py::arg("end") = py::none(), py::arg("filter") = py::none(), R"(get_score(self, date[, start=0, end=Null]) From d775fc2fb6a324dc0ce68b2b7cf02f9b173a8bda Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 14:51:36 +0800 Subject: [PATCH 119/601] =?UTF-8?q?=E5=AE=8C=E5=96=84=20MF=20python=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_sys/trade_sys.py | 20 ++++++++++++- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 37 ++++++++++++++---------- 2 files changed, 40 insertions(+), 17 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index e43d5604..62f78154 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -2,7 +2,8 @@ from hikyuu.core import ( System, SystemPart, ConditionBase, EnvironmentBase, MoneyManagerBase, - ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase, AllocateFundsBase + ProfitGoalBase, SelectorBase, SignalBase, SlippageBase, StoplossBase, AllocateFundsBase, + MultiFactorBase ) @@ -180,6 +181,23 @@ def crtAF(allocate_weight_func, params={}, name='crtAF'): return meta_x(name, params) +# ------------------------------------------------------------------ +# multi_factor +# ------------------------------------------------------------------ +def crtMF(calculate_func, params={}, name='crtMF'): + """ + 快速多因子合成算法 + + :param calculate_func: 合成算法 + :param {} params: 参数字典 + :param str name: 自定义名称 + :return: 自定义多因子合成算法实例 + """ + meta_x = type(name, (MultiFactorBase, ), {'__init__': part_init, '_clone': part_clone}) + meta_x._calculate = calculate_func + return meta_x(name, params) + + # ------------------------------------------------------------------ # slippage # ------------------------------------------------------------------ diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 104464c6..5051d506 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -170,12 +170,15 @@ void export_MultiFactor(py::module& m) { m.def( "MF_EqualWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, - const Stock& ref_stk, int ic_n) { + const py::object& ref_stk, int ic_n) { IndicatorList c_inds = python_list_to_vector(inds); StockList c_stks = python_list_to_vector(stks); - return MF_EqualWeight(c_inds, c_stks, query, ref_stk, ic_n); + return MF_EqualWeight(c_inds, c_stks, query, + ref_stk.is_none() ? getStock("sh000300") : ref_stk.cast(), + ic_n); }, - py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk"), py::arg("ic_n") = 5, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk") = py::none(), + py::arg("ic_n") = 5, R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5]) 等权重合成因子 @@ -183,21 +186,22 @@ void export_MultiFactor(py::module& m) { :param sequense(Indicator) inds: 原始因子列表 :param sequense(stock) stks: 计算证券列表 :param Query query: 日期范围 - :param Stock ref_stk: 参考证券 + :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) :param int ic_n: 默认 IC 对应的 N 日收益率 :rtype: MultiFactor)"); m.def( "MF_ICWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, - const Stock& ref_stk, int ic_n, int ic_rolling_n) { - // MF_EqualWeight + const py::object& ref_stk, int ic_n, int ic_rolling_n) { IndicatorList c_inds = python_list_to_vector(inds); StockList c_stks = python_list_to_vector(stks); - return MF_ICWeight(c_inds, c_stks, query, ref_stk, ic_n, ic_rolling_n); + return MF_ICWeight(c_inds, c_stks, query, + ref_stk.is_none() ? getStock("sh000300") : ref_stk.cast(), 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, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk") = py::none(), + py::arg("ic_n") = 5, py::arg("ic_rolling_n") = 120, R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) 滚动IC权重合成因子 @@ -205,7 +209,7 @@ void export_MultiFactor(py::module& m) { :param sequense(Indicator) inds: 原始因子列表 :param sequense(stock) stks: 计算证券列表 :param Query query: 日期范围 - :param Stock ref_stk: 参考证券 + :param Stock ref_stk: (未指定时,默认为 sh000300 沪深300) :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 :rtype: MultiFactor)"); @@ -213,14 +217,15 @@ void export_MultiFactor(py::module& m) { m.def( "MF_ICIRWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, - const Stock& ref_stk, int ic_n, int ic_rolling_n) { - // MF_EqualWeight + const py::object& ref_stk, int ic_n, int ic_rolling_n) { IndicatorList c_inds = python_list_to_vector(inds); StockList c_stks = python_list_to_vector(stks); - return MF_ICIRWeight(c_inds, c_stks, query, ref_stk, ic_n, ic_rolling_n); + return MF_ICIRWeight(c_inds, c_stks, query, + ref_stk.is_none() ? getStock("sh000300") : ref_stk.cast(), + 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, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("ref_stk") = py::none(), + py::arg("ic_n") = 5, py::arg("ic_rolling_n") = 120, R"(MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) 滚动ICIR权重合成因子 @@ -228,7 +233,7 @@ void export_MultiFactor(py::module& m) { :param sequense(Indicator) inds: 原始因子列表 :param sequense(stock) stks: 计算证券列表 :param Query query: 日期范围 - :param Stock ref_stk: 参考证券 + :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 :rtype: MultiFactor)"); From 32af840049d5122a7228be55280f4a7fca9265a6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 15:42:59 +0800 Subject: [PATCH 120/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20Selector=20in=20py?= =?UTF-8?q?thon?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_sys/trade_sys.py | 13 ------------- hikyuu_pywrap/trade_sys/_Selector.cpp | 9 +++++++-- 2 files changed, 7 insertions(+), 15 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index 62f78154..43047353 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -133,19 +133,6 @@ def crtSG(func, params={}, name='crtSG'): # ------------------------------------------------------------------ # Selector # ------------------------------------------------------------------ -def se_add_stock_list(self, stk_list, proto_sys): - result = True - for stk in stk_list: - success = self.add_stock(stk, proto_sys) - if not success: - result = False - break - return result - - -SelectorBase.add_stock_list = se_add_stock_list - - def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE'): """ 快速创建交易对象选择算法 diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 64d9bbfc..7f1306a0 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -102,8 +102,13 @@ void export_Selector(py::module& m) { :param Stock stock: 加入的初始标的 :param System sys: 系统策略原型)") - .def("add_stock_list", &SelectorBase::addStockList, py::arg("stk_list"), py::arg("sys"), - R"(add_stock_list(self, stk_list, sys) + .def( + "add_stock_list", + [](SelectorBase& self, py::sequence stk_list, const SYSPtr& sys) { + self.addStockList(python_list_to_vector(stk_list), sys); + }, + py::arg("stk_list"), py::arg("sys"), + R"(add_stock_list(self, stk_list, sys) 加入初始标的列表及其系统策略原型 From ddebb76217e67a050f4d6dfd7d56785805765ab6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 30 Mar 2024 19:02:35 +0800 Subject: [PATCH 121/601] add SE_Multi_factor, SG_Allways_buy --- hikyuu_cpp/hikyuu/trade_sys/all.h | 2 +- .../MultiFactorBase.cpp | 44 +++++++-- .../{factor => multifactor}/MultiFactorBase.h | 15 +-- .../{factor => multifactor}/ScoreRecord.cpp | 0 .../{factor => multifactor}/ScoreRecord.h | 0 .../{factor => multifactor}/build_in.h | 0 .../crt/MF_EqualWeight.h | 0 .../crt/MF_ICIRWeight.h | 0 .../{factor => multifactor}/crt/MF_ICWeight.h | 0 .../imp/EqualWeightMultiFactor.cpp | 0 .../imp/EqualWeightMultiFactor.h | 0 .../imp/ICIRMultiFactor.cpp | 0 .../imp/ICIRMultiFactor.h | 0 .../imp/ICMultiFactor.cpp | 0 .../imp/ICMultiFactor.h | 0 .../hikyuu/trade_sys/selector/build_in.h | 1 + .../trade_sys/selector/crt/SE_Multi_factor.h | 22 +++++ .../selector/imp/MultiFactorSelector.cpp | 93 +++++++++++++++++++ .../selector/imp/MultiFactorSelector.h | 45 +++++++++ .../hikyuu/trade_sys/signal/SignalBase.h | 4 +- hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h | 1 + .../trade_sys/signal/crt/SG_Allways_buy.h | 15 +++ .../trade_sys/signal/imp/AllwaysBuySignal.cpp | 38 ++++++++ .../trade_sys/signal/imp/AllwaysBuySignal.h | 25 +++++ .../trade_sys/factor/test_MF_EqualWeight.cpp | 2 +- .../trade_sys/factor/test_MF_ICIRWeight.cpp | 2 +- .../trade_sys/factor/test_MF_ICWeight.cpp | 2 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 +- hikyuu_pywrap/trade_sys/_Signal.cpp | 3 +- 29 files changed, 292 insertions(+), 24 deletions(-) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/MultiFactorBase.cpp (93%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/MultiFactorBase.h (95%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/ScoreRecord.cpp (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/ScoreRecord.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/build_in.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/crt/MF_EqualWeight.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/crt/MF_ICIRWeight.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/crt/MF_ICWeight.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/imp/EqualWeightMultiFactor.cpp (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/imp/EqualWeightMultiFactor.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/imp/ICIRMultiFactor.cpp (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/imp/ICIRMultiFactor.h (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/imp/ICMultiFactor.cpp (100%) rename hikyuu_cpp/hikyuu/trade_sys/{factor => multifactor}/imp/ICMultiFactor.h (100%) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/all.h b/hikyuu_cpp/hikyuu/trade_sys/all.h index 1ef6898f..8581f007 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/all.h +++ b/hikyuu_cpp/hikyuu/trade_sys/all.h @@ -19,6 +19,6 @@ #include "system/build_in.h" #include "allocatefunds/build_in.h" #include "selector/build_in.h" -#include "factor/build_in.h" +#include "multifactor/build_in.h" #endif /* ALL_TRADE_SYS_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp similarity index 93% rename from hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 7c7bf1db..3b7d2c8d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -123,6 +123,14 @@ void MultiFactorBase::paramChanged() { m_calculated = false; } +void MultiFactorBase::reset() { + std::lock_guard lock(m_mutex); + _reset(); + + // 仅重置 m_calculated,其他缓存不重置,否则线程不安全 + m_calculated = false; +} + MultiFactorPtr MultiFactorBase::clone() { std::lock_guard lock(m_mutex); MultiFactorPtr p; @@ -142,23 +150,23 @@ MultiFactorPtr MultiFactorBase::clone() { p->m_stks = m_stks; p->m_ref_stk = m_ref_stk; p->m_query = m_query; - p->m_stk_map = m_stk_map; - p->m_all_factors = m_all_factors; - p->m_date_index = m_date_index; - p->m_stk_factor_by_date = m_stk_factor_by_date; p->m_ref_dates = m_ref_dates; - p->m_ic = m_ic.clone(); - p->m_calculated = m_calculated; p->m_inds.reserve(m_inds.size()); for (const auto& ind : m_inds) { p->m_inds.emplace_back(ind.clone()); } - p->m_all_factors.reserve(m_all_factors.size()); - for (const auto& ind : m_all_factors) { - p->m_all_factors.emplace_back(ind.clone()); - } + p->m_calculated = false; + // 强制重算,不克隆以下缓存,避免非线程安全 + // p->m_stk_map = m_stk_map; + // p->m_date_index = m_date_index; + // p->m_stk_factor_by_date = m_stk_factor_by_date; + // p->m_ic = m_ic.clone(); + // p->m_all_factors.reserve(m_all_factors.size()); + // for (const auto& ind : m_all_factors) { + // p->m_all_factors.emplace_back(ind.clone()); + // } return p; } @@ -181,6 +189,22 @@ const ScoreRecordList& MultiFactorBase::getScore(const Datetime& d) { return m_stk_factor_by_date[iter->second]; } +ScoreRecordList MultiFactorBase::getScores(const Datetime& date, size_t start, size_t end) { + ScoreRecordList ret; + HKU_IF_RETURN(start >= end, ret); + + const auto& cross = getScore(date); + if (end == Null() || end > cross.size()) { + end = cross.size(); + } + + for (size_t i = start; i < end; i++) { + ret.emplace_back(cross[i]); + } + + return ret; +} + ScoreRecordList MultiFactorBase::getScores(const Datetime& date, size_t start, size_t end, std::function&& filter) { ScoreRecordList ret; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h similarity index 95% rename from hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h index 3dcf7fca..04e96dea 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/factor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h @@ -82,6 +82,8 @@ public: /** 获取指定日期截面的所有因子值,已经降序排列 */ const ScoreRecordList& getScore(const Datetime&); + ScoreRecordList getScores(const Datetime& date, size_t start, size_t end = Null()); + /** * 获取指定日期截面 [start, end] 范围内的因子值(评分), 并通过filer进行过滤 * @param date 指定日期 @@ -89,13 +91,11 @@ public: * @param end 排序起始点(不含该点) * @param filter 过滤函数 */ - ScoreRecordList getScores( - const Datetime& date, size_t start, size_t end = Null(), - std::function&& filter = std::function()); + ScoreRecordList getScores(const Datetime& date, size_t start, size_t end, + std::function&& filter); - ScoreRecordList getScores(const Datetime& date, size_t start, size_t end = Null(), - std::function&& filter = - std::function()); + ScoreRecordList getScores(const Datetime& date, size_t start, size_t end, + std::function&& filter); /** 获取所有截面数据,已按降序排列 */ const vector& getAllScores(); @@ -123,9 +123,12 @@ public: */ vector getAllSrcFactors(); + void reset(); + typedef std::shared_ptr MultiFactorPtr; MultiFactorPtr clone(); + virtual void _reset() {} virtual MultiFactorPtr _clone() = 0; virtual IndicatorList _calculate(const vector&) = 0; diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/ScoreRecord.cpp similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.cpp rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/ScoreRecord.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/ScoreRecord.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/ScoreRecord.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/ScoreRecord.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/build_in.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/build_in.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/build_in.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_EqualWeight.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICIRWeight.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/crt/MF_ICWeight.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.cpp rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/imp/EqualWeightMultiFactor.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.cpp rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICIRMultiFactor.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.cpp rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.h similarity index 100% rename from hikyuu_cpp/hikyuu/trade_sys/factor/imp/ICMultiFactor.h rename to hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h index 506c1155..6ba4c3db 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h @@ -10,6 +10,7 @@ #define TRADE_SYS_SELECTOR_BUILD_IN_H_ #include "crt/SE_Fixed.h" +#include "crt/SE_Multi_factor.h" #include "crt/SE_Signal.h" #endif /* TRADE_SYS_SELECTOR_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h new file mode 100644 index 00000000..fcaf9219 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/multifactor/MultiFactorBase.h" +#include "../SelectorBase.h" + +namespace hku { + +SelectorPtr HKU_API SE_Multi_factor(const MFPtr& mf, int topn = 10); + +SelectorPtr HKU_API SE_Multi_factor(const IndicatorList& src_inds, const StockList& stks, + const KQuery& query, int topn = 10, int ic_n = 5, + int ic_rolling_n = 120, const Stock& ref_stk = Stock(), + const string& mode = "MF_ICIRWeight"); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp new file mode 100644 index 00000000..63a42900 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#include "hikyuu/trade_sys/multifactor/build_in.h" +#include "MultiFactorSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::MultiFactorSelector) +#endif + +namespace hku { + +MultiFactorSelector::MultiFactorSelector() : SelectorBase("SE_Multi_factor") { + setParam("topn", 10); +} + +MultiFactorSelector::MultiFactorSelector(const MFPtr& mf, int topn) +: SelectorBase("SE_Multi_factor"), m_mf(mf) { + HKU_CHECK(mf, "mf is null!"); + setParam("topn", topn); + checkParam("topn"); +} + +MultiFactorSelector::~MultiFactorSelector() {} + +void MultiFactorSelector::_checkParam(const string& name) const { + if ("topn" == name) { + int topn = getParam("topn"); + HKU_ASSERT(topn > 0); + } +} + +void MultiFactorSelector::_reset() { + if (m_mf) { + m_mf->reset(); + } + m_stk_sys_dict.clear(); +} + +SelectorPtr MultiFactorSelector::_clone() { + MultiFactorSelector* p = new MultiFactorSelector(); + p->m_mf = m_mf->clone(); + p->m_stk_sys_dict = m_stk_sys_dict; + return SelectorPtr(p); +} + +bool MultiFactorSelector::isMatchAF(const AFPtr& af) { + return true; +} + +SystemWeightList MultiFactorSelector::getSelected(Datetime date) { + SystemWeightList ret; + auto scores = m_mf->getScores(date, 0, getParam("topn"), + [](const ScoreRecord& sc) { return !std::isnan(sc.value); }); + for (const auto& sc : scores) { + ret.emplace_back(m_stk_sys_dict[sc.stock], sc.value); + } + return ret; +} + +void MultiFactorSelector::_calculate() { + for (const auto& sys : m_real_sys_list) { + m_stk_sys_dict[sys->getStock()] = sys; + } +} + +SelectorPtr HKU_API SE_Multi_factor(const MFPtr& mf, int topn) { + return make_shared(mf, topn); +} + +SelectorPtr HKU_API SE_Multi_factor(const IndicatorList& src_inds, const StockList& stks, + const KQuery& query, int topn, int ic_n, int ic_rolling_n, + const Stock& ref_stk, const string& mode) { + Stock n_ref_stk = ref_stk.isNull() ? getStock("sh000300") : ref_stk; + MFPtr mf; + if ("MF_ICIRWeight" == mode) { + mf = MF_ICIRWeight(src_inds, stks, query, n_ref_stk, ic_n, ic_rolling_n); + } else if ("MF_ICWeight" == mode) { + mf = MF_ICWeight(src_inds, stks, query, n_ref_stk, ic_n, ic_rolling_n); + } else if ("MF_EqualWeight" == mode) { + mf = MF_EqualWeight(src_inds, stks, query, n_ref_stk, ic_n); + } else { + HKU_THROW("Invalid mode: {}", mode); + } + + return make_shared(mf, topn); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h new file mode 100644 index 00000000..1df475df --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/multifactor/MultiFactorBase.h" +#include "../SelectorBase.h" + +namespace hku { + +class MultiFactorSelector : public SelectorBase { +public: + MultiFactorSelector(); + MultiFactorSelector(const MFPtr& mf, int topn); + virtual ~MultiFactorSelector(); + + virtual void _checkParam(const string& name) const override; + virtual void _reset() override; + virtual SelectorPtr _clone() override; + virtual SystemWeightList getSelected(Datetime date) override; + virtual bool isMatchAF(const AFPtr& af) override; + virtual void _calculate() override; + +private: + MFPtr m_mf; + unordered_map m_stk_sys_dict; + + //============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_mf); + } +#endif +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index f7b38925..8ef57de0 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -82,7 +82,7 @@ public: * 获取交易对象 * @return 交易对象(KData) */ - KData getTO() const; + const KData& getTO() const; /** 复位操作 */ void reset(); @@ -198,7 +198,7 @@ typedef shared_ptr SGPtr; HKU_API std::ostream& operator<<(std::ostream&, const SignalBase&); HKU_API std::ostream& operator<<(std::ostream&, const SignalPtr&); -inline KData SignalBase::getTO() const { +inline const KData& SignalBase::getTO() const { return m_kdata; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h index 0bf3b6c5..0081fc52 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h @@ -9,6 +9,7 @@ #ifndef SIGNAL_BUILD_IN_H_ #define SIGNAL_BUILD_IN_H_ +#include "crt/SG_Allways_buy.h" #include "crt/SG_Cross.h" #include "crt/SG_CrossGold.h" #include "crt/SG_Flex.h" diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h new file mode 100644 index 00000000..041eabfc --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once +#include "../SignalBase.h" + +namespace hku { + +SignalPtr HKU_API SG_Allways_buy(); + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp new file mode 100644 index 00000000..baae8bb6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#include "AllwaysBuySignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::AllwaysBuySignal) +#endif + +namespace hku { + +AllwaysBuySignal::AllwaysBuySignal() : SignalBase("SG_Allways_buy") { + setParam("alternate", false); +} + +void AllwaysBuySignal::_checkParam(const string& name) const { + if ("alternate" == name) { + bool alternate = getParam(name); + HKU_CHECK(!alternate, "param alternate must be false!"); + } +} + +void AllwaysBuySignal::_calculate() { + const auto& kdata = getTO(); + for (auto iter = kdata.cbegin(); iter != kdata.cend(); ++iter) { + _addBuySignal(iter->datetime); + } +} + +SignalPtr HKU_API SG_Allways_buy() { + return SignalPtr(new AllwaysBuySignal); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.h new file mode 100644 index 00000000..11435ee4 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once + +#include "../SignalBase.h" + +namespace hku { + +class AllwaysBuySignal : public SignalBase { + SIGNAL_IMP(AllwaysBuySignal) + SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + AllwaysBuySignal(); + virtual ~AllwaysBuySignal() = default; + + virtual void _checkParam(const string& name) const override; +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp index c0b09b37..72bdffbb 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp @@ -14,7 +14,7 @@ #include #include #include -#include +#include using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp index 2cabe06a..8c422a8c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp @@ -17,7 +17,7 @@ #include #include #include -#include +#include using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp index 32da6b91..791b5b1c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp @@ -16,7 +16,7 @@ #include #include #include -#include +#include using namespace hku; diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 5051d506..a305cca6 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -5,7 +5,7 @@ * Author: fasiondog */ -#include +#include #include "../pybind_utils.h" namespace py = pybind11; diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 74930f57..346cf6fe 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -51,7 +51,8 @@ void export_Signal(py::module& m) { .def_property("name", py::overload_cast<>(&SignalBase::name, py::const_), py::overload_cast(&SignalBase::name), py::return_value_policy::copy, "名称") - .def_property("to", &SignalBase::getTO, &SignalBase::setTO, "设置或获取交易对象") + .def_property("to", &SignalBase::getTO, &SignalBase::setTO, py::return_value_policy::copy, + "设置或获取交易对象") .def("get_param", &SignalBase::getParam, R"(get_param(self, name) From e5446e0636b5ea9742df465713d7e552d3ad19bf Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 31 Mar 2024 02:18:44 +0800 Subject: [PATCH 122/601] add AF_MultiFactor/SE_MultiFactor//SG_Allways_buy --- .../hikyuu/trade_sys/allocatefunds/build_in.h | 1 + .../allocatefunds/crt/AF_MultiFactor.h | 21 ++++++++++ .../imp/MultiFactorAllocaterFunds.cpp | 25 +++++++++++ .../imp/MultiFactorAllocaterFunds.h | 23 ++++++++++ .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 21 ++++++---- .../hikyuu/trade_sys/selector/build_in.h | 2 +- .../trade_sys/selector/crt/SE_MultiFactor.h | 42 +++++++++++++++++++ .../trade_sys/selector/crt/SE_Multi_factor.h | 22 ---------- .../selector/imp/MultiFactorSelector.cpp | 29 +++++++++---- .../trade_sys/signal/crt/SG_Allways_buy.h | 7 +++- hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 5 +++ hikyuu_pywrap/trade_sys/_Selector.cpp | 32 ++++++++++++++ 12 files changed, 189 insertions(+), 41 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_MultiFactor.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h delete mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/build_in.h index 43a8b80e..e2d2123f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/build_in.h @@ -11,5 +11,6 @@ #include "crt/AF_EqualWeight.h" #include "crt/AF_FixedWeight.h" +#include "crt/AF_MultiFactor.h" #endif /* TRADE_SYS_ALLOCATEFUNDS_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_MultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_MultiFactor.h new file mode 100644 index 00000000..cace8020 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/crt/AF_MultiFactor.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once + +#include "../AllocateFundsBase.h" + +namespace hku { + +/** + * 创建 MultiFactor 评分权重的资产分配算法实例 + * @return AFPtr + * @ingroup AllocateFunds + */ +AFPtr HKU_API AF_MultiFactor(); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.cpp new file mode 100644 index 00000000..86c6427a --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.cpp @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#include "MultiFactorAllocaterFunds.h" + +namespace hku { + +MultiFactorAllocaterFunds::MultiFactorAllocaterFunds() : AllocateFundsBase("AF_Multi_factor") {} + +MultiFactorAllocaterFunds::~MultiFactorAllocaterFunds() {} + +SystemWeightList MultiFactorAllocaterFunds::_allocateWeight(const Datetime& date, + const SystemWeightList& se_list) { + return se_list; +} + +AFPtr HKU_API AF_MultiFactor() { + return make_shared(); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.h new file mode 100644 index 00000000..099f1c57 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/imp/MultiFactorAllocaterFunds.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once + +#include "../AllocateFundsBase.h" + +namespace hku { + +class MultiFactorAllocaterFunds : public AllocateFundsBase { + ALLOCATEFUNDS_IMP(MultiFactorAllocaterFunds) + ALLOCATEFUNDS_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + MultiFactorAllocaterFunds(); + virtual ~MultiFactorAllocaterFunds(); +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 3ca54f0f..bae087f9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -370,18 +370,21 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { // 跟踪打印持仓情况 //---------------------------------------------------------------------- if (trace && !m_running_sys_set.empty()) { + // clang-format off HKU_INFO( - "+------------+------------+------------+--------------+--------------+-------------+"); + "+------------+------------+------------+--------------+--------------+-------------+-------------+"); HKU_INFO( - "| code | name | position | market value | remain cash | close price |"); + "| code | name | position | market value | remain cash | open price | close price |"); HKU_INFO( - "+------------+------------+------------+--------------+--------------+-------------+"); + "+------------+------------+------------+--------------+--------------+-------------+-------------+"); + // clang-format on 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); + KRecord krecord = stk.getKRecord(date, m_query.kType()); #if HKU_OS_WINDOWS auto stk_name = runningInPython() && pythonInJupyter() ? stk.name() : UTF8ToGB(stk.name()); @@ -390,18 +393,18 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { 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); + HKU_INFO("| {:<11}| {}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}| {:<12.2f}|", + stk.market_code(), stk_name, position, funds.market_value, funds.cash, + krecord.openPrice, krecord.closePrice); #else auto stk_name = stk.name(); - HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}|", + HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}| {:<12.2f}|", stk.market_code(), stk_name, position, funds.market_value, funds.cash, - stk.getKRecord(date, m_query.kType()).closePrice); + krecord.openPrice, krecord.closePrice); #endif HKU_INFO( "+------------+------------+------------+--------------+--------------+-------------" - "+"); + "+-------------+"); if (++count >= 10) { break; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h index 6ba4c3db..dea8a63c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h @@ -10,7 +10,7 @@ #define TRADE_SYS_SELECTOR_BUILD_IN_H_ #include "crt/SE_Fixed.h" -#include "crt/SE_Multi_factor.h" +#include "crt/SE_MultiFactor.h" #include "crt/SE_Signal.h" #endif /* TRADE_SYS_SELECTOR_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h new file mode 100644 index 00000000..77f209ce --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/multifactor/MultiFactorBase.h" +#include "../SelectorBase.h" + +namespace hku { + +/** + * 基于 MultiFactor 选股算法 + * @param mf MultiFactor 实例 + * @param topn 只选取时间截面中前 topn 个系统 + * @return SelectorPtr + * @ingroup Selector + */ +SelectorPtr HKU_API SE_MultiFactor(const MFPtr& mf, int topn = 10); + +/** + * 基于 MultiFactor 选股算法 + * @param src_inds 原始因子公式 + * @param stks 证券列表 + * @param query 查询条件 + * @param topn 只选取时间截面中前 topn 个系统 + * @param ic_n ic 对应的 ic_n 日收益率 + * @param ic_rolling_n 计算滚动 IC (即 IC 的 n 日移动平均)周期 + * @param ref_stk 参照对比证券,未指定时,默认使用 sh000300 沪深300指数 + * @param mode "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称 + * @return SelectorPtr + * @ingroup Selector + */ +SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, const StockList& stks, + const KQuery& query, int topn = 10, int ic_n = 5, + int ic_rolling_n = 120, const Stock& ref_stk = Stock(), + const string& mode = "MF_ICIRWeight"); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h deleted file mode 100644 index fcaf9219..00000000 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Multi_factor.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (c) 2024 hikyuu.org - * - * Created on: 2024-03-30 - * Author: fasiondog - */ - -#pragma once - -#include "hikyuu/trade_sys/multifactor/MultiFactorBase.h" -#include "../SelectorBase.h" - -namespace hku { - -SelectorPtr HKU_API SE_Multi_factor(const MFPtr& mf, int topn = 10); - -SelectorPtr HKU_API SE_Multi_factor(const IndicatorList& src_inds, const StockList& stks, - const KQuery& query, int topn = 10, int ic_n = 5, - int ic_rolling_n = 120, const Stock& ref_stk = Stock(), - const string& mode = "MF_ICIRWeight"); - -} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index 63a42900..651ad8b5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -14,13 +14,16 @@ BOOST_CLASS_EXPORT(hku::MultiFactorSelector) namespace hku { -MultiFactorSelector::MultiFactorSelector() : SelectorBase("SE_Multi_factor") { +MultiFactorSelector::MultiFactorSelector() : SelectorBase("SE_MultiFactor") { + // 只选择发出买入信号的系统,此时选中的系统会变成资产平均分配,参考 AF 参数:ignore_zero_weight + setParam("only_should_buy", false); setParam("topn", 10); } MultiFactorSelector::MultiFactorSelector(const MFPtr& mf, int topn) -: SelectorBase("SE_Multi_factor"), m_mf(mf) { +: SelectorBase("SE_MultiFactor"), m_mf(mf) { HKU_CHECK(mf, "mf is null!"); + setParam("only_should_buy", false); setParam("topn", topn); checkParam("topn"); } @@ -56,8 +59,18 @@ SystemWeightList MultiFactorSelector::getSelected(Datetime date) { SystemWeightList ret; auto scores = m_mf->getScores(date, 0, getParam("topn"), [](const ScoreRecord& sc) { return !std::isnan(sc.value); }); - for (const auto& sc : scores) { - ret.emplace_back(m_stk_sys_dict[sc.stock], sc.value); + if (getParam("only_should_buy")) { + for (const auto& sc : scores) { + auto sys = m_stk_sys_dict[sc.stock]; + if (sys->getSG()->shouldBuy(date)) { + ret.emplace_back(sys, sc.value); + } + } + + } else { + for (const auto& sc : scores) { + ret.emplace_back(m_stk_sys_dict[sc.stock], sc.value); + } } return ret; } @@ -68,13 +81,13 @@ void MultiFactorSelector::_calculate() { } } -SelectorPtr HKU_API SE_Multi_factor(const MFPtr& mf, int topn) { +SelectorPtr HKU_API SE_MultiFactor(const MFPtr& mf, int topn) { return make_shared(mf, topn); } -SelectorPtr HKU_API SE_Multi_factor(const IndicatorList& src_inds, const StockList& stks, - const KQuery& query, int topn, int ic_n, int ic_rolling_n, - const Stock& ref_stk, const string& mode) { +SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, const StockList& stks, + const KQuery& query, int topn, int ic_n, int ic_rolling_n, + const Stock& ref_stk, const string& mode) { Stock n_ref_stk = ref_stk.isNull() ? getStock("sh000300") : ref_stk; MFPtr mf; if ("MF_ICIRWeight" == mode) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h index 041eabfc..70309199 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h @@ -10,6 +10,11 @@ namespace hku { +/** + * 一个总是发出买入信号的系统,主要用于 PF 中 MultiFactor 选股匹配 + * @return SignalPtr + * @ingroup Signal + */ SignalPtr HKU_API SG_Allways_buy(); -} \ No newline at end of file +} // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index 88bf00c3..9ec76db2 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -98,4 +98,9 @@ void export_AllocateFunds(py::module& m) { 固定比例资产分配 :param float weight: 指定的资产比例 [0, 1])"); + + m.def("AF_MultiFactor", AF_MultiFactor, R"(AF_MultiFactor() + + 创建 MultiFactor 评分权重的资产分配算法实例 + )"); } diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 7f1306a0..accadde6 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -158,4 +158,36 @@ void export_Selector(py::module& m) { :param list stk_list: 初始划定的标的 :param System sys: 系统策略原型 :return: SE选择器实例)"); + + m.def("SE_MultiFactor", py::overload_cast(SE_MultiFactor), py::arg("mf"), + py::arg("topn") = 10); + m.def( + "SE_MultiFactor", + [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, int topn, + int ic_n, int ic_rolling_n, const py::object& ref_stk, const string& mode) { + IndicatorList c_inds = python_list_to_vector(inds); + StockList c_stks = python_list_to_vector(stks); + Stock c_ref_stk = ref_stk.is_none() ? getStock("sh000300") : ref_stk.cast(); + return SE_MultiFactor(c_inds, c_stks, query, topn, ic_n, ic_rolling_n, c_ref_stk, mode); + }, + py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("topn") = 10, py::arg("ic_n") = 5, + py::arg("ic_rolling_n") = 120, py::arg("ref_stk") = py::none(), + py::arg("mode") = "MF_ICIRWeight", + R"(SE_MultiFactor + + 创建基于多因子评分的选择器,两种创建方式 + + - 直接指定 MF: + :param MultiFactorBase mf: 直接指定的多因子合成算法 + :param int topn: 只选取时间截面中前 topn 个系统 + + - 参数直接创建: + :param sequense(Indicator) inds: 原始因子列表 + :param sequense(stock) stks: 计算证券列表 + :param Query query: 日期范围 + :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) + :param int ic_n: 默认 IC 对应的 N 日收益率 + :param int ic_rolling_n: IC 滚动周期 + :param str mode: "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称 + )"); } From eb76ff9466484cd7d7680b33dc237c3236dc2231 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 31 Mar 2024 02:21:51 +0800 Subject: [PATCH 123/601] update --- .../trade_sys/{factor => multifactor}/test_MF_EqualWeight.cpp | 0 .../trade_sys/{factor => multifactor}/test_MF_ICIRWeight.cpp | 0 .../trade_sys/{factor => multifactor}/test_MF_ICWeight.cpp | 0 hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp | 2 -- 4 files changed, 2 deletions(-) rename hikyuu_cpp/unit_test/hikyuu/trade_sys/{factor => multifactor}/test_MF_EqualWeight.cpp (100%) rename hikyuu_cpp/unit_test/hikyuu/trade_sys/{factor => multifactor}/test_MF_ICIRWeight.cpp (100%) rename hikyuu_cpp/unit_test/hikyuu/trade_sys/{factor => multifactor}/test_MF_ICWeight.cpp (100%) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_EqualWeight.cpp similarity index 100% rename from hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_EqualWeight.cpp rename to hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_EqualWeight.cpp diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_ICIRWeight.cpp similarity index 100% rename from hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICIRWeight.cpp rename to hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_ICIRWeight.cpp diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_ICWeight.cpp similarity index 100% rename from hikyuu_cpp/unit_test/hikyuu/trade_sys/factor/test_MF_ICWeight.cpp rename to hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_ICWeight.cpp diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp index a98a056f..5cd50762 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_algorithm.cpp @@ -56,6 +56,4 @@ TEST_CASE("test_parallel_for_index") { for (size_t i = 0, len = expect.size(); i < len; i++) { CHECK_EQ(result[i], expect[i]); } - - // parallel_for_index(0, values.size(), [](size_t i) { HKU_INFO("i: {}", i); }); } From 9edf6149124d96b2432df11c62e8b91b85438914 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 31 Mar 2024 03:21:39 +0800 Subject: [PATCH 124/601] =?UTF-8?q?MF=20=E5=A4=9A=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/multifactor/MultiFactorBase.cpp | 11 +++- .../imp/EqualWeightMultiFactor.cpp | 40 +++++++++++++++ .../multifactor/imp/ICIRMultiFactor.cpp | 50 ++++++++++++++++++ .../multifactor/imp/ICMultiFactor.cpp | 51 +++++++++++++++++++ 4 files changed, 151 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 3b7d2c8d..3c6f6dc1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -6,6 +6,7 @@ */ #include +#include "hikyuu/utilities/thread/algorithm.h" #include "hikyuu/indicator/crt/ALIGN.h" #include "hikyuu/indicator/crt/ROCP.h" #include "hikyuu/indicator/crt/REF.h" @@ -342,13 +343,21 @@ Indicator MultiFactorBase::getICIR(int ir_n, int ic_n) { IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { bool fill_null = getParam("fill_null"); +#if 0 vector all_returns; all_returns.reserve(m_stks.size()); for (const auto& stk : m_stks) { auto k = stk.getKData(m_query); - all_returns.push_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null)); + all_returns.emplace_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, + fill_null)); } return all_returns; +#else + return parallel_for_index(0, m_stks.size(), [this, ndays, fill_null](size_t i) { + auto k = m_stks[i].getKData(m_query); + return ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null); + }); +#endif } vector MultiFactorBase::getAllSrcFactors() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp index 42a37523..e6052cf1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include "hikyuu/utilities/thread/algorithm.h" #include "hikyuu/indicator/crt/PRICELIST.h" #include "hikyuu/indicator/crt/IC.h" #include "hikyuu/indicator/crt/ICIR.h" @@ -29,6 +30,7 @@ vector EqualWeightMultiFactor::_calculate( size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); +#if 0 value_t null_value = Null(); vector sumByDate(days_total); vector countByDate(days_total); @@ -69,6 +71,44 @@ vector EqualWeightMultiFactor::_calculate( } return all_factors; +#endif + + return parallel_for_index(0, stk_count, [&, this](size_t si) { + vector sumByDate(days_total); + vector countByDate(days_total); + + const auto& curStkInds = all_stk_inds[si]; + for (size_t di = 0; di < days_total; di++) { + for (size_t ii = 0; ii < ind_count; ii++) { + const auto& value = curStkInds[ii][di]; + if (!std::isnan(value)) { + sumByDate[di] += value; + countByDate[di] += 1; + } + } + } + + // 均值权重 + for (size_t di = 0; di < days_total; di++) { + sumByDate[di] = + (countByDate[di] == 0) ? Null() : sumByDate[di] / countByDate[di]; + } + + Indicator ret = PRICELIST(sumByDate); + ret.name("IC"); + + // 更新 discard + for (size_t di = 0; di < days_total; di++) { + if (!std::isnan(ret[di])) { + ret.setDiscard(di); + break; + } + if (di == days_total - 1 && std::isnan(ret[di])) { + ret.setDiscard(di); + } + } + return ret; + }); } MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks, diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp index 72690844..96c80c94 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include "hikyuu/utilities/thread/algorithm.h" #include "hikyuu/indicator/crt/PRICELIST.h" #include "hikyuu/indicator/crt/IC.h" #include "hikyuu/indicator/crt/ICIR.h" @@ -43,6 +44,7 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i int ic_n = getParam("ic_n"); int ir_n = getParam("ic_rolling_n"); +#if 0 size_t discard = 0; vector icir(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { @@ -51,8 +53,21 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i discard = icir[ii].discard(); } } +#else + vector icir = parallel_for_index(0, ind_count, [this, ic_n, ir_n](size_t ii) { + return ICIR(m_inds[ii], m_stks, m_query, m_ref_stk, ic_n, ir_n); + }); + + size_t discard = 0; + for (size_t ii = 0; ii < ind_count; ii++) { + if (icir[ii].discard() > discard) { + discard = icir[ii].discard(); + } + } +#endif // 以 ICIR 为权重,计算加权后的合成因子 +#if 0 IndicatorList all_factors(stk_count); PriceList new_values(days_total, 0.0); PriceList sum_weight(days_total, 0.0); @@ -89,6 +104,41 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i } return all_factors; +#else + return parallel_for_index(0, stk_count, [&, this, discard, ind_count, days_total](size_t si) { + PriceList new_values(days_total, 0.0); + PriceList sum_weight(days_total, 0.0); + for (size_t di = 0; di < discard; di++) { + new_values[di] = Null(); + } + for (size_t ii = 0; ii < ind_count; ii++) { + const auto* ind_data = all_stk_inds[si][ii].data(); + const auto* icir_data = icir[ii].data(); + for (size_t di = discard; di < days_total; di++) { + new_values[di] += ind_data[di] * icir_data[di]; + sum_weight[di] += std::abs(icir_data[di]); + } + } + + for (size_t di = discard; di < days_total; di++) { + if (!std::isnan(new_values[di]) && sum_weight[di] != 0.0) { + new_values[di] = new_values[di] / sum_weight[di]; + } + } + + Indicator ret = PRICELIST(new_values); + ret.name("ICIR"); + + const auto* data = ret.data(); + for (size_t di = discard; di < days_total; di++) { + if (!std::isnan(data[di])) { + ret.setDiscard(discard); + } + } + + return ret; + }); +#endif } MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp index 4dce321e..264d8912 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include "hikyuu/utilities/thread/algorithm.h" #include "hikyuu/indicator/crt/PRICELIST.h" #include "hikyuu/indicator/crt/IC.h" #include "hikyuu/indicator/crt/MA.h" @@ -43,6 +44,7 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind int ic_rolling_n = getParam("ic_rolling_n"); // 计算每个原始因子的滚动IC值 +#if 0 size_t discard = 0; IndicatorList ic(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { @@ -51,8 +53,20 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind discard = ic[ii].discard(); } } +#else + IndicatorList ic = parallel_for_index(0, ind_count, [this, ic_n, ic_rolling_n](size_t ii) { + return MA(IC(m_inds[ii], m_stks, m_query, m_ref_stk, ic_n), ic_rolling_n); + }); + size_t discard = 0; + for (size_t ii = 0; ii < ind_count; ii++) { + if (ic[ii].discard() > discard) { + discard = ic[ii].discard(); + } + } +#endif // 以滚动 IC 为权重,计算加权后的合成因子 +#if 0 IndicatorList all_factors(stk_count); PriceList new_values(days_total, 0.0); PriceList sum_weight(days_total, 0.0); @@ -89,6 +103,43 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind } return all_factors; + +#else + return parallel_for_index(0, stk_count, [&, this, ind_count, days_total, discard](size_t si) { + PriceList new_values(days_total, 0.0); + PriceList sum_weight(days_total, 0.0); + for (size_t di = 0; di < discard; di++) { + new_values[di] = Null(); + } + + for (size_t ii = 0; ii < ind_count; ii++) { + const auto* ind_data = all_stk_inds[si][ii].data(); + const auto* ic_data = ic[ii].data(); + for (size_t di = discard; di < days_total; di++) { + new_values[di] += ind_data[di] * ic_data[di]; + sum_weight[di] += std::abs(ic_data[di]); + } + } + + for (size_t di = discard; di < days_total; di++) { + if (!std::isnan(new_values[di]) && sum_weight[di] != 0.0) { + new_values[di] = new_values[di] / sum_weight[di]; + } + } + + Indicator ret = PRICELIST(new_values); + ret.name("IC"); + + const auto* data = ret.data(); + for (size_t di = discard; di < days_total; di++) { + if (!std::isnan(data[di])) { + ret.setDiscard(discard); + } + } + + return ret; + }); +#endif } MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, From 3eb48810da53b64aeba8ccc794a0ae1771033202 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 31 Mar 2024 04:23:17 +0800 Subject: [PATCH 125/601] =?UTF-8?q?Selector,=20Portfolio=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 7 ++++--- hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp | 1 + 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index bae087f9..3ea5cf35 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -117,6 +117,10 @@ void Portfolio::_readyForRun() { FundsRecord funds = m_tm->getFunds(); HKU_CHECK(funds.total_assets() > 0.0, "The current tm is zero assets!"); + // 从 se 获取原型系统列表 + auto pro_sys_list = m_se->getProtoSystemList(); + HKU_CHECK(!pro_sys_list.empty(), "Can't fetch proto_sys_lsit from Selector!"); + reset(); // 生成资金账户 @@ -127,9 +131,6 @@ void Portfolio::_readyForRun() { m_af->setCashTM(m_cash_tm); m_af->setQuery(m_query); - // 从 se 获取原型系统列表 - auto pro_sys_list = m_se->getProtoSystemList(); - // 获取所有备选子系统,为无关联账户的子系统分配子账号,对所有子系统做好启动准备 TMPtr pro_tm = crtTM(m_tm->initDatetime(), 0.0, m_tm->costFunc(), "TM_SUB"); size_t total = pro_sys_list.size(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 58b99da0..8aef8d67 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -120,6 +120,7 @@ void SelectorBase::calculate(const SystemList& pf_realSysList, const KQuery& que void SelectorBase::calculate_proto(const KQuery& query) { if (m_proto_query != query && !m_proto_calculated) { + HKU_WARN_IF_RETURN(m_pro_sys_list.empty(), void(), "m_pro_sys_list is empty!"); for (auto& sys : m_pro_sys_list) { sys->run(query); } From fc20f5a5bb4646a5d37d98faf985154d065be45b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 31 Mar 2024 04:47:44 +0800 Subject: [PATCH 126/601] =?UTF-8?q?SG=5FAllwaysBuy=20=E5=BC=95=E5=87=BA?= =?UTF-8?q?=E8=87=B3=20python?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h | 2 +- .../signal/crt/{SG_Allways_buy.h => SG_AllwaysBuy.h} | 2 +- hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp | 4 ++-- hikyuu_pywrap/trade_sys/_Signal.cpp | 2 ++ 4 files changed, 6 insertions(+), 4 deletions(-) rename hikyuu_cpp/hikyuu/trade_sys/signal/crt/{SG_Allways_buy.h => SG_AllwaysBuy.h} (84%) diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h index 0081fc52..c6b975dd 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h @@ -9,7 +9,7 @@ #ifndef SIGNAL_BUILD_IN_H_ #define SIGNAL_BUILD_IN_H_ -#include "crt/SG_Allways_buy.h" +#include "crt/SG_AllwaysBuy.h" #include "crt/SG_Cross.h" #include "crt/SG_CrossGold.h" #include "crt/SG_Flex.h" diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_AllwaysBuy.h similarity index 84% rename from hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h rename to hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_AllwaysBuy.h index 70309199..c2dd9323 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Allways_buy.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_AllwaysBuy.h @@ -15,6 +15,6 @@ namespace hku { * @return SignalPtr * @ingroup Signal */ -SignalPtr HKU_API SG_Allways_buy(); +SignalPtr HKU_API SG_AllwaysBuy(); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp index baae8bb6..e0e6cc29 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp @@ -13,7 +13,7 @@ BOOST_CLASS_EXPORT(hku::AllwaysBuySignal) namespace hku { -AllwaysBuySignal::AllwaysBuySignal() : SignalBase("SG_Allways_buy") { +AllwaysBuySignal::AllwaysBuySignal() : SignalBase("SG_AllwaysBuy") { setParam("alternate", false); } @@ -31,7 +31,7 @@ void AllwaysBuySignal::_calculate() { } } -SignalPtr HKU_API SG_Allways_buy() { +SignalPtr HKU_API SG_AllwaysBuy() { return SignalPtr(new AllwaysBuySignal); } diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 346cf6fe..35cef9c7 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -207,4 +207,6 @@ void export_Signal(py::module& m) { SG_Band(MA(C, n=10), 100, 200) )"); + + m.def("SG_AllwaysBuy", SG_AllwaysBuy); } From 1b7647d879b4c5e20dba25fee527a14683b6b0a3 Mon Sep 17 00:00:00 2001 From: martin Date: Sun, 31 Mar 2024 17:58:30 +0800 Subject: [PATCH 127/601] use force = true for find_tool to determine python version --- hikyuu_pywrap/xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 6d514baf..71d1278a 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -85,7 +85,7 @@ target("core") local dst_obj = dst_dir .. "core.so" if not is_plat("cross") then import("lib.detect.find_tool") - local python = assert(find_tool("python", {version = true}), "python not found, please install it first! note: python version must > 3.0") + local python = assert(find_tool("python", {version = true, force = true}), "python not found, please install it first! note: python version must > 3.0") local tmp = string.split(python.version, "%.") dst_obj = dst_dir .. "core" .. tmp[1] .. tmp[2] end From 94f79eba57a7cc76b69c0a3c11b5e2ab1d1fe755 Mon Sep 17 00:00:00 2001 From: martin Date: Mon, 29 Jan 2024 19:16:03 +0800 Subject: [PATCH 128/601] use python -c to get python version --- hikyuu_pywrap/xmake.lua | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 71d1278a..7812702e 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -83,13 +83,13 @@ target("core") local dst_dir = "$(projectdir)/hikyuu/cpp/" local dst_obj = dst_dir .. "core.so" + + -- need xmake 445e43b40846b29b9abb1293b32b27b7104f54fa if not is_plat("cross") then - import("lib.detect.find_tool") - local python = assert(find_tool("python", {version = true, force = true}), "python not found, please install it first! note: python version must > 3.0") - local tmp = string.split(python.version, "%.") - dst_obj = dst_dir .. "core" .. tmp[1] .. tmp[2] + local stmt = [[python -c 'import sys; v = sys.version_info; print(str(v.major)+str(v.minor))']] + local python_version = os.iorun(stmt):trim() + dst_obj = dst_dir .. "core" .. python_version end - -- print(dst_obj) if is_plat("windows") then os.cp(target:targetdir() .. '/core.pyd', dst_obj .. ".pyd") From a48c38fafa5fce06b8e9b6e44dcb804271a99d5d Mon Sep 17 00:00:00 2001 From: martin Date: Sun, 28 Jan 2024 23:02:40 +0800 Subject: [PATCH 129/601] revert 1edf726 --- hikyuu_pywrap/xmake.lua | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 7812702e..9e023b97 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -65,15 +65,8 @@ target("core") end -- get python include directory. - local pydir = nil; - if os.getenv("CONDA_PREFIX") ~= nil then - local py3config = os.getenv("CONDA_PREFIX") .. "/bin/python3-config" - pydir = try { function () return os.iorun(py3config .. " --includes"):trim() end } - else - pydir = try { function () return os.iorun("python3-config --includes"):trim() end } - end - assert(pydir, "python3-config not found!") - target:add("cxflags", pydir) + local pydir = try { function () return os.iorun("python3-config --includes"):trim() end } + target:add("cxflags", pydir) end) after_build(function(target) From 975d764eab90057b813497755774f59273224323 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 1 Apr 2024 00:31:27 +0800 Subject: [PATCH 130/601] debug pf for not delay sys --- hikyuu_cpp/hikyuu/Stock.h | 2 +- .../allocatefunds/AllocateFundsBase.cpp | 143 +++++++++++------- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 112 ++++++++++++-- .../hikyuu/trade_sys/portfolio/Portfolio.h | 2 + .../hikyuu/trade_sys/selector/SystemWeight.h | 2 +- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 7 +- 6 files changed, 198 insertions(+), 70 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 5269d09c..621ac38c 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -132,7 +132,7 @@ public: /** 获取不同类型K线数据量 */ size_t getCount(KQuery::KType dataType = KQuery::DAY) const; - /** 获取指定日期时刻的市值,即小于等于指定日期的最后一条记录的收盘价 */ + /** 获取指定日期时刻的市值,即小于等于指定日期的最后一条记录的收盘价, 如果证券已失效,则为0 */ price_t getMarketValue(const Datetime&, KQuery::KType) const; /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 2b715bb1..4265a473 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -266,7 +266,21 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( bool trace = getParam("trace"); HKU_INFO_IF(trace, "[AF] {} _adjust_with_running", date); - HKU_IF_RETURN(se_list.size() == 0, delay_list); + + // 如果选中列表为空,则需要全部执行清仓,这里不能返回 + // HKU_IF_RETURN(se_list.size() == 0, delay_list); + + //----------------------------------------------------------------- + // 回收所有运行中系统剩余资金,用于重新分配 + //----------------------------------------------------------------- + for (const auto& sys : running_set) { + auto sub_tm = 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); + HKU_INFO_IF(trace, "[AF] Recycle cash: {:<.2f} from {}", sub_cash, sys->name()); + } + } // 获取计划分配的资产权重 SystemWeightList sw_list = _allocateWeight(date, se_list); @@ -281,30 +295,40 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( //----------------------------------------------------------------- // 先将已不在 sw_list 中的运行系统进行清仓,回收可分配资金 //----------------------------------------------------------------- - std::unordered_set running_in_sw_list; + std::unordered_set running_in_sw_set; for (const auto& sw : sw_list) { if (running_set.find(sw.sys) != running_set.cend()) { - running_in_sw_list.insert(sw.sys); + running_in_sw_set.insert(sw.sys); } } for (const auto& sys : running_set) { - if (running_in_sw_list.find(sys) == running_in_sw_list.cend()) { + if (running_in_sw_set.find(sys) == running_in_sw_set.cend()) { + PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); if (sys->getParam("buy_delay")) { - delay_list.emplace_back(sys, MAX_DOUBLE); + // 延迟买入的系统,上一调仓日指示买入,可能尚未被执行,需要放入延迟列表中 + delay_list.emplace_back(sys, position.number); + HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name()); + } else { // 非延迟卖出的系统,立即强制卖出并回收资金 - auto tr = sys->sellForceOnClose(date, MAX_DOUBLE, PART_ALLOCATEFUNDS); - // HKU_DEBUG_IF(trace && tr.isNull(), "[AF] failed to sell: {}", sys->name()); + auto tr = sys->sellForceOnClose(date, position.number, PART_ALLOCATEFUNDS); 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}", + HKU_INFO_IF(trace, "[AF] Clean position sell: {}, recycle cash: {:<.2f}", sys->name(), sub_cash); } + } else { + // 清仓卖出失败情况,也加入到延迟卖出列表中,以便下一交易日可执行 + PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); + if (position.number > 0.0) { + delay_list.emplace_back(sys, position.number); + HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name()); + } } } } @@ -316,8 +340,7 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( // 获取当前总资产市值,计算需保留的资产 int precision = m_cash_tm->getParam("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 total_funds = funds.total_assets(); price_t reserve_funds = roundEx(total_funds * reserve_percent, precision); std::unordered_set reduced_running_set; // 缓存已执行过减仓的运行中系统 @@ -327,34 +350,62 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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_total_funds = sub_funds.total_assets(); 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("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_cash = sub_tm->currentCash(); - if (sub_tm->checkout(date, sub_cash)) { - m_cash_tm->checkin(date, sub_cash); - m_tm->addTradeRecord(tr); // 向总账户加入交易记录 - } + + // 获取当前最后的收盘价 + price_t last_close_price = stock.getMarketValue(date, query.kType()); + if (last_close_price <= 0.0) { + // 证券已失效,无法处理,资产全部损失 + HKU_WARN_IF(trace, "{} has been delisted!", iter->sys->name()); + continue; + } + + double hold_num = sub_tm->getHoldNumber(date, stock); + if (hold_num <= 0.0) { + // 实际无持仓 + continue; + } + + // 预期需要卖出的数量 + double min_num = stock.minTradeNumber(); + double need_back_num = + static_cast(need_back_funds / last_close_price / min_num) * min_num; + if (hold_num - need_back_num < min_num) { + need_back_num = hold_num; + } + + if (need_back_num == 0.0) { + continue; + } + + if (iter->sys->getParam("buy_delay")) { + delay_list.emplace_back(iter->sys, need_back_num); + HKU_INFO_IF(trace, "[AF] Deduce delay {}, num: {}", iter->sys->name(), + need_back_num); + + } else { + auto tr = iter->sys->sellForceOnClose(date, need_back_num, PART_ALLOCATEFUNDS); + if (!tr.isNull()) { + 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] Deduce position {}, sell num: {}, recycle cash: {}", + iter->sys->name(), need_back_num, sub_cash); } + } else { + // 卖出失败的情况,也加入到延迟交易列表中 + delay_list.emplace_back(iter->sys, need_back_num); + HKU_INFO_IF(trace, "[AF] Delay deduce position {}, need sell num: {}", + iter->sys->name(), need_back_num); } } } @@ -383,13 +434,8 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( 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); + price_t will_funds = roundUp(total_funds * iter->weight, precision); // 如果该系统是当前运行中系统 if (running_set.find(iter->sys) != running_set.cend()) { @@ -408,40 +454,35 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( // 未执行过减仓的系统,需要予以相应资金分配 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; } - // 如果期望的资金连一手都买不起,则跳过 - auto krecord = iter->sys->getStock().getKRecord(date, query.kType()); - if (krecord.isValid() && - need_cash < krecord.closePrice * iter->sys->getStock().minTradeNumber()) { + + // 如果期望的资金连一手都买不起(含退市),则跳过 + auto last_price = iter->sys->getStock().getMarketValue(date, query.kType()); + if (need_cash < last_price * iter->sys->getStock().minTradeNumber()) { continue; } 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 += (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)) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 3ea5cf35..b369eedf 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -72,6 +72,8 @@ void Portfolio::paramChanged() { void Portfolio::reset() { m_real_sys_list.clear(); m_running_sys_set.clear(); + m_failed_sys_map.clear(); + m_dlist_sys_list.clear(); m_delay_adjust_sys_list.clear(); m_tmp_selected_list.clear(); m_tmp_will_remove_sys.clear(); @@ -92,7 +94,7 @@ PortfolioPtr Portfolio::clone() { p->m_name = m_name; p->m_query = m_query; p->m_real_sys_list = m_real_sys_list; - p->m_need_calculate = m_need_calculate; + p->m_need_calculate = true; if (m_se) p->m_se = m_se->clone(); if (m_af) @@ -195,9 +197,33 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { bool trace = getParam("trace"); HKU_INFO_IF(trace, "{} ===========================================================", date); - HKU_INFO_IF(trace && adjust, "[PF] Position adjustment will be made today."); + if (trace && adjust) { + HKU_INFO("****************************************************"); + HKU_INFO("** **"); + HKU_INFO("** [PF] Position adjustment will be made today. **"); + HKU_INFO("** **"); + HKU_INFO("****************************************************"); + } HKU_INFO_IF(trace, "[PF] current running system size: {}", m_running_sys_set.size()); + //--------------------------------------------------- + // 检测运行系统中是否存在已退市的证券 + //--------------------------------------------------- + for (auto iter = m_running_sys_set.begin(); iter != m_running_sys_set.end(); /*++iter*/) { + auto& sys = *iter; + if (sys->getStock().getMarketValue(date, m_query.kType()) == 0.0) { + auto sub_tm = 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_dlist_sys_list.emplace_back(sys); + m_running_sys_set.erase(iter++); + } else { + ++iter; + } + } + //--------------------------------------------------- // 开盘前处理各个子账户、资金账户、总账户之间可能的误差 //--------------------------------------------------- @@ -241,11 +267,53 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { } //---------------------------------------------------------------------- - // 开盘时,优先处理上一交易日确定的需延迟调仓卖出的系统,在开盘时先卖出调整 + // 开盘时,优先处理上一交易日强制清、减仓失败的系统 //---------------------------------------------------------------------- + // for (auto iter = m_failed_sys_map.begin(); iter != m_failed_sys_map.end();) { + // const auto& sys = iter->first; + // Stock stk = sys->getStock(); + // if (date > stk.lastDatetime()) { + // // 已退市 + // HKU_WARN_IF(trace, "{} has been delisted!", sys->name()); + // m_dlist_sys_list.emplace_back(sys); + // m_failed_sys_map.erase(iter++); + // continue; + // } + + // auto tr = sys->sellForceOnOpen(date, iter->second, PART_PORTFOLIO); + // if (!tr.isNull()) { + // HKU_INFO_IF(trace, "[PF] Process pre-failed sys: {}", tr); + // m_tm->addTradeRecord(tr); + + // // 卖出后,尝试将资金取出转移至影子总账户 + // TMPtr sub_tm = 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_failed_sys_map.erase(iter++); + // continue; + + // } else { + // // 如果实际已没有持仓,则将其从失败列表中移除 + // PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); + // if (position.number <= 0.0) { + // m_failed_sys_map.erase(iter++); + // continue; + // } + // } + + // ++iter; + // } + + //---------------------------------------------------------------------- + // 开盘时,优先处理上一交易日遗留的延迟调仓卖出的系统 + //---------------------------------------------------------------------- + HKU_INFO_IF(trace, "[PF] process delay adjust sys, size: {}", m_delay_adjust_sys_list.size()); + SystemWeightList tmp_continue_adjust_sys_list; 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); @@ -256,15 +324,23 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { if (sub_cash > 0.0 && sub_tm->checkout(date, sub_cash)) { m_cash_tm->checkin(date, sub_cash); } + + } else { + // 强制卖出失败的情况下,如果当前仍有持仓,则需要下一交易日继续进行处理 + PositionRecord position = sys.sys->getTM()->getPosition(date, sys.sys->getStock()); + if (position.number > 0.0) { + tmp_continue_adjust_sys_list.emplace_back(sys); + // m_failed_sys_map[sys.sys] = sys.weight; + } } } - // 清空,避免循环至下一个非调仓日时被重复处理 - m_delay_adjust_sys_list.clear(); + m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list); - //--------------------------------------------------------------- - // 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收 - //--------------------------------------------------------------- +//--------------------------------------------------------------- +// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收 +//--------------------------------------------------------------- +#if 0 m_tmp_will_remove_sys.clear(); for (auto& running_sys : m_running_sys_set) { Stock stock = running_sys->getStock(); @@ -278,14 +354,14 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { min_cash = krecord.openPrice * stock.minTradeNumber(); } - // 如果系统的剩余资金小于交易一手的资金,则回收资金 + // 如果系统的剩余资金小于交易一手的资金,则回收资金??? TODO 放到 AF 中进行处理不足一手的 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) { + if (position.number <= 0.0) { m_tmp_will_remove_sys.emplace_back(running_sys, 0.); HKU_INFO_IF(trace, "[PF] Recycle running sys: {}", running_sys->name()); } @@ -297,6 +373,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { HKU_INFO_IF(trace, "Recycling system {}", sub_sys.sys->name()); m_running_sys_set.erase(sub_sys.sys); } +#endif //--------------------------------------------------- // 调仓日,进行资金分配调整 @@ -312,9 +389,18 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { } // 资产分配算法调整各子系统资产分配 - m_delay_adjust_sys_list = m_af->adjustFunds(date, m_tmp_selected_list, m_running_sys_set); + tmp_continue_adjust_sys_list = + m_af->adjustFunds(date, m_tmp_selected_list, m_running_sys_set); - // 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统,并执行 + if (m_delay_adjust_sys_list.empty()) { + m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list); + } else { + for (auto& sw : tmp_continue_adjust_sys_list) { + m_delay_adjust_sys_list.emplace_back(sw); + } + } + + // 如果选中的系统不在已有列表中,且账户已经被分配了资金,则将其加入运行系统列表 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) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index c53db206..b5f9c850 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -117,6 +117,8 @@ protected: // 用于中间计算的临时数据 std::unordered_set m_running_sys_set; + std::unordered_map m_failed_sys_map; // 强制卖出失败的系统集合 + SystemList m_dlist_sys_list; // 因证券退市,无法执行买入的系统(资产全部损失) SystemWeightList m_delay_adjust_sys_list; // 延迟调仓卖出的系统列表 SystemWeightList m_tmp_selected_list; SystemWeightList m_tmp_will_remove_sys; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h index 9bdf746c..27c8b817 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SystemWeight.h @@ -20,7 +20,7 @@ namespace hku { */ struct HKU_API SystemWeight { SystemPtr sys; - price_t weight{1.0}; + double weight{1.0}; SystemWeight() = default; SystemWeight(const SystemPtr& sys_, double weight_) : sys(sys_), weight(weight_) {} diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 1482880c..2afb5b80 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -707,11 +707,10 @@ TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool _getRealSellPrice(krecord.datetime, on_open ? src_krecord.openPrice : src_krecord.closePrice); double min_num = m_stock.minTradeNumber(); - double real_sell_num = num; - if (real_sell_num >= position.number) { + // 对待卖出的数量进行最小交易单位取整数倍,如果剩余不足最小交易单位,则一次全部卖出 + double real_sell_num = static_cast(num / min_num) * min_num; + if (position.number - real_sell_num < min_num) { real_sell_num = position.number; - } else if (min_num > 1) { - real_sell_num = static_cast(num / min_num) * min_num; } record = From 62ccf5697b8399e204e75d64df77f0dbf3765e15 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 1 Apr 2024 01:44:54 +0800 Subject: [PATCH 131/601] fixed PF for delay sys --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 8 ++++---- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 11 +++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index b369eedf..f0870a23 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -409,12 +409,11 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { } } - // 从已运行系统列表中立即移除已没有持仓且没有资金的非延迟买入的系统 + // 从已运行系统列表中立即移除已没有持仓且没有资金的系统 m_tmp_will_remove_sys.clear(); for (auto& sys : m_running_sys_set) { auto sub_tm = sys->getTM(); - if (!sys->getParam("buy_delay") && sub_tm->currentCash() < 1.0 && - 0 == sub_tm->getHoldNumber(date, sys->getStock())) { + if (sub_tm->currentCash() < 1.0 && 0 == sub_tm->getHoldNumber(date, sys->getStock())) { m_tmp_will_remove_sys.emplace_back(sys, 0.0); } } @@ -437,6 +436,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { // 执行所有运行中的系统,无论是延迟还是非延迟,当天运行中的系统都需要被执行一次 //---------------------------------------------------------------------------- for (auto& sub_sys : m_running_sys_set) { + HKU_TRACE_IF(trace, "run: {}", sub_sys->name()); auto tr = sub_sys->runMoment(date); if (!tr.isNull()) { HKU_INFO_IF(trace, "[PF] {}", tr); @@ -492,7 +492,7 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { HKU_INFO( "+------------+------------+------------+--------------+--------------+-------------" "+-------------+"); - if (++count >= 10) { + if (++count >= 100) { break; } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 2afb5b80..d2be7092 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -586,15 +586,12 @@ TradeRecord System::_buyNow(const KRecord& today, const KRecord& src_today, Part // 获取可买入数量 double number = _getBuyNumber(today.datetime, planPrice, planPrice - stoploss, from); + double min_num = m_stock.minTradeNumber(); + number = int64_t(number / min_num) * min_num; if (number == 0 || number > m_stock.maxTradeNumber()) { return result; } - double min_num = m_stock.minTradeNumber(); - if (min_num > 1) { - number = number / min_num * min_num; - } - price_t realPrice = _getRealBuyPrice(today.datetime, planPrice); price_t goalPrice = _getGoalPrice(today.datetime, planPrice); TradeRecord record = @@ -645,9 +642,7 @@ TradeRecord System::_buyDelay(const KRecord& today, const KRecord& src_today) { } double min_num = m_stock.minTradeNumber(); - if (min_num > 1) { - number = number / min_num * min_num; - } + number = int64_t(number / min_num) * min_num; price_t realPrice = _getRealBuyPrice(today.datetime, planPrice); TradeRecord record = m_tm->buy(today.datetime, m_stock, realPrice, number, stoploss, goalPrice, From 92cc19c33806e08c3461c9229ca90c30d42bf6d4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 1 Apr 2024 04:03:04 +0800 Subject: [PATCH 132/601] update AF --- .../allocatefunds/AllocateFundsBase.cpp | 76 ++++++++----------- .../moneymanager/imp/NotMoneyManager.cpp | 9 ++- 2 files changed, 39 insertions(+), 46 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 4265a473..b4730122 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -293,7 +293,8 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( getParam("ignore_zero_weight")); //----------------------------------------------------------------- - // 先将已不在 sw_list 中的运行系统进行清仓,回收可分配资金 + // 先将已不在 sw_list 中的运行系统进行强制清仓,回收可分配资金 + // 不需要区分延迟买入系统,不管什么类型的系统,都是立刻使用收盘价进行清仓 //----------------------------------------------------------------- std::unordered_set running_in_sw_set; for (const auto& sw : sw_list) { @@ -305,30 +306,22 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( for (const auto& sys : running_set) { if (running_in_sw_set.find(sys) == running_in_sw_set.cend()) { PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); - if (sys->getParam("buy_delay")) { - // 延迟买入的系统,上一调仓日指示买入,可能尚未被执行,需要放入延迟列表中 - delay_list.emplace_back(sys, position.number); - HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name()); - + auto tr = sys->sellForceOnClose(date, position.number, PART_ALLOCATEFUNDS); + 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] Clean position sell: {}, recycle cash: {:<.2f}", + sys->name(), sub_cash); + } } else { - // 非延迟卖出的系统,立即强制卖出并回收资金 - auto tr = sys->sellForceOnClose(date, position.number, PART_ALLOCATEFUNDS); - 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] Clean position sell: {}, recycle cash: {:<.2f}", - sys->name(), sub_cash); - } - } else { - // 清仓卖出失败情况,也加入到延迟卖出列表中,以便下一交易日可执行 - PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); - if (position.number > 0.0) { - delay_list.emplace_back(sys, position.number); - HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name()); - } + // 清仓卖出失败情况,也加入到延迟卖出列表中,以便下一交易日可执行 + PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); + if (position.number > 0.0) { + delay_list.emplace_back(sys, position.number); + HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name()); } } } @@ -385,28 +378,21 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( continue; } - if (iter->sys->getParam("buy_delay")) { - delay_list.emplace_back(iter->sys, need_back_num); - HKU_INFO_IF(trace, "[AF] Deduce delay {}, num: {}", iter->sys->name(), - need_back_num); - - } else { - auto tr = iter->sys->sellForceOnClose(date, need_back_num, PART_ALLOCATEFUNDS); - if (!tr.isNull()) { - 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] Deduce position {}, sell num: {}, recycle cash: {}", - iter->sys->name(), need_back_num, sub_cash); - } - } else { - // 卖出失败的情况,也加入到延迟交易列表中 - delay_list.emplace_back(iter->sys, need_back_num); - HKU_INFO_IF(trace, "[AF] Delay deduce position {}, need sell num: {}", - iter->sys->name(), need_back_num); + auto tr = iter->sys->sellForceOnClose(date, need_back_num, PART_ALLOCATEFUNDS); + if (!tr.isNull()) { + 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] Deduce position {}, sell num: {}, recycle cash: {}", + iter->sys->name(), need_back_num, sub_cash); } + } else { + // 卖出失败的情况,也加入到延迟交易列表中 + delay_list.emplace_back(iter->sys, need_back_num); + HKU_INFO_IF(trace, "[AF] Delay deduce position {}, need sell num: {}", + iter->sys->name(), need_back_num); } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp index 8c4e3f4a..bbbd86e7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp @@ -13,12 +13,19 @@ BOOST_CLASS_EXPORT(hku::NotMoneyManager) namespace hku { -NotMoneyManager::NotMoneyManager() : MoneyManagerBase("MM_Nothing") {} +NotMoneyManager::NotMoneyManager() : MoneyManagerBase("MM_Nothing") { + // 如果已有持仓,则不再买入 + setParam("if_have_a_position_will_not_buy", false); +} NotMoneyManager::~NotMoneyManager() {} double NotMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, price_t risk, SystemPart from) { + if (getParam("if_have_a_position_will_not_buy") && + m_tm->getHoldNumber(datetime, stock) > 0.) { + return 0.0; + } return m_tm->cash(datetime, m_query.kType()) / price; } From e9477a9f8a27e522ee0094a58ca62c9715a71d67 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 1 Apr 2024 04:06:10 +0800 Subject: [PATCH 133/601] =?UTF-8?q?update=20sys=5Fperformance=20=E5=90=8C?= =?UTF-8?q?=E6=97=B6=E6=94=AF=E6=8C=81=20PF=20=E7=BB=98=E5=9B=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/__init__.py | 3 +- hikyuu/draw/drawplot/matplotlib_draw.py | 100 ++++++++------------ hikyuu_cpp/hikyuu/trade_sys/system/System.h | 6 ++ hikyuu_pywrap/trade_sys/_System.cpp | 1 + 4 files changed, 47 insertions(+), 63 deletions(-) diff --git a/hikyuu/draw/drawplot/__init__.py b/hikyuu/draw/drawplot/__init__.py index 3197e52e..a0648914 100644 --- a/hikyuu/draw/drawplot/__init__.py +++ b/hikyuu/draw/drawplot/__init__.py @@ -29,7 +29,7 @@ # 1. 20171122, Added by fasiondog # =============================================================================== -from hikyuu.core import KData, Indicator, SignalBase, ConditionBase, EnvironmentBase, System +from hikyuu.core import KData, Indicator, SignalBase, ConditionBase, EnvironmentBase, System, Portfolio import matplotlib from matplotlib.pylab import gca as mpl_gca @@ -123,6 +123,7 @@ def use_draw_with_matplotlib(): System.plot = mpl_sysplot System.performance = mpl_sys_performance + Portfolio.performance = mpl_sys_performance def use_draw_with_echarts(): diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 03255abf..24a51a87 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -743,16 +743,13 @@ def sysplot(sys, new=True, axes=None, style=1): def sys_performance(sys, ref_stk=None): - k = sys.to - if k is None or k.empty(): - hku_info("sys.to is None or empty!") - return - - query = Query(k[0].datetime.start_of_day(), k[-1].datetime.start_of_day() + TimeDelta(1), Query.DAY) - if ref_stk is None: ref_stk = get_stock('sh000300') + ref_k = ref_stk.get_kdata(sys.query) + + query = Query(ref_k[0].datetime.start_of_day(), ref_k[-1].datetime.start_of_day() + TimeDelta(1), Query.DAY) ref_k = ref_stk.get_kdata(query) + ref_dates = ref_k.get_datetime_list() profit = sys.tm.get_profit_curve(ref_dates) @@ -762,8 +759,8 @@ def sys_performance(sys, ref_stk=None): funds_return = profit / REF(funds, 1) + 1 # funds_return = cum_return(funds) funds_return.name = "系统累积收益率" - cum_return = get_part("default.ind.累积收益率") - ref_return = cum_return(ref_k.close) + # cum_return = get_part("default.ind.累积收益率") + ref_return = ROCR(ref_k.close, 0) ref_return.name = ref_stk.name per = Performance() @@ -787,56 +784,35 @@ def sys_performance(sys, ref_stk=None): t3 = '系统胜率: {:<.2f}% 盈/亏比: 1 : {:<.2f} 夏普比率: {:<.2f}'.format( per['赢利交易比例%'], per['净赢利/亏损比例'], sharp) - mp_back = matplotlib.get_backend() - if "nbagg" in mp_back: - fg = figure(figsize=(13, 10)) - ax1 = fg.add_axes([0.05, 0.35, 0.65, 0.6]) - ax2 = fg.add_axes([0.05, 0.05, 0.65, 0.25], sharex=ax1) - - ref_return.plot(axes=ax1, legend_on=True) - funds_return.plot(axes=ax1, legend_on=True) - - label = t1 + '\n\n' + t2 + '\n\n' + t3 - ax1.text(1.01, - 1, - text, - horizontalalignment='left', - verticalalignment='top', - transform=ax1.transAxes, - # color='r' - ) - ax2.text(0.02, - 1.0, - label, - horizontalalignment='left', - verticalalignment='top', - transform=ax2.transAxes, - # color='r' - ) - # ax2.set_visible(False) - ax2.xaxis.set_visible(False) - ax2.yaxis.set_visible(False) - ax2.set_frame_on(False) - - else: - ref_return.plot(legend_on=True) - funds_return.plot(legend_on=True, new=False) - axis = gca() - axis.text(-0.05, - 0.97, - text, - horizontalalignment='right', - verticalalignment='top', - transform=axis.transAxes, - # color=text_color - ) - - label = t1 + '\n\n' + t2 + '\n\n' + t3 - axis.text(0.05, - -0.06, - label, - horizontalalignment='left', - verticalalignment='top', - transform=axis.transAxes, - # color=text_color - ) + import matplotlib.pyplot as plt + fg = plt.figure(figsize=(15, 10)) + gs = fg.add_gridspec(5, 4) + ax1 = fg.add_subplot(gs[:4, :3]) + ax2 = fg.add_subplot(gs[:, 3:]) + ax3 = fg.add_subplot(gs[4:, :3]) + ref_return.plot(axes=ax1, legend_on=True) + funds_return.plot(axes=ax1, legend_on=True) + ax1.set_title(f"{sys.name} 累积收益率") + label = t1 + '\n\n' + t2 + '\n\n' + t3 + ax2.text(0, + 1, + text, + horizontalalignment='left', + verticalalignment='top', + transform=ax2.transAxes, + # color='r' + ) + ax3.text(0.02, + 0.9, + label, + horizontalalignment='left', + verticalalignment='top', + transform=ax3.transAxes, + # color='r' + ) + ax2.xaxis.set_visible(False) + ax2.yaxis.set_visible(False) + ax2.set_frame_on(False) + ax3.xaxis.set_visible(False) + ax3.yaxis.set_visible(False) + ax3.set_frame_on(False) diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index bf44bcf9..d850443d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -132,6 +132,8 @@ public: /** 设定交易的证券 */ void setStock(const Stock& stk); + const KQuery& getQuery() const; + /** 获取实际执行的交易记录,和 TM 的区别是不包含权息调整带来的交易记录 */ const TradeRecordList& getTradeRecordList() const; @@ -527,6 +529,10 @@ inline void System::setStock(const Stock& stk) { } } +inline const KQuery& System::getQuery() const { + return m_kdata.getQuery(); +} + inline const TradeRecordList& System::getTradeRecordList() const { return m_trade_list; } diff --git a/hikyuu_pywrap/trade_sys/_System.cpp b/hikyuu_pywrap/trade_sys/_System.cpp index 7a5f17d5..2ac89bb1 100644 --- a/hikyuu_pywrap/trade_sys/_System.cpp +++ b/hikyuu_pywrap/trade_sys/_System.cpp @@ -94,6 +94,7 @@ void export_System(py::module& m) { .def_property("name", py::overload_cast<>(&System::name, py::const_), py::overload_cast(&System::name), py::return_value_policy::copy, "系统名称") + .def_property_readonly("query", &System::getQuery, py::return_value_policy::copy, "查询条件") .def_property("tm", &System::getTM, &System::setTM, "关联的交易管理实例") .def_property("to", &System::getTO, &System::setTO, "交易对象 KData") .def_property("mm", &System::getMM, &System::setMM, "资金管理策略") From 344279be6ca06edfadcf094e1d0c8b3b11a6cd07 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 1 Apr 2024 14:22:34 +0800 Subject: [PATCH 134/601] update PF for trace --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index f0870a23..8ac5275b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -54,8 +54,9 @@ Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFP Portfolio::~Portfolio() {} void Portfolio::initParam() { - setParam("adjust_cycle", 1); // 调仓周期 - setParam("trace", false); // 打印跟踪 + setParam("adjust_cycle", 1); // 调仓周期 + setParam("trace", false); // 打印跟踪 + setParam("trace_max_num", 10); // 打印跟踪时,显示当前持仓证券最大数量 } void Portfolio::baseCheckParam(const string& name) const { @@ -489,12 +490,14 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { stk.market_code(), stk_name, position, funds.market_value, funds.cash, krecord.openPrice, krecord.closePrice); #endif - HKU_INFO( - "+------------+------------+------------+--------------+--------------+-------------" - "+-------------+"); - if (++count >= 100) { + // clang-format off + HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); + if (++count >= getParam("trace_max_num")) { + HKU_INFO("+ ... ... more +"); + HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); break; } + // clang-format on } } } From 8d28ef9dd6e18cdcad4efbf01d8cee1431fc0c44 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 1 Apr 2024 23:50:02 +0800 Subject: [PATCH 135/601] update PF --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 43 ------------------- .../hikyuu/trade_sys/portfolio/Portfolio.h | 1 - 2 files changed, 44 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 8ac5275b..cddfdec2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -73,7 +73,6 @@ void Portfolio::paramChanged() { void Portfolio::reset() { m_real_sys_list.clear(); m_running_sys_set.clear(); - m_failed_sys_map.clear(); m_dlist_sys_list.clear(); m_delay_adjust_sys_list.clear(); m_tmp_selected_list.clear(); @@ -267,47 +266,6 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { funds.cash + funds.market_value, funds.cash, funds.market_value); } - //---------------------------------------------------------------------- - // 开盘时,优先处理上一交易日强制清、减仓失败的系统 - //---------------------------------------------------------------------- - // for (auto iter = m_failed_sys_map.begin(); iter != m_failed_sys_map.end();) { - // const auto& sys = iter->first; - // Stock stk = sys->getStock(); - // if (date > stk.lastDatetime()) { - // // 已退市 - // HKU_WARN_IF(trace, "{} has been delisted!", sys->name()); - // m_dlist_sys_list.emplace_back(sys); - // m_failed_sys_map.erase(iter++); - // continue; - // } - - // auto tr = sys->sellForceOnOpen(date, iter->second, PART_PORTFOLIO); - // if (!tr.isNull()) { - // HKU_INFO_IF(trace, "[PF] Process pre-failed sys: {}", tr); - // m_tm->addTradeRecord(tr); - - // // 卖出后,尝试将资金取出转移至影子总账户 - // TMPtr sub_tm = 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_failed_sys_map.erase(iter++); - // continue; - - // } else { - // // 如果实际已没有持仓,则将其从失败列表中移除 - // PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); - // if (position.number <= 0.0) { - // m_failed_sys_map.erase(iter++); - // continue; - // } - // } - - // ++iter; - // } - //---------------------------------------------------------------------- // 开盘时,优先处理上一交易日遗留的延迟调仓卖出的系统 //---------------------------------------------------------------------- @@ -331,7 +289,6 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { PositionRecord position = sys.sys->getTM()->getPosition(date, sys.sys->getStock()); if (position.number > 0.0) { tmp_continue_adjust_sys_list.emplace_back(sys); - // m_failed_sys_map[sys.sys] = sys.weight; } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index b5f9c850..62295567 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -117,7 +117,6 @@ protected: // 用于中间计算的临时数据 std::unordered_set m_running_sys_set; - std::unordered_map m_failed_sys_map; // 强制卖出失败的系统集合 SystemList m_dlist_sys_list; // 因证券退市,无法执行买入的系统(资产全部损失) SystemWeightList m_delay_adjust_sys_list; // 延迟调仓卖出的系统列表 SystemWeightList m_tmp_selected_list; From 2db4bf7692369a43371ceefd1940710139a46721 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 11:57:01 +0800 Subject: [PATCH 136/601] Signal add Cycle --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 60 ++++++------------- .../hikyuu/trade_sys/portfolio/Portfolio.h | 2 +- .../hikyuu/trade_sys/signal/SignalBase.cpp | 30 ++++++++-- .../hikyuu/trade_sys/signal/SignalBase.h | 18 ++++++ hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h | 1 + .../hikyuu/trade_sys/signal/crt/SG_Cycle.h | 16 +++++ .../trade_sys/signal/imp/CycleSignal.cpp | 35 +++++++++++ .../hikyuu/trade_sys/signal/imp/CycleSignal.h | 25 ++++++++ hikyuu_pywrap/trade_sys/_Signal.cpp | 1 + 9 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index cddfdec2..6d6efcc6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -173,15 +173,18 @@ void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) { HKU_IF_RETURN(datelist.empty(), void()); size_t cur_adjust_ix = 0; + Datetime cur_cycle_end; 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; + cur_cycle_end = + cur_adjust_ix < total ? datelist[cur_adjust_ix] : datelist.back() + Seconds(1); } const auto& date = datelist[i]; - _runMoment(date, adjust); + _runMoment(date, cur_cycle_end, adjust); } m_need_calculate = false; @@ -191,7 +194,7 @@ void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) { m_tmp_will_remove_sys = SystemWeightList(); } -void Portfolio::_runMoment(const Datetime& date, bool adjust) { +void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool adjust) { // 当前日期小于账户建立日期,直接忽略 HKU_IF_RETURN(date < m_cash_tm->initDatetime(), void()); @@ -295,44 +298,6 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list); -//--------------------------------------------------------------- -// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收 -//--------------------------------------------------------------- -#if 0 - 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(); - } - - // 如果系统的剩余资金小于交易一手的资金,则回收资金??? TODO 放到 AF 中进行处理不足一手的 - 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.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); - } -#endif - //--------------------------------------------------- // 调仓日,进行资金分配调整 //--------------------------------------------------- @@ -395,6 +360,11 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { //---------------------------------------------------------------------------- for (auto& sub_sys : m_running_sys_set) { HKU_TRACE_IF(trace, "run: {}", sub_sys->name()); + if (adjust) { + auto sg = sub_sys->getSG(); + sg->startCycle(date, nextCycle); + } + auto tr = sub_sys->runMoment(date); if (!tr.isNull()) { HKU_INFO_IF(trace, "[PF] {}", tr); @@ -449,9 +419,13 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) { #endif // clang-format off HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); - if (++count >= getParam("trace_max_num")) { - HKU_INFO("+ ... ... more +"); - HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); + count++; + int trace_max_num = getParam("trace_max_num"); + if (count >= trace_max_num) { + if (count > trace_max_num) { + HKU_INFO("+ ... ... more +"); + HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); + } break; } // clang-format on diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index 62295567..50ce9a24 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -101,7 +101,7 @@ private: /** 运行前准备 */ void _readyForRun(); - void _runMoment(const Datetime& date, bool adjust); + void _runMoment(const Datetime& date, const Datetime& nextCycle, bool adjust); protected: string m_name; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 51ee60c2..b09e1051 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -25,17 +25,21 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SignalPtr& sg) { } SignalBase::SignalBase() : m_name("SignalBase"), m_hold_long(false), m_hold_short(false) { - setParam("alternate", true); // 买入卖出信号交替出现 - setParam("support_borrow_stock", false); // 支持发出空头信号 + initParam(); } SignalBase::SignalBase(const string& name) : m_name(name), m_hold_long(false), m_hold_short(false) { - setParam("alternate", true); - setParam("support_borrow_stock", false); + initParam(); } SignalBase::~SignalBase() {} +void SignalBase::initParam() { + setParam("cycle", false); // 仅在指定周期范围内计算 + setParam("alternate", true); // 买入卖出信号交替出现 + setParam("support_borrow_stock", false); // 支持发出空头信号 +} + void SignalBase::baseCheckParam(const string& name) const {} void SignalBase::paramChanged() {} @@ -66,7 +70,12 @@ SignalPtr SignalBase::clone() { void SignalBase::setTO(const KData& kdata) { HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; - if (!kdata.empty()) { + bool cycle = getParam("cycle"); + if (!cycle) { + m_cycle_start = Datetime::min(); + m_cycle_end = Datetime::max(); + } + if (!cycle && !kdata.empty()) { _calculate(); } } @@ -80,6 +89,17 @@ void SignalBase::reset() { _reset(); } +void SignalBase::startCycle(const Datetime& start, const Datetime& close) { + HKU_IF_RETURN(!getParam("cycle"), void()); + HKU_ASSERT(start != Null() && close != Null() && start < close); + HKU_CHECK(start >= m_cycle_end, "curretn start: {}, pre cycle end: {}", start, m_cycle_end); + m_cycle_start = start; + m_cycle_end = close; + if (!m_kdata.empty()) { + _calculate(); + } +} + DatetimeList SignalBase::getBuySignal() const { DatetimeList result(m_buySig.size()); std::copy(m_buySig.begin(), m_buySig.end(), result.begin()); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index 8ef57de0..8e6e0d23 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -84,6 +84,10 @@ public: */ const KData& getTO() const; + void startCycle(const Datetime& start, const Datetime& end); + const Datetime& getCycleStart() const; + const Datetime& getCycleEnd() const; + /** 复位操作 */ void reset(); @@ -106,6 +110,9 @@ public: /** 子类计算接口,在setTO中调用 */ virtual void _calculate() = 0; +private: + void initParam(); + protected: string m_name; KData m_kdata; @@ -118,6 +125,9 @@ protected: std::set m_buySig; std::set m_sellSig; + Datetime m_cycle_start{Datetime::min()}; + Datetime m_cycle_end{Datetime::min()}; + //============================================ // 序列化支持 //============================================ @@ -218,6 +228,14 @@ inline bool SignalBase::shouldSell(const Datetime& datetime) const { return m_sellSig.count(datetime) ? true : false; } +inline const Datetime& SignalBase::getCycleStart() const { + return m_cycle_start; +} + +inline const Datetime& SignalBase::getCycleEnd() const { + return m_cycle_end; +} + } /* namespace hku */ #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h index c6b975dd..ba577ff9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/build_in.h @@ -12,6 +12,7 @@ #include "crt/SG_AllwaysBuy.h" #include "crt/SG_Cross.h" #include "crt/SG_CrossGold.h" +#include "crt/SG_Cycle.h" #include "crt/SG_Flex.h" #include "crt/SG_Single.h" #include "crt/SG_Bool.h" diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h new file mode 100644 index 00000000..6c4bf843 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-01 + * Author: fasiondog + */ + +#pragma once + +#include "../SignalBase.h" + +namespace hku { + +SignalPtr HKU_API SG_Cycle(); + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp new file mode 100644 index 00000000..ed7f8a43 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-30 + * Author: fasiondog + */ + +#include "CycleSignal.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::CycleSignal) +#endif + +namespace hku { + +CycleSignal::CycleSignal() : SignalBase("SG_AllwaysBuy") { + setParam("cycle", true); +} + +void CycleSignal::_checkParam(const string& name) const { + if ("cycle" == name) { + bool cycle = getParam(name); + HKU_CHECK(cycle, "param cycle must be true!"); + } +} + +void CycleSignal::_calculate() { + _addBuySignal(getCycleStart()); +} + +SignalPtr HKU_API SG_Cycle() { + return make_shared(); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.h new file mode 100644 index 00000000..6db684dd --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-01 + * Author: fasiondog + */ + +#pragma once + +#include "../SignalBase.h" + +namespace hku { + +class CycleSignal : public SignalBase { + SIGNAL_IMP(CycleSignal) + SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + CycleSignal(); + virtual ~CycleSignal() = default; + + virtual void _checkParam(const string& name) const override; +}; + +} \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 35cef9c7..ba255c97 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -209,4 +209,5 @@ void export_Signal(py::module& m) { )"); m.def("SG_AllwaysBuy", SG_AllwaysBuy); + m.def("SG_Cycle", SG_Cycle); } From 554df392373d5f862612e8b68f33fcd56a4ca04a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 12:08:07 +0800 Subject: [PATCH 137/601] update --- hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp | 8 ++++---- hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index b09e1051..1b7ca65e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -64,6 +64,8 @@ SignalPtr SignalBase::clone() { p->m_hold_short = m_hold_short; p->m_buySig = m_buySig; p->m_sellSig = m_sellSig; + p->m_cycle_start = m_cycle_start; + p->m_cycle_end = m_cycle_start; return p; } @@ -71,10 +73,8 @@ void SignalBase::setTO(const KData& kdata) { HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; bool cycle = getParam("cycle"); - if (!cycle) { - m_cycle_start = Datetime::min(); - m_cycle_end = Datetime::max(); - } + m_cycle_start = Datetime::min(); + m_cycle_end = cycle ? Datetime::min() : Datetime::max(); if (!cycle && !kdata.empty()) { _calculate(); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index 8e6e0d23..bf0dbf1d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -125,8 +125,8 @@ protected: std::set m_buySig; std::set m_sellSig; - Datetime m_cycle_start{Datetime::min()}; - Datetime m_cycle_end{Datetime::min()}; + Datetime m_cycle_start; + Datetime m_cycle_end; //============================================ // 序列化支持 From fa57d64a6c9a17302b6be59009021902a1756a7e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 12:27:20 +0800 Subject: [PATCH 138/601] =?UTF-8?q?KData=20=E6=96=B0=E5=A2=9E=20getKData?= =?UTF-8?q?=20=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/base/datatype.rst | 8 ++++++++ hikyuu_cpp/hikyuu/KData.cpp | 10 ++++++++++ hikyuu_cpp/hikyuu/KData.h | 8 ++++++++ hikyuu_pywrap/_KData.cpp | 8 ++++++++ 4 files changed, 34 insertions(+) diff --git a/docs/source/base/datatype.rst b/docs/source/base/datatype.rst index aeafa5c5..178184c1 100644 --- a/docs/source/base/datatype.rst +++ b/docs/source/base/datatype.rst @@ -435,6 +435,14 @@ K线数据 获取关联的Stock :rtype: Stock + + .. py:method:: get_kdata() + + 通过当前 KData 获取一个保持数据类型、复权类型不变的新的 KData(注意,不是原 KData 的子集) + + :param Datetime start: 新的起始日期 + :param Datetime end: 新的结束日期 + :rtype: KData .. py:method:: tocsv(filename) diff --git a/hikyuu_cpp/hikyuu/KData.cpp b/hikyuu_cpp/hikyuu/KData.cpp index 53043375..05094362 100644 --- a/hikyuu_cpp/hikyuu/KData.cpp +++ b/hikyuu_cpp/hikyuu/KData.cpp @@ -62,6 +62,16 @@ void KData::tocsv(const string& filename) { file.close(); } +KData KData::getKData(const Datetime& start, const Datetime& end) const { + const Stock& stk = getStock(); + if (stk.isNull()) { + return KData(); + } + + const KQuery& query = getQuery(); + return KData(stk, KQueryByDate(start, end, query.kType(), query.recoverType())); +} + Indicator KData::open() const { return OPEN(*this); } diff --git a/hikyuu_cpp/hikyuu/KData.h b/hikyuu_cpp/hikyuu/KData.h index 79af39d9..04530040 100644 --- a/hikyuu_cpp/hikyuu/KData.h +++ b/hikyuu_cpp/hikyuu/KData.h @@ -57,6 +57,14 @@ public: return getKRecord(datetime); } + /** + * 通过当前 KData 获取一个保持数据类型、复权类型不变的新的 KData + * @note 新的 KData 并不一定是原 KData 的子集 + * @param start 起始日期 + * @param end 结束日期 + */ + KData getKData(const Datetime& start, const Datetime& end) const; + /** 按日期查询对应的索引位置,注:是 KData 中的位置,不是在 Stock 中原始K记录的位置 */ size_t getPos(const Datetime& datetime) const; diff --git a/hikyuu_pywrap/_KData.cpp b/hikyuu_pywrap/_KData.cpp index 1bc8028a..6f31073f 100644 --- a/hikyuu_pywrap/_KData.cpp +++ b/hikyuu_pywrap/_KData.cpp @@ -85,6 +85,14 @@ void export_KData(py::module& m) { :rtype: Stock)") + .def("get_kdata", &KData::getKData, R"(get_kdata(self, start_date, end_date) + + 通过当前 KData 获取一个保持数据类型、复权类型不变的新的 KData(注意,不是原 KData 的子集) + + :param Datetime start: 新的起始日期 + :param Datetime end: 新的结束日期 + :rtype: KData)") + .def("tocsv", &KData::tocsv, R"(tocsv(self, filename) 将数据保存至CSV文件 From 966c8e66575a17f999c19faacaa0db7fab798e53 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 13:33:24 +0800 Subject: [PATCH 139/601] update SG for support cycle --- docs/examples/quick_crtsg.py | 13 ++++---- hikyuu/examples/Turtle_SG.py | 13 ++++---- hikyuu/examples/quick_crtsg.py | 12 ++++---- hikyuu/test/Signal.py | 4 +-- .../hikyuu/trade_sys/signal/SignalBase.cpp | 30 +++++++++++++++---- .../hikyuu/trade_sys/signal/SignalBase.h | 4 +-- .../trade_sys/signal/imp/AllwaysBuySignal.cpp | 3 +- .../trade_sys/signal/imp/BandSignal.cpp | 6 ++-- .../hikyuu/trade_sys/signal/imp/BandSignal.h | 2 +- .../trade_sys/signal/imp/BoolSignal.cpp | 8 ++--- .../hikyuu/trade_sys/signal/imp/BoolSignal.h | 2 +- .../trade_sys/signal/imp/CrossGoldSignal.cpp | 8 ++--- .../trade_sys/signal/imp/CrossGoldSignal.h | 2 +- .../trade_sys/signal/imp/CrossSignal.cpp | 8 ++--- .../hikyuu/trade_sys/signal/imp/CrossSignal.h | 2 +- .../trade_sys/signal/imp/CycleSignal.cpp | 2 +- .../trade_sys/signal/imp/SingleSignal.cpp | 6 ++-- .../trade_sys/signal/imp/SingleSignal.h | 2 +- .../trade_sys/signal/imp/SingleSignal2.cpp | 6 ++-- .../trade_sys/signal/imp/SingleSignal2.h | 2 +- .../hikyuu/trade_sys/signal/test_Signal.cpp | 2 +- hikyuu_pywrap/trade_sys/_Signal.cpp | 4 +-- 22 files changed, 78 insertions(+), 63 deletions(-) diff --git a/docs/examples/quick_crtsg.py b/docs/examples/quick_crtsg.py index 7bc645bc..1879ff43 100644 --- a/docs/examples/quick_crtsg.py +++ b/docs/examples/quick_crtsg.py @@ -2,20 +2,19 @@ # -*- coding: utf8 -*- # cp936 -#=============================================================================== +# =============================================================================== # Aothor: fasiondog # History: 20160407, Added by fasiondog -#=============================================================================== +# =============================================================================== from hikyuu import * -def TurtleSG(self): +def TurtleSG(self, k): n = self.get_param("n") - k = self.to c = CLOSE(k) - h = REF(HHV(c, n), 1) #前n日高点 - L = REF(LLV(c, n), 1) #前n日低点 + h = REF(HHV(c, n), 1) # 前n日高点 + L = REF(LLV(c, n), 1) # 前n日低点 for i in range(h.discard, len(k)): if (c[i] >= h[i]): self._add_buy_signal(k[i].datetime) @@ -30,7 +29,7 @@ if __name__ == "__main__": s = get_stock("sh000001") k = s.get_kdata(Query(-500)) - #只有设置交易对象时,才会开始实际计算 + # 只有设置交易对象时,才会开始实际计算 sg.to = k dates = k.get_datetime_list() for d in dates: diff --git a/hikyuu/examples/Turtle_SG.py b/hikyuu/examples/Turtle_SG.py index ce0c9b08..cbf82384 100644 --- a/hikyuu/examples/Turtle_SG.py +++ b/hikyuu/examples/Turtle_SG.py @@ -2,10 +2,10 @@ # -*- coding: utf8 -*- # cp936 -#=============================================================================== +# =============================================================================== # Aothor: fasiondog # History: 20160407, Added by fasiondog -#=============================================================================== +# =============================================================================== from hikyuu import * @@ -18,12 +18,11 @@ class TurtleSignal(SignalBase): def _clone(self): return TurtleSignal() - def _calculate(self): + def _calculate(self, k): n = self.get_param("n") - k = self.to c = CLOSE(k) - h = REF(HHV(c, n), 1) #前n日高点 - L = REF(LLV(c, n), 1) #前n日低点 + h = REF(HHV(c, n), 1) # 前n日高点 + L = REF(LLV(c, n), 1) # 前n日低点 for i in range(h.discard, len(k)): if (c[i] >= h[i]): self._add_buy_signal(k[i].datetime) @@ -38,7 +37,7 @@ if __name__ == "__main__": s = get_stock("sh000001") k = s.get_kdata(Query(-500)) - #只有设置交易对象时,才会开始实际计算 + # 只有设置交易对象时,才会开始实际计算 sg.to = k dates = k.get_datetime_list() for d in dates: diff --git a/hikyuu/examples/quick_crtsg.py b/hikyuu/examples/quick_crtsg.py index 7bc645bc..0b819080 100644 --- a/hikyuu/examples/quick_crtsg.py +++ b/hikyuu/examples/quick_crtsg.py @@ -2,20 +2,20 @@ # -*- coding: utf8 -*- # cp936 -#=============================================================================== +# =============================================================================== # Aothor: fasiondog # History: 20160407, Added by fasiondog -#=============================================================================== +# =============================================================================== from hikyuu import * -def TurtleSG(self): +def TurtleSG(self, k): n = self.get_param("n") k = self.to c = CLOSE(k) - h = REF(HHV(c, n), 1) #前n日高点 - L = REF(LLV(c, n), 1) #前n日低点 + h = REF(HHV(c, n), 1) # 前n日高点 + L = REF(LLV(c, n), 1) # 前n日低点 for i in range(h.discard, len(k)): if (c[i] >= h[i]): self._add_buy_signal(k[i].datetime) @@ -30,7 +30,7 @@ if __name__ == "__main__": s = get_stock("sh000001") k = s.get_kdata(Query(-500)) - #只有设置交易对象时,才会开始实际计算 + # 只有设置交易对象时,才会开始实际计算 sg.to = k dates = k.get_datetime_list() for d in dates: diff --git a/hikyuu/test/Signal.py b/hikyuu/test/Signal.py index 1d1e8d4d..56f213ce 100644 --- a/hikyuu/test/Signal.py +++ b/hikyuu/test/Signal.py @@ -26,7 +26,7 @@ class SignalPython(SignalBase): p._x = self._x return p - def _calculate(self): + def _calculate(self, kdata): self._add_buy_signal(Datetime(201201210000)) self._add_sell_signal(Datetime(201201300000)) @@ -91,7 +91,7 @@ class SignalTest(unittest.TestCase): self.assertEqual(p_clone.get_param("test"), 30) -def testSignal(self): +def testSignal(self, kdata): self._add_buy_signal(Datetime(201201210000)) self._add_sell_signal(Datetime(201201300000)) diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 1b7ca65e..b4d68fd9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -72,11 +72,28 @@ SignalPtr SignalBase::clone() { void SignalBase::setTO(const KData& kdata) { HKU_IF_RETURN(m_kdata == kdata, void()); m_kdata = kdata; + HKU_IF_RETURN(kdata.empty(), void()); + bool cycle = getParam("cycle"); - m_cycle_start = Datetime::min(); - m_cycle_end = cycle ? Datetime::min() : Datetime::max(); - if (!cycle && !kdata.empty()) { - _calculate(); + m_cycle_start = kdata[0].datetime; + + const KQuery& query = kdata.getQuery(); + if (query.queryType() == KQuery::DATE) { + m_cycle_end = query.endDatetime(); + } else { + size_t last_pos = kdata.lastPos(); + const Stock& stk = kdata.getStock(); + if (last_pos + 1 >= stk.getCount(query.kType())) { + m_cycle_end = Null(); + } else { + KRecord krecord = stk.getKRecord(last_pos + 1, query.kType()); + m_cycle_end = krecord.datetime; + } + } + + KData cycle_kdata = kdata.getKData(m_cycle_start, m_cycle_end); + if (!cycle) { + _calculate(cycle_kdata); } } @@ -95,8 +112,9 @@ void SignalBase::startCycle(const Datetime& start, const Datetime& close) { HKU_CHECK(start >= m_cycle_end, "curretn start: {}, pre cycle end: {}", start, m_cycle_end); m_cycle_start = start; m_cycle_end = close; - if (!m_kdata.empty()) { - _calculate(); + KData kdata = m_kdata.getKData(start, close); + if (!kdata.empty()) { + _calculate(kdata); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index bf0dbf1d..7b15a51f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -108,7 +108,7 @@ public: virtual SignalPtr _clone() = 0; /** 子类计算接口,在setTO中调用 */ - virtual void _calculate() = 0; + virtual void _calculate(const KData&) = 0; private: void initParam(); @@ -196,7 +196,7 @@ public: \ virtual SignalPtr _clone() override { \ return SignalPtr(new classname()); \ } \ - virtual void _calculate() override; + virtual void _calculate(const KData&) override; /** * 客户程序都应使用该指针类型,操作信号指示器 diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp index e0e6cc29..8a4bb311 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp @@ -24,8 +24,7 @@ void AllwaysBuySignal::_checkParam(const string& name) const { } } -void AllwaysBuySignal::_calculate() { - const auto& kdata = getTO(); +void AllwaysBuySignal::_calculate(const KData& kdata) { for (auto iter = kdata.cbegin(); iter != kdata.cend(); ++iter) { _addBuySignal(iter->datetime); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp index 2131acc5..8121f695 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp @@ -30,13 +30,13 @@ SignalPtr BandSignal::_clone() { return SignalPtr(p); } -void BandSignal::_calculate() { - Indicator ind = m_ind(m_kdata); +void BandSignal::_calculate(const KData& kdata) { + Indicator ind = m_ind(kdata); size_t discard = ind.discard(); size_t total = ind.size(); auto const* inddata = ind.data(); - auto const* ks = m_kdata.data(); + auto const* ks = kdata.data(); for (size_t i = discard; i < total; ++i) { if (inddata[i] > m_upper) { _addBuySignal(ks[i].datetime); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.h index f0b86213..cb9fa0c6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.h @@ -21,7 +21,7 @@ public: virtual ~BandSignal(); virtual SignalPtr _clone() override; - virtual void _calculate() override; + virtual void _calculate(const KData& kdata) override; private: Indicator m_ind; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp index e923b65e..0aae59c1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp @@ -28,16 +28,16 @@ SignalPtr BoolSignal::_clone() { return SignalPtr(p); } -void BoolSignal::_calculate() { - Indicator buy = m_bool_buy(m_kdata); - Indicator sell = m_bool_sell(m_kdata); +void BoolSignal::_calculate(const KData& kdata) { + Indicator buy = m_bool_buy(kdata); + Indicator sell = m_bool_sell(kdata); HKU_ERROR_IF_RETURN(buy.size() != sell.size(), void(), "buy.size() != sell.size()"); size_t discard = buy.discard() > sell.discard() ? buy.discard() : sell.discard(); size_t total = buy.size(); auto const* buydata = buy.data(); auto const* selldata = sell.data(); - auto const* ks = m_kdata.data(); + auto const* ks = kdata.data(); for (size_t i = discard; i < total; ++i) { if (buydata[i] > 0.0) _addBuySignal(ks[i].datetime); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.h index 183b6ea4..31681a9a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.h @@ -21,7 +21,7 @@ public: virtual ~BoolSignal(); virtual SignalPtr _clone() override; - virtual void _calculate() override; + virtual void _calculate(const KData& kdata) override; private: Indicator m_bool_buy; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp index 8198f3b0..41a3f264 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp @@ -28,16 +28,16 @@ SignalPtr CrossGoldSignal::_clone() { return SignalPtr(p); } -void CrossGoldSignal::_calculate() { - Indicator fast = m_fast(m_kdata); - Indicator slow = m_slow(m_kdata); +void CrossGoldSignal::_calculate(const KData& kdata) { + Indicator fast = m_fast(kdata); + Indicator slow = m_slow(kdata); HKU_ERROR_IF_RETURN(fast.size() != slow.size(), void(), "fast.size() != slow.size()"); size_t discard = fast.discard() > slow.discard() ? fast.discard() : slow.discard(); size_t total = fast.size(); auto const* fastdata = fast.data(); auto const* slowdata = slow.data(); - auto const* ks = m_kdata.data(); + auto const* ks = kdata.data(); for (size_t i = discard + 1; i < total; ++i) { if (fastdata[i - 1] < slowdata[i - 1] && fastdata[i] > slowdata[i] && fastdata[i - 1] < fastdata[i] && slowdata[i - 1] < slowdata[i]) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.h index 90ae6eec..609ddb9b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.h @@ -21,7 +21,7 @@ public: virtual ~CrossGoldSignal(); virtual SignalPtr _clone() override; - virtual void _calculate() override; + virtual void _calculate(const KData& kdata) override; private: Indicator m_fast; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp index 73cd0948..d1b2ee20 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp @@ -28,16 +28,16 @@ SignalPtr CrossSignal::_clone() { return SignalPtr(p); } -void CrossSignal::_calculate() { - Indicator fast = m_fast(m_kdata); - Indicator slow = m_slow(m_kdata); +void CrossSignal::_calculate(const KData& kdata) { + Indicator fast = m_fast(kdata); + Indicator slow = m_slow(kdata); HKU_ERROR_IF_RETURN(fast.size() != slow.size(), void(), "fast.size() != slow.size()"); size_t discard = fast.discard() > slow.discard() ? fast.discard() : slow.discard(); size_t total = fast.size(); auto const* fastdata = fast.data(); auto const* slowdata = slow.data(); - auto const* ks = m_kdata.data(); + auto const* ks = kdata.data(); for (size_t i = discard + 1; i < total; ++i) { if (fastdata[i - 1] < slowdata[i - 1] && fastdata[i] > slowdata[i]) { _addBuySignal(ks[i].datetime); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.h index 3408d71a..5becffbb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.h @@ -21,7 +21,7 @@ public: virtual ~CrossSignal(); virtual SignalPtr _clone() override; - virtual void _calculate() override; + virtual void _calculate(const KData& kdata) override; private: Indicator m_fast; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp index ed7f8a43..3ac99aaa 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp @@ -24,7 +24,7 @@ void CycleSignal::_checkParam(const string& name) const { } } -void CycleSignal::_calculate() { +void CycleSignal::_calculate(const KData& kdata) { _addBuySignal(getCycleStart()); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp index 5561318b..e03772ad 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp @@ -43,11 +43,11 @@ SignalPtr SingleSignal::_clone() { return SignalPtr(p); } -void SingleSignal::_calculate() { +void SingleSignal::_calculate(const KData& kdata) { int filter_n = getParam("filter_n"); double filter_p = getParam("filter_p"); - Indicator ind = m_ind(m_kdata); + Indicator ind = m_ind(kdata); Indicator dev = STDEV(DIFF(ind), filter_n); size_t start = dev.discard(); @@ -55,7 +55,7 @@ void SingleSignal::_calculate() { auto const* inddata = ind.data(); auto const* devdata = dev.data(); - auto const* ks = m_kdata.data(); + auto const* ks = kdata.data(); size_t total = dev.size(); for (size_t i = start; i < total; ++i) { double dama = inddata[i] - inddata[i - 1]; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h index 3de41838..66edfe5f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.h @@ -22,7 +22,7 @@ public: virtual void _checkParam(const string& name) const override; virtual SignalPtr _clone() override; - virtual void _calculate() override; + virtual void _calculate(const KData& kdata) override; private: Indicator m_ind; diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp index 053c9aba..8629833f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp @@ -46,11 +46,11 @@ SignalPtr SingleSignal2::_clone() { return SignalPtr(p); } -void SingleSignal2::_calculate() { +void SingleSignal2::_calculate(const KData& kdata) { int filter_n = getParam("filter_n"); double filter_p = getParam("filter_p"); - Indicator ind = m_ind(m_kdata); + Indicator ind = m_ind(kdata); Indicator dev = REF(STDEV(DIFF(ind), filter_n), 1); size_t start = dev.discard(); @@ -62,7 +62,7 @@ void SingleSignal2::_calculate() { auto const* buydata = buy.data(); auto const* selldata = sell.data(); auto const* devdata = dev.data(); - auto const* ks = m_kdata.data(); + auto const* ks = kdata.data(); for (size_t i = start; i < total; ++i) { double filter = filter_p * devdata[i]; if (buydata[i] > filter) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h index 22bd590f..92747700 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.h @@ -22,7 +22,7 @@ public: virtual void _checkParam(const string& name) const override; virtual SignalPtr _clone() override; - virtual void _calculate() override; + virtual void _calculate(const KData& kdata) override; private: Indicator m_ind; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp index b87b44b3..12360cde 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp @@ -33,7 +33,7 @@ public: return SignalPtr(p); } - virtual void _calculate() {} + virtual void _calculate(const KData &) override {} private: int m_x; diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index ba255c97..76cfda63 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -18,8 +18,8 @@ public: using SignalBase::SignalBase; PySignalBase(const SignalBase& base) : SignalBase(base) {} - void _calculate() override { - PYBIND11_OVERLOAD_PURE(void, SignalBase, _calculate, ); + void _calculate(const KData& kdata) override { + PYBIND11_OVERLOAD_PURE(void, SignalBase, _calculate, kdata); } void _reset() override { From c7d5c105cc0c3f9549d4035d1d1eba11a80ac50a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 17:00:36 +0800 Subject: [PATCH 140/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20TradeManager=20?= =?UTF-8?q?=E8=8E=B7=E5=8F=96=E8=B5=84=E9=87=91=E6=9B=B2=E7=BA=BF=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=96=B9=E6=B3=95=E5=8F=8A=E5=85=B6=E4=BB=96=20python?= =?UTF-8?q?=20=E5=BC=95=E5=85=A5=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/strategy/AccountTradeManager.h | 24 ----- hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h | 3 + .../hikyuu/trade_manage/TradeManager.cpp | 34 ------ hikyuu_cpp/hikyuu/trade_manage/TradeManager.h | 18 ---- .../hikyuu/trade_manage/TradeManagerBase.h | 101 ++++++++++++++---- .../allocatefunds/AllocateFundsBase.cpp | 2 +- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 2 +- .../hikyuu/trade_sys/signal/SignalBase.cpp | 3 +- hikyuu_pywrap/bind_stl.cpp | 16 +-- hikyuu_pywrap/bind_stl.h | 14 +-- hikyuu_pywrap/pybind_utils.h | 2 +- hikyuu_pywrap/trade_manage/_FundsRecord.cpp | 6 ++ hikyuu_pywrap/trade_manage/_TradeManager.cpp | 38 +++++-- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 +- hikyuu_pywrap/trade_sys/_Selector.cpp | 8 +- hikyuu_pywrap/trade_sys/_Signal.cpp | 5 +- 16 files changed, 150 insertions(+), 128 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h index 3c748abe..6540b593 100644 --- a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h @@ -387,30 +387,6 @@ public: return FundsRecord(); } - /** - * 获取资产净值曲线,含借入的资产 - * @param dates 日期列表,根据该日期列表获取其对应的资产净值曲线 - * @param ktype K线类型,必须与日期列表匹配,默认KQuery::DAY - * @return 资产净值列表 - */ - virtual PriceList getFundsCurve(const DatetimeList& dates, - KQuery::KType ktype = KQuery::DAY) override { - HKU_WARN("The subclass does not implement this method"); - return PriceList(); - } - - /** - * 获取收益曲线,即扣除历次存入资金后的资产净值曲线 - * @param dates 日期列表,根据该日期列表获取其对应的收益曲线,应为递增顺序 - * @param ktype K线类型,必须与日期列表匹配,默认为KQuery::DAY - * @return 收益曲线 - */ - virtual PriceList getProfitCurve(const DatetimeList& dates, - KQuery::KType ktype = KQuery::DAY) override { - HKU_WARN("The subclass does not implement this method"); - return PriceList(); - } - /** * 直接加入交易记录 * @note 如果加入初始化账户记录,将清除全部已有交易及持仓记录 diff --git a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h index 1f05c470..c9e2f582 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h +++ b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h @@ -89,6 +89,9 @@ private: #endif }; +typedef vector FundsList; +typedef vector FundsRecordList; + /** * 输出TradeRecord信息 * @ingroup TradeManagerClass diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 8be35f2b..24573db7 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -1471,40 +1471,6 @@ FundsRecord TradeManager::getFunds(const Datetime& indatetime, KQuery::KType kty return funds; } -PriceList TradeManager::getFundsCurve(const DatetimeList& dates, KQuery::KType ktype) { - size_t total = dates.size(); - PriceList result(total); - int precision = getParam("precision"); - for (size_t i = 0; i < total; ++i) { - FundsRecord funds = getFunds(dates[i], ktype); - result[i] = roundEx( - funds.cash + funds.market_value - funds.borrow_cash - funds.borrow_asset, precision); - } - return result; -} - -PriceList TradeManager::getProfitCurve(const DatetimeList& dates, KQuery::KType ktype) { - size_t total = dates.size(); - PriceList result(total); - if (total == 0) - return result; - - size_t i = 0; - while (i < total && dates[i] < m_init_datetime) { - result[i] = 0; - i++; - } - int precision = getParam("precision"); - for (; i < total; ++i) { - FundsRecord funds = getFunds(dates[i], ktype); - result[i] = roundEx(funds.cash + funds.market_value - funds.borrow_cash - - funds.borrow_asset - funds.base_cash - funds.base_asset, - precision); - } - - return result; -} - /****************************************************************************** * 每次执行交易操作时,先根据权息信息调整持有仓位及现金记录 * 采用滞后更新的策略,即只在需要获取当前持仓情况及卖出时更新当前的持仓及资产情况 diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h index 29d035ae..8700d69a 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h @@ -336,24 +336,6 @@ public: virtual FundsRecord getFunds(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override; - /** - * 获取资产净值曲线,含借入的资产 - * @param dates 日期列表,根据该日期列表获取其对应的资产净值曲线 - * @param ktype K线类型,必须与日期列表匹配,默认KQuery::DAY - * @return 资产净值列表 - */ - virtual PriceList getFundsCurve(const DatetimeList& dates, - KQuery::KType ktype = KQuery::DAY) override; - - /** - * 获取收益曲线,即扣除历次存入资金后的资产净值曲线 - * @param dates 日期列表,根据该日期列表获取其对应的收益曲线,应为递增顺序 - * @param ktype K线类型,必须与日期列表匹配,默认为KQuery::DAY - * @return 收益曲线 - */ - virtual PriceList getProfitCurve(const DatetimeList& dates, - KQuery::KType ktype = KQuery::DAY) override; - /** * 直接加入交易记录 * @note 如果加入初始化账户记录,将清除全部已有交易及持仓记录 diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index bf50a80f..46f7584d 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -191,6 +191,85 @@ public: m_broker_list.clear(); } + /** + * 获取指定日期列表中的所有日资产记录 + * @param dates 日期列表 + * @param ktype K线类型,必须与日期列表匹配,默认KQuery::DAY + * @return 日资产记录列表 + */ + FundsList getFundsList(const DatetimeList& dates, KQuery::KType ktype = KQuery::DAY) { + size_t total = dates.size(); + FundsList result(total); + HKU_IF_RETURN(total == 0, result); + for (size_t i = 0; i < total; ++i) { + result[i] = getFunds(dates[i], ktype); + } + return result; + } + + /** + * 获取资产净值曲线,含借入的资产 + * @param dates 日期列表,根据该日期列表获取其对应的资产净值曲线 + * @param ktype K线类型,必须与日期列表匹配,默认KQuery::DAY + * @return 资产净值列表 + */ + PriceList getFundsCurve(const DatetimeList& dates, KQuery::KType ktype = KQuery::DAY) { + FundsList funds_list = getFundsList(dates, ktype); + PriceList ret(funds_list.size()); + int precision = getParam("precision"); + for (size_t i = 0, total = funds_list.size(); i < total; i++) { + ret[i] = roundEx(funds_list[i].total_assets(), precision); + } + return ret; + } + + /** + * 获取收益曲线,即扣除历次存入资金后的资产净值曲线 + * @param dates 日期列表,根据该日期列表获取其对应的收益曲线,应为递增顺序 + * @param ktype K线类型,必须与日期列表匹配,默认为KQuery::DAY + * @return 收益曲线 + */ + PriceList getProfitCurve(const DatetimeList& dates, KQuery::KType ktype = KQuery::DAY) { + FundsList funds_list = getFundsList(dates, ktype); + PriceList ret(funds_list.size()); + int precision = getParam("precision"); + for (size_t i = 0, total = funds_list.size(); i < total; i++) { + ret[i] = roundEx(funds_list[i].profit(), precision); + } + return ret; + } + + /** + * 获取累积收益率曲线 + * @param dates 日期列表 + * @param ktype K线类型,必须与日期列表匹配,默认为KQuery::DAY + * @return 收益率曲线 + */ + PriceList getProfitCumChangeCurve(const DatetimeList& dates, + KQuery::KType ktype = KQuery::DAY) { + FundsList funds_list = getFundsList(dates, ktype); + PriceList ret(funds_list.size()); + for (size_t i = 0, total = funds_list.size(); i < total; i++) { + ret[i] = funds_list[i].total_assets() / funds_list[i].total_base(); + } + return ret; + } + + /** + * 获取投入本值资产曲线 + * @param dates 日期列表,根据该日期列表获取其对应的收益曲线,应为递增顺序 + * @param ktype K线类型,必须与日期列表匹配,默认为KQuery::DAY + * @return 价格曲线 + */ + PriceList getBaseAssetsCurve(const DatetimeList& dates, KQuery::KType ktype = KQuery::DAY) { + FundsList funds_list = getFundsList(dates, ktype); + PriceList ret(funds_list.size()); + for (size_t i = 0, total = funds_list.size(); i < total; i++) { + ret[i] = funds_list[i].total_base(); + } + return ret; + } + /** * 根据权息信息更新当前持仓与交易情况 * @note 必须按时间顺序调用 @@ -578,28 +657,6 @@ public: return FundsRecord(); } - /** - * 获取资产净值曲线,含借入的资产 - * @param dates 日期列表,根据该日期列表获取其对应的资产净值曲线 - * @param ktype K线类型,必须与日期列表匹配,默认KQuery::DAY - * @return 资产净值列表 - */ - virtual PriceList getFundsCurve(const DatetimeList& dates, KQuery::KType ktype = KQuery::DAY) { - HKU_WARN("The subclass does not implement this method"); - return PriceList(); - } - - /** - * 获取收益曲线,即扣除历次存入资金后的资产净值曲线 - * @param dates 日期列表,根据该日期列表获取其对应的收益曲线,应为递增顺序 - * @param ktype K线类型,必须与日期列表匹配,默认为KQuery::DAY - * @return 收益曲线 - */ - virtual PriceList getProfitCurve(const DatetimeList& dates, KQuery::KType ktype = KQuery::DAY) { - HKU_WARN("The subclass does not implement this method"); - return PriceList(); - } - /** * 直接加入交易记录 * @note 如果加入初始化账户记录,将清除全部已有交易及持仓记录 diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index b4730122..275b957c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -42,7 +42,7 @@ void AllocateFundsBase::initParam() { // 仅针对剩余现金比例调整没有意义,即使分配由于交易成本原因可能也无法完成实际交易 // adjust_running_sys: True - 主动根据资产分配对已持仓策略进行增减仓 // adjust_running_sys: False - 不会根据当前分配权重对已持仓策略进行强制加减仓 - setParam("adjust_running_sys", false); + setParam("adjust_running_sys", true); // 自动调整权重,此时认为传入的权重为各证券的相互比例(详见ignore_zero_weight说明) // 否则,以传入的权重为指定权重不做调整(此时传入的各个权重需要小于1) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 6d6efcc6..ffb46b1e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -422,7 +422,7 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool count++; int trace_max_num = getParam("trace_max_num"); if (count >= trace_max_num) { - if (count > trace_max_num) { + if (m_running_sys_set.size() > trace_max_num) { HKU_INFO("+ ... ... more +"); HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index b4d68fd9..bb911c39 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -109,7 +109,8 @@ void SignalBase::reset() { void SignalBase::startCycle(const Datetime& start, const Datetime& close) { HKU_IF_RETURN(!getParam("cycle"), void()); HKU_ASSERT(start != Null() && close != Null() && start < close); - HKU_CHECK(start >= m_cycle_end, "curretn start: {}, pre cycle end: {}", start, m_cycle_end); + HKU_CHECK(start >= m_cycle_end || m_cycle_end == Null(), + "curretn start: {}, pre cycle end: {}", start, m_cycle_end); m_cycle_start = start; m_cycle_end = close; KData kdata = m_kdata.getKData(start, close); diff --git a/hikyuu_pywrap/bind_stl.cpp b/hikyuu_pywrap/bind_stl.cpp index 3327d848..8369d396 100644 --- a/hikyuu_pywrap/bind_stl.cpp +++ b/hikyuu_pywrap/bind_stl.cpp @@ -12,21 +12,25 @@ using namespace hku; namespace py = pybind11; +// pybind 默认会将 vector 和 list 互转,数据量过大的情况下会影响性能 +// 只考虑引出影响可能性能的类型 + void export_bind_stl(py::module& m) { // py::bind_vector(m, "PriceList"); // py::bind_vector(m, "StringList"); py::bind_vector(m, "DatetimeList"); py::bind_vector(m, "KRecordList"); - py::bind_vector(m, "StockList"); + // py::bind_vector(m, "StockList"); py::bind_vector(m, "StockWeightList"); // py::bind_vector(m, "Indicatorist"); py::bind_vector(m, "TimeLineList"); py::bind_vector(m, "TransList"); - py::bind_vector(m, "BorrowRecordList"); - py::bind_vector(m, "LoanRecordList"); - py::bind_vector(m, "PositionRecordList"); - py::bind_vector(m, "TradeRecordList"); + // py::bind_vector(m, "BorrowRecordList"); + // py::bind_vector(m, "LoanRecordList"); + // py::bind_vector(m, "PositionRecordList"); + // py::bind_vector(m, "FundsList"); + // py::bind_vector(m, "TradeRecordList"); py::bind_vector(m, "SystemWeightList"); - py::bind_vector(m, "SystemList"); + // py::bind_vector(m, "SystemList"); py::bind_vector(m, "ScoreRecordList"); } \ No newline at end of file diff --git a/hikyuu_pywrap/bind_stl.h b/hikyuu_pywrap/bind_stl.h index 7012a8bd..cce88f7e 100644 --- a/hikyuu_pywrap/bind_stl.h +++ b/hikyuu_pywrap/bind_stl.h @@ -8,6 +8,7 @@ #pragma once #include +#include #include using namespace hku; @@ -20,15 +21,16 @@ using namespace hku; // PYBIND11_MAKE_OPAQUE(PriceList); PYBIND11_MAKE_OPAQUE(DatetimeList); PYBIND11_MAKE_OPAQUE(KRecordList); -PYBIND11_MAKE_OPAQUE(StockList); +// PYBIND11_MAKE_OPAQUE(StockList); // StockList 数据量不大,让 pybind 自动从 list 互转比较方便 PYBIND11_MAKE_OPAQUE(StockWeightList); // PYBIND11_MAKE_OPAQUE(IndicatorList); // 无法编译 PYBIND11_MAKE_OPAQUE(TimeLineList); PYBIND11_MAKE_OPAQUE(TransList); -PYBIND11_MAKE_OPAQUE(BorrowRecordList); -PYBIND11_MAKE_OPAQUE(LoanRecordList); -PYBIND11_MAKE_OPAQUE(PositionRecordList); -PYBIND11_MAKE_OPAQUE(TradeRecordList); +// PYBIND11_MAKE_OPAQUE(BorrowRecordList); +// PYBIND11_MAKE_OPAQUE(LoanRecordList); +// PYBIND11_MAKE_OPAQUE(PositionRecordList); +// PYBIND11_MAKE_OPAQUE(FundsList); +// PYBIND11_MAKE_OPAQUE(TradeRecordList); PYBIND11_MAKE_OPAQUE(SystemWeightList); -PYBIND11_MAKE_OPAQUE(SystemList); +// PYBIND11_MAKE_OPAQUE(SystemList); PYBIND11_MAKE_OPAQUE(ScoreRecordList); \ No newline at end of file diff --git a/hikyuu_pywrap/pybind_utils.h b/hikyuu_pywrap/pybind_utils.h index 364c9c3c..60c2b028 100644 --- a/hikyuu_pywrap/pybind_utils.h +++ b/hikyuu_pywrap/pybind_utils.h @@ -10,7 +10,7 @@ #define HIKYUU_PYTHON_BIND_UTILS_H #include -#include + #include #include #include diff --git a/hikyuu_pywrap/trade_manage/_FundsRecord.cpp b/hikyuu_pywrap/trade_manage/_FundsRecord.cpp index a9be63ac..02bde239 100644 --- a/hikyuu_pywrap/trade_manage/_FundsRecord.cpp +++ b/hikyuu_pywrap/trade_manage/_FundsRecord.cpp @@ -26,6 +26,12 @@ void export_FundsRecord(py::module& m) { .def_readwrite("borrow_cash", &FundsRecord::borrow_cash, "当前借入的资金(float),即负债") .def_readwrite("borrow_asset", &FundsRecord::borrow_asset, "当前借入证券资产价值(float)") + .def_property_readonly("total_assets", &FundsRecord::total_assets, "总资产") + .def_property_readonly("net_assets", &FundsRecord::net_assets, "净资产") + .def_property_readonly("total_borrow", &FundsRecord::total_borrow, "总负债") + .def_property_readonly("total_base", &FundsRecord::total_base, "投入本值资产") + .def_property_readonly("profit", &FundsRecord::profit, "当前收益") + .def(py::self + py::self) .def(py::self += py::self) diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index 81ce2a0b..0551cba6 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -212,16 +212,6 @@ public: ktype); } - PriceList getFundsCurve(const DatetimeList& dates, KQuery::KType ktype) override { - PYBIND11_OVERRIDE_NAME(PriceList, TradeManagerBase, "get_funds_curve", getFundsCurve, dates, - ktype); - } - - PriceList getProfitCurve(const DatetimeList& dates, KQuery::KType ktype) override { - PYBIND11_OVERRIDE_NAME(PriceList, TradeManagerBase, "get_profit_curve", getProfitCurve, - dates, ktype); - } - bool addTradeRecord(const TradeRecord& tr) override { PYBIND11_OVERRIDE_NAME(bool, TradeManagerBase, "add_trade_record", addTradeRecord, tr); } @@ -426,6 +416,14 @@ void export_TradeManager(py::module& m) { :param Query.KType ktype: K线类型 :rtype: FundsRecord)") + .def("get_funds_list", &TradeManagerBase::getFundsList, py::arg("dates"), + py::arg("ktype") = KQuery::DAY, R"(get_funds_list(self, dates[, ktype = Query.DAY]) + + 获取指定日期列表的每日资产记录 + :param Datetime datetime: 指定时刻 + :param Query.KType ktype: K线类型 + :rtype: FundsList)") + .def("get_funds_curve", &TradeManagerBase::getFundsCurve, py::arg("dates"), py::arg("ktype") = KQuery::DAY, R"(get_funds_curve(self, dates[, ktype = Query.DAY]) @@ -446,6 +444,26 @@ void export_TradeManager(py::module& m) { :param DatetimeList dates: 日期列表,根据该日期列表获取其对应的收益曲线,应为递增顺序 :param Query.KType ktype: K线类型,必须与日期列表匹配 :return: 收益曲线 + :rtype: PriceList)") + + .def("get_profit_cum_change_curve", &TradeManagerBase::getProfitCumChangeCurve, + py::arg("dates"), py::arg("ktype") = KQuery::DAY, + R"(get_profit_cum_change_curve(self, dates[, ktype = Query.DAY]) + + 获取累积收益率曲线 + + :param DatetimeList dates: 日期列表 + :param Query.KType ktype: K线类型,必须与日期列表匹配 + :rtype: PriceList)") + + .def("get_base_assets_curve", &TradeManagerBase::getBaseAssetsCurve, py::arg("dates"), + py::arg("ktype") = KQuery::DAY, + R"(get_profit_curve(self, dates[, ktype = Query.DAY]) + + 获取投入本值资产曲线(投入本钱) + + :param DatetimeList dates: 日期列表 + :param Query.KType ktype: K线类型,必须与日期列表匹配 :rtype: PriceList)") .def("checkin", &TradeManagerBase::checkin, R"(checkin(self, datetime, cash) diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index a305cca6..bd049ac7 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -144,7 +144,7 @@ void export_MultiFactor(py::module& m) { }); } }, - py::arg("datet"), py::arg("start") = 0, py::arg("end") = py::none(), + py::arg("date"), py::arg("start") = 0, py::arg("end") = py::none(), py::arg("filter") = py::none(), R"(get_score(self, date[, start=0, end=Null]) diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index accadde6..023a5e55 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -26,8 +26,12 @@ public: } SystemWeightList getSelected(Datetime date) override { - PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected", getSelected, - date); + // PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected", getSelected, + // date); + auto self = py::cast(this); + py::sequence py_ret = self.attr("get_selected")(date); + auto c_ret = python_list_to_vector(py_ret); + return c_ret; } bool isMatchAF(const AFPtr& af) override { diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 76cfda63..f0530f82 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -121,7 +121,10 @@ void export_Signal(py::module& m) { .def("reset", &SignalBase::reset, "复位操作") .def("clone", &SignalBase::clone, "克隆操作") - .def("_calculate", &SignalBase::_calculate, "【重载接口】子类计算接口") + .def("_calculate", &SignalBase::_calculate, R"(_calculate(self, kdata) + + 【重载接口】子类计算接口)") + .def("_reset", &SignalBase::_reset, "【重载接口】子类复位接口,复位内部私有变量") DEF_PICKLE(SGPtr); From 5064e7e3b1365698187c83c155c683eaaa42d883 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 17:00:46 +0800 Subject: [PATCH 141/601] update documents --- docs/source/trade_manage/TradeManager.rst | 8 + docs/source/trade_manage/record_struct.rst | 11 +- docs/source/trade_sys/signal.rst | 2 +- hikyuu/draw/drawplot/matplotlib_draw.py | 10 +- hikyuu/examples/notebook/001-overview.ipynb | 122 ++++---- .../examples/notebook/007-SystemDetails.ipynb | 163 ++++++++--- hikyuu/examples/notebook/010-Portfolio.ipynb | 235 +++++++++++++-- hikyuu/examples/notebook/Demo/Demo1.ipynb | 233 +++++++-------- hikyuu/examples/notebook/Demo/Demo2.ipynb | 273 +++++++++++++----- 9 files changed, 744 insertions(+), 313 deletions(-) diff --git a/docs/source/trade_manage/TradeManager.rst b/docs/source/trade_manage/TradeManager.rst index 5c83accf..84c9b0ef 100644 --- a/docs/source/trade_manage/TradeManager.rst +++ b/docs/source/trade_manage/TradeManager.rst @@ -228,7 +228,15 @@ :param Datetime datetime: 指定时刻 :param Query.KType ktype: K线类型 :rtype: FundsRecord + + .. py:method:: get_funds_list(self, datetime, [ktype = Query.DAY]) + + 获取指定日期列表每日资产记录 + :param Datetime datetime: 指定时刻 + :param Query.KType ktype: K线类型 + :rtype: FundsList + .. py:method:: get_funds_curve(self, dates[, ktype = Query.DAY]) 获取资产净值曲线 diff --git a/docs/source/trade_manage/record_struct.rst b/docs/source/trade_manage/record_struct.rst index 82d292db..de3ef588 100644 --- a/docs/source/trade_manage/record_struct.rst +++ b/docs/source/trade_manage/record_struct.rst @@ -129,4 +129,13 @@ .. py:attribute:: base_cash 当前投入本金(float) .. py:attribute:: base_asset 当前投入的资产价值(float) .. py:attribute:: borrow_cash 当前借入的资金(float),即负债 - .. py:attribute:: borrow_asset 当前借入证券资产价值(float) \ No newline at end of file + .. py:attribute:: borrow_asset 当前借入证券资产价值(float) + + 只读属性,自动根据上面的属性计算得到的结果: + + .. py:attribute:: total_assets 总资产 + .. py:attribute:: net_assets 净资产 + .. py:attribute:: total_borrow 总负债 + .. py:attribute:: total_base 投入本值资产(本钱) + .. py:attribute:: profit 收益 + diff --git a/docs/source/trade_sys/signal.rst b/docs/source/trade_sys/signal.rst index 46c4eaa4..7e50dd6b 100644 --- a/docs/source/trade_sys/signal.rst +++ b/docs/source/trade_sys/signal.rst @@ -235,7 +235,7 @@ 克隆操作 - .. py:method:: _calculate(self) + .. py:method:: _calculate(self, kdata) 【重载接口】子类计算接口 diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 24a51a87..74076824 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -752,14 +752,12 @@ def sys_performance(sys, ref_stk=None): ref_dates = ref_k.get_datetime_list() - profit = sys.tm.get_profit_curve(ref_dates) - profit = VALUE(profit) - funds = sys.tm.get_funds_curve(ref_dates) + funds_list = sys.tm.get_funds_list(ref_dates) + funds = [f.total_assets for f in funds_list] funds = VALUE(funds) - funds_return = profit / REF(funds, 1) + 1 - # funds_return = cum_return(funds) + funds_return = [f.total_assets / f.total_base for f in funds_list] + funds_return = VALUE(funds_return) funds_return.name = "系统累积收益率" - # cum_return = get_part("default.ind.累积收益率") ref_return = ROCR(ref_k.close, 0) ref_return.name = ref_stk.name diff --git a/hikyuu/examples/notebook/001-overview.ipynb b/hikyuu/examples/notebook/001-overview.ipynb index addf7578..12dfe980 100644 --- a/hikyuu/examples/notebook/001-overview.ipynb +++ b/hikyuu/examples/notebook/001-overview.ipynb @@ -22,24 +22,29 @@ "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-04-02 13:40:04,206 [INFO] hikyuu version: 1.3.5_202404021336_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:91) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "warning: can't import TA-Lib, will be ignored! You can fetch ta-lib from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib\n", - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2023-10-14 02:20:24.982 [HKU-I] - Using SQLITE3 BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2023-10-14 02:20:24.982 [HKU-I] - Loading market information... (StockManager.cpp:499)\n", - "2023-10-14 02:20:24.983 [HKU-I] - Loading stock type information... (StockManager.cpp:512)\n", - "2023-10-14 02:20:24.983 [HKU-I] - Loading stock information... (StockManager.cpp:426)\n", - "2023-10-14 02:20:25.036 [HKU-I] - Loading stock weight... (StockManager.cpp:529)\n", - "2023-10-14 02:20:25.430 [HKU-I] - Loading KData... (StockManager.cpp:134)\n", - "2023-10-14 02:20:25.434 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:157)\n", - "2023-10-14 02:20:25.435 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:160)\n", - "2023-10-14 02:20:25.435 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:163)\n", - "2023-10-14 02:20:25.449 [HKU-I] - 0.02s Loaded Data. (StockManager.cpp:145)\n", - "Wall time: 1.15 s\n" + "2024-04-02 13:40:04.592 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-04-02 13:40:04.614 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", + "2024-04-02 13:40:04.626 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", + "2024-04-02 13:40:04.639 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", + "2024-04-02 13:40:04.800 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", + "2024-04-02 13:40:05.926 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", + "2024-04-02 13:40:06.602 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", + "2024-04-02 13:40:06.603 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", + "2024-04-02 13:40:06.603 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", + "2024-04-02 13:40:06.607 [HKU-I] - 0.68s Loaded Data. (StockManager.cpp:150)\n", + "CPU times: total: 203 ms\n", + "Wall time: 2.84 s\n" ] } ], @@ -92,7 +97,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9gAAAMTCAYAAABaBb8GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hUVf7H8fdMegcSgZAQOgFpmlBEKQoCirq6uoo/RVHBjqi46rKrrgou9ooVxS4WLCACioAiAlJCJwSkJUAoCSQhPZk5vz+GjAxJSJv0z+t55pk7d84953tnMgzfOeeeYzHGGERERERERESkSqy1HYCIiIiIiIhIQ6AEW0RERERERMQNlGCLiIiIiIiIuIESbBERERERERE3UIItIiIiIiIi4gZKsEVERERERETcQAm2iIiIiIiIiBsowRYREZEKSUtLw2az1XYYIiIidY4SbBERkWpUUFBAZmZmhW/Z2dll1v3xxx+zaNGicsfy0ksvsWvXrqqcDjabjcsvv5yrr766SvXURY899hgrVqxw2Zednc1NN91EfHw8hYWFfPLJJ9jt9lqKUERE6jol2CIiItXomWeeISgoqMK39u3bl1n33Llzueqqq/jzzz/LLLtlyxYeeOAB8vLynPumTJlCly5dSr1NmzatWD2TJ0/mjz/+4D//+Q8A48aNw2KxlHrr0qVLBV6tss2ePZvPPvvMZd+ePXtOG8Opt8cff7xYvZs2bWLy5MkYY1z2WywWPvzwQxITEzlw4ABTpkxh8ODB7Nu3z6XcrFmz+Oabb9x6riIiUv8owRYRacSWLl3K3//+d6KiovDy8iIoKIjY2Fj27NkDwE033VRmsvLee++VWPfEiRNp2bIlBQUFAMTHx3PZZZcREhJCUFAQl1xyCdu3by923Pfff0/fvn3x8/OjefPm3HnnnWRkZLiUsdvtPPfcc3Tu3BlfX186dOjACy+8UCw5SktL484776Rly5b4+vrSu3dv5s2bV6zN8sYGUFhYyOuvv05QUFCZry/AvffeS1JSUok3gFdffbXE59atW+esY/v27Wzbtq3Y7cEHH8TX15d33323xOeTk5Oddbz99tv07t0bPz8/9uzZw9GjRzl48CBt2rRh2rRpxW6hoaGkpKS4nMtXX33FlClTePnll4mNjQUcSXp8fHypt/nz55frdSqP5cuXc+2119K2bdsSn58xY8ZpY4mPj6dVq1YlHjt16lQuueQSzj33XJf9vr6+WCwW8vLyiIqKYvXq1TRr1qzY332HDh244YYbWLx4sVvOVURE6ikjIiKN0ltvvWUAM2DAAPPuu++ab7/91rz++uvmoosuMmvXrjXGGLNx40Yzf/78Yrc33njDWCwWM2TIEGO324vVnZ2dbZo2bWoeeeQRY4wxu3fvNs2aNTM9evQwn3zyiZk+fboJDw834eHhJjU11Xncd999ZywWi7niiivM119/bZ588knj5eVlRowY4VL//fffb6xWq3nooYfMt99+a2688UYDmKlTpzrLFBQUmL59+5pmzZqZV1991XzxxRfmnHPOMZ6enua3335zlitvbElJSea5554z7du3N4CpyFdoXFyc+e9//1tsP2A+/vhjl30zZswwzz33nMvrGhAQ4GyzIrcxY8YYY4xJTk42QUFBLs/dfffd5u6773aWOdXll1/uEvP7779vPDw8zL333muMMebo0aPlPn93KCgoMO3btzcPPPBAsed2795tALNw4cIy62nTpk2x92LTpk3Gy8vLbNy40aSlpZm0tDSX5wMCAswXX3xh8vLyzK5du8wvv/xiPv74YzNjxgyXco8//riJiIgwubm5FT9BERFpEJRgi4g0Uq1atTJt2rQxBQUFFTqusLDQnHfeeaZZs2Zm3759JZZ59913jYeHh0lKSjLGGDNmzBgTEBBgkpOTnWUWL15sAPPUU08ZY4yx2+2mXbt2pk+fPsZmsznLPfbYYwYwv//+uzHGmD///NN4eHiYiRMnurQ5ePBgExQUZHJycowxjoQQMHPmzHGWOXr0qAkJCTHDhg1z7itPbMYYc++995rg4GDzyCOPmFGjRlUowV62bJnx9PQ0b7zxhsv+UxPsP//80wQGBprRo0e7lAsICDAzZ8502bdixQrz7rvvlvr+XX755c7kecyYMeb8888vVubuu+82AwcONB9//HGxW2xsrDMRXbVqlbFYLObhhx82xhjz7bffmtDQUHP48OFyvwZVNWPGDOPr62sOHjxY7LmqJtjDhg0zt956qzHG8T5369bNvPTSS+b+++83//jHP4yXl5cJDAw0FovFACY4ONh0797d/N///Z9LPceOHTPBwcFm2rRplT9RERGp15Rgi4g0UiEhIZVKsJ988kkDmFmzZpVaJiYmxlx5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7tOnD8899xznn38+AQEBPProo85yN998M1dccQX//e9/ycrK4sEHH2T79u2MGjUKT09PLrjgAt577z06dOhQqdeiKkJDQwFISUkp9/rj5TVnzhy+++47/Pz8CA0NJTAwkDvuuMNlFEZ+fj779+93rh8+cuRIhg0bRufOnYvVd+TIEaDkyw5ERKTh0zXYIiKNVHJycrF9+fn5wF+9cOBIvPLy8vj4449dJnM61dy5c9m7dy933XWXy34/Pz+GDBnC7NmzXXqKf/31V/bu3cvVV18NwMCBAwkJCeHDDz90Of7DDz/Ew8ODK6+8EoBLL70UoNjEXB9++CHt2rVzXqd76aWXsnv3bpYuXeosk5aWxpw5c7jyyiuxWq3ljq2yfH196dKlS6k3cAzhLu35U4d3f/fddyxevJiMjAz69evHeeedx0cffcQFF1xAQUEBn376abEYPDw8iIyMJDIyEg8PD8LCwoiMjKRp06Yu5QYNGkSnTp245pprOOuss4iKimLr1q3ExMTQq1cvZs2addr3vzpFRkYCOH8ocKfY2FheeeUVZs+eTVJSEsePH3dJrgHOPPNM2rRpw/3338/AgQO57LLL+PDDD1m7di0FBQUuZRMTEwHHDxsiItL4qAdbRKSRio6OZtSoUQwcOJAmTZqQmJjItGnT8Pb25u677wZg2rRpzJ8/n7Fjx7Jt2za2bdtWrJ4ePXoQERHB66+/zsCBA+nZs2exMpMnT2bgwIGMGDGCiRMnkpGRwWOPPUa/fv2cw6N9fX154oknuO+++xg9ejT/+Mc/2LhxI0899RT33Xefs+e0f//+XHHFFUyZMsU5WdesWbP49ddf+e6775zXWN9999289dZbjBo1iscff5ymTZvy4osvYrVaeeKJJyoUW10yePBgBg8ezMaNG7nsssucw/EffPDBEq/hLs0ff/zh8thisfDyyy8zdOhQLrnkEj755BOX5z09a++/DD169KBZs2YsWbKEQYMGubXuyMhIJkyYADhGYWzYsIHly5dz88034+vrC8C+ffsIDQ3lu+++44UXXmDixIkAXHnllUyaNMnlR6UlS5bQrFkz56z2IiLSyNTW+mAiIlK7/vnPf5qzzz7bNGvWzPj6+pr27dubG2+80axfv94YY0xqaqrx8/MzwGlv77//vtm+fbuxWCzm888/L7W9xYsXm379+hkfHx9zxhlnmNtvv90cO3asWLlp06aZjh07Gi8vL9O+fXvz7LPPGpvN5lImKyvL3HvvvaZ58+bGx8fHxMbGmjlz5hSra/fu3eaKK64wQUFBJjAw0IwcObLY+tkVia1IZdbBLgknrYNdXjt37jRTpkwxbdq0Md7e3uaf//ynufvuu42vr69p2rSp+fe//20OHjxY4rGtWrUyL7zwghk0aJAZMmSIufvuu82YMWPML7/8YgYMGGD+/e9/my+++MJ4e3ubq6++2hw+fLhYHW3atDFTp049bYy9evVy+zrZN9xwg4mJiSnxuaJ1sGfMmGHi4+NPe2vVqpXLOtirVq0yjz/+uBk+fLgJDg42np6epm/fviY/P98cPnzYPPzww8bX19f07dvXBAcHO9djz8zMNBaLxSxbtswlln79+pnRo0e79dxFRKT+UIItIiJVdt9995mWLVua/Pz82g6lXilPgp2dnW1ee+01M3bsWNOxY0cDmJCQEHPvvfeaXbt2OcslJyebf/7zn8bf39/4+PiYW2+91WRlZTmfT0hIMBaLxVitVnP33XebtLQ0M2TIEOPv72+8vLzM7bffbhITE40xxvzyyy/OHy9uvPFGU1BQ4KynpAR7zZo15pVXXjHvv/++ee6554yHh4d5/PHH3fESOW3cuNFYLBazcOHCYs8VJdjlvZ2cYE+ePNn079/f/Otf/zILFiwwx48fN8uWLTPXXnut8fb2Nh06dDAzZ840drvdzJw501gsFvPAAw+YSZMmmaCgIJOXl+esa8mSJcZisZgNGza49dxFRKT+0BBxERGpkuzsbD744APuueeeYjM0S9X5+fnx5Zdfcvz4cYYPH84rr7zChRdeWGz26pYtW/Lcc8/xz3/+k6effpo9e/a4DBkPDAykZ8+eTJs2jQEDBmCz2cjKymLo0KG88MILLstNDR48mF27dvH666+Tn59f5vDw5ORk7r//fux2Ox4eHsTGxnLrrbe69XXo0aMHN998M5MmTWLw4MEl/q0tXLiQCy+88LT1tG3b1uXxI488wiOPPOKyLy4ujoMHD/L5559z+eWXO5clu/baawkODubf//43O3fu5Nlnn3W+D4WFhUyaNIkbb7yxxMskRESkcbAYY0xtByEiIvXXn3/+ySeffMLtt99e5prLUruMMc5r1AHS09OLzVRel2VnZ9OvXz8uvvhinn322doOx8W///1vvvnmG9asWVPqOvEiItLwKcEWERERERERcQMt0yUiIiIiIiLiBkqwRURERERERNxACbaIiIiIiIiIG9TbWcTtdjsHDhwgKCjIZcIWERERERERadiMMRw/fpxWrVo5V3uoC+ptgn3gwAFat25d22GIiIiIiIhILUlKSiIyMrK2w3Cqtwl2UFAQ4HhBg4ODazkaERGRmlVQUMBPP/3E8OHDtf64iIg0CBX5bsvIyKB169bOvLCuqLcJdtGw8ODgYCXYIiLS6BQUFODv709wcLASbBERaRAq891W1y4XrjuD1UVERERERETqMSXYIiIiIiIiIm6gBFtERERERETEDZRgi4iIiIiIiLiBEmwRERERERERN1CCLSIiIiIiIuIGSrBFRERERERE3EAJtoiIiIiIiIgbKMEWERERERERcQMl2CIiIiIiIiJuoARbRERERERExA2UYIuIiIiIiIi4gRJsERERERERETdQgi0iIiIiIiLiBpVKsDds2EBMTAzLli0r8fnffvsNi8XClClTSq0jKioKi8XicktJSalMOCIiIiIiIiK1zrMihePi4nj66aeZO3cuOTk5JZYxxvDQQw+VWdfRo0f59NNP6du3r3Nfs2bNKhKOiIiIiIiISJ1RoQT7m2++wcfHh7lz5zJ06NASy7z77rvY7XaioqJKrSc/P5+srCx69OhBx44dKxaxiIiIiIiISB1UoQR78uTJWCwW9uzZU+LzycnJTJo0iR9//JGrrrqq1HqOHj0KQFhYWEWaFxEREREREamzKpRgWyyWUp8zxjBmzBiuu+46YmNjT1tPamoqAO3atSM0NJTY2FimTJlCz549Sz0mLy+PvLw85+OMjAwACgoKKCgoqMhpiIiI1HtF3336DhSRhqzQXoiHxeO0eYg0HBX5bqur338VSrBP59lnnyUpKYnZs2eXWTYqKoqVK1fi7+/Pnj17ePrppxk8eDCbNm0iMjKyxGOmTp3KE088UWz/Tz/9hL+/f5XjFxERqY8WLlxY2yGI1DkZhRkczj9MR/+GdSnimvQ1bM3ayo2tbiz2XIG9gGOFxzhacBQvixcd/DvUQoTuMXbLWKZ0nEK4TziLUhex+Ohinur0VG2HVWVJuUmEeoXi71E8d/nPjv9wY6sbiQ6IBsBmbFixOn9YSM5LZlritAbxOpRHeb7bsrOzayCSinNL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141DQ0pCizTEczrdzNhKrhsOzSIuIiIiIiIi4gZKsEVERERERETcQAm2iIiIiIiIiBsowRYRERERERFxAyXYIiIiIiIiIm6gBFtERERERETEDZRgi4iIiIiIiLiBEmwRERERERERN2hUCfadc++s7RBERERERESkgWpUCfb+4/trOwQRERERERFpoBpVgi0iIiIiIiJSXTxrO4CakJieSEp2Cum56cQlxxHmH0ZUSFRthyUiIiIiIiINSINPsBPTE4meFk1uYS4Ase/E4uvpS8L4BCXZIiIiIiIi4jYNfoh4SnaKM7kukluYS0p2Si1FJCIiIiIiIg1Rg0+wRURERERERGqCEmwRERERERERN1CCLSIiIiIiIuIGSrBFRERERERE3EAJtoiIiIiIiIgbKMEWERERERERcYMGvw726SSmJ7os1xXmH6a1sUVERERERKRSGm2CnZyZzHkzznNZI9vX05eE8QlKskVERERERKTCGu0Q8bTcNJfkGiC3MNelR1tERERERESkvBp8gh3mH4avp6/LPl9PX5r4NqmdgERERERERKRBavBDxKNCokgYn0BKdgr3L7ifly56iTD/MPVUi4iIiIiIiFs1+AQbHEl2VEgUIb4hxITHACjBFhEREREREbdq8EPERURERERERGqCEmwRERERERERN2gUQ8TzCm18tWYfhw+dhTEGi8VS2yGJiIiIiIhIA9OgE2yb3fBN3D5e/nkH+9NygH4s3ZHC4M5n1HZoIiIiIiIi0sA0yATbAD9uTub5n7bz5+FMAHzIx46V1buPKsEWERERERERt2twCfaOea+x9UywfHEzUyxptPBNp5VHBj62TDKNL59vGQfDp9R2mCIiIiIiItLANKgEO2/3HvJWfcDlvn+6PmFz3AVachmXMQ37p9vx7H97zQcoIiIiIiIiDVbDmUU8MRGf7t0o3JDH7/u6kLnYAt/b4OIZMH4N5uE9TLZdS57xwvrnT3T9aiyXmQb1+4KIiIiIiIjUooaTYaakQHY2Z/1jEnTtCvHxMHo0eLWDsE5YgC/9evLb8bOZGfouoZk7mIM/b5t8JpJLtiYWFxERERERkSpoOD3YRbp2hZgYx/0pAgIOst205rHmr3Ko5yjsGG7HmzUE0NTUQqwiIiIiIiLSYDS8BPs0AgIOArBybxb7zrmdC8lmP3a64sE7+DmmHxcRERERERGphEaVYPv5HcHb00pqVj7JaTaWWGz8jWwKMPwDL27Gq7ZDFBERERERkXqqUSXYVqudXpEhAMQfyAcgzmLnEfIAeBVffNL31Vp8IiIiIiIiUn81qgQboHfbZgBsO5FgAzxHPospJBALbRdNAVtBbYUnIiIiIiIi9VTjS7DbNAVgW/JfCbaxwI3kcBRDwJEEWPK/2gpPRERERERE6qlGl2DHnkiw9x+zYTXBzv37LYZbyXE8WPYS7P6tNsITERERERGReqrRJdhN/L3p1DwQAB+761Je31gKSekyEjDw7e2Qc6wWIhQREREREZH6qNEl2PDXddg+9jOLPbfv3PHQrANk7Ifv76vhyERERERERKS+apQJdp+2jmHivrZuxZ6ze/nBVdPB6glbv4M9y2o4OhEREREREamPGmWC3buNowfb23TAYryLF4iIhZgbHdu/PF2DkYmIiIiIiEh91SgT7NbN/GgaYMWCF972TiUXGjARrF6w5zfY83vNBigiIiIiIiL1TqNMsC0WC11bOXquS7oOG4AmreHs0Y7tX9WLLSIiIiIiIqfXKBNsgC7hZSTYAANP9GLvXgp7l9dQZCIiIiIiIlIfNdoE+68e7K5gLCUXahL1Vy+2rsUWERERERGR02i0CXbbMzyxk4MHgXiZqNILOnuxf4W9K2ouQBEREREREalXGlWCHREU4dz2sFrIsyYAZQwTbxIFZ1/v2Na12CIiIiIiIlKKhpdgx8dDXJzj/hRvXvqmy+M861agjAQbTswo7gm7foHEle6KVERERERERBqQhpNgh4WBvz+MHg2xsY57f3/H/lIUJdi+9q6nr7tpGzjrRC+2rsUWERERERGREjScBDsqytFrvXYtDBrkuI+Pd+wvRZ41AYMNT9MSDxN6+voHPnCiF3sJJP7h5uBFRERERESkvms4CTY4kumYGAgJcdyfJrkGMJYcCix7APCxR5++7qZt4KzrHNu6FltERERERERO0bAS7EoomujM296l7MIDHwCLFXYuhqO7qzkyERERERERqU+UYFu3AeXowQZo2hbaDXZsb55VfUGJiIiIiIhIvaME+0SC7W3vCMaz7AN6XO243/gVGFONkYmIiIiIiEh90ugT7ELLAWwcx4oP3qaty3N3zr2z+AFdLwUPH0hJgEObayZIERERERERqfMafYKNBfJPXIftc8p1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fLPY7pM/syINTW18D4qIiFSn8ny31dXvPrfOvtWhQwd69+7NzTffzIwZM5g5cybbt28v87igoCA6duzI3r173RlOrerQ3PGGxydnUGizV08jXU70yP25CApKXxJNpNHYv7+2IxARERGRRszN01v/xdPT0Tlus9nKLJuWlsb27dsb1IziLZt4EOjjSW6BnZ1HsqqnkfCzIKgVFGQ5ZhQXERERERGRWuO2BPuGG25g7ty5bN68me+++45x48YxYMAAunZ1XH88ZswYpk2bBsAPP/zAiy++SFxcHIsWLeKyyy4jKCiIMWPGuCucWme1WDizlePa8E3VtR62xQLRFzu2EzSBk4iIiIiISG1yW4LdpEkT7rjjDnr37s0DDzzA5Zdfzty5c53Px8fHO4eAN2nShOnTp3PeeecxZswY2rRpw6pVq2jatKm7wqkTekSEALC5uhJsgOgTw8QT5oO9moaii4iIiIiISJkqNclZ27ZtMcZ16anXXnuN1157rdRjVq1a5dw+77zziI+Pr0zT9UpRgl1tPdgA7QaCdxBkHoQD6yAytvraEhERERERkVJV2zXYAj0j/0qw8wrLvha9Ujx9oONQx7aGiYuIiIiIiNQaJdjVqF1YAKEB3uQX2tm8vxrXaXMOE1eCLSIiIiIiUluUYFcji8VCbBvHdeVr9x6tvoY6DQOLBxzeCkd3V187IiIiIiIiUiol2NWsd1tHgr16z7Hqa8S/GbQ517GdML/62hEREREREZFSKcGuZrFtmgEQt/dYsYnh3ErDxEVERERERGqVEuxq1j0iGG9PK6lZ+exOyaq+horWw967HLKrcTi6iIiIiIiIlEgJdjXz8fSg14nZxNfsrcZh4s3aQfMzwdhgx8Lqa0dERERERERKpAS7BvRu6xgmvmZPNfcsO4eJ/1C97YiIiIiIiEgxSrBrQO8TM4lXaw82QJcTCfafi6Awr3rbEhERERERERdKsGtA0VJdu45kcTQrv/oaCj8bAltCfibs/q362hEREREREZFilGDXgCb+3nRsHgjA2ursxbZa/5rsTMPERUhMTyQuOY703HTikuNITE+s7ZBEREREpAFTgl0JEUERFT6mz4n1sKv9OuwulzjuE+ZDdS4LJlLHJaYnEj0tmth3YlmauJTYd2KJnhatJFtEREREqo0S7Ep489I3K3xM0XrY1X4ddtuB4BUA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bd2E25KCYcklPPAfxa73NfJ5Y0wMTy3awjsrdvPsN1txJh0lMbd+TxlXBltERERERETKJTKw/Gv6XdZDGJbcUq+3WS08eWk0j1/SGYsFFqyKJy3tCk8Ms9YowBYRERERkXrD398ff3//2h7GaWvmCM+u6bdYLIw7pw2vX98LP28bDsefHj1+TdMUcRERERGRBqAimcX6bPLkyWRlZdX2MKpduF84DruD7PxswOwlHeIIqd1BVaOLu0bQq2UQl73979oeSpUogy0iIiIi0gB4OrNYVzVq1IjmzZvX9jCqXVRwFNsmbmNQ1CBix8eybeI2IgIjantY1SrM37u2h1BlymCLiIiIiIjUQVHBUUQ3jqZXRK9jWxJrdTwe1UD7syvAFhERERERqaNKnJkQF1f8a310Qn92Z6qTQ+mHANibtpfWYa1rY1RVVqkp4hs2bKBXr178+uuvJ123a9cuzj//fBYsWHDKY/z5558MGjQIX19f2rRpw/z58yszFBERERERkdNDeDj4+cGYMRATY3718zO312POVCcdp3dk0NuDCPMKI+atGJypztoeVqVUKMBeu3YtV199Nf3792fdunXFrtu5cyfjxo2jR48eLF269JTHSUtLY9iwYbRu3ZpVq1Zx6623csstt7By5cqK3wMREREREZHTQVSUmbWOjT1+iYszt9eCgkJsRVWmGFtiZmJhMbc7W95Jdn42iZn1czp8haaIL1y4EB8fHxYtWsTQoUOLXffDDz9w+PBhli9fTs+ePU95nHfeeQeXy8WsWbPw9vamW7duLFq0iJkzZ9KvX7+K3wsREREREWnYbrkF3n3X/N7LCzp1gsmT4cora3NUNS8qqtYC6hMVFGIrGgyH+4XX2+DYEyoUYD/11FNYLBZ279590nV33HEHd955Z7mOs2zZMoYMGYK39/EqcUOGDOGzzz6ryHBEREREROR0MmCAGWRnZMCcOXDNNWYWt1u32h7ZaSsqOIqo4OIBvwLscrJYLJW67kS7du3i4osvLrYtKiqKvXv3lrpPTk4OOTk5hT+npaUBkJeXR15eXrnPLSIi0hAUvPbpNbDmud1uDMOo0cfeMAwAXC6Xfudy2rK53eBw4GrVytzw8svY58/HvXQp7s6da3dwUozb5cbX6nvSttL+fxXcvmAfX6vvKW8Pdff1r1aqiGdkZODn51dsm5+fX7EA+kTPPfccU6ZMOWn7999/f9KxREREThdLliyp7SGcduLj40lJSeGbb76psXMWBNgbN26s0fOK1CU99+7FNymJFceeAxaXi4uzsli/bx/79Lyocz7o9kGxn/fF7mMf+8q8/RvxbzC3y9wyb5+ZmemZgXpYrQTYPj4+5ObmFtuWnZ19ykD50Ucf5f777y/8OS0tjZYtWzJ8+HCCgoKqbawiIiJ1UV5eHkuWLGHYsGF4eXnV9nBOK19++SUZGRknzcarTgUBdrdu3Wr0vCJ1ie2zz8BiMZ8DaWlYn3gCa9u2dH/ySbo7HGUfQGrMhoMbGPT2oGLblo9dTvem3UvdZ2/aXg5nHCZpVxJn9juTVqGtTnmOghnNdU2tBNiRkZHEx8cX2xYfH0+bNm1K3cfHxwcfH5+Ttnt5eemNhYiInLb0Oljz7r33XpKSkmr0cS8IsG02m37fcvqyWuGnn7AGBkJODvj7w6xZeAUG1vbI5ARWm5Usd9ZJ2071/6t1WGtaBLUAoFVoqzL/19XV/4W1EmAPHDiQOXPm4HK5sNlsACxduvSkyuQiIiIidU337qVnYESkmvXtC3PnQmYmrFkDd90Fu3fDo4/W9shEgAr2wa6Km2++menTpwMwbtw4UlJSmDhxIn/++SdPPfUUmzZt4t57762p4YiIiIiISH3j52e25+rVC8aPhwcfhFdfre1RyQlO7I/tsDsI9wuvxRHVnBoLsOPi4tizZw9gThH/6quv+PXXX4mJieHzzz/nm2++oVWrU8+zFxERETld+fn54e/vX9vDEKlb3G4o0vpX6oaC/tix42MZFDWIbRO3ndTKq6Gq1BTxM844o3AtUElKum716tXFfj733HPZtGlTZU4vIiIiclqxWCysW7eO1q1b1/ZQRGpXVhbs3Am5ubB6Nbz8MmgWbJ1U0B872BF82gTXUEtrsEVERESkYjp06FDbQxCpfStWQPv2YLdD69bm2usHH6ztUYkUUoAtIiIiIiJ13zvvmBepVyIDI2t7CDWqxtZgi4iIiNRrt9wCFot58faGbt1g4cLaHpWISJ02c8TM2h5CjVKALSIiIlJeAwbAjh3m2s9zz4VrroGNG2t7VCIiDUaYV1htD6FKFGCLiIiIlJevL7RrBz16mK2B/P3hp59qe1QiIg3GnS3vrO0hVIkCbBEREZHKcLnMSsZNmlTveTQ1XUSk3lCALSIiIlJRaWnwj3+YVYxHjqz+82lquohIvaAq4iIiIiLltWwZOByQk2NOD581y/y5uhVMTQdzavr8+ebU9G7dqv/cIiJSbgqwRURERMqrb1+YOxcyM2HNGrjrLti92+zFW1Nqamq6iIhUmAJsERERkfLy84NOnczve/WCw4fNjHJNBdhpafDYYzU3NV1ERCpEAbaIiIhIZbndZuGx6lZbU9NFRKRCFGCLiIiIlFdWFuzcaU7RXr0aXn4Z7r23+s9bF6ami4hImRRgi4iIiJTXihXQvj3Y7eY07UcfhQcfrP7z1vbUdBERKRcF2CIiIiLl8c475qUuqKmp6SIiUiEKsEVERETqutqami4iIhWiAFtERESkrqutqekiIlIhCrBFRERE6rK6NDVdREROyVrbAxARERERERFpCBRgi4iIiIiIiHiAAmwRERERERERD1CALSIiIiIiIuIBCrBFREREREREPEABtoiIiIiIiIgHKMAWERERERER8QAF2CIiIiIiIiIeoABbRERERERExAMUYIuIiIiIiIh4gAJsEREREREREQ9QgC0iIiIiIiLiAQqwRURERERERDxAAbaIiIiIiIiIByjAFhEREREREfEABdgiIiIiIiIiHqAAW0RERERERMQD7LU9gMoyDAOAtLS0Wh6JiIhIzcvLyyMzM5O0tDS8vLxqezgiIiJVVpHXtoI4sCAurCvqbYCdnp4OQMuWLWt5JCIiIiIiIlIb0tPTCQ4Oru1hFLIYdS3kLye3283+/fsJDAzEYrHU9nBERERqVFpaGi1btiQ+Pp6goKDaHo6IiEiVVeS1zTAM0tPTad68OVZr3Vn5XG8z2FarlRYtWtT2MERERGpVUFCQAmwREWlQyvvaVpcy1wXqTqgvIiIiIiIiUo8pwBYRERERERHxAAXYIiIi9ZCPjw9PPvkkPj4+tT0UERERj2gIr231tsiZiIiIiIiISF2iDLaIiIiIiIiIByjAFhEREREREfEABdgiIiIiIiIiHqAAW0RERERERMQDFGCLiIhU0saNGxk+fDh+fn40a9aMsWPHkpSUVHj9zJkzad26Nb6+vgwZMoRdu3YVXpecnMytt95KWFgYwcHBDBs2jI0bNxZev3v3bkaMGEF4eDghISFcfvnl7Nmzp9xj+/DDD+nevTsOh4NmzZqxbNmyMvd544036NChAw6Hg06dOvHmm2+WeLvPPvuM5s2bl3ssIiJSN9Xl17Hc3FyefvppLrroopOui4+PZ8SIEfj7+9O8eXNeeumlCt3v0l7HXnvtNTp27Iivry/t2rXjjTfeqNBxQQG2iIhIpU2cOJHBgwezcuVK5syZw88//8xNN90EwCeffMKkSZOYOnUqv/76K3l5eVx22WW43W4Ann32WaxWK19//TWLFy8G4MILLyQtLQ0Ap9PJWWedxeLFi1m4cCG7du3i6quvLte4Zs+ezbhx47j99ttZs2YN8+bNo2nTpqfcZ9OmTcyYMYPnn3+e1atXM3bsWCZMmMAnn3xSeJuFCxfSr18/rr/+eg4dOlThx0tEROqWuvg65nK5eOGFF2jfvj1PPfUUeXl5J11/ySWXkJ+fzy+//MKUKVP45z//yccff1zmsct6Hfvrr7948cUXWblyJbfccgsTJkzg22+/LfO4xRgiIiJSKU6ns9jP7733nmG1Wo2jR48aPXv2NO69997C6+Li4gzA+PHHH0vcd9++fQZgLF68uMRzffzxxwZgpKSknHJMSUlJRmBgoPHOO+9U6L4kJSUZGRkZxbYNGzbMuPbaawt/vuiii4zHHnvMeO211wybzVah44uISN1TF1/H0tPTjU6dOhmzZs0ybrrpJmPo0KHFrv/iiy8MLy8v4+DBg4XbrrnmGmPQoEFl3t+Kvo5FR0cbkyZNKvN2RSmDLSIiUkktW7Ys9rPD4cDtdpOSksK6deuKTWvr1KkTERERrFy5stR9wfxkviQulwuHw4G/v/8px/Tpp58SEBDADTfcUKH7EhoaetKxHQ5HsfF8/fXXPPPMMwQEBFTo2CIiUjfVxdexgIAA4uLiGDduHBaL5aTrly1bRq9evWjSpEnhtiFDhrBq1SoMwzjlsSv6OuZyuQgLCyvXbQsowBYREfEAwzCYM2cOffv25eDBgwC0bt262G2ioqLYu3dvifvPmjULX19f+vXrV2y7y+Vi7dq1PPXUUzz00EPY7fZTjmPlypV07dqVl19+mRYtWnDGGWfw6KOPnjTFriy7d+/mhx9+YMiQIYXbSnqjIyIiDUNdeR0ry65du0ocV05ODomJiafct7yvY6mpqTzzzDOkpqYyduzYCo2vavdOREREyMvL46677mLZsmUsX76cjIwMAPz8/Irdzs/Pj5ycnJP2nz17Nv/617945ZVXCA0NLdx+++23M3fuXNxuNzfeeCOPPPJImWNJSEhg/fr1REZGsnDhQjZv3sw999yDj48PkydPLtf92b59OyNGjKBXr17ceuut5dpHRETqr7r0OlaWjIwMwsPDTxoXUOLYKmLPnj106NCB3NxcmjVrxrx58ypc1FMZbBERkSrYu3cvgwcPZtGiRfz444/07t0bHx8fwKyAWlR2dnaxNyvZ2dmMHz+eu+++mxkzZnD33XcXu/3UqVNZt24d//vf/3A6ncTExBQWj5k6dSp2u73wMnXqVADy8/MJDAxk9uzZ9OnTp7BY2bx58wCYN29esf1uu+22Yuf8/PPP6dOnD506deLbb7/F29vbsw+YiIjUKXXtdawsPj4+JY4LzED7559/LnbcoUOHlvuxaN68ORs2bOC3337jvvvuY+TIkRWuJK4MtoiISCVt376d8847j65du7Jhw4bC9WCRkZGA2Uakbdu2hbePj48vrKCalZXFBRdcQEJCAr///ju9evU66fgRERFERETQrVs3hg8fTmhoKB9++CHjx4/nrrvuKlaNteDT/CZNmmCxWLBaj3+G3qFDh8Lpfpdffjl//vln4XXBwcGF30+fPp0HHniA5557jkmTJmlKuIhIA1cXX8fKEhkZyY4dO4pti4+PJzg4mNDQUM4666xir3MnZuFPxcvLi06dOgEwYMAAsrKymDp1KnfeeWe5j6EAW0REpJKuv/56+vfvz8cff1wsoI2MjOSMM85gyZIlDB48GDDfxOzdu7fwk/QnnniC/fv3s3r16mLT6UpTEDQXFI8JDw8v8c3IgAEDmDJlCtnZ2YUFZzZv3kyHDh0AM6AuGlQX2LBhA5MmTeKTTz5h5MiRFXocRESkfqqLr2NlGThwIO+99x6pqamFr2dLly4tHJefn19hkFxVdru91KJtpe7jkTOLiIicZrZv305sbCyPPPIIu3btKnZd48aNuf/++3n00Ufp0aMHrVu3ZtKkSYwYMYKuXbsC8P7773PDDTeQnJxMcnJy4b4BAQE0a9aMJ554gvbt29O9e3dSU1N5/vnn8fX15corrzzluMaMGcPTTz/NzTffzEMPPcSGDRt48803mTNnzin3++ijj2jZsiVdunRh586dxa5r27atstkiIg1MXX0dK8tVV13F448/ztixY3nyySdZtWoVn332GcuXL6/ScePi4pg5cyZXXHEFYWFhrFixghdeeIH77ruvYgeqUFMvERERMQzDMH7++WcDKPHy2muvGW6323jiiSeM8PBwIyAgwLjhhhuMI0eOFO5vsVhK3HfUqFGGYRjG7NmzjY4dOxoOh8OIiIgwrrnmGmP79u3lGtvGjRuNgQMHGt7e3kZUVJTxyiuvlLnP2LFjS70/6enpxW779ttvqw+2iEg9V5dfxwrcfPPNJ/XBNgzD2LRpk9GvXz/D29vb6Nixo/HZZ59V6LglvY4dPHjQGDFihBEWFmb4+fkZ3bp1M2bOnGm43e4KHdtiGGU0CxMRERERERGRMqmKuIiIiIiIiIgHKMAWERERERER8QAF2CIiIiIiIiIeoABbRERERERExAMUYIuIiIiIiIh4gAJsEREREREREQ9QgC0iIiIiIiLiAQqwRURERERERDxAAbaIiIiIiIiIByjAFhEREREREfEABdgiIiIiIiIiHqAAW0RERERERMQDFGCLiIiIiIiIeMD/Awf+ZmejoRCzAAAAAElFTkSuQmCC\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA88AAAMTCAYAAACfdyWXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3RU1drH8e9MeiAJJXQMnQACYoICUgWxomJFrwgKqCBce8NrB+W9YhdFAbGAYkFsqHgFREBpJtJjAClDCSVACgmTZCbn/WObRnqdBH6ftc6ayTn77POcmRDmmd1slmVZiIiIiIiIiEih7J4OQERERERERKS6U/IsIiIiIiIiUgwlzyIiIiIiIiLFUPIsIiIiIiIiUgwlzyIiIiIiIiLFUPIsIiIiIiIiUgwlzyIiIiIiIiLFUPIsIiIipZKQkIDb7fZ0GCIiIlVKybOIiEglysjI4MSJE6XeUlNTi617zpw5LFmypMSxvPrqq+zcubM8t4Pb7ebqq6/mhhtuKFc91dFTTz3FqlWr8uxLTU3ltttuIyYmBpfLxdy5c8nMzPRQhCIi4klKnkVERCrRf//7X4KCgkq9tW7duti6Fy5cyHXXXceOHTuKLbtlyxYefPBB0tLSsvdNnjyZDh06FLpNmzYtXz2TJk1izZo1/Oc//wFgzJgx2Gy2QrcOHTqU4tUq3jfffMMnn3ySZ9/u3buLjOHU7ZlnnslX76ZNm5g0aRKWZeXZb7PZ+PDDD3E4HBw4cIDJkyfTv39/9u3bl6fc/PnzWbBgQYXeq4iIVC9KnkVEzmDLly/nmmuuISwsDB8fH4KCgoiMjGT37t0A3HbbbcUmIu+9916BdT/wwAM0btyYjIwMAGJiYrjyyisJCQkhKCiIK664gm3btuU777vvvuP8888nICCAhg0bMm7cOJKSkvKUyczMZOrUqbRv3x5/f3/atGnDyy+/nC/xSUhIYNy4cTRu3Bh/f3+6d+/ODz/8kO+aJY0NwOVy8dZbbxEUFFTs6wtw7733snfv3gI3gDfeeKPAY3/++Wd2Hdu2beOvv/7Ktz388MP4+/sza9asAo/HxcVl1/Huu+/SvXt3AgIC2L17N8eOHePgwYO0aNGCadOm5dvq169PfHx8nnv54osvmDx5Mq+99hqRkZGAScBjYmIK3X788ccSvU4l8fvvv3PTTTfRsmXLAo/Pnj27yFhiYmJo2rRpgedOmTKFK664ggsuuCDPfn9/f2w2G2lpaYSFhbFu3Trq1auX7/e+TZs23HrrrSxdurRC7lVERKohS0REzkjvvPOOBVh9+vSxZs2aZX311VfWW2+9ZV166aVWVFSUZVmWtXHjRuvHH3/Mt7399tuWzWazBg4caGVmZuarOzU11apbt671xBNPWJZlWbt27bLq1atndenSxZo7d641c+ZMq0mTJlaTJk2so0ePZp/39ddfWzabzRo6dKj15ZdfWs8995zl4+NjXXLJJXnqv//++y273W498sgj1ldffWWNGDHCAqwpU6Zkl8nIyLDOP/98q169etYbb7xhffbZZ1bPnj0tb29va8WKFdnlShrb3r17ralTp1qtW7e2AKs0/4VGR0dbTz/9dL79gDVnzpw8+2bPnm1NnTo1z+taq1at7GuWZhs5cqRlWZYVFxdnBQUF5Tk2fvx4a/z48dllTnX11Vfnifn999+3vLy8rHvvvdeyLMs6duxYie+/ImRkZFitW7e2HnzwwXzHdu3aZQHWzz//XGw9LVq0yPdebNq0yfLx8bE2btxoJSQkWAkJCXmO16pVy/rss8+stLQ0a+fOndayZcusOXPmWLNnz85T7plnnrGaNWtmOZ3O0t+giIhUe0qeRUTOUE2bNrVatGhhZWRklOo8l8tl9e7d26pXr561b9++AsvMmjXL8vLysvbu3WtZlmWNHDnSqlWrlhUXF5ddZunSpRZgPf/885ZlWVZmZqbVqlUr67zzzrPcbnd2uaeeesoCrN9++82yLMvasWOH5eXlZT3wwAN5rtm/f38rKCjIOnnypGVZJtkDrG+//Ta7zLFjx6yQkBBr8ODB2ftKEptlWda9995rBQcHW0888YQ1bNiwUiXPK1eutLy9va233347z/5Tk+cdO3ZYtWvXtoYPH56nXK1atax58+bl2bdq1Spr1qxZhb5/V199dXZiPHLkSGvAgAH5yowfP97q27evNWfOnHxbZGRkdpK5du1ay2azWY8++qhlWZb11VdfWfXr17cOHz5c4tegvGbPnm35+/tbBw8ezHesvMnz4MGDrTvuuMOyLPM+n3322darr75q3X///db1119v+fj4WLVr17ZsNpsFWMHBwVbnzp2tm2++OU89x48ft4KDg61p06aV/UZFRKTaUvIsInKGCgkJKVPy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t956Cx8fn+zEPCEhgVtuuYWBAwfmG4t+1llnZa8pfd555zF16lQGDBhArVq1ePLJJ7PL3X777QwdOpSnn36alJQUHn74YbZt28awYcPw9vbmwgsv5L333qNNmzZlei3Ko379+gDEx8eXeH3tkvr222/5+uuvCQgIoH79+tSuXZuxY8fm6T2Rnp7O/v37s9fHvvzyyxk8eDDt27fPV9+RI0eAgocCiIhIzaYxzyIiZ6i4uLh8+9LT04Gc1jMwSVVaWhpz5szJMzHSqRYuXMiePXu4++678+wPCAhg4MCBfPPNN3laeH/99Vf27NnDDTfcAEDfvn0JCQnhww8/zHP+hx9+iJeXF9deey0AQ4YMAcg3ydWHH35Iq1atssfFDhkyhF27drF8+fLsMgkJCXz77bdce+212O32EsdWVv7+/nTo0KHQDUy36sKOn9rl+uuvv2bp0qUkJSXRo0cPevfuzUcffcSFF15IRkYGH3/8cb4YvLy8aN68Oc2bN8fLy4vQ0FCaN29O3bp185Tr168f7dq148Ybb6Rbt26EhYWxdetWIiIiOOecc5g/f36R739lat68OUD2lwAVKTIyktdff51vvvmGvXv3kpycnCdxBujUqRMtWrTg/vvvp2/fvlx55ZV8+OGHREVFkZGRkaesw+EAzJcWIiJyelHLs4jIGSo8PJxhw4bRt29f6tSpg8PhYNq0afj6+jJ+/HgApk2bxo8//sjo0aP566+/+Ouvv/LV06VLF5o1a8Zbb71F37596dq1a74ykyZNom/fvlxyySU88MADJCUl8dRTT9GjR4/sLsv+/v48++yz3HfffQwfPpzrr7+ejRs38vzzz3Pfffdlt3j26tWLoUOHMnny5OyJr+bPn8+vv/7K119/nT2mefz48bzzzjsMGzaMZ555hrp16/LKK69gt9t59tlnSxVbddK/f3/69+/Pxo0bufLKK7O7yD/88MMFjpkuzJo1a/L8bLPZeO211xg0aBBXXHEFc+fOzXPc29tzHxm6dOlCvXr1+OWXX+jXr1+F1t28eXPuuecewPSe2LBhA7///ju33347/v7+AOzbt4/69evz9ddf8/LLL/PAAw8AcO211zJx4sQ8Xxj98ssv1KtXL3v2dxEROY14ao0sERHxrIceesg699xzrXr16ln+/v5W69atrREjRljr16+3LMuyjh49agUEBFhAkdv7779vbdu2zbLZbNann35a6PWWLl1q9ejRw/Lz87MaNGhg3XXXXdbx48fzlZs2bZrVtm1by8fHx2rdurX14osvWm63O0+ZlJQU695777UaNmxo+fn5WZGRkda3336br65du3ZZQ4cOtYKCgqzatWtbl19+eb71oUsTW5ayrPNcEHKt81xSf//9tzV58mSrRYsWlq+vr/XQQw9Z48ePt/z9/a26detajz/+uHXw4MECz23atKn18ssvW/369bMGDhxojR8/3ho5cqS1bNkyq0+fPtbjjz9uffbZZ5avr691ww03WIcPH85XR4sWLawpU6YUGeM555xT4etA33rrrVZERESBx7LWeZ49e7YVExNT5Na0adM86zyvXbvWeuaZZ6yLL77YCg4Otry9va3zzz/fSk9Ptw4fPmw9+uijlr+/v3X++edbwcHB2euNnzhxwrLZbNbKlSvzxNKjRw9r+PDhFXrvIiJSPSh5FhGRcrvvvvusxo0bW+np6Z4OpUYpSfKcmppqvfnmm9bo0aOttm3bWoAVEhJi3XvvvdbOnTuzy8XFxVkPPfSQFRgYaPn5+Vl33HGHlZKSkn08NjbWstlslt1ut8aPH28lJCRYAwcOtAIDAy0fHx/rrrvushwOh2VZlrVs2bLsLyZGjBhhZWRkZNdTUPL8xx9/WK+//rr1/vvvW1OnTrW8vLysZ555piJeomwbN260bDab9fPPP+c7lpU8l3TLnTxPmjTJ6tWrl/XYY49ZixYtspKTk62VK1daN910k+Xr62u1adPGmjdvnpWZmWnNmzfPstls1oMPPmhNnDjRCgoKstLS0rLr+uWXXyybzWZt2LChQu9dRESqB3XbFhGRcklNTeWDDz7g3//+d76ZjKX8AgIC+Pzzz0lOTubiiy/m9ddf56KLLso3y3Pjxo2ZOnUqDz30EP/3f//H7t2783Tjrl27Nl27dmXatGn06dMHt9tNSkoKgwYN4uWXX86z5FL//v3ZuXMnb731Funp6cV22Y6Li+P+++8nMzMTLy8vIiMjueOOOyr0dejSpQu33347EydOpH///gX+rv38889cdNFFRdbTsmXLPD8/8cQTPPHEE3n2RUdHc/DgQT799FOuvvrq7KW5brrpJoKDg3n88cf5+++/efHFF7PfB5fLxcSJExkxYkSBQxdERKTms1mWZXk6CBERqbl27NjB3Llzueuuu4pdU1g8y7Ks7DHhAImJiflm9K7OUlNT6dGjB5dddhkvvviip8PJ4/HHH2fBggX88ccfha6DLiIiNZuSZxEREREREZFiaKkqERERERERkWIoeRYREREREREphpJnERERERERkWLU2Nm2MzMzOXDgAEFBQXkmPxEREREREZHTm2VZJCcn07Rp0+xVESpbjU2eDxw4wFlnneXpMERERERERMRD9u7dS/PmzavkWjU2eQ4KCgLMixUcHOzhaERERERERKSqJCUlcdZZZ2XnhVWhxibPWV21g4ODlTyLiIiIiIicgapyCK8mDBMREREREREphpJnERERERERkWIoeRYREREREREphpJnERERERERkWIoeRYREREREREphpJnERERERERkWIoeRYREREREREphpJnERERERERkWIoeRYREREREREphpJnERERERERkWKUKXnesGEDERERrFy5Mnvfnj17uO666wgODqZ+/fpce+217Nmzp9A6Nm/eTL9+/QgICKB169bMmTOnLKGIiIiIiIiIVLpSJc/R0dHceOON9OrViz///DPPsUcffZTWrVuzbNky5s+fz+7du7nyyivJzMzMV09SUhKDBw+mVatWrFmzhlGjRnHbbbexevXq8t2NiIiIiIiISCXwLk3hBQsW4Ofnx8KFCxk0aFCeY1OnTuWss87K/nnatGn07t2b7du3Ex4enqfsBx98gNvtZubMmfj6+tK1a1cWLlzI9OnT6dmzZzluR0RERERERKTilSp5njRpEjabjd27d+c7ljtxBvD39wfA7XbnK/vLL78wcOBAfH19s/cNHDiQL7/8sjThiIiIiIiIiFSJUnXbttlsJS47c+ZMmjdvTvv27fMd27lzJ61atcqzLywsjH379hVaX1paGklJSXk2ERERERERkapQ4bNtW5bFs88+y4wZM3j77bfx9s7fuH3ixAkCAwPz7AsMDCQtLa3QeqdMmUJISEj2dmpLt4iIiIiIiEhlqdDkOSEhgaFDh/Lyyy/z9ddfc+WVVxZYzs/Pj/T09Dz7nE5nvoQ6t4kTJ5KYmJi97d27tyJDFxERERERESlUqcY8F+XIkSMMGDCAwMBA/vzzT9q0aVNo2WbNmuVLfvfu3Uvr1q0LPcfPzw8/P7+KCldERERERESkxCqs5XncuHGEhISwYsWKIhNn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Jw/zhIEDTQv6DTeYFuznnjOJ6tSp5r1r2tRMGrd0ac7v86+/mgR6xw7ze7tsmalryBDTWu/nZxJ8b2/T6p2SYuYMmDXLo7dakTyRD6rlWURERORM4nbndGN/7bWStzrXZAV1X09JMWPn3W5o1870TqjpsmaJP+88kzhdcokZb/vllzmzVNfUxDnLnDkmWV63znw5ADnzDLz9tkkiH37YoyGWitttYu/Vy8yU/s47Zn3shQtNS3dysmmV/uIL0xr9+utwzjk5EyPabHl7mHh5meWfzjkn73WqYsKwM4Banj1ILc8iIiLiEU5n4UvdyOkj9yRSp4us1OV0uq+MDNNCXNA9paefGV9wlYFankVERESk8ilxPjOcTglmltPxnoqaWVqJc7WipapEREREREREiqHkWURERERERKQYSp5FREREREREiqHkWURERERERKQYSp5FREREREREiqHkWURERERERKQYSp5FREREREREiqHkWURERERERKQYSp5FREREREREiqHkWURERERERKQYSp5FREREREREiqHkWURERERERKQYSp5FREREREREiqHkWURERERERKQYSp49wJHoIDoumkRnIo5Eh6fDERERERERkWJ4ezqAM40j0UH4tHCcLicA4dPCiZ0QS1hImIcjExERERERkcKo5bmKxafGZyfOAE6Xk/jUeA9GJCIiIiIiIsVR8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsVQ8iwiIiIiIiJSDCXPIiIiIiIiIsU4o5LncQvHeToEERERERERqYHOqOR5f/J+T4cgIiIiIiIiNdAZlTyLiIiIiIiIlIWSZxEREREREZFiKHmWSnXiBDz5JLRrB/7+UK8ejB7t6ahERERERERKx9vTAcjp7cYbweGAV1+Fli3N8zVrPB2ViIiIiIhI6Sh5lkpz9Cj8+CN89x0MGWL2de4Ml1/u2bhERERERERKS922pdIEBIC3N6xd6+lIREREREREykfJs1SawEC4916YNAluugl27fJ0RCIiIiIiImVzWibP4xaO83QI8o+XXoLp02HxYujY0Uwe5nZ7OioREREREZHSOS2T5/3J+z0dguQydizs3AkPPABTpsDtt3s6IhERERERkdI5LZNnqX6Cg+GFF+CNN2DOHNi929MRiYiIiIiIlJySZ6k0mZn5911wgXnMyKjaWERERERERMpDybNUGocDBg+G+fNh61b49VfTdTsiAtq29XR0IiIiIiIiJXdarfPsSHQQnxpPojOR6LhoQgNDCQsJ83RYZ6x69Ux37fvugyNHoHFjuPRSePZZsNk8HZ2IiIiIiEjJnTbJsyPRQfi0cJwuJwCRMyLx9/YndkKsEmgPCQ6GL7/0dBQiIiIiIiLld9p0245Pjc9OnLM4XU7iU+M9FJGIiIiIiIicLk6b5FlERERERESksih5FhERERERESmGkmcRERERERGRYih5FhERERERESmGkmcRERERERGRYih5FhERERERESmGkmepdA6HgyNHjng6DBERERERkTJT8iyVbtSoUTzzzDOeDkNERERERKTMlDxLpTt58iSpqameDkNERERERKTMlDyLiIiIiIiIFEPJs4iIiIiIiEgxlDyLiIiIiIiIFEPJs4iIiIiIiEgxlDyLiIiIiIiIFEPJs4iIiIiIiEgxlDyLiIiIiIiIFEPJ85lm3DhPRyAiIiIiIlLjeHs6gKrgSHQQnxpPojOR6LhoQgNDCQsJ83RYnrF/v6cjEBERERERqXFO++Q57kQcvWf3xulyAhA5IxJ/b39iJ8RmJ9DjFo5j+pDpngxTREREREREqrHTLnl+wx1JOzJIx0Ua6TRZ9CbT0oNIs3mzh3TeIZ0kl5P41Pjs5Hl/slpjRUREREREpHCnTfIcGhiKv7c/F7hPEmnfl3Pg6GEibGCGd/vzIP48YZ2ETLeHIhUREREREZGa5rRJnsNCwoidEEvsrwt4buv/6FCnMxnpmZxIOUniiST8bF5cYV9DW/sBZhBA6oK7YMjr0Kqvp0MXERERERGRau60SZ7BJNBhV93HmylLeermFwGIjosmckYkQRlDeTP9/7jJ+yMe9P6Zukf/hg+HcLxVXwLT4s+MicQcDkhMhOhoCA2FsNP4XkVERERERCrQab9UVVZ37lSv33DhzRzXSM6xB3Cw4xDcWNTdtYLZB7byzrs9CX8zHEeiw9MhVw6HAzp2hOXLITLSPHecpvcqIiIiIiJSwU775DmrO/fasYvw9Y/DhhcP91vEgX4P0o0UfsFFIDZmEMBHLhvHj+/ydMiVIz4eUlNh7lyzpaaafSIiIiIiIlKs0z55BpNARzSJoF7dPQCs2ZEOwGZbJoNI5WGcZGBxAz50mH8HONZ4MtzK1bGj2URERERERKTEzojkOUtIiGlVXrXzKMknMwGwbPCSLZ0LSOFvMvE7cQjevwyWT9WM3CIiIiIiIgKcYcmzn18SnZoE4860WLvTmefYH7ZMzuUEx9oOAssNSyfD3GshPcVD0YqIiIiIiEh1cUYlzwCXdW4MwKodznzHkm2we+B/YOg74FMLdi6DeTdBxskqjlJERERERESqkzMqeW4W1IzLujQBYKMjDZtVK38hmw263QwjvgHf2rBrOXx2K7jSqjhaERERERERqS7OqOR5+pDptG1Ym/aNauPKhED3+YUXPus8+Nfn4B0AO36G+aPAnVF1wYqIiIiIiEi1UabkecOGDURERLBy5cp8x3bu3MlFF13E3Llzi6wjLCwMm82WZ4uvoqWTLu1sWp8D3RcUXbBlb7j5E/Dyg78Wwldjyz2JWNa601n8vf0JDQwtV50iIiIiIiJSuUqVPEdHR3PjjTfSq1cv/vzzzzzHduzYwZgxY+jWrRtLliwptq5jx47x8ccfs3379uytXr16pYu+jC7vYsY9B2RGYrMCii7cZiDc+BHYvWHzfPj2HsjMLPO1s9adjrozin5h/YidEEtYSFiZ6xMREREREZHKV6rkecGCBfj5+bFw4cJ8xxYvXsyRI0dYvnx5sfWkp6eTkpJCly5daNu2bfZmt1dNL/LwRkE0reOFDV8C3OeV4IRL4frZYLPD+rmw6LFyXT9r3ekQ/xAlziIiIiIiIjVAqbLVSZMmMWfOHFq3bp3v2F133cU333xDt27diq3n2LFjAISGeqa7ss1mo2db03W62K7bWTpdDde8C9hg7buweUHlBegB4xaO83QIIiIiIiIi1VapkmebzVamY6c6evQoAK1ataJZs2ZcddVVbNy4schz0tLSSEpKyrOVR692prt2QGZ3bJZfyU7qeiP0e8g8X3gfJO4rVwzVgSPRQXRcNFuPbCU6LhpHosPTIYmIiIiIiFQ7HpltOywsjNWrV7Nu3Treeecdjh49Sv/+/dm3r/BkdMqUKYSEhGRvZ511VrliaN3AG5ftIHb88c+MKPmJ/R+FZpHgTPxnArGyj3/2NEdqHOHTwomcEclyx3IiZ0QSPi1cCbSIiIiIiMgpPJI8BwUF0aNHD7p06cKVV17JokWLAJg3b16h50ycOJHExMTsbe/eveWKwWazkeL1GwC13L1LfqKXD1w7E3xqwe4VsOrNcsXhSfHpCThdzjz7nC4n8alVM+u5iIiIiIhITVEt1nkOCgqibdu27Nmzp9Ayfn5+BAcH59nKK/Wf5DnAfT42y7+Y0rnUbwOXTjHPl0yCuA1lun6zoGYF7tf4YxERERERkeqlWiTPCQkJbNu2jbZt21bpddNt28iwxWEnkLoZo0t3csQI6DAEMjPgyzsg42Sprz99yPQC9+9P3l/qukRERERERKTyVFnyPHLkSKZNmwbA999/zyuvvEJ0dDRLlizhyiuvJCgoiJEjR1ZVOIYNjvmYmILclxHgPr8U59rgyjegdiOIj4Wfn6qkIGumefPmcdlll5GZa0z4iRMn6N69O6tWrfJgZCIiIiIiIqVXZclzTExMdrfsOnXqMHPmTHr37s3IkSNp0aIFa9eupW7dulUVTjan1wYSvc2yU/XT7yEhxV3yk2vVh6Fvm+drZ8D2n8sVS9bM14nOxBo/aVezZs1YtGgR3377bfa+t956i40bN9KsWcHd1UVERERERKor77Kc1LJlSyzLKvR4QcfWrl2b/bx3797ExMSU5dKVIsH7IwLc5+JrtWLa4kQubGOVfOmtthdBj7Gw5h34ehzctQKCm5Q6Bkeig/Bp4dkTeIVPCyd2QixhIWGlrqs66NevHxdeeCHPPvssAQEBZGRkMHXqVEaPHk1YWM28JxEREREROXNVizHPHmdzEe/7EhbpRO9OY+6aUrb6XvQMNOoMKUdg/ihwu0odQnxqfJ6Zr0+HWa+ffvpp1q9fz7Fjx/jrr79ISkpi4sSJng5LRERERESk1JQ8/yPDvofjPh8A8Pz3W9lx+ETJT/YJgBs/At8gcPwOS5+rnCBrmP79+3PhhReyd+9eNm/erFZnERERERGpsU7L5LmwJaCKk+z1Hd3CfHFmZHLfZ3+S7sos/qQs9dvA1WbyMX57Hf76oUwxVDvjyrds1tNPP01qaioZGRlqdRYRERERkRrrtEyeC1sCqlg2iwmD61An0IfN+5N4bfG20p1/9lDo8U+y+fVYOL67bHFUJ/vLt2xW//79ad++PRdddJFanUVEREREpMY6LZPn8qhX24v/u7YLAG8v28HeY6mlq2Dwc9D8PHAmwucjIMNZ/DmnudjYWH766SdPhyEiIiIiIlJmSp4LcGnnJvRpGwrYmLe2lJOHefvC9e9DQF2I2wA/qauyiIiIiIhITafkuRDDe5ouxp+t20uaqxRrPwPUOQuunQXY4I/ZsHYmFLG0V000bmH5xkKLiIiIiIjUJEqeC3FRx0Z4e6dwNCWdRZsPlr6CdhdBv4fM8x8eghkDYMeSmpNEOxwQHQ2JiebRkbcFfn9y+cZCi4iIiIiI1CRKngvh7WWnXv0YAD5eXcqu21kGTDSbTy2IWw9zr4UPhoBjTcUFWgl89sVBx44QGQnLl5vHjh3zJdAiIiIiIiJnCiXPRahfPwYvu421u48RezC59BXYvWDAY3DvBug5Hrz8YM9KmH0xfDIMDm6q+KArgPexBEhNhblzISrKPKamQny8p0MTERERERHxCCXPRfDxSWVwx0YAfLxmT/b+Uo/3rd0ALn0B7omGiBFg84Jti+CdPjB/NBz9uyLDrjgdO0JEhHkUERERERE5gyl5Lsbwni0AWBC9n5Q0F1CO8b4hzeGqN2H8Wuh8ndm3eT5MO4+wX1+iuWWriJBFRERERESkgil5LsYFberTKrQWJ9JcfLP+QMVUGtoWrp8Nd62AdpeA5Sb0r+/ZTm1etvyoW0PmFBMRERERETlTKHkuht1u45YeZtmqOav3YFXkbNlNusItn8Oon0hu0hV/bDyAH6uoRZhaoUVERERERKoNJc8lcH1kc/y87cTEJRHtSKj4C4T1ZPuVr3ExKewhk3C8+J1a+B/dWfHXKidHooPouGgSnYlEx0XjSNQM3CIiIiIicvpT8lwCdQJ9GdK1KQAfr95TTOkystn42ebmAlLYhJtm2Gn/7b2wZ1XlXK8MHKlxhE8LJ3JGJMsdy4mcEUn4tPCSJdDjSjnJWimVehI3ERERERGRUlDyXELDe5qu2ws3xeFy+VXadQ7YLPqRwkpceKefgDlD4a8fKu16hXHZ7Ly19QQPfLaeuFQ3APHpCThdzjzlnC4n8aklWMJqfxknWSuhMk/iJiIiIiIiUgJKnkuo21l1OLtpMOmuTI4fC6/UayXYYDCpJLS4AFxO+OwWiP6owq8T6lsHf2//PPv8vf1x2+owfNhkpm5OZsGf+7nsf0f4X9seFX59ERERERGRmkLJcwEKGtdrs9myl606erRTxU4cVgCnDXZe/BycOxysTPj237Dq7Qq9RlhgE2InxBJ1ZxT9wvoRdWcUn10Vzajf7axu0ZVAbxvhjYJISLe487onmRkTAJZPhcYgIiIiIiJSEyh5PkXcicLH9V7drSkBPl6kp4eweX9S5Qdj94KrpkHv+8zPP02EqA8r9BJhIWFENIkg2C+EddtCuH/eLg47M2l/ZA/fXhTKd//uw5j2tQD4ca8fTdJewTuzeYXGICIiIiIiUt0peT5FgrPwcb2Bvt4MCG8AwKItcVUTkM0GFz0DF9xjfv7uXtj8ZYVeIjE1gz27L2bKj3/hzrQYGubP13MeoG2wN77edp7oFsz7XzxNsE8mvlYrmqS9Ri3XRRUag4iIiIiISHWm5PkUdfwLHgccGhgKwKWdGwPw4+aDld51O5vNBoOfg8jbAQsW3Anbfip3tVuOZ/D4V5u44P+WkJTUCl8vO89f05lXe9QhMCMtT9kLd0bxygXJnLT/iR1/QjPuI9DVr9wxiIiIiIiI1ARKnk/RpHb+ccCxE2IJCzGzbQ/s0BCbzc3OIynsOHyi6gKz2eCKl6HLDZDpgs9HwK4Vpa7G6bKYvzuVobe+xBU/x/PJGgcp6W78/I4xf1wvbunRApvNVuC59fwsDvs+RZLXNwDUz7gH38w25botERERERGRmkDJcwGyxgGH+IcQ0SQiO3EGCPL3oXbtfQAs2nywTPWnudy8/L9Yvv5zP5mZpWi9tnvB0OnQ/jIzC/e8m2BfVIlP/6V1d87/7hAPrU1kfdMO+NhhSNcmzLujJ+3Dv6Br8zpFnu+dnkR3bAzw+YDB3h/Tzbaf9un3kpDiKvk9iIiIiIiI1EDeng6gJgoJ2UVycgsWbTnIvwe1K/X5n67dy5tLdwDw/u+7eebKTlDSSay9fOCGD+CTG2DXcph7LfzrMwjrmbfcuHEwfXqeXS9ceDtJGRbNa3lx8w+zufHlh2lwQQQAzWOa5RTMdEMLL9gyHTbGweG/4T9BdF39GOuobcp4f282IHWuP5kNW2Ov2xJCmkPtRhDUGGo3hqBG5pEq6uIuIiIiIiJSCZQ8l4Ij0UF8ajz4bcJuG8CWA0nsPZbKWfUCS1XPD5tyJhvbsDeBa97+nf4dAvCy6uO2HS2+Ah9/uGkefHQ17P8DZl8KPe6CgU+C3z/J7f79eU45dNLN9tAW2LD4LuwYdVd/Af6PZR+fftH/mYnIYhfBXz/CbbXg789zKvC2YWFjP272kokbaGX50MxmEYgTDm81W2G6ept4m0VC0wj2BzfmkD2n40NoYGieFv6SynpPEp2JOBIdZapDRERERESkOEqeS8iR6CB8Wnj2TNyNbP3wt85h3h8xPHJxZInrOZKcxtrdxwD46u4L+GSNgy+i9vHrXydpyjsken9BkvcCsBXTFdqvNty6ABZNhPUfw5p3IPYHuPJ1aDMwX/HfUn0B6By3g7pT74eWgXBsOSycC3HrIW6DGUudJTUT2l8C3a+DI2lwwyj+nP8Bkb+NzCljgzruCCIy7uIs2xHu7ubN+fWccOIQJB/MeUyNB28X7FxmNsC0c2fyCy6+xMUyb282/Du2VMnvqe9J+LTwPOPTRUREREREKoqS5xKKT43Ps4RVqtcq/DPPYUnMUR65uOT1/LTlIJYF55xVh3PD6nJuWF2G92zBIwvWEhsHdV0j8LZCOeb7dvGV+YfA0Leh87Xw3X2Q4IA518C5w8HLBa50OL4L4rfju305L3r/RZ8mf8HT9SEzA1Y+nbe+0PbQ/lKwtYVLboM/HoduERAdDYmWGXN9igSvaHp178Dc35uxcr2N1286l0Zt/UhJd5OS5iIl7jDOY0c578updLi5Jzh3kxq/Hr9jf9MMO8PxZTi+nHBZZHwzASJug3YX57SgF+HU9yRrSTElzyIiIiIiUtGUPJdRqtcq6mWMJTYug8NJThoG+xd/EvDjZtNl+/J/lrwCk0i/cEN9Bkz/D/Uz/k0t90COWx+UPJi2F8Hdq2DJc7B2Bvw5Fzrb4fnGYLkBGALm3fYGMgG/EGjSFZp2gybdTHfqeq1MfdHRpRqifE33WiSmBPHdhgOM/yS64EJtJjDqra95ZN1X/LXsY/p+O4Tz8OIqvLkOH1pgh52/ms3bH1r1gzaDoO0gqN/WzDYuIiIiIiLiIUqey8htO0qa7S/8rA78tPUQt/ZsUew5R0+ksXqn6bJ9WecmeY7ZbDZOeP1EkOsqfK0W1HL3LV1AfkFw+VQ4+1r4dgIc3WESYN/aOENas+hgbfbQjHGOrfi+MgfqtgJ7MZOtx8TkfSyEzWbjxeu6kngyg60HEqnl500tX29qZZwkcO0qMruew4oTPsw+byi/tziHsfuSSLXBr7j5FTcPWmlEYmdhtztp7FgNx3bC9v+ZDSAkzCTRbQeZhNq3dGPMRUREREREykvJczmkev2On6sDizbHFZ08/zPz9f+2HsKdadG5WTBh9QtIAG1wwmsx9Vyjqe0aXLagWvSCcb/D8EthxscQ1ITPVu3h6W+3cEGb+twb/yHUL2Zt5tBQCAyE4cNz9gUGEtq4Nf7e/nm6Svt7+xMaGEqArxcfjTo/bz3R0fDwM/BYFEsDm/HwvGj+atiKR1ZbBHkNIdlrIdjMfUeRyYEed9D46rfhcAzsWGw2xypIdEDU+2bL9IGet0Pk7WV7fURERERERMpA6zyXQ6rX7wCs3nmM4ynphRf8Z+brrFm2T211zi3F+xcs3PhZHdh7NKNsgR04BHGZsOMg7N3Lyh3xAPRuG5pv+aoChYWZ1uaoKOjXzzzGxBDWqRexE2KJujOKfmH9iLozqsQTdA3s0IhFl4Qy4O8/SM+0US9jLA3Sn8Zu1clb0GaDRp2g9z0w8lt4dDf863M4/y6oEwb2DNM1fXov2n89gRGWD/5aBUtERERERCrZGZs8hwaG4u+dd5xyVitqSbnsB2kZ6o0702JxzCHGLRxXaNnjKen8/rdZhuqyXOOdT5VpS+CkfS0AS7eeLHEs2RwO6NgRli+HyEhcnc5m9Y4jAPRpW/J7IywMIiIgJMQ8hpkEOSwkjIgmEXRq0ImIJhH5EueiXoMG/l68P/8ZRoenYpFOYOZ5NHG+jm9m/pbw7Hp8a5lZvy9/Ee7ZANu7QMerwO5N7UNb+JAADhDEa5YfHa3q8etc1GsgIiIiIiI1U/XINjwgLCQsuxU1ayvLMkc925oE/KctB9mfvL/Qcj/HmC7bHRoH0bpB0TNJn/D+GYBlMSfJcGeWKh7i4yE1FebOhblz2RTclOT0TIL9vencLKR0dRVh+pCCW7CLeg3A9NK+okU6cX73k2Hbizf1aZT2XwLcPYqvx26H5HowbA7cv4X9541mF5nUxca9+LGV2rT/5h7Y8BlkOPOfX0WKew1ERERERKTmOWOTZ8hpRc3ayrLEUY82Jnlevj0et9un0HI//tNl+4ouhXfZznLSHoWb4ySezGRZ7JFSxwSY1ueOHfmtZTcALmgTipe94Bmr161bx4MPPli265RRhn0PcX4PcdIejR1/GqT/h6+jTmBZJeyDHdSYQxHDacsJLiWFr8jAhUXtg5vgqzvhlQ6w6HEz+ZiIiIiIiEg5ndHJc3GaBTUrtkxYfW9ahdYi3ZVJclLByXei3Td73PFlhSTPebqR29w4fX4F4PM/9pYh8hwrW3QDoHe7wrtsjxgxgldeeYU///yzXNcqLcuWwmHfZ0n2+gEbdj5amczEBZtK1dqeaYOfbG6utZ2kBSc40P12CG4OJ4/D6rfgjQiY9y/YtQJKmpiLiIiIiIicQslzEQrrmpybzWbj0n/GMCcmti6wzJKgFmS4Ldo3qk3bhgV32c7djbxfWD++vO1hAJb+dZjDyWXrgnzSZRHdrCNQ+HjntWvX8tdffwHw7LPPluk6xYqJMTNvF7Tklc3NMZ+3OeYzA7sNPl23l5Gz1+I8WZfDyU7SXSVPpA/YLA5GjoD7NppJxtoOBiyI/R4+HALv9oX188CVVnH3JiIiIiIiZwQlzxXg8n9mz05MbM3z32/FnZm3hfOHYJNUFzXLNuR0Iw/xD6Ffm/acG1YHd6bF13+WbQztuvh00r19aBpop2VBS2NhEuaGDRsSEBDAN998k7/1uVnxre+Fyr3kVWSkeQwMhDp18pazQbL3tzx2ZV1q+Xrx+99H2bbtRs5/fgntn/iRTk8toteUJVz++go+rdOh+OvavcwkY8Pnw/h10H0UeAfAwU3w9Vh4rQusnQluV9nvTUREREREzihKnitASFAiN/U0LcozV+xi+HsrSHaaZaaSnRksr3UWAJeXYLxzbjd2N+d98ce+ko8FzuW3Q6aFtXdDP2y2/OOd165dyw8//MBFF12Ej48P7dq1y9/6XJKlrQpTyJJXNCn4dejeyp9pt7aifWMf7PaTZEWcmu4mLtHJ1rgkHmvan5e+WFvy16NBexjyKjywFQY9DUFN4cQh+OEhmH4BbP9Z3blFRERERKRYSp7LKe5EHOHTwvnvhgEc8fkvmaSx6u9krpr2K3uPpbL0r8Ok271o3aAW7RsVPcv2qYZ0bYK/j53th0+wfm9CqWNb+U/y3KeRX4HHn332WTp06MA555yDzWbjySefLLj1uTwKWfKqIHEn4rj88278nHgJu/xuYLf/lRyuPZLPrw7h288e457f5gEwLeoIj89Zla+Fv0iB9aDvA6ZL9xUvQ2B9iI+Fj6+HudfCoa3lvVMRERERETmNKXkupwRnAk6XGZOc6r2CQ36P4eIou+LTuPqt35i5wsz2fHnnJgW2/hYlyN8nu0v453/sK9W5x9Iy2ZJguiVf0Mg33/F9+/bxww8/8NRTT2G3m1+Dm2++mXbt2jFjxoxSXQvAkeggOi6aRGci0XHROBIdpa4j92sJgM3ipPsoQUl76Lp7Mw8MaMULXjuxZ7qZt/U4L/+QAFbhM5wXyMsHzhsD/46GC/4Ndh/4eym80xu+uw+cSaWOW0RERERETn9KnitYun07B/0foHVDb46lpLN5v0nGSttlO8sN/3Td/m7DAU6mu0t83u+HTatzh8O7aODvle9448aNWbx4McOGDcve5+3tzY8//sj9999fqhgdiQ7Cp4UTOSOS5Y7lRM6IJHxaeKEJdJ6Zxf/h7+1PHf86BZZ31atjxkpPnsy/XriHtxa9hq/dxuq/nTRKfxabVfB47iIF1IGLJ8OEtdDxKrAyIep9mHURHP279PWJiIiIiMhpTclzJXDbjvL89aFc3sXMwt0m7TgdmwSVqa4ererRtI4vJ9JcTF+5qsQtur8dSgeg954NBR739vZm0KBB2a3OWdq0aUP79u1LFWN8anzeFmPA6XISnxpfYPlTZxaPujOK2AmxNKld8BcMGc2b5Bk7fdnCD/hg9PkE+Nrwz+xK47QpeGUWvhRXkeq1hmFz4LbvzXjo+FiYORB2/lq2+kRERERE5LSk5LmS+PnYmHZzBO8Mj2TW3kWl7rKdZV/yXralfgDAyz9vIfzNs0uUQGdNFtZn9/oyXbc0CmtJDg3MldCeMmt37pnFI5pEEBZS+Fhoc0KusdNhYVzQJpTnrquPmwR8rTY0S5tJvfS7OZxUxhm0W/aBO3+BZpHgTIA515gZuTWZmIiIiIiIoOS5UtntZg3oVullH0cbnxrPMftXuDmOj9Ucn7RBhbboZnGENMKR4sbbBufv3Vzma5dUYS3JeRLi8szaXYg2DX046PcwTvtmbPgQ5L6c8R8e4aEvNrArPqX0FQY1htt+gK7DwHKbGbkX3g/ujAqPXUREREREahYlzzWAZTtJgs8cAOpk3EzSycwiy//WshsA59b3oVaGs8iyFaXULckVxGWP45DfYxz0fZST9mjcmTA/ah+DXl7GPfP+LH0S7eMP17wLFz0L2Mw46I+uhsMxlRK/iIiIiIjUDEqea4gTXotJt/2NndrMW5VcaLnE9ExmnTcUgN6FLFF1Okrz2sJhv6f4v2H1uahjQzIt+HbDAS5+9VcmL9xK4slStB7bbNDnPvjXZ+AbBHt+g7d7wmfDIa7gMeQlMW7huDKfKyIiIiIinqXkuaawZXLMZyYAP29O5a+D+buCu9yZTFh1nL/rn0XjADu3tCnDLNQ1XPvGvswaeR7f39OHAeENyHBbzFq5iwtfWsbc1XtwuYtutc9b2SVw5zIzGzdAzHfwbj/4+EbY90epY9ufvL/U54iIiIiISPWg5LmESru8UmVI89pMiv03Mi2YtHAr1imTWU1auJUVh9IJSHcyq0+9Apeoqm6aBeVMJFaWiccKc3bTED64/Xw+uP082jaszbGUdJ74ejND3lzJbzuKHjOeR2hbMxv33auhyw1gs8P2n2DWIHi8PUR9ACeOlLw+ERERERGpkZQ8l1Bpl1eqLAk+s/H2gt92HGVxzOHs/XNW7ebDVXsAeHXhy3Su61OlcZXV9CE5E4lVxsRjA8Ib8uO9fXn2qrOpE+jDXweTuWXWGt5etqN0gTbsCNfNggl/QLuhYPMC30Pw3b3wcnt4/3KO//I8m7f9QKIzkei46BIvKyYiIiIiItWfkudSyJoUq1ODTlU6KVZuLvshrjq3FgDPf7+VNJebFduP8Mx3WwF4uEsQl25fVeVxVZQKn3hs3Dh8vOyMvKAlyx4awIheLQB4cVEs86P2lb6+FB+4cz68mgBLnXDADVYm7PmNur++SOdPbublPVHMe7cXF70ZXr4EepzGSIuIiEj1oflb5Eyn5LkMcreWFsnhgOhoSEw0j46KaYm87rzaNAjyY/fRVCYt3MrdH0fjzrS49txm3N2hVoVco8bLeu23bs1+3esE+vLc1Z0Z278NAI99uZHl20rZ5To+HlJT4a058Noq6P8uvJbM3tbX8ysuMrHojhdT8Weby5d6n9wEa96F5EOlv4f9GiMtIiIi1Yfmb5EznZLnSuKzLw46doTISFi+3Dx27FghCXSAr52HLwkHYO5qB8lOF5Et6jLlui7YbLZy118euccwe4zDkfe1P+V13+16naHdmuLKtBg3N4rN+xNLf42OHSEiwjwmWhxpPpABtlQac4KxnGTZP4l07UNb4MdH4JUOTHash9XvQIK6c4uIiIiI1DRKniuJ97EE00I5dy5ERZnH1FTTclkBro9oTudmwQA0qxPAu7dG4uft+QnCStwqX5myWoefeMJsuV53R6KDmPit3Nwnk8iWtUhJd3Pb++vYeyy1Qi59xGbxri2DC22pnMUJ9vYaT1qTrmBl0vVkIix6FF7rAu/0hWX/hYOb4ZSJ30REREREpPrx9nQAp72sFsoKZrfbeG1YN277eC4fDr+V0NpnzprOWbJm53a6nECumblDgcBAmDzZFAwMhNBQHIkOwqeF43Q56fledwK86nJBwy/YcdjJyNlradVmAe9d80bZYvGtkycWgGM+fuzrdCXt/3iVxqQxFB+G4k0fvPA6uBEOboRlL4A7AM4eBM0ioGkEND0XAuqU89UREREREZGKpJbnGqxtwyDqNf6RNg1qezoUjzh1du7smbnDwiAmxrT49+tnnoeFEZ8anye5Pek+zmNXBtOsTgA741NYtekcnBnussUS2KTAmcL9vP1wupzstlm8ZkvP7tq9u/8jEH45ePuD10n4ayEseQ7mDIX/toA3I+GsbbB7JWSWLSYREREREak4Sp6lRss9O3eembnDwkyLf0iIeV6IerW9+OD28wj29yY1tTHTl/1dIbEUNVN4vM3iWIfL4OZ58MguiO0GFz8PZ18Ldcxs4BzdAQ3i4IMr4NWzYdFE2PeHuniLiIiIiHiIkmeplipj4rGsbt5Zsrp5t2sUxPPXdAFg9m+7SDyZUeHXLpRvIKSEwAUT4Ib34b6N8PBOuPkziG8MfiGQHAer34ZZg+D1rvDb6+BKr7oYRUREREREybNUT5Ux8VhWN+/c3aqzWoe7tMjAx/cIyU4Xry5eX+ZrxB5MJuVEYzIzy9FCfDQFUhrCpsZw0edw0ZvQ5QbwqWVm6v75KZh+Afy9tOzXEBERERGRUlHyLKeFkrZUh4WE5etW7Uh00PGtDhxgFmBan7cc3FXia7ttdn7a72TYu6u45LXl/P331Qx65Vfmrt5DmquUSfSpy2yd3wsuGQfnPQUP74Cr3oRaDeHodphzDXw+AhL3ATBu4bjSXUtEREREREpMs22frmJiPB1BlSpPS3X2RGL230m37cLXasUHvzuYem2rIs9Ldmbw+bYUPrhzBnt/Ow6At91GJk52xcMTX28mOMBOSMa/SPb+nkxbCdaT/meZrcT/PI1fWHP89+4xs4bHx/8zjnsEdLoafpkCa9+Frd+Que1/xEXcwvbDm4mOiyY0MLTQ8dYiIiIiIlI2Sp5PN6GhZmmm4cPNz/8s0yQlYLNI9JlHg/THWbg+hf9cmk6dQN8Ci86KPcFr3yzlRJoL6jSmjq+Nf13Qmlt7teCpZQ9zQeiDvLdyF/uOn6QO/yLEdT2pXqtJt+0g3b6boyfcWJaFzWYD4HhKOmt2HWVVTDqrxrzNNlcYYVFxzPl8Fi1OfQ/9Q+Cy/4Nzh+P89t/4H4im2dpZvIiba9/tziEfvzxd0kVEREREpPyUPJ9uspZpio83P4eGFjnbtOSVal9Fum0XpLfivZW7ePDi8Hxlpve4jv9uSAagTZAXo754nWtnTCbg/A4AzLx6GgC39mzB9JWreGHRWvys9tRy96MW/QC4473D1An8mfBGQSS1vo6/Jv+cM5F2/X+6k9dtwg33zubjoW1oV9B72LgzW4e8xKszevEKfkTgRRS1uCnjJPGp8UqeRUREREQqkMY8n46ylmmKiFDiXFo2iwSfTwB4/7fdJKTmndX6vW0p/HfA7QA8dHF7fr60AbdsWESAty1fVd5ednq3D+Cg3wMc9H2YBO85pHitIMO2F7sNElIzWLPrGDH+oVgWtGtYmxG9WjD9lgj+d38/2ruTOOzMZNh3e9i8v5Au3zYbc20ZRJLCOtzUx84iAmm0fp6WtRIRERERqUBqeRY5xUn7alqGerM73sWsFbt46BLT+jxn9R4mrU8C4N5OtZkwsB1ERxdfoQ3SvGJI88oZh/77qD8IOlyb2D1H8Jv7ET0euoMGzRvl+bLjs2euY+T7a9m4L5GbZ6zm/dvPo3vLegVeYq/Noq+Vwtv4Mwpfmq2ZAcmH4Oq3wC+oHK+GiIiIiIiAWp5F8rNZDOtpEs73f9vF8ZR0Plvn4MmvNwMwbtUX3Hd27XJdovbBg3TuF8F11/VlyFczadD7fDPLtsORXaZuLV8+HtOD81vWIznNxa3vrWXF9iOFzqqdZoPROBnLSTLt3rD1G5h1ERzaWq5YRUREREREybNIgc5v7UenJsGkpLu5a04Ujy3YBMCodrV4ZPmH2RN9lZX3sQRITYW5cyEqyjympuaMVf9HkL8PH446n37tG3Ayw83oD/4gZl8RHUZs8K4tg+1Xvga1G8ORv+CdPrDwfjhxuFwxi4iIiIicyZQ8ixTAZrNx30XtAFi7+xiWZSYAe7JbEOVLm0/RsaMZm96xY6FFAny9mDkikn7hwaS7M9njuJBf/l6HI9FR6Dkpjc+Gu5ZDhyFgueGP2fBGBCx/CTJOVuQdiIiIiIicEZQ8yxmnWVCzEpUb3KkRXZuHADCs+1k8e9XZpW5xDg0Mxd/bP88+f29/6vjXKVU9h1L288X+S0mz7cDK9OP62R8SPi2cuBNxhZ8U1Ahu+hhu+x6angvpybB0ErwZCRs+hXFjSxWDiIiIiMiZTBOGyRln+pDpJSpns9mYNaI7G/YlMrBDQ+z20rc5h4WEETshlvjUeO5fdD+vXvoqoYGhNPn7n+7ZMTF5HwsRnxqP053KcZ+ZNE7/L7XdF5Oc/j0JzoTig2jZB8Yshc1fwpJnIXEvfHUX1AqGwzHQsPBWbxERERERMZQ8yxkvq3XY6XICpmU4NDAUgIbB/gzu5F/U6cUKCwkjLCSMEP8QIppE/HNRIDAQhg/PKRgYaNblLkKa1xZS7CupldmHuhljSr4ald0OXW+AjkNgzTum+3btJHinL/S5H/o+CD7lu08RERERkdOZkucazJHoINGZSHRcNKGBoYSFaE3nssjdOgzkeS0diY68+yvsomGmtTn3BGGhoSValzvB530C084nILMbMftOlO66PgEmWe5yAzzdD+ocheUvwpav4MrXoWXvUt6IiIiIiMiZQclzDeVIdBA+LRyny0nkjEj8vf2JnRCrBPpUzUo2vjmrdTi33K8xmBbpnX3m06SsoZw61josrETJ8qlc9kMkeX9NiOtGfowOAMsbbK7SVRLSHHaeDVNGw4+PwNHt8MHlEDECBj8HAXVLHZeIiIiIyOlME4bVUPGp8dlJHYDT5cxuIZVcppdsfHNBCnqNSzTGuLBQSjjWuiQSvb/AxTGOnfAiyHVlGWuxwdlDYfwaiLzN7Ir+CKadb1qiS9wnXERERETk9KfkuZwqajbl3HK3UJ5af9Z43ML2l8WAAQMYN25cmeOVqmfZTpLg8xEAdVw3YbdCyl5ZQF3TZfu2H6B+O0g5DF/cBvNugsR9FROwiIiIiEgNp27b5VTsbMplkLuFsqjxuLETYrl1wa3Z1yxrl+3u3bvTvXv3MscrnpHitYSuQRM4cKwWdTKGc8z3rfJV2LI3jPsNVrwMK16BbYtg90oY+CScfwfYvSomcBERERGRGkgtzxUgLCSMiCYR2bMpV/S446z6T6079wzOGut8BrJZXBF5EoDa7ovxyWxZ/jq9/eDCx2HsSjirJ6SfgEWPwnuDYX9U+esXEREREamhlDzXcPkmoZIzSsuGblK8VmDDi3oZd0JFDVNu2AFu/xGueAX8gk3iPHMgfDkGEvZW0EVERERERGoOJc81XEVOQiU103Hv98kkDf/MrgS6+xV/QlwcREdDYqJ5Xhi7Hc4bDePXwjk3m32bvoA3I2HxM+BMqpD4RURERERqAiXPIjVAURPTue2HSfL+HIC6GaOxWQFFV/buuxAZCcuXm+fFCW4C17wDdy6DFn3AnQYrX4U3zoV170Gmu4x3JSIiIiJScyh5rmwxMaaVLybG05FIDZY1cVzUnVH0C+tH1J1RxE6IpUlts+p0ovcCMmwH8KY+Ia6bi67srrsgKgr69TPPS6rpuXDbQrjpE6jfFlLj4fsH4L2L4dDWctydiIiIiEj1p+S5soSGQmAgDB9uWvmGDzc/h5ZtOalKcdttYLOZzdcXunaFBQs8HZUUosiJ6WwZHPN5B4Bg19Xsic8ovKImTSAiAkJCzPPSsNmgwxVw92q49L//jIf+A97tB7+8AK60MtyZiIiIiEj1p+S5soSFmdbmrBa+qCjzc1g1mxX7ggtg+3ZYuxb694dhw2DjRk9HJWXg9Iom1f47NryYuSyJPQl7iI6LJjouGkeio2Iv5uUDPcfC+DUQfjlkZsCv/4V3+sLetRV7LRERERGRakDJc2UKC8tp4YuIqH6JM0BAALRtC926wRtvQK1asGyZp6OSMjrmM5NMnGzdn0631+4gckYkkTMiCZ8Wnp1AOzPcrNwez0lbBSzzHtzUdOO+/n2o1QDiY0037h8fA3cRrd8iIiIiIjWMkmfJ4XZDejo0bOjpSKSM3PYjJHp/BkAt563YrEAAnC4ncUlHmLNqNwOmLmP4e2u4qeUQkp0VkODabND52n9m5f4XYMGa6bBsSvnrFhERERGpJpQ8V4VmNWAt5qQkuO8+aNUKhg71dDRSDkneX1E/yI039aiT8S+wvKntupTxHx7myW+2cDDJCcCGgEbc9v46UtJcFXPhwHpwzXS4Zob5eeWr4FhTMXWLiIiIiHiYkueqML0ar8X8yy/g72+6ln/wATzxhPlZai6biyu7nwQgyH0lzdJmUD9jAvEnMmkU7Mekq89mwd0XEOxOI2rPcUZ/uI6T6RW43NQ5w6DrTWBlwld3QtqJiqtbRERERMRDlDyf6Xr0gPXrzYRmr7wCd98NU9TdtqZr19RFiv03bHjhbTXExTFG9w/m14cv5NZeLYkIq8tHe76ntp83q3ce4845f+DMqMAE+vIXIbg5HN8N//tPxdUrIiIicpo5cQKefBLatTNtWPXqwejRno5KCqLk+UwXGAgdOpgJze68Ex56yEwcJjXeMd/pnPBazDGfGRzwH8MV3Wrh7+OVfbyb8wgfjjqPQF8vVmyPZ9zcKNJcbsYtHFf+i/uHmC7cAFEfwLafyl+niIiIyGnoxhvhq6/g1Vfhjz9g7lxo3tzTUUlBlDxLXpmZZs1nqfEybQkc9X2NZO9vsWzpBZaJbFGP2bedh7+PnV9ijzDhkz/ZlxRXMQG06gc9x5vn30yAlPiKqVdERETkNHH0KPz4I/zf/8GQIdC5M1x+OTz7rKcjk4IoeT7TnTwJO3bA1q1mzPPLL8PIkZ6OSorQLKhiJ6Dr2bo+s0ach6+3nZ+3HmJb7HX8EnsYy7LKX/mgp6BBB0g5DN/dCxVRp4iIiMhpIiAAvL1h7VpPRyIloeT5TPf772aAxTnnwAsvwMSJ8PTTno5KijB9SMVPQNenXSgzbo2kbqAPaWn1uP39dYyYvZa/DiaVr2Iff7h2Bth94K+FsGFexQQsIiIichoIDIR774VJk+Cmm2DXLk9HJEVR8lyBKrpFsNJ98IFpCbQsyMiAbdvg0UfBy6vYU88EoYGh+HvnzDzu7+1PHf86nguokg0Ib8iyhy8ktMEGfL3srNgez+Wvr2Digo0cTnYWfNK4EoyPbnIODHjMPP/hEUg6UHFBi4iIiNRwL71kFudZvBg6djSTh7krcB5XqThKnitQZbQIiueEhYQROyGWfmH9iLozitgJsTQJauLpsCpVSIAPTZuuZvED/bmiSxMyLZi3di/9X1zGmA/XMWvFTjbvT8Sd+U/36/37S1Zx7/ugWXdIT4alkystfhEREZGaaOxY2LkTHnjALHxz++2ejkgK4u3pAESqs7CQMDo16EREk4h/9pxGk141K7ynRFj9QN66JYLbdx9j8vcxrN+bwOKYwyyOOQxAkL83PVrVo1/ds7nJlYmvdzHfw3l5w2UvwqyBsP4T6DEWmnStyLsRERERqdGCg80oyubNYfx4eO45aNnS01FJbmp5FilGgT0KYmIgOto81lTT896XI9FBdFw0ic5EHIkOALq3rMdXd1/AtxN68/jlHRjYoSG1/bxJdrpYHHOYp5r0Yfwn0WS4M4u/XvNI6Hw9YJm1nzV5mIiIiJzhMgv4CHXBBeYxI6NqY5HiqeVZpDRCQ83MDsOH5+wLDDT7azBHooPwaeE4XWZsc/i0cGInxBIWEobNZqPr8xPpOn06d/Zrg8udyda4JFZsj+f1H7fw89ZD3PfZel4f1g1vr2K+jxv0FMR8B7uWm7Wfwy+tgrsTERERqZ4cDrjjDrjrLujUCY4cMctURURA27aejk5OpZZnkdIICzOtzVFROVtMjNlfg8WnxmcnzgBOl5P41Fxd1HONbfb2stO1eR3GX9iWd/f9Dx8vG99vjOOR+RvJzCymNbluC+g51jz/+Ulw6ytVEREROXPVq2e6a993H5x7LowYYRbC+f57sNk8HZ2cqkzJ84YNG4iIiGDlypX5ju3cuZOLLrqIuXPnFlnH5s2b6devHwEBAbRu3Zo5c+aUJRSRqhcWZr4OzNpqeOJcZg4HF+7fzJs96uBlgwV/7uc/X28ufn3ovg9CYH2I3wZRH1RJqCIiIiLVUXAwfPkl7NsHaWmwZw+8+y40buzpyKQgpUqeo6OjufHGG+nVqxd//vlnnmM7duxgzJgxdOvWjSVLlhRZT1JSEoMHD6ZVq1asWbOGUaNGcdttt7F69erS34HIGezU5bSgipbUcjjMWgrLl3Pp1b159cfXsNtg3loHz363lbHfFbGElX8IDJhoni+bAs7Eyo1VRERERKQClGrM84IFC/Dz82PhwoUMGjQoz7HFixdz5MgRli9fzrnnnltkPR988AFut5uZM2fi6+tL165dWbhwIdOnT6dnz56lvwuRM1TWclq5u1iHBobm7XJdXg4HJCaaCdJCQ01Le3w8pKbCE08AcNXkyaQ/9TQPrU3kg99306BBcyzLwlZYf6PI22DNu3B0O6x4BQY/W3HxioiIiIhUglIlz5MmTcJms7F79+58x+666y7Gjh1bonp++eUXBg4ciK+vb/a+gQMH8uWXX5YmHBHBJNBhIXm7jldY8pzVwpyaCpGRZnK0mJicidMm/7Nmc2Ag13cPw9nU4omvN3PkSDfW7jpGj9b1C67XywcungTzboLV06H7KDMeWkRERESkmipVt+1CW5GKOXaqnTt30qpVqzz7wsLC2LdvX6HnpKWlkZSUlGcTkUqWu4X5iSfM8/j4nInT+vXLM2na8J4tuKJLEwDW7jpWdN3tL4WWfcGdBkueq4KbEREREREpO4/Mtn3ixAkCAwPz7AsMDCQtLa3Qc6ZMmUJISEj2dtZZZ1V2mCJnjFPHTvt7+xMaGJq3hXny5LzLcoWFQUhIvknTzg2rA8Cm/cWMZbbZ4JLnARtsng8HN1XwXYmIiIiIVByPJM9+fn6kp6fn2ed0OvMl1LlNnDiRxMTE7G3v3r2VHabIGSNr7HTUnVH0C+uXvcZzYS3MhXEkOvDzPwRAtOMIjkRH0Rducg6cfY15vuKVirodEREREZEK55HkuVmzZvmS371799K6detCz/Hz8yM4ODjPJiIVJywkjIgmEYT4h+QdQ11IC/OpHIkOwqeFM/J7M5lg/IlMOrxxXvEJdN8HzeOWryB+R3lvQ0RERESkUngkee7Tpw9Lly7F7XZn71uyZEm+GbxFpOaIT43H6XJi2U6SYdsPgJXRvPjJyxp3NuOfseC31yo9ThERERGRsqiy5HnkyJFMmzYNgDFjxpCQkMCECRPYvHkzkyZNYtOmTdxzzz1VFY7Iaa3QMcxVJN1uWpB9M9uU7ISs1ucNn0Ji4RMHioiIiJyO1qxZQ4MGDXA6nZ4ORYpQZclzTEwMe/bsAUy37e+++46VK1cSGRnJV199xQ8//ECLFlqqRqQiFDqGuYqk27KS57YlO+Gs883M25kZ8Pu0SoxMREREpPrZv38/8fHxnDx50tOhSBFKtc5zlpYtW2JZVqHHCzq2du3aPD/379+fTZs0u65IZcla/znfGOYqkPZPy7OfVcLkGaDvA7B7BUR9AP0eglpV11IuIiIiIlIcj4x5FpHTW7r9bwC8rUYkn8ws2UmtL4Sm54LrJKyeXonRiYiIiIiUnpJnEalwli2VDNsBAP4+nFGyk2y2nLHPa2eCs5h1okVEREREqpCSZ5HTXLOgZpVYeeF1Z00aVuLkGSD8CmjQAdISYd2s8kYnIiIiIlJhlDyLnOamD6nELtDTC687a9KwUiXPdjv0ecA8X/U2pKeWJzoRERERkQqj5FlE8iiwpbqIFuYspy6PldXyvPuIu7BTCtb5OqgTBqnx8Oec0p0rIiIiIlJJlDyLSB4FtlQX0cKc5dTlsX66/T0ADiZmkJCaXvIAvLyh933m+W9vgKsU54qIiIiIVBIlzyJSYcJCwohoEkGIfwj9WnUnrF4gAJv3J5Wuom63QO3GkLQPNnxSCZGKiIiIiJSOkmcRqTRdmoUAsGl/KWfO9vGH3vea5yteBncpxk2LiIiIiFQCJc8iUmk6/5M8by5t8gwQeRvUaggJDtjwacUGJiIiIiJSSkqeRaTSlLnlGcA3EHrfY56veAncrgqMTERERESkdJQ8i0il6dwsGADHsVQSU8vQ9br7KAisD8d3w6bPKzY4EREREZFSUPIsIpWmTqAvZ9ULAGDzgUTGLRxXugp8a8EF/zbPl6v1WUREREQ8R8mziFSqrK7bi2K2svXIVqLjonEkOkpewXl3QEA9OPY3bFlQSVGKiIiIiBRNybOIVLhmQc2ynzevZwHwzurvWe5YTuSMSMKnhZc8gfarDb3Gm+e/vgiZ7ooOV0RERHIbV8qeYiJnCCXPIlLhpg+Znv28cV0z1tk3s232PqfLSXxqfMkrPP9O8K8DR7fDlq8qKkwRERHJzeGA6GjYutU8F5E8lDyLSKVq09AHAB+rKTYrsGyV+AfntD4vnwqZmRUUnYiIiAAmWe7YESIjYfly81wJtEgeSp5FpFIFBdhx2Q4B4JvZpuwV9bgL/ELgyF8Q800FRSciIiIAxMdDairMnWu21FSzT0SyKXkWkUqXZtsBgF+urtul5h8CPf8Zg/XrVLCsCohMRERE8ujY0Wwiko+SZxGpdOl2kzz7WuVIngF6jgXf2nB4C/y9pAIiExERkeI4Eh1Ex0WT6Ews3YoZIqcZJc8iUunS7X8DeScNK5OAuhAxwjxf9VY5oxIREZHiOBIdhE8LJ3JGJMsdy/OtmDFuoWbmljOHkmcRqXRZLc8+VrOyTxqWpcddYLPD30vh0NYKiE5EREQKE58aj9PlzP751BUz9ifv90RYIh6h5FlEKl2mLSl70jC/zA7lq6xuS+h4pXm+Wq3PIiIiIlI1lDyLSJU4aV8PQIC7e/kr6zXBPG78HE4cLn99IiIiIiLFUPIsIlXipNc6AAIyz4fyTpR91vnQ/Dxwp8O6WeUPTkRERESkGEqeRaRKOO3rsUjHx2qMj3VW+SvsNd48rpsFGSfLX5+IiIiISBGUPItIlbBsTpz2jQAEuM8vf4UdroSQMEg9Chs/K399IiIiIiJFUPIsIlUmNXfX7fLy8jbrPgOsehus8vYFFxERkVOFBobi7+2f/bO/tz+hgaEejEjEc5Q8i0iVOWlfC5gZt5NPZpa/wnNvBd8giI+FHUvKX5+IiIjkERYSRuyEWKLujKJfWD9iJ8QSFhLm6bBEPELJs4hUGbf9COm2XdjwInpPWvkr9A+GyJHm+app5a+vBjtxAp58Etq1A39/qFcPRo/2dFQiInI6CAsJI6JJBJ0adFLiLGc0Jc8iUqWyZt2O2uWsmAp73AU2O+z8BQ5tqZg6a6Abb4SvvoJXX4U//oC5c6F5c09HJSIip5PpQ6Z7OgQRj1LyLCJVKqvrdvTuNDLcFdB1u04YdLraPF/1Vvnrq4GOHoUff4T/+z8YMgQ6d4bLL4dnn/V0ZCIiIlIYy7L497//zZ49e/LsX7JkCa+//rqHopKiKHkWkSqVZt+Gm0RS0y3+2H28YirtNcE8bvwckuIqps4aJCAAvL1h7VpPRyIiIiIlZVkWX331FU888UT2vvT0dMaMGcNa/adeLSl5FpGqZcvkpNcfACz961DF1Nm8O4RdAJkZsObM61IWGAj33guTJsFNN8GuXZ6OSERERIpjt9uZOHEin3zyCfv37wfg008/Zffu3UycONHD0UlBlDyLSJU76WW+TV3y1+GKq7T3vebxj/fBmVhx9dYQL70E06fD4sXQsaOZPMzt9nRUIiIiUpTRo0fTpEkT5s+fD8Arr7zCDTfcQOfOnT0cmRREybOIVLmT9mi87LDzSAq74lMqptJ2F0ODjpCWZBLoM9DYsbBzJzzwAEyZArff7umIREREpCj+/v5MnDiRlStXAuBwOHjqqac8HJUURsmziFQ5y3aSs5v5ArAkpoK6btvt0Pse83z1dHBVwFJYNVBwMLzwArzxBsyZA7t3ezoiERERKcro0aOpU6cOAFdffbXHW521/GXhlDyLiEdEtvIHYGlFdt3ufD0EN4MTB2HjZxVXbzWXWcCk5RdcYB4zMqo2FhERqQEcDoiONpvD4eloznj+/v6MGDECgP/85z8ejkbLXxZFybOIeMR5rfwAWLvrGEnOCsrwvH2h593m+W9vFJxVnoYcDhg8GObPh61b4ddfTdftiAho29bT0YmISLXicJjJMSIjzdaxoxLoauCVV15h06ZNnHfeeR6NQ8tfFk3Js4h4ROM63rRpUAtXpsXybUcqruLIkeAXAke3Q+wPFVdvNVavnumufd99cO65MGKE6Wr1/fdgs3k6OhERqVbi4yE11TQnzp1rnsfHezqqM57NZvN4d23Q8pfFUfIsIh4zqGMjAJbGVGDXbb8gOO+fgTm/vQaWVXF1V1PBwfDll7BvH6SlwZ498O670LixpyMTEZFqq2NHs4nkouUvi6bkWUQ8ZmCHhgD8EnsYd2YFJrk9xoKXH+xbB45VFVeviIiIZHMkOkh0JhIdF40jUV2/Txda/rJwSp5FxGO6t6hLsL83x1MzWL/3eMVVHNQIut1snv/2esXVKyIiIoBJnMOnhbPcsZzIGZGETwtXAn0a0fKXBVPyLCIe4+1lp1/7BgAsi63Acc8Avf4N2GDbIji0tWLrFhEROcPFp8bjdDmzf3a6nMSnauz06UTLX+an5FlEPGpAeE7X7QoV2hY6DjHP1fosIiIiUiwtf1k0Jc8i4lH9/2l53rw/icPJzmJKl1Lv+83jps/hyLaKrVtERETkNKPlL4um5FlEPKpBkB9dm4cA8GtFd91uHgntLwMrE36ZXLF1i4iIiJxmtPxl0ZQ8i4jHDaiscc8Ag54EbLD1GzjwZ8XXLyIiInKa0PKXRVPyLCIeN+CfJauWbz+Cy13AYJvyaHQ2dLnBPF/yXMXWXc3s2rWLnTt3ejoMERERkdOSkmcR8bhzmtehbqAPyU4X0Y6Eir/AhRPB7g1/L4VdKyq+/mri0Ucf5ZFHHvF0GCIiIlIW48Z5OgIphpJnEfE4L7ste8mqCp91G6Bea4gYaZ4veRYsq+KvUQ2kp6eTlpbm6TBERESkLPbv93QEUgwlzyJSLVz4z5JVlTLuGaD/I+AdAPvWmbWfRURERERKQcmziFQL/do3wGaDmLgkDiZW8JJVAEGNocdd5vmSSQUvZCgiIiIiUgglzyJSLdSr5cs5zesA8Ou2Sui6DdDnPvALgcNbYPP8yrmGiIiIiJyWlDyLSLWR1XX7l78qqet2QF3ofY95/svz4EqvnOuIiIiIyGlHybOIVBsDws2kYSt3xJPuqqRu1T3HQa2GcHw3/PlR5VxDREREpAZ79dVX+fbbbz0dRrWj5FlEqo0uzUKoX8uXE2kuovYcr5yL+NYyk4cB/PICpB6rnOuIiIiI1FCffPIJP/zwg6fDqHaUPItItWG32+j/T+vzsspYsipL5G3QsBOkHoXFz1TedURERETktKHkWUSqlQFZ454rM3n28oErXjHPoz+Evesq71oiIiIiclpQ8iwi1Uq/dqHYbbDt0An2J5ysvAu16AXdbjHPF94PblflXUtEREREajwlzyJSrdQJ9CUirC5QyV23AQY/B/514NAmWDezcq8lIiIijFs4ztMhiJSZkmcRqXYGZI97rqQlq7LUCoWLnjHPlz4PSXGVe72COBwQHZ2zORxVH4OIiEgV2Z+839MhiJSZkmcRqXayxj3/tiOeZGdG5V4sYiQ06w7pyfDTxMq91qkcDujYESIjc7aOHZVAi4iIiFRDSp5FpNo5u2kwrRvUIjXdzRtLtlfuxex2GPIK2Oyw5SvYsaRyr5dbfDykpsLcuRAVZR5TU81+EREREalWlDyLSLVjs9l4ckgnAN7/bTc7DidX7gWbnAPn32We//AQZDgr93qn6tgRIiLMo4iIiIhUS0qeRaRaujC8IRd1bIgr0+LZ77ZiWVYlX/BxqN0Yju2EFS9X7rVEREREpMZR8iwi1daTQzrh621nxfZ4ftpyqHIv5h8Ml04xz1e8BHtWVe71RERERKRGUfIsIp43ruBlK1rUr8WdfVsDMPn7rTgz3JUbR+droetNYGXCl2Mg9VjlXk9EREREagwlzyLiefsLX7bi7gvb0CTEn33HT/LOr39XfixXvAT1WkPSPvjuHqjs7uKFcCQ6iI6Lzt4ciZqBW0RERMSTlDyLSLUW6OvN45ebibSmL/ubfcdTK/eCfkFw/Wyw+0DMd/DH7Mq9XgEcqXGETwsnckZk9hY+LVwJtIiIiHheIT0GzwRKnkWk2hvStQk9W9cjzZXJ89/HVP4Fm54LFz1jnv/0OBzaWvnXzCU+PQGnK++M306Xk/hULWElIiKVLCbGbCKFKaLH4OlOybOIVHs2m41nrjobuw1+3HyQ33ZUQRLZ825oexG4nDB/FKRXcou3iIiIJ4WGQmAgDB9utsBAs0+kqlXjlm0lzyJSI3RoHMytPVsA8Ox3Wyp/6Sq7HYa+A7UawpEY0wItIiJyugoLMy3OUVHQr595Hhbm6ajkTOJwQHQ0bN1qnldDSp5FpMZ4YHA4/j52th06wda4pMq/YO0GcO275nnU+7D5y8q/poiIiKeEhUFEBISEnDmJczVu5TyjOBzQsSNERsLy5eZ5NUyglTyLSI0REuhD33YNAPh5ayWv+5ylzUDofZ95/tVY2LWiaq4rIiIile8MHr9brcTHQ2oqzJ1rttRUs6+aUfIsIjXK4E6NgCpMngEGPQUdhoA7HT79F8RtrLpri4iIiJwpOnY0WzWl5FlEapRBHRpit8GWA0nsTzhZNRe1e8F170GL3pCWBHOvg2O7qubaIiIiIjXAuIWnfxd4Jc8iUqPUr+1HZIu6ACyuytZnH3+46RNo1BlSDsOca+DE4aq7voiIiEg1tj/59O8Cr+RZRGocj3TdBgioA8O/hDot4Pgu0wLtrIKJy0RERKpSs2aejkCkWlLyLCI1zuBOjQFYvfMoiSczqvbiQY3h1q8gMBQOboTPbgFXWtXGICIiUpmmT/d0BCLVkpJnEalxWj35EG0b1saVabEs1gNdp+u3geHzwbc27FoOn9yoFmgRERGpFGfCWOKaQsmziHiOwwHR0ZCYaB5Lup7f/v2e67qdpem5cPM8k0DvXAbvXwZJBzwSimVZJdonIiIi1ZTDUejnoWozlrisn9tOI0qeRaRShQaG4u/tn2efv7c/jY6mmaUIIiNh+XLz2LFjnj/ERX3TmpU8/xp7hHRXZuUEX5xW/eC276F2Izi0GWYNhsMxQPm+JQ71rVPgaxYaGJr9c1b93377Leeccw5OpzP7WFpaGt26dePrr78ucwwiIiLlMq5iWkvPiFZXh8N8Birk81C1kBVjEZ/bzgRKnkWkUoWFhBE7IZaoO6PoF9aPqDujiJ0QS7N0P0hNhblzISrKPKamQnw8jkQH0XHRbD2ylei4aByJ+f8wd2tehwZBfiSnuVi986gH7uwfTbvB6J+hfjtI2gezL4HdK8v1LXFYYJMCX7OwkLDsMln1d+jQgS1btjBr1qzsY++99x6bN2+mYzVeJ1FERE5z+0v4/2BWa2bW9k8yVpLPAqeN+Picz0S5Pg9VK7ljPOVz25nE29MBiMjpLywkjLCQMDo16EREk4h/9v7zx7ZjR4iIyC7rSI0jfFpvnC7Tkho5IxJ/b/98yaPdbuOijg2Zt3YvP289RL/2DarqdvKr2wJG/w/m3QR718Cca+jTsE25qsx6zUL8Q3K9Zvm1b9+eW265hSlTphAREYHb7eaFF17gX//6F+Hh4eWKQUREpFJltWampubsCwzEsW4x4V8NLPazQLUyblz5J1qrCV96n/K57UyjlmcRqTLThxT/n0p8ekL2f5ZZnC4n8an5v9nM6rq9OOaQ58f4BtaDEd9AhyHgTueRuBhY/Ay4K3828CeeeIKDBw+yZ88e9u7dS1xcHE888USlX1dERKRcCmnNjD+4s8SfBaqNkra0S42m5FlEaqwL2oQS6OtFXKKTzfurwWzXPgFw40fQ45/xWStfhfcGw9G/K+wSWd3YEp2J2d3Yslqft2/fzo4dO9TqLCIiNUtWa2ZNaHmVM5qSZxGpsfx9vOjXznTX/nnrQQ9H8w+7F1z2f/xf007gXwcO/Anv9IXoOVCG1vFmQc2ynzsSHYRPCydyRiTLHcuJnBFJ+LRwHIkOnnjiCZxOJ06nU63OIiIiIpVAybOI1GhZXbf/56klqwrxe1ADGPcbtOwLGSnw7QT4YiSkHitxHfEn0njgvBdzfk6NL7QbW/v27Rk4cCADBgxQq7OIiIiU2v79+4mKisq3f+nSpZzIPS79DKYJw0SkRhvYoSFedht/HUxm77FUzqoX6OmQcoQ0N+Ogf3sdfnketn4Du3+Dxp0hqCmkAuf5QNxK2J0Kh7dAe28y9y3l950rWbxxD6luGz3O6cI1A3phc6UVebklS5ZUzX2JiIjIaefTTz/lueeeY9euXdn7Vq9ezaBBg1g2Ywb9y1jvuIXjCp73piImWatiSp5FpEarW8uX7i3qsmbXMX7eeohRfVp5OqS87F7Q9wFoPQC+HAPH/oady3KOXx4A656Edf/8fHMg9uhJ9AH62DH9g7aa7VzgILXZg0UsbtbgZhVucLuq9p5ERETEcDjMxGeJieZ5WMXOBu5IdGTPcxIaGFqps43feuutPPnkk7z22mvZ+5599lk6depE33PPLTLG+NT4QuMsdPnOGjjJmpJnEanxBndqxJpdx/jf1oNVnjwX+m3qqZpFwLjfzVJWSQcgOQ52boTvP4feXbHsLuKTXew/acdp8yXD5keLRnXxsVycOLyLphyhli2NRthpBJyPF7f+U3Xm+1dAs0ho3h3aXwYte1fmLYuIiAjkX2qrY0eIiamwBDprrhOny1kly3U1bNiQ8ePH8/rrr9O6dWsOHTrEokWL+Oyzz7DbCx7tW9IlRk8XSp5FxLNiYvI+FiX3t7vR0RAaCmFhXHJ2Y174IYbVO48RtecYkS3qVW7MWeEkOth6ZGvJvw328YfWuTo91YqG2z4kdtzr3Lshjb+SkgEY0NiP52/rTbM6AQCs35vAwI/WkZZ8lMa2vYT4zCLcvpueeNETb+q508Gxymy/vwkRI+GSF8CvdmXduoiIiOReagtg+HCzr6zJ8ymfhU6d6yRrnpPKTEoffvhh3nrrLQ4dOsTRo0fp1KkT119/PaxfX2D5opYYLW2ce4Mb8sqaBB7uEkTTst5AJdOEYSLiGaGhEBho/qOJjDSPgYFQp06BxX32xZlvdCMjYfly89ixIzgcnFUvkBsizwJg8vcxVbLmc9a3wafOel1aFjBhVQJ/HUymrq+NV797iff71s1OnAG6nVWH7/7dl4aN6/GX1YnV6VN53TWEK3BSn2S2DPsQhk6HrjcBNoj+EN7pDXtWVdwNi4iISME6dix0ma1xC8cVf37uz0RZn4dCQys4yJLJan0+ePAge/fu5emnny601bmivTBwNF/tOcl/ohKr5HploeRZRDwjLMx8wxoVBf36mceYGGjSpMDi3scSzLe7TzwBM2eax9RU8w0v8ODF7Qn09eJPRwILN8ZVeviFfRtcWtFNO7A9yUWAjxc/XdKAa7Yuw2az5SvXMNifSdfVJ9nrJ2x4Udc1ikbpL+BtNSatThh0+xdc+y6M/A5CzoLju+H9y+Dnp6GYicZERESkchQ63je3rM9EuT8PVfDY6dJ4+OGHsdls1K1b17Q6V4HfD6XxY3hv7DZ4tEtQlVyzLJQ8i4jnhIVBRASEhJjHIv6jcNWrY76JnTwZ7rjDPOb6ZrZhsD/NGm8F4L+L/sKZ4a6KOyi3+V0uAuDyLk1oGOBVZFkfbxvHfN7kqM80MjmJf2YXmqRN4/v1KWRm/tPa3qqvWSKr2y2ABb+9BjMuhIObK/dGREREyuio083qnUfJrIKeY9VWWFi+z0OhgaH4e/tnF/H39ic0MKdFukSt2mXQsGFDpk6dykcffVQlrc4udybP/JkEwK1tAulQx6fSr1lWSp5FpEbIaN6k4JbqXAm3X8hKGgf7s+/4ST78fbfngi2hky6L7zr2A+D6yOYlO8kGJ7wXEec3Aad9I3b8ee/XJG6auZrd8SmmjH8IDH0bhn0MgaFmCazZl4BjTSXdiYiISNnsDWnEFT/Hc9OM1Vy1OJ7fWpzj6ZCqjbCQMGInxNIvrB9Rd0blm4SrRK3aZfTAAw8wZMiQSqs/t7mr97AtyUXd1ETuP7v6tjqDkmcRqUmKaam22108dEk4ANOW7uDoierdXXnR/pOc8AvkrFpe9GhVuknOXPZDHPL9D0d93sLfx8baXce49PXlfLYu17jrjkPg7tXQsi+kn4C518G+Pyr4LkT+n737jo+qSv84/pmSZDIpEyCUEBg6ITQxEVBUVOyKvSuWXXdRXFzLrru66+6ylmX359pRVuyCvVdUUKpIS6RJDJ2hBEgImfQy5ffHTQKB9Mykft+v17zm5ubec84AIfPMOed5REQaJ6PQy/XXPsq+Ih8AGw55uOHaR3koJYIQXysrPdlALreL1IxU3MXuRuVEqeB0OHHYHCTFJVUGzke2nZqR2qT2W9rB/BKemLcJgD8smUNMWOsOT1v36EREGujy4+MZ1jOavBIPT3+3uaWHU6v3txcBcGXfcMzmY/c518nkJ986lydviOWk/l0oLvNx/0fr2X2o8PA1kV3h+vfKA+g8mH0Z7EkJ0CsQEZGOKoQYokgmwnMGIb6+2CzhVZYU1yUzr4QbFh1kV0wP+kRamHvXqdwyyE6It4w1B0OIK3maLqX3YPF1DeKrCI6KpKLJs5JZ7Frc6KSi9Wm7KUlLAyE2NKbK0nI4dnl5bR6ft4ncYg+JMVauW/tNMIYYUI0KnteuXUtSUhJLly6tcn7mzJn069eP8PBwJkyYwLZt22psw+l0YjKZqjyyshqebEdE5Ehms4m/XmhkvHxzhYstB/JbeETV25VdyLIDpZj8Pq7oG173DbXo7rDy5m/GMqZfZ/x++Gzt3qoXhNrh+nfBOQ5Kco0Aeu+aJvUpIiIdR5nXxxfr9jL9qzRufmUlY/81n4ueTqNz0T+JLfsDPUtmMNj7EdM/z+SNH3ew5UAete1eziks5caXV7Atz0vP3AO8eVpnEuOimXa8g/kvTeHkHqWYMBPpPZOeJc+2uQA6UElF69N2oNtvKKc9jvSp6aRMTqlxefkxXC5ITWVDWRhvrzCC/n8e78Di9xlb8upTvrSFNCh4Tk1N5eqrr+akk07ip59+qvK9999/n3vuuYeHHnqIpUuXUlZWxsUXX4zP56u2rezsbN588002b95c+ejcuXlqs4pI+zZuQCxnJXbD6/Pz77mt6z/giuQeH6buBmDcznX0irA2uV2z2cQVSfEAfPLTnmPLdYVGwA3vQe8TodgNb1wCGeua3K+IiLR/b61wMfWtn3hh8TYWbcpkf66xLapXp3Ds9gzCQyy4i7zM3bCPv3/6M2c9sZgxgybxx/fXMnd9BnnFZZVteb0h3PzKSn7Zl0dXm5k333mwyu/BPjn7+MPIQjLC7qHUtAMzkUR5m2fvbbs3JTgJxpwOJ0lxSccsL6+WywWJifiTk5nWZTR+4KKBDsYk9mw15bpq06B3bB999BFhYWF88cUXnHnmmVW+N336dG677TZuvPFGAF588UUSExNZtGgRZ5xxRpVrS0tLKSgoYMSIEQwcOLCJL0FEOjqX20VWYVbl3p9Yeyz3n5/IgvRM5qcdYNnWLMYNaB3/Ae/J24PP5+eDFCN4vmr9fEjra3yziZ+0njc8jr+9v4ZN+/NJy8hjaM/oqheERcEN78Ocy2H3KiOAvvlz6DG8Sf2KiEj7tnZXDgAn9e/CxOPiGNIjisHdo4iyhXDx20/xwVWfsG53Dj9uPciP2w6SsvMQmUTwQcpuPkjZTYjFxOi+nTm+bwhbtp5NSZEbR7iFN8d3ot8/91bbZ6l5Mzkhr9Ot9B9Ees7BbX2rGV9xO7UneAnGqnPk+zOX22UE1VlZUFjIZzPeZfWuCMJLi3lgSLfD5bqysuCee2D27BYt11WTBs08P/zww8yePZv+/ftXOZ+Tk8NPP/3E+eefX3luyJAhxMXFsXz58mPayc7OBiC2FX6aICItID6+0bfWtPcnNCybSWON/3TvemcNizZlBmq0TbZ8+0F2HyoiKtTMubvXGJ+wJic3+ZNWR3gIZ+bvBODTNTX8grRFw6QPoWcSFGXD7Evh4NbGvRAREekQtmQaW6BuHteHG8b2IblPZ6Jsh8sJhVrNnNC3M3eeOYi3fnsia/9xDm/u/IJbT+lH/9gIyrx+lm09yHPf7aOkqDc+8tlmupcoc+1LjYvMKZSZMrAQRYT39GC+RAmA+KjD7+dq2/edZXcwPcvIqv275e/R015eqrMiMezQoa0ycIYGBs8mU/UJbbZv3w5Av35Vs+I5nU527959zPUHDx6svD4+Pp6LL76YdetqXz5YUlJCbm5ulYeItBMzZzb61tr2/tx91mAGdoskM6+Em19ZyT8+3UBRafDrP7vcrlozYH6w2vh/ceKoeMLXrzXKblU8jiq/1VCXuI0kaZ+u2YvXV8OOM5sDbvwYeoyAgkwjgM7NaHSfIiLSfvn9/sr8IQO7RdbrHlvGHk7eu5G/9Szm+6v7s/CPp/Pr8dEUmVMpM+1if+g08v1p5BTn1N6QyUee5UsAojwTj92SJK3KzImH388d/f6stCyUt1e6uHHRQcb+7g32FfnoHWHhNys/rqahxr8vDLamb7QD8vONHyi73V7lvN1up6Tk2FIxTqeT5cuXY7fb2bFjB//+97857bTTWL9+Pb16VV/rdPr06fzzn/8MxHBFpBWLtcdis9qq/IfbkKyNR+oUEcrnU0/hP1//wmvLdvD6jztZsiWLp64ZxcheMQEc9WEVn7QWe4pJnpWMzWqrkjjD6w3hq41GoHplcm9wdqp3sFyfP5sz8l1E26zsyy1mxfaDNS9XD4+BSR8Z9Z+ztxlLuX/1FYR3atwLFxGRdmmvu5jCUi9Ws4k+XSKO+f6Rs41A5Z5WCguNVVV2O33T0ph4fAT/WPX3Bvefb51HjGcSof5+bNxTSnJP4/yUL6ZUCdbahSasxGtJm/bn8cx3myko8RBlCyE63EqhJ5fosisBLzZfEjbfSGZ+5zZuMFsYGmPlX8kObN6yWttubQJSqiosLAww9jIfqbi4+JiAGiAqKoqxY8cyYsQILrroIr7++msA3n777Rr7eOCBB3C73ZWPXbt2BWLoItLKOB3OhmdtrEV4qIVpFw/j9V+PoVtUGNsyC7j8+WU8891mPN7qExo2RW0ZNl1uFxmZcRSX+XB2DiPJGdOgtmv9synPXBmWc4gLexpL6T75qY69TZHdjBnoyB5wYCO8dQ2UFtZ+j4iIdCgVs859utgJsRwbOhwTwJbvaeXBB41HYaFxrpH8pgIKLAsA+HLt4d9Re/Kad/9us2jFM67VKTNbeObnPC58ZglfrMtgQXomn63dy5zlLj5aXUAnzy108txKuO94TFjo19XKfSOiWPjCb/nqnK6M6hLa0i+hwQISPMeXf0pydEC7a9euY/ZHVycqKoqBAweyc+fOGq8JCwsjOjq6ykNE2qcGZW2sp9MGd+Wbu8dz4Yg4PD4/T8zbxDlPLuadlS5KPPVcyt2ELJUVM9J7M43X8nP+y+zKbfiHgNX+2VR8yp+cDIsXc+nDvwdg7rq9FJfV8do69TUCaJsDdq2A926CNvYpsIiIBE9Dl2wTG2vk73jkEeNRnsujYvVUBZvVRowtpvomjqodnGf9AoBVW4vZm1PUuBfShh0zu99cannfs+FQGRff9CRP/JxPmdfPWYnd+L8rRvLghYn8/sxBXHlCF4qs31NgXsoh62tkRUzl9d8k8rvESPrmtN2tYgELnvv27cu8efMqz23atIndu3cfk5W7Ojk5OWzatEmZt0UkqDpFhDLj+uN56ppROMJD2JZVwP0frefU/yzgf4u2kltcR9DYhCyVWYVZeEo7Y/MNw48Xt/nbwNVkrPiUf84cSElh9PQH6Jl7gLxSHwt+OVD3/d2HwvXvgzUctsyDT6ZADWUGRUSkY2lw8FyRNXn8+Cq5PCpWTx25ciouKq76Jo6qHbz89o84vk8EXj/MWV7zZFt71WLL06t531Ps9fN/X//CJfOzSOven06hJp6+dhQv3nQCV4/uzW9O7c+9Zw/mv1eeyKr77uKbO69i1IADbPz9oronQ7weWP2qUVKzlQrInmeAe++9lwceeIBRo0bRr18/7rnnHiZOnMiIESMAuPnmmxk9ejRTp07lyy+/JD09ndNPP51Dhw4xbdo0oqKiuPnmmwM1HBGRaplMJi49Pp6zhnbnnZUuXlqynX25xfx77i889/0Wrj/RyTlDezCkRxQRYfX4L9Jvxkw0Fr+Dda4Scot8RHkmYvZHY/bbMWPnsS8PUVTqo1vpNACKzKl4TdmBf3GJiZCUhBm45MVnmHnSVXz80x7OH1H9m5MqnGPh6jfgnetg/fsQYocLnwBLwH5NiIhIG7S1PHge1C2q/jc5neBwGJmTjzztcFaunDLU/CGy0+Gscv1tp+7j9p0pvLNqF78/c1BDX0ZwNbHUZFuRG2rnqvlZpLv3ATAxbTHT/nwlsaOqnxk/8u+wzsB56/fw9V8gM83IxXLOw4EefkAE7F3R1KlTyczM5I477qC4uJhLLrmEGTNmVH4/LS2Nbt26ARATE8OLL77IX//6V7p06cLpp5/Ou+++S6dOSlQjIs0jMszKb956jJuefY5P1+zhhcXb2HIgnxcWbeOFRdswmaBvlwgS46IYGhfNgK6R5MQMYc836ew+VMjWLDe9il/H7O+EqXwRz7SPjYC4M7dX6evHLcYe6BCMLCf51rmNToJWX5duXMjMk65iYXomOYWlxNjrsa9o8Dlw6Uz4aDKkvg45LrjqNSO5mIiIdEgVZarqPfMcJGcldiM+Jpw9OUV8vrb62tDNrmKJ+qRJxteV5SYDtLIMWLQpk29/3sedEwbRw2Gr+4YgevP480l3e4iNDOXR4yI49z//B/+4pmmNdjHDir/A/h+Nr8M7Qee6t/22lEYFz3379j0mVbzJZOKhhx7ioYceqvaelStXVh6ffPLJpHWQT2hEpBXbs4dQq5mrTujNFUm9+P6XA7y90sX6PW4O5JWwPauA7VkFfLXe+ISVnqfBgi2Vt1voAoAJiA630C0qnM4RoYSHeVidsYhzBp1CbEQkvWJiiQyzUuTN4dnVD/HRpTOJtccGZC93TRKydjLEYeUXt4ev1u/j+rH17Gvk1cas80eTYdsCeOksuP5d6DIgaGMVEZHW6WB+CdkFRkLg/l2PzbTdnKwWM5NO7MN/vv6F13/cgalLMw/A5Tqc+Cw21phddzrxb9zImk0ZDH30L4S98ZpxPiMwwfO63Tn89o3VlHqMbViv/3pM08budkNq6uHxN0CZz88bSRMB+PN5QzjXnNm4sVQozYNNb8CUCCNwNlthzGQ47U+tuvKH1uOJiABms4mzhnbnrKHdYcoUsh57irSMXNIyctm4N5ftWQV0SltPr7NPoVcnO706hdOrk50HFtzGF5PewWI2VWlvyhdv8cTE04/qpRfvbik+YrlacF3WJ5zp6/L4ZM0elmROr/+eqcSJ8Ouv4e3r4OBmeHECXDMb+o0P7oBFRKRVqdjvHB8Tjj205cOGa0f35qn5m9iwJ5cB4d2rv2jKlMBnrT6y/BYYM8zle7lf2eXj4e8OcmHipTzXwIC0NgfzS7h9dgqlHh9Ws4m97mKumLmMP0901H3zkX8GR4+9vHxYxfjr6+vdxWREdyU2zMxFx/WE9Y0Inr1lkJkCF9jgu0lQlgsWE3Q/Ea6aAbGtbDl+NQKSMExEpF3Zs4fYyDBOHdSVyeMH8NS4Lnw6zs5rG97hkSQHt582gIkjezKqdwwhIUXHBM7QQsk90tKMT5TLV/Zc7AzHZIKV27PZfrCByTfiRsJvv4f4E6A4B2ZfBqtfCfyYRUSk1WotS7YrdIoI5ZJRxhaorKzh1V/UhOSeNToyMeecOZXlt7LyS3hq3iYAvnQMYPGmJs7GlvN4ffz+nZ/Y6y6mX2wE3//hdJKcMeQWe5j28UHCvSfV3sCRfwblYy98Yw6FK1ZVGX9DvLK5AIBJA+3YQizHfH/z5s2UlVWTeLW0kLF5WfDx7fDYQPjxjzA61Aico/rAnAIYO71NBM6g4FlEpHZHlYEiMdE415ocuecqOdl4ttuJc3bnxH7GuracQ42oZhDVHW75EkZcDT4PfHEPfPsgHLVtR0RE2qcGZ9o+UnxwyivdPK4vAO6cgZz31GKenr+ZTfvzjtlSGhSJicaj3BPzNpFX4sFa/iH6tM9/ptTT9GoVj32Tzg9bDmIPtfDCjck4u9h58zcnclZid8q80LX0ASI9F9TaRpnXx6od2Ty5IY+rrv8PIzfGcOo3h9jWq+FBaqrrED8dLCPUU8YNA+zHfP/AgQMkJCRw5ZVXGieKDsHad+CdG+D/+vPXvT/D2reND+NDYyC11AiYT3sZttazXGgr0fLrL0REmtuRe39crsPLlqo7f+SnzWAEpllZDd4rFFQVZUGysuCee+DJJyv3M506JJsft8HB7P6k7E2ha0TXhu21DrHB5bOgawJ8/zAsexaKcuCip8F87CfPIiLSfjQpeA700ulyURFuLkmK4NPUXH7Zl8cv+/J4cv4m+neN4LxhPbgqNJp+Qem5ql9yynhnpVGv+IUbk/nzy4vZlgmv/LCdMYMb3+6X6zJ4YfE2AB678jgGdzeynIeHWvjfpCTueHsR324opEvZHYT6+uExZQBW3v4xj2/LVlKaX8iOuLNY8Y+vKfCUf6DQexj44WBBKbd+t4+PbZHEAC63q0rZzJrysbz6ww4ALk5bSNfto8FmqZJh/LHHHsPv9/PN3K/Ieu58Yg+uND50L7ffGkb3E26FIRPhYCj8dQxc44DsTY3/g2ohCp5FpGM5eu9PYuLhXwA1na/4uhrxUcH5ZL0ux/RbnrjkyNIgLreLPy4+k1hepKy0K+NeuAFz6E7Sp6Y3LIA2mWD8HyEqDj75Hfw0G0rz4bJZYK1HFm8REWmTDpepah3Ltl1uFwkzEij2FGO2RRLuHUOUbzyRjGZbZgHPL9zKa/2v5IvMfPp3rTrmKV9MCdiWKj/w8JpcfH64YEQPzkzszv37V/DH+DN45rvNPN2zcdnMNu3P474P1gJw2/j+XDiyaqlJq8XMbROiee+XWcR4JhHlPb/ye++vzAeMvy86DQCPn85FuZw0IJZT5jzLsF1pTLn0L2x3dGPK5Q/yaGgxw8r/LCvYrLZj3iNkuIv4ar3xIcGvNnwLk54+PKAIOwf2/MBzzzyJ1Qx+n4cn3vmef51pg66JRg6VxIv47YIH+ey86cY9ZlcNGcrbBi3bFpGOpYZ9SzWer0OL7G2uZ79ZhVkUebMptCwBIMpzHsWe4iqfMjfI8TfA9kQwh8DPH8M710NpYePaEhGRVi2/xMNetxFYtZY9z1mFWZXBns+UT4H1e/aFTuPV33bj6WtHMTw+mkJzCH94fy0eb9Xl03vyArcX+rsBY/jhQCmhFjMPnG98uH65exPJfTpRWOrljSW5DW4zt7iM22anUFjqZdyALtx3bkK115lMJtwh75AZ+i8KLAvJt3xPnuUbJsaXcOuqT7i9azEPHhfFlwPzWP3MDTx3Sheu+2AGI798l5d3fEGE1cSPvYfzr5+KKS4rrtJ2de8R3vhxJ16fnxP7d2bYkq8hJQXOPhHenAp/78lj0/+IFS+hZjh1UCeeTTGRdd08+N1ymPAgxB1nfAhfoWK1XEoKjB/f4MRlLU3Bs4i0PYHYR3XUvqU6z7dheZavAbB7T8Xkb2KpkZyucP07YA2HLfNgzhVQ3PA3CSIi0rpVzDrHRoYSY2/dq4zsYWYuGRXPrBtPIMpbwk+unMqlz402ZUq1p0u9fh6dcCsAt57aj96djT3AZuCfFw/DZIIlm4oJ89aQ0KwG//f1L2zPKqCnw8az1x2P1WKEaXPmzCEhIQGPx1Pl+kLLMrJC/8vB0CfIDn2W3w0u4m/fv8T9Z/TjN9eNZ1jSYMyUL9t2OiEpiSEhZTw7KRmzCeb/XESU59Jax1RosvLWCiPPy2/HdIVDy2Djo3DSRtj0Bgcy9/Hc6jLuuvJkTKF2Tr/hXvxmK0+8/kntL7Z8PDgcbSpwBgXPItIWBWkfVXtVav6FUtN2zNiI9J7R9AYHngU3fQJh0eBaBq9fBAUHm96uiIi0GhX7nQd0bR2zzvXRMyacaft+AOCp+Zv4eW8DK00cqYas3bO3FrK9czyxYWbuOH1Ale8Nj3dww1gjGOxcdjv465cbpMzr4/O1xtLo6VeMpEtkGAB+v5+//e1vbNq0iTfffLNxr8NbCrtTYOWL0GMHE7LfY/ZxP3OZeQnX+UZwkXcoY/wW+vhN2I7KufZpp76cXLKE1yOeYcLn4+Dj22DLfDAB/cbzVM7ZWG2R3PPsZ2AyERkZyZ133smzzz6L292EP/tWTHueRUTq48j9z22NCfKsX9OlbAqRnvMDk5HUeSLc8oVRwipjDbx5hZGZO7SJM9siItIqtLYyVXUqT/p5uWs13445n2/2lHDvu2v57M6TCbMGJsFldomPp3/OA+CPI6KIsoUcc80fz0ng0zW7yCvuS5T3QvKsn9XZ7g9bsnAXlREbGcYpAw/v/507dy47duwA4OGHH+aGG26ovSEz4N4CKeth3Tz4bQR8dSH4y2etewLfPsjJwMlVFhMc/t3tfeVCiOqBP7wLl/ZZy3WmUqhIiN1lEAy/Ap7/DqZ9TqJ5Nv8740o6d+5cef99991HTk4OZnP7nKNtn69KRNqFlkrGVcWRZaDKS0A1JbFF0F9TDUvaCywL8VFMqL8Pv2RUU4exMeKOg1/NhfDOsPcn+ODX4PXUfZ+IiLR6Tcq0HUD1+b0ZsjujsqykafFi/vXnK4gtzCF9fx5PzAtcRuenfs4jt8xP4v5tXNU3vNprYuyhTDo52jguuwGzP6bOdisScp03vDuW8rJXfr+fadOmMWDAAEwmE1u3bmVOReUPwOSHIX4zk/whPO0Po8/6x+D+KFj0W/j897Dzc+hpMQJnexcYeDZkxRnlJ4dMxB2fzEpfPBt9fdjt70Jx+Sy5pawQsrdh2rOKcFMpu/1dKTnxLrh9KUxdBWc8ACXGUvUbb7yR66+/vspr6dy5MzNnziQqKqruP9AglTMLJgXPItJqtVQyrioCnNgi6K+phiXtflNBZeKwb9cHMMlX1wS4/l2w2mDT1zD3T6oDLSLSDmxtzuA5LQ1SU6td5VWf35vW7JzDST9TUujy4vP8a+6zAMxavI1VO7KbPMSUnkN4c6vx+/Nv379YGeRWZ8LQcEpMmzATgaPsmlrbLfP6+HbjfgAuGHE4u/bcuXNZtWoVF110ESaTicsuvYRHpv2NLilv8pk/nCyiSCOS2YTze8Kw52+HEBNYI6DfeBh4LbxXCGe9DfdthUkfgGswXPEiXPsmWyf+l5PMLs4s+xWnlDzLkJI3GFH8EmeUPM59kf9mmu3PXFzyMK8kf0rYeQ9BjxFVE38FQhvchqfgWUTavxqSftRbG05scaQ861wAlm0uIqewNHAN9x5j1ILGBKtfhmXPBK5tERFpdqUeHzuzjUBxULd6zCA21pGru5KTm77CKzHR+H2dmMg5W1ZwZd9w/H74w3tr8Xobv1t1RWYJN139EF4/nN/LxjjX+lqvt5hN5IS8DkCk90xM/upnqQF+3HqQnMIyYiNDGdvPKHFVMet88riTGBKeDX4ff++5hK07d7Pgpee5iBA6Y6IQP0vx8BQl7Bl4CzybD+d/Bjd/DkNvgzQP2HscDnqPmun1mQrYF3Yve8Omcsj6BpnmHezwx/F+lpPXco5jva8/t5xcTdXsNjhjHCgKnkWk/XK5jE+yN240nl2ulh5Riyo1baLUtI0yL3yYGriSHQAMvQTO/ZdxPO/vsOHDwLYvIiLNZsfBArw+P5FhVrpHhwWvo6NXd6WkBLR00d9HRRMfE44ru5Bdrgn8vNfd4LwfizdlcvPibArC7IzrFsrjYxz1uq/YvJZSkwszdiK9Z9Z4XcWS7XOH9aiczT50IIP169bw8HF7MK1/D4BRXT1cNyqCr/Z35l6KGU0+DvI41VTIPaYScruOgWwfmGoJ76qb6TVBmXkHuSHvsz/sT7zy2248cfVxXH58PP/c9wPOLvb6tdNBKHgWkXbpyP1PLF5sPCcmQkZG4xtt65+0mg7PPr+1YmdgEocd6aQ7YGz5LP/Ht8POZYFtX0REmsXm/eWZtrtFYgr0Ut2jVazuGjrUeA7gCq/oUDOPXTkSgNzcflz4zFLOfnIxz363mZ0HC+q8f97G/fzm9dUUe+GMrat45ZTO2K01hE9Hv0cwQZ71CwCiPBPxVfM7t8zr45uf9wFw4Yg4KC2EH5+j8+wzyP6DjTO65RiVLUwm+N1KZq/K4d63P+RJUymrTT48QfircdgtXJ7UiyeuGcVNh34OfAdtnIJnEWmXjt7/xJw5xtc5ObXfmJZWc2btdvBJa4FlIbYQE1szC1i5vel7wI5x7qMwZKJRGuPt6yAzcIlaRESkeVQmC2vOMlVB+h3bq2sh0y7rjD0ynRCL8doen7eJ0x5byKXP/cBzXUaxcns2xWXeKvd9Ed2fKXNSKPX6OL+XjRc+ehSbtZZotWL8JXlE7PuZyf4Q/mpZxAXmRXTxx7DOdex2qeXbDpJTWMIZ9m2cuOVJeHokfPMXyN9HeJfecMF/4cy/AybomoDFaiU0rBlqbles3HO7tXLvKCpVJSLtW8X+p7ocue8KmpxVO5jOPfdctmzZwpYtWxo8I+A3FXHKYBvzfy7irZUuxvbvEtjBmS1w+YvwxsWwexXMuRx+/Q042visvYhIBxLMMlXNWUnDVZhBwoyTKfYUA2AKseMIGc+ZPe4ndWc+a3blsKb7WHjhR0ItZkb0cnBCn05Ehll5Mv5MfD4/l47qyX8H+rD6aqgmkZ8JKa9CxlrYvwEO7SABeIHyfc6hL+D1m9j83WAouRIGngXdhsKOpYTOfZ2VYYvo6nPD8vL2YvrAqffCcdeDNRTWzKrSXaw9FpvVVvmaAGxWGzG2mID8mYXszoDTTzUmHMBYuWe3B3Q5fVum4FlEBA7vu8rKgnvugdmzW+UvCZfLxfz58/H5fCxYsIAJEyY0uI1zRtiZ/3MRc9fv4x8XldI5IsCfYofa4bp34ZVz4eBmI4D+1Vywd677XhERaXHBLFPVnJU0skpzqgSZflMhOXzNXy55lF6RY/l6wz6Wz/6MVfFDycwrIWXnIVJ2HjIuNpm5dnRvHr1sBJY1Px3buM8LK16A7x+FEneVb5Xau/B94X5c+DjZb2OYyceQsnRY8KjxMJnB72MsgAk8IZFYh5wPiRdBwgVgObZ+dAWnw0n61HSyCrO45+t7ePK8J4m1xxK3NSsAf2JHrdxLTDTeG02aZLw/aoXvi5qbgmcRkQpOp/FoxVm1p0+fTkhICBaLhWnTpnHGGWc0ePZ5QLcQhvWM5ue9uXyYspvfju/f5HFN+WJK1TdEEV3gxo/g5XMh8xd46xq46RMIjWhyXyIiEjxen59tQZx5bi26Rdm46aS+3DR9Pv7n7sSVXciqHYdYvSObtbvdnL5+EfdddgHm6kpSOS2waDLkbTO+7jHCmCnuPgy6D2dD7k7On5VsfM9UzPElf+Rck5dbum0hoSAVSvMotXXl/fyRLAs9iafvuxNCbfUeu9PhxOlwMrTrUJLiKlbXBSZ4rlTflXsdjPY8i4i0ES6Xi5dffpkRI0bQu3dvlixZwoIFCxrcjslk4vqxxocDry3bQX5JDUvRGmBPXjXZu2OcRgBti4HdK+G9m8Fb1uS+REQkePYcKqLE4yPUaqZ3p5pLLLUnJpOJPl0iuDK5F/++YiRz7zqVPx9YeWzgXJQJKQ/DryKMwDm8E1z4BExeZCTN7H+a8eHxUX6xLuZt75lcnTOVonu2wO9/YtqgD/mr51aih5+LtQGB85GacxZfDAqeRaRjqi0xWCs1ffp0HA4HQ4cOJSYmhuTkZKZNm9aorNmXjoonPiacPTlFTPus8dk0XW4XqRmpuIvduNzVJBTplgjXvwfWcNgyDz79Hfh8je5PRESCa/OBPAD6x0ZgtbTjUKGmpFhHn9+WDnsXwRXhMP8G2PM9+P3Q52K4MxVG32rk+6hFkXk13aItuIvK+GzDATyOvny98QAAF4yIC+zrSkszxt3G3uO0FVq2LSJtWoMTZ8TEtJnEYEeqmHV+5JFH2LRpEyaTiWnTpnHRRRc1au9zRJiVJ68ZxbWzfuSDlN2ckdCNC0c27Be4y+0iYUZC5Z99wowE0qem43QcteTdORaufgPevhbWvQv2WCMrd7DLn4iISINV7Hce0I6XbNeYFGv+fDjrLKNk1EAr/OsUGGyFUBMMDwF/GezywPfAokfrn8vD5OO8kXbeWJrH68t28kH6LLILTqKTPYSTApW48+jEp9Bq3uM0Z5K4YGvHHyeJSEdQkTgjZXIK453jSZmcQvrUdOKiaggE4+KMT2NTUmD8+DaTPXLfvn0cf/zx3HHHHZXnLrzwQi655BL279/fqDbH9OvMlNMHAPDAR+vYm1NU88UZGYc/iS+vlZ1VmFXlQ4tiTzFZhTXsuRp8Dlz6vHG8/DmY+2ct4RYRaYVapExVM6uSFGvlj/DSozCkDH58BK43wYOd4Rq7ETCHmiC8Owy/BU6dCVuSYVHD3zucOcxOmNXMxoxcNmwdCMC5w3oEbna/IvFpxfublJRW8x6nPS0vV/AsIm2e0+EkKS4Jh81BUlzSsTOfx9zgNJJgtOLEYEcbM2YMK1asIDLy8JsZk8nEJ598wnXXXdfodu8+azAjeznILfbwh/fW4vPVsAT8hReMT+YXLzaOG+O4a+G8fxvHK1+ANy41SnyIiEirEcwyVa2B2Q/heVvhjDA48BTMvRB2/QcuCofcpRBvAb8HHL1hwDXwYj6c9TZc+TSceT04Yhr13iHKZubSUcYMbFFRVyAIS7Yr3t8MHWo8t5H3OG2JgmcRkQ4sxGLmqWtGER5i4cdtB3lxybbqL7zttsOfZt92W+M7PHEKXPMmhEbCzqUw63TYk9r49kREJGD8fn9Qy1S1lE5+uN5vZY4/nANE0nfD4zA+DNybwOeB0GjY4oFO58D7hTBhNty9HobdDnt9AdtmdNO4PpXHMfYQThoQoCXbR5vZfmZ6WxsFzyLSbrSnPTXNqX/XSP5x0VAA/vttOhv2VK1XmV1Qyod7vdy/3cqyuERj6XtTJE6E334PXQZC7m545TxY83bT2hQRkSbLzCshr9iD2QT9Ytt+acHw/F284rexlyjexM4NhNAFM15LOGwog+Pvh7s3wLmfwJuFEHspbPRAZK+g5OUY1tPB6L6dADhnaHdCWjAhW0XOmCPVmjNGAAXPItKOtKc9Nc3tmtG9OXdYd8q8fu565yc27HEzc+FWrpy5jBMemccf3l/LO6t2cVvvc9h9qLDpHXZNMALoweeBtwQ+uR2++pP2QYuItKCKWWdnZzu2kNozSLdaXo+RHftmO4mp0/kVodgwsR4v0ynhVArYNPr/4MMi6H0uxPQ+NlAOYsbqhy8dTkynTdx11uCAt90QdeaMUdbuainbtojI0eI73gy2yWTi35eP5CfXYrZmFjDx2aVVvp8YF02Z18eWA3Dvu2t5e/KJTe/U5oBr34ZF/4FF/zb2QR/cDFe9ZnxPRESa1dasAgAGtNVkYds/hUU3gXsX9LXiN5l5x1/C05SywuQ9fJ2phg8Gjq7IAQHPWD2kRzRO5wLiY+4JWJuN5XQ4cTqclTljAIil1Wbtbg008ywicrQOuleoU0QoT1w9ihCLiVCLmdMGd+XhS4bxw/0TmHvXqbxy82givKWs3JHN/xZtDUynZjOc8QBc+xZ4zbD1e2MZd86uwLQvIiL1diDXqKDQw2Gr48pWxueBS2yw/ikjcA51wOISNox5hOtNRVUD59ocXZGjFWWsbjatOGt3a6CZZxERqXTKoFh+uH8CEaFWIsKq/opwdrHzz5KN/NE+iifnbaJrp3rWt6yPIRfCplEwei8c2AgvnQnXvws9jw9cHyIiUqsDuSUAdItqO8FzZ78JZ9qzMCoUTGY4+yEIOQH+Oo6yB2Ma3qDTaTwcDiNj9dE6wuq0uv4MOjDNPIuISBXdomzHBM4Vrnj6L1w4Ig6Pz89TX+dg8ocFruOiKPjtd9BtGOTvh1cvgF++DFz7IiJSqwN5xsxzt+gA/t8eRAP9Zn7ETkTuZijxw9jpMO5OsARx/B10dZoYFDyLiEi9mUwmHr1sOD2ibezN8dKp7DeB7cDRC379NQw4E8oK4Z0bYLneqIiINIcDeRUzz60/eB7vt7AcO4OxUBbaGV4ugG5jGtaIkmJJAyl4FpGOqyMsvarDlC+m1PCNGs4DMfZQnrjmOExAlPd8wr0BSB52JFu0sWQ7+RbAD1/fb2Ti9tVzz5qIiDTK4eC5dS7briivdIM/hHnY6YKZVSYf2Sc8Apm+Y68Pjam2HFN0fP/DSbGSk43nACbFOroMlM1qI9auhFvtgfY8i0jHpaVX7MnbU8M3ajhfbtyAWC5JjuCTlAK6lN5Jhi0dr+lQ4AZmCYGJT0Hn/jDv70Ym7pydcMXLENZGs8CKiLRiXp+fg/nlwXMrXbbtdDhxnfscsV/ciwk/h/qfRo8LHqO7q6D66+1xpE9NJ6swq/JcrD2WeEd5Uqysw+eJjQ1YUqyKMlBZhVnc8/U9zL58Nk6HEm61BwqeRUSkUa47MYr3UtcR6h9Ap7LfkBX6WGA7MJng5Lsgpg98fBts+hpePR+ufw+i4wLbl4hIB3ewoASf3/ivt0tEaEsPp3o7l9F17v2AnyWxAzh10id0MpvBlVrjLRXlmI79hjOoGaSPLAPVZgNnrdA7hpZti0j7pv1M1XK5XaRmpOIudpOakYrL7WpwGyFWE1mhTwFg956K1RekX7LDLoWbvwB7LOxbZ2Ti3rchOH2JiHRQFZm2u0SEYbW0whBh/0Z4+1rwlkDChZw6ZaVR7lCCRyv0jqF/cSLSPsXGBnU/U1uWkZ9BwowEkmcls9i1mORZySTMSGhUAF1m3k6heTkmzDg8VwdhtOX7snuPNjJxxw6G3D3wyrmweX5Q+hMR6YgyW3OyMPdumHMFFLuh91i48mWwaAFtoMRHaYa5vhQ8i0j75Czfz5SSAuPHG89paUFdotVW5BTnUOwprnKu2FNcZU9YQ7hD3gUgwns6+9yeJo/vaJX7sjv1hVu/hb6nQmk+vHU1rHkr4P2JiHRErbZMVWE2zL4c8vZCbAJc9w6EhFd/bRtYbeZyu5q06isYZk7UDHN96SMbEWm/KvYzDR0KSUktPZpmV5Ht88hA2Wa1EWOLCWg/pebNFJlTCPcl8/HqAi4YEtDmqwrvBJM+gs9/D2vfhk+mQFEOnHRHEDsVEWn/KpZtt6qZ57IiePs6yEqHqJ4w6UOwdz72uiNXm1Vo6mqzAO33PXJW1+V2kTAjgWJPMcmzkrFZbaRPTW+7e6I7IM08i0j710H37FRk+0yZnMJ453hSJqeQPjWduMjAJ9tyW43Z5wUbC9mbUxTw9quwhsIlz8NJU42vv3kAvn8E/P7g9isi0o61yjJVn06FXcshzAGTPoCY3tVfF4zVZgF673DkrG5WYVaVD7SbsupLWoaCZxGRdszpcJIUl8TQrkNJiksK2qfbJZaNFJvX4fHBrMXbgtJHFWYznPMITPib8fXix+DLP4Dv2DqfIiJSt1a3bNu1HDZ8ACYLXPcWdB9W+/VOp7HKzOEwnrVNS4JAwbOISAfQHPuZKmaf317pqnwTFlQmE4z/I1z4BGCC1S/DR78BT2nw+xYRaWcqZp67RraC4Nnvh/nTjOPjJ0HfU1p0OCIVFDyLiEhAFJvXkhAXQonHx0tLtjfsZpcL3G4j0YurgQlURt9qZF41W2HDh/DO9VDWDMG7iEg7UrnnuTXMPG+eB64fwWqD0/7c0qOReoiPjye+A9SFVvAsIiKBYYIrR0cCMGf5TrIL6jkD7HJBYiIsXmyUFUtMbHgAPfwKuO5dCLHDlnnw8WTweRv4AkREOia/339EqaoW3vPs88F3/zSOx/wWHO0/IGsP0tPTueqqq1p6GEGn4FlERA5zuYzZ30bOAif1DWN4fDSFpV5e/aGes89ZWVBYCHPmGI/CQuNcQw06yyhhYgmFjZ/C3D8riZiISD24S/2Ueo2cEV1bOtv2hg9h/wYIi4ZT7m3ZsYgcRcGziIgAELI7w5j1TU5u9CywyWRi6hmDAHjthx24i8rqP4DEROPRFP1Pg8teAEyw6kVY/N+mtSci0gEcKDZW6kTbrNhCLC03EE8pLHjEOD7599WXpRJpQQqeRUQEAGt2zuEZ4JSURs8CnzO0O4O7R5JX4uEfn26gzNv4DNgutwt3sZvUjFRc7noG8cMvh/P/YxwveARSXm90/yIiHcGBYuP/6W7RLbxk+6c34NAOiOgGY6e07FhaCbPZjNmskK21sLb0AEREpJVJTDTKfDSS2WzigfMTufX1VXyyZi8HC0p5/oYkomwhtd5X6vUTajFVfu1yu0iYkUCxp5jkWcnYrDbSp6bXr9zW2Nsgfz8seRy+uBsiusKQCxr9mkRE2rMDRcbMc7eWXLJdWgCL/s84Pu1PEBbZuHbaWdKqyy+/nK5du7b0MKScPsYQEZGAO2NIN166+QTsoRaWbM7i6heWs89dfQbsVZmlXHnDfxj28T4+2lFYeT6rMItiz+F7ij3FZBU2YBZ8wt+MEid+H3zwK6NmqIiIHKNy5rklg+cV/zM+9IzpA0k3N76dmcEvzdicOnfuzCWXXNLSw5ByCp5FRDqYWHssNmvVpXk2q40YW0xA+5kwpDujhi4kNjKMtIxcLnv+B37Zl1v5/bSMXH792iquWnCQ1b2GUeaDP61ys6TvqMAMwGSCiU/D4PPAUwxvXQPZ2wLTtohIO3KgqIWXbRdmw9KnjeMJD4I1tGXGIQ13yy3G71uTCUJDYeRI+Oijlh5V0Ch4FhHpYJwOJ+lT00mZnMJ453hSJqeQPjWduKg444K0NCPTdlpak/vK92/k4zvGMaBrBBnuYq6a+SOf/LSHe95dwwXPLOH7Xw5gMcH1P83lwl42PH6Yculf+PlQAxKN1cZihStfhfgToDgH3r0JSgvrvE1EpCOpSBjWYjPPPzwFJW7oNgyGX9kyY5DGGzcONm+GlSvhtNPgmmtg3bqWHlVQKHgWEemAnA4nSXFJOGwOkuKSjH3EsbFgt8OkSUam7UmTjK9jY6tt4+gZbJvVRqz92Gt7d7bz0ZSTGdOvM3klHu5+dw0f/7QHvx8uHBnHvPO68q9vn+OJsTGc2DWU/DA7v1qSTWZugOo0h9rhmtnGvuf964090CphJSJSqWLZdouUqSrJh1WvGMcTHgQlx2p7wsNh4EAYNQqeeQYiImDhwpYeVVDoX6eIiBicTmO2OSUFxo83ntPSjPPVXX7UDHZtybwc9hBm3zqGS0b1BODUQbF8PvUUnrs+if5RRu7KMIuJF07uxODMnRwo9vHIp9mY/RGBeW3RPeGq18BkgXXvwsoXA9OuiEg7kFmZMKwFlm2vfx9K86Bzf2ObjbRtXi+UlkK3bi09kqBQ8CwiIoc5nUambYfDeK4hcK68/IgZ7LqyYIdZLTx97fH89LezmX3rWEb0clS9IC0Nx9Z0Xnv/H3QPN7Mr20PX0gfBH6DCEH1PgbMfMo6/eUAJxEREymVWlqpq5plnvx9WvWwcn3CrZp3butxcuPtu6NcPLr20pUcTFPoXKiIix2pgqY/4qPpf3yniqEQwRy4XnzSJnt5CXruoP+GhJmy+EcSW3Qt+U/WNNdRJv4Nhl4PPA+/dDHn7A9OuiEgbVRgSRr7H2MrS7Hued68yttNYbTDq+ubtuwXUd7sT0LYScS1YADab8cH7a6/Bgw8aX7dDCp5FRNoYu92O3W4PbicNLPUxc2LV611uF6kZqbiL3bjcrtpvrlgufsRS8cSkBP50YSf8eIjwjie27D7w114nul5MJrj4WeiaCPn74P1bwBug5GQiIm3QgYjOAISHWIgMC9BKn/pa9ZLxPPwKsHdu3r5bQMV2pyOTdda6aqutJOIaOxbWrDF+hz/xBNxxB0yf3tKjCgoFzyIibcw//vEPXnvttYC01ZAZ4/pyuV0kzEggeVYyi12LSZiRUDWAnjLl2JucThg6tMpS8eOcYWSFPI6fMiK84+le+jB55UsLmyQsEq6ZA2HR4FoG8/7e9DZFRNqoA5FG0NotOgyTKUCrfOqj4CD8/LFxPPrW5uu3hTkdzqrJOmvTVhJx2e0wZIjxO3zyZPjjH43xtkMKnkVE2pguXbrQu3fvgLR19IxxIGQVZlHsKa78uthTTFZh1uEL9uypYTDHjqXQuoQDof/ARwE233D+8l4Wu7IDUGoqdiBc9j/jePnzsCe16W2KiLRBByI7AS2wZPun2eAthbhREJ/cvH23RW0pEZfPZyw1b4cUPIuISKtWbFnHvrA/4TFlsueQl8tnLmPDHnfTGx5yIRx3nXH87d9UvkpEOqSKZdvNmmnb54PV5eWpRv+m+fptq1p7Iq6iItiyBTZuNPY8P/443HxzS48qKBQ8i4hIq1dm3sm+sD/SJ9ZKZl4JV7/wIwvSDzS94QkPGolqdi6F9LlNb09EpI2pmHlu1hrPW7+DnJ1gcxj7naV6bSUR17JlMGgQHHcc/Otf8MAD8I9/tPSogkLBs4iItAle00EeubILJw/sQmGpl9+8vppFmzKb1qijF5x4h3E87+9KHiYiHU7lzHNzlqmqSBQ26gYIDXICzFao3vlG2kIirtdeM1Zu+f1QVgabNsGf/wwWS0uPLCgUPIuISPNxucDthtRU47iBIsLMvHrLGCaOjMPr8zP1rVS2ZuY3bUyn3AP2WDi4GVJfb1pbIiJtTGblnudmmtE8tBM2fWMcn/Dr5umzlal3vpEOlIirrVDwLCLSVrSlmo/VcbkgMREWL4bkZOO4EQF0qNXM41cfR3KfTuQVe/jN66txFzZhxtgWDaffbxwvmA7FuY1vS0SkjanItt1sy7ZTXgP80O80iB3UPH22F+04EVdboeBZRKQtaSs1H6uTlQWFhTBnjvEoLDTONUKY1cILNyYTHxPO9qwCfvdWKh5vE8pYJd8CXQZCYRb88FTj2xERaWMORDRjtm1PCaS+YRwrUVjdOlAirrZCwbOISFvSVmo+1iYx0Xg0UWxkGC/edAL2UAtLt2TxyJdpjW/MEgJnP2Qc//gcuGsopyUi0o6Uev0csjuAZgqe0z43PqSMioOEC4LfX1vXgRJxtRUKnkVE2qq2VPMxSIb2jObJa0YB8NqyHby5YmfjG0u4AJzjwFMM3z8SmAGKiLRimcXGih2rCTrZm2E5cMprxnPyLWCxBr+/tqyDJeJqKxQ8i4i0Ra295mMzOndYD+47NwGAf3z6Mz9uPdi4hkwmOKc8aF77NmS0keXwIiKNdKDYC0BXmxmz2RTcznJcsGMJYDKybIu0QQqeRUTakrZS87GZ3XH6AC4Z1ROPz8+UN1PIzCtpXEO9kmH4lYAf5v0toGMUEWltDpTPPHcLb4bZzHXvGs/9ToWY3sHvTyQIFDyLiLQlbaHmYwswmUz854qRJMZFk1NYxvMLtzS+sTP/DuYQ2LYQXCsCNkYRkdbmQNHhmeeg8vth7TvG8XHXBbcvkSBS8Cwi0pZ0oJqPsfZYbNbDs+o2q41Ye+zhC6ZMqXK9LcTCXy4YAsCby13szSlqXMed+sBx1xrHS/7buDZERNqAij3P3WxBnnnekwIHt4A1HBIvCm5fIkGk4FlEpC1rizUf09KMRx2cDifpU9MZ7xxPyuQU0qem43Q4ISMDUlON0h2pqcbX5U4ZGMuJ/TtT6vXx7PebGz/GU+4Bkxk2fwsZaxvfjohIK1Yx89wtPMghwdq3jefEiyAsKrh9iQSRgmcRkbakDdR8rHHGODbWmDmfNMl42O3GuVo4HU4cNgdJcUlG4AzwwguQnAyLFxvPL7xQeb3JZKpMHvbe6t1szypo3IvoMgCGX2EcL9bss4i0TweaY+bZUwobPjSOK1b1iLRRCp5FRNqSNlDzsWLGOGVyCuOd4w/PGDudxozz+PHGnu20NONcQ912m3F/RTu33Vbl28l9OnNGQle8Pj9Pzd/U+Bdyyr3Gc9rnkJne+HZERFqpymXbwZx53vwtFB2CyB7Q//Tg9SPSDBQ8i4i0FW2o5qPT4SQpLgmHzXF4xhiMYNnhMPZs1zNwjo+Kr3oiLs64v6KduLhj7vnDOcbs82dr9/LLvtzGvYjuQ2HIRMAPS55oXBsiIq1YRamqbsFMGFaxZHvk1WBufb+vRBpCwbOIiLRqMyfObPA9w+MdXDgiDr8fHv+2CbPPp/7BeF7/PmRvb3w7IiKtjNfnJyvYy7YLs2HTN8axlmxLO6DgWURE2qV7zh6M2QTzNu5nza6cxjUSnwQDzgS/F354KpDDExFpUdmlPrx+MPl9xAZr5nnDh+Argx4joPuw4PQh0owUPIuISLsy5QujhNXAbpFcdnwvAB7/tgl7lsf/0Xhe8xbk7m3q8EREWoUDRcasc5dCN1azKTidrHvXeFZtZ2knFDyLiEi7sidvT+Xx3WcNIsRiYsnmLH7cerBxDfYZB85x4C2FZc8GaJQiIi2rYr9z1/xDwekgawvsXgUmCwy/Mjh9iDQzBc8iIhI0xyT7ama9O9u5ZnRvAP77bTp+v79xDVXMPq9+FQqyAjQ6EZGWk1k+89ytIDs4Hax7x3geeCZEdQ9OHyLNTMGziIgETWOSfTWWy+0iNSMVd7Gb1IxUXG4XAHdOGESY1UzKzkMs2dzIwHfABOh5PHiK4MfnAjhqEZGWUZlpOxgzzz4frC1fsj3ymsC3L9JCFDyLiEib53K7SJiRQPKsZBa7FpM8K5mEGQm43C66R9u4YWwfAJ7+bnPjZp9NJji1fPZ51UtQ7A7g6EVEmt+Bikzb+UGYeXYtA7cLwqJhyIWBb1+khSh4FhGRNi+rMItiT3GVc8WeYrIKjZnm207rT2j57HOj9z4nXABdh0BJLqx+palDFhFpUQcql20HYeZ53XvG89BLICQ88O2LtBAFzyIi0rziA78POtYei81qq3LOZrURa48FoHu0jevK9z4/9d3mxnViNsPJdxvHPz4PZcW1Xi4i0podXrYd4Jlnv/9wbedhlwW2bZEWpuBZRESa18zA74N2OpykT00nZXIK453jSZmcQvrUdJwOZ+U1t58+gFCLmZXbs1m+rZGzzyOuhOheUHAA1r4VoNGLiDS/imXbXQM985yxFvL3QUgE9D0lsG2LtDAFzyIi0i44HU6S4pJw2BwkxSVVBs4VicQyCtM4Y6gxO/30/EbOPltCYNydxvEPT4PXE4ihi4g0Kz9woChIM8+bvzWeB5wB1rDAti3SwqwtPQAREZFgqUgkVrEf2uLrSjyz+HHbQVZuz2ZMv84NbzTpRlj0Hzi0A9I+heFXBHbQIiJBlhsWQakx8Rz4Pc+bvjaeB50T2HZFWgHNPIuISLt1dCIxrzmTfMt8AJ79vpGzz6ERMPZ243jpk8b+PhGRNuRApPHBYVSICZunNHAN52fCnlTjWMGztEMKnkVEpF2Jj6o9IZnb+j4WMyzZnEXKzkbOuIz5rbGfb9962Ppd49oQEWkhFcFzN1uAQ4Et8wA/xB0H0XGBbVukFVDwLCIi7crMibUnJPOaD3B6olE65ZnGZt62d4bkW4zjJU82rg0RkRaSZY8BoKvNEtiGK7JsDzo3sO2KtBIKnkVEpG1qQsmrK06IxGI2sWhTJmt25TSukZN+B+YQ2LkUdq1s9FhERJrbofAoADqFBTAU8JbB1u+N48EKnqV9UvAsIiJtUxNKXvWIsXLpKCP4fnr+psY14oiHkdcYx0ufavRYRESa26HwaABiQgMYCrh+hJJcsMdCz6TAtSvSiih4FhGRDmnqhIEALEjP5GB+SeMaOfkuwATpX8KBXwI3OBGRIMqpmHkONQWu0col2+eAWSGGtE/6ly0iIh1Sv9gIBnWLBGDVjkYmDus6GBInGsc/PB2gkYmIBFeOLQjLtivqOw9Wlm1pvxQ8i4hIhzW6vM7z6h3ZjW/k5HuM5/XvQc6uAIxKRCS4KvY8B2zZdvY2yNoEZisMmBCYNkVaIQXPIiLSYY3pawTPq5oSPPdKhn7jweeBH58L0MhERIInJ9AJwzaVzzo7TwKbIzBtirRCCp5FRKTDqph53rA3l4IST+MbOqV89jn1dSg4GICRiYgET0XCsE6BmnneXL7fWVm2pZ1T8CwiIh1WfEw48THheH1+fnLlNL6h/mdA3HFQVggrZwVsfCIiwZATyGXbJfmwY6lxrPrO0s4peBYRkQ5tdN9OAKxsytJtk+nw7PPKF4w3kyIirVCZz09eWAQQoGXb2xaCtxQ69YPYQU1vT6QVU/AsIiIdWsXS7VXbmxA8AyReDJ37Q9EhSH0jACMTEQm8nFIfACbAERKAUlVHLtk2BbD0lUgrpOBZREQ6tIqkYT/tOkSpx9f4hsyW8rrPwI8zwFMagNGJiARWTonx/1x0qAmLuYnBrt8Pm+cZx4NUokrav0YFz2vXriUpKYmlS5dWOT9z5kz69etHeHg4EyZMYNu2bTW2sWHDBsaPH094eDj9+/dn9uzZjRmKiIhIkzy+8k90sodQXOZjw1530xo77jqI7AG5e2D9+4EZoIhIAB0q9QMBSha2bx3kZUBIBPQ9pentibRyDfqpSU1N5eqrr+akk07ip59+qvK9999/n3vuuYeHHnqIpUuXUlZWxsUXX4zPd+yn+Lm5uZx99tn069ePFStW8Otf/5pbbrmF5cuXN+3ViIiINNDe/D0k9wnQ0m1rGJx0h3H8w1NQze9AEZGWdKh82XZAkoVVlKgacIbx/59IO9egn5qPPvqIsLAwvvjii2O+N336dG677TZuvPFGkpOTefHFF/n5559ZtGjRMde+9tpreL1eXnzxRUaOHMmDDz7I6NGjmTlzZuNfiYiIyFFi7bHYrLYq52xWG7H22CrnxvQzkoY1qd5zheRfGXVOszZB+ldNb09EJIAqlm0HJHjescR4HnBG09sSaQMa9FPz8MMPM3v2bPr371/lfE5ODj/99BPnn39+5bkhQ4YQFxdX7WzyggULmDBhAqGhoZXnJkyYUOvMc0lJCbm5uVUeIiIitXE6nKRPTSdlcgrjneNJmZxC+tR0nA5nletGl+97Xr3zED6fv2md2qJh9G+N46VPGHsCRURaiYqZ5yZn2vaWwe7VxrFzXBNHJdI2NOinxlRDBr3t27cD0K9fvyrnnU4nu3fvPub6bdu21fvaCtOnT8fhcFQ+evfu3ZChi4hIB+V0OEmKS8Jhc5AUl3RM4AwwPN5BeIiFnMIytmQGoMzU2NvBaoM9KYdnZkREWoGcymXbTUwWtm8dlBWALQa6Dmn6wETagIBk287PN95o2O32KuftdjslJSXVXl/fays88MADuN3uyseuXbsCMHIREREIsZg53hkDwMqm7nsGiOwKx99oHP/wdNPbExEJkIpl201OGLbzR+PZeRKYVcBHOoaA/EsPCzMSBJSWVi3LUVxcfEyQXHF9fa898p7o6OgqDxERkcZyuV2kZqTiLnaTmpHK4DgLEKB9zwAn/Q4wwZb5kLU5MG2KiDRRwJZtuyqC5xObOCKRtiMgwXN8fDzAMbPBu3btOmZ/dMX19b1WREQk0FxuFwkzEkielcxi12KSZyXzZMrvgABk3K7QuR8klOcCWfFCYNoUEWmiilJVTUoY5vcfDp77aL+zdBwBC5779u3LvHnzKs9t2rSJ3bt3c+aZZx5z/SmnnML333+P1+utPPfdd99Ve62IiEigZRVmUewprnIuz78Oixn2uovZfagwMB2Nvc14XvMWFDexhrSISABULttuysxz1mYoPAjWcIgbFZiBibQBAdugcO+99/L000/z/vvvs3r1am699VYmTpzIiBEjALj55puZMWMGAL/5zW/Iyclh6tSpbNiwgYcffpj169fz+9//PlDDERERaRC/qYT+XUOAAC7d7ncadE00kur8NCcwbYqINEFA6jy7lhnPvU4Aa2jt14q0IwELnqdOncq9997LHXfcwRlnnEGfPn2YPXt25ffT0tLYuXMnYMxUf/755yxdupTk5GQ+/vhjvvrqK/r06ROo4YiIiDRYYrzxJnDl9kOBadBkOjz7vHIW+Ly1Xy8iEkR+v78y23aTEobt1H5n6Zisjbmpb9+++I+qW2kymXjooYd46KGHqr1n5cqVVb4+7bTTWL9+fWO6FxERabD4qPg6rxnaM5TPUgtYtSObKV9MYebEmU3veOQ1MH8aHNoBm76BIRc0vU0RkUYoKPVSZsTOdAprQqmqipln50lNH5RIG6K88iIi0iHUJxAe0tOYed5yIJ+dhw4GpuNQOyTfbByv+F9g2hQRaYRDBUa1m1BPKeGWRgbP7j2Q4wKTGXqPCeDoRFo/Bc8iIiLlosPNDOwWCUBBQffANTz6t2CywPZFsH9j4NoVEWmAnMIyADoV5WEyNTJ4rsiy3WMkhEUFaGQibYOCZxERkSMkls8+H3J3JjUjFZfb1fRGY3pD4kTjWLPPItJCDhUaM8+dinIb30hlfWct2ZaOR8GziIh0OLH2WGxWW5VzNquNEk8Jc375BwCZOQ6SZyWTMCMhMAH02NuN53XvQWGAsnmLiDRARfDsKM5vfCMVycL6KHiWjqdRCcNERETaMqfDSfrUdLIKsyrPxdpjySrMIs//Ew4g1D8Qsz+KYk8eWYVZOB3OJnZ6krHMcd86SH0dTrmnae2JiDSQu6hi2XYjZ56LDsGB8q0nmnmWDkgzzyIi0iE5HU6S4pIqHxXBsdecRalpOyYshHuTA9ehyXR49nnlS+D1BK5tEZF6OFRweM9zo+xaCfihy0CI7Ba4gYm0EQqeRUREjlJkWQFAuC/AmWSHXwH2WMjdDb98Edi2RUTqULFsO6axwfPOihJVqu8sHZOCZxERkaMUWlYCEO49AfwB3OEUYoMTfmUcL38+cO2KiNRDTkXCsOJGLtuuTBY2LkAjEmlbFDyLiIgcpdS0GS+HMGPH5hse2MZH/xYsobBrRfkSSBGR5nGovFRVo2aey4pgT6pxrGRh0kEpeBYRETmayX/E7HOAl25HdYeRVxvHy54NbNsiIrXIaUqpqj2p4CuDyB7QqV+ARybSNih4FhERqUbFvme7byx+vz+wjZ801Xj+5QvI3hbYtkVEalAx89yohGGuI/Y7m0wBHJVI26HgWUREpBrF5rX4KMHq747rYIAzY3dLhIFng98Hy2cGtm0RkRo0KWFYZX1n7XeWjkvBs4iISDX8phKKzWsBWLWtOPAdjLvTeP5pDhRmB759EZEjeLw+8oqNDwIbvGzb5z2co0H1naUDU/AsIiJSg4ql26u2lQS+8X7joccIKCuE1a8Evn0RkSPkFJVVHjuK8xt28/4NUJoHYdHQfViARybSdih4FhERqUFRedKwzfvLOJAX4NlnkwnG/d44XjkLPEEI0EVEylUkC4sOMWH1+xp2s2u58dx7LJgtAR6ZSNuh4FlERKQGXtMhSkybAPg+7UDgOxh2GUTHQ/5+WP9+4NsXESlXmSwsrBFv//f+ZDz3OiGAIxJpexQ8i4iI1KJi6fb8YATPlhAYe7txvGwGBDqrt4hIuZyKGs+hjXj7n2HkfyBuVOAGJNIGKXgWERGpRUW956VbMikq9Qa+g+SbITQKMtNgy3eBb19EhCMybTc0eC4thMxfjOO44wI8KpG2RcGziIhILcpM24mNslBc5uOHLVmB78DmMAJogGXPBL59EREO73nuFNrAGs37fzbK6kV0g6geQRiZSNuh4FlERKQ2JhjdPwyA737ZH5w+xt4OJgtsXwQZ64LTh4hUNWVKS4+gWVXseY5p6J7njDXGc89RRqJDkQ5MwbOIiEgdRvezAca+Z58vCPuSY3obycMAfpwR+PZF5Fh79rT0CJrV4ZnnhgbPFfudtWRbRMGziIhIHYbFhxIZZiUzr4R1e9zB6WTcVON5w4fg7lhv6kUk+A4VlGfbVvAs0mgKnkVEROoQYjUxfnAsAN+lBWnpds/joe+p4PPAiv8Fpw8R6bAqE4Y1ZNm2pwQOpBnHCp5FFDyLiIjUx1mJ3QH4esM+/MEqKTXuTuM55TUozg1OHyLSIVWUqmrQzPOBNPCVQXgncPQO0shE2g4FzyIiIvVwZmJ3bCFmNh/IJ9V1KDidDDwbYgdDSS78NDs4fYhIh1Qx89ypITPPRy7ZVrIwEQXPIiIi9eEID+GikT0BmLPcFZxOzGY4qXzv8/KZ4PUEpx8R6VD8fn/lzHNMQ0pVab+zSBUKnkVEROpp0ol9APhyXQbZBaXB6WTkNRDRFdy7YOMnwelDRDqUwlIvpV4f0MBl2wqeRapQ8CwiIlJPI3s5GB4fTanXxwcpu4LTSYgNxkw2jpc9C8HaXy0iHUZOkTHrHGoxY7fWc+bZ64H9G4zjuFHBGZhIG6PgWUREpJ5MJhOTxhqzz2+ucAWn5jPACbeCNRwy1sDOH4LTh4h0GIf+Og2AGHsIpvruXc7aBJ5iCI2CTv2CNziRNkTBs4iISANcPKonUWFWdh4sZOmWrOB0EtEFRl1vHC97Njh9iEiHkZNtZO/vZA+t/02VS7ZHGvkYRETBs4iISEPYQ61cnhQPwJsrdgavo5N+B5hg09eQmR68fkSk3TtksQHgsIfU/6bK4HlU4Ack0kYpeBYREWmgG8oTh81PO8A+d3FwOukyAIZcaBz/+Fxw+hCR9s3lgtRUckrLk4XRgAz+ShYmcgwFzyIiIg00uHsUY/p2xuvz886qIJWtAhh3p/G89h3IPxC8fkSk/XG5IDERkpM5tMf4/6PTFx9DRkbd9/p8sG+dcazgWaSSgmcREZFGuOFEJwDvrNyFp7wETMD1HgvxJ4C3BFa9HJw+RKTtK59hrny4XJCVBYWFMGcOh35tZPCPycuGnJy628veCqX5RuLC2EHBHbtIG6LgWUREpBHOG96DLhGh7MstZn5akGaFTSY46Q7jOOVV8ASptrSItF1HzDBXPhITD88wJyaSE9kJgE5FefVrs2LJdo8RYLYEYdAibZOCZxERkUYIs1q46oTeQJAThyVeDJE9IH8/pH0WvH5EpG06YoaZlBTjubCwygzzoULjg7f6B89rjGct2RapQsGziIhII10/xonJBEs2Z7EjqyA4nVhC4IRfG8crXghOHyLS9iUmQlKS8XyUQ4VlAMQU5davLSULE6mWgmcREZFGcnaxM35QVwDeWhnExGHJt4A5BHavhL0/Ba8fEWmXchoy8+z3K3gWqYGCZxERkSa4sbxs1dsrXLjLZ3cCLqo7DLvMOF4xKzh9iEj7lJbGodwiADoV12PmOWcnFLvBEgpdhwR5cCJti4JnERGRJpgwpBtDekSRV+Lh5R+2B6+jsbcZzxs+hIKs4PUjIu1DTAzY7XhvvIncMr9xCo9xvjYVs87dhoI1NKhDFGlrFDyLiIg0gdls4q4zjVIury7dHrzZ5/hk6Hm8UbYq9fXg9CEi7UdcHKSl4f5hReWpmNXLjfMAaWnG42hasi1SIwXPIiIiTXTusB4kdA/y7LPJBGPKZ59XvQJeT3D6EZH2w+nk0EBj6XWUzYq1bx+IjQW7HSZNMh52u3GuQkXw3HNU849XpJVT8CwiItJEZrOJu84qn33+YTvuoiDNPg+/HOyxkLsb0r8MTh8i0q5UJguzly/BdjqNGeeUFBg/3jh2Oo3v+f2wd41xrJlnkWMoeBYREQmA8ypmn4s9vLI0SLPP1jAj8zYocZiI1MuhgvIyVfaQwyedTqOs1dChhwNngLwMKMwCkwW6DWvmkYq0fgqeRUREAuDI2edXgjn7fMKvjTe2O5fCvg3B6UNE2p60NEhNPWYf86HymecYezXJv2bOrPp1ZbKwRAixBWOUIm2agmcREZEAOXL2+dVg7X12xEPiRON4pWaf26r8fPjb32DQILDZoHNnuPXWlh6VtElH7mFOTj5mH3NOeRLDTkfOPNekoo68lmyLVEvBs4iISICYzSZ+X555++WlQZx9rkgctu49KDoUnD4kqK6+Gj7+GJ58ElavhjlzoFevlh6VtElH72FOSamyj/nQ0Xuea7Mn1XjueXywRivSpil4FhERCaDzh/dgcPfI4M4+9xkH3YeDpwhSVLaqrTl4EObOhX//GyZOhOHD4YIL4J//bOmRSZtVsYfZ4TCej9jHfKiwmj3P1fH7YW9F8JwUrJGKtGkKnkVERALIqPs8GAji7LPJBCfeYRyv+B94SgLfhwRNeDhYrbByZUuPRDqCY7Jt18S9CwoPgjkEegxvhpGJtD0KnkVERALsyNnn15ftCE4nI66CqDgjO+7694PThwSF3Q533QUPPwzXXgvbg7RAQQSOTBhWx8xzxZLt7kONzP4icgwFzyIiIgFmNpv43RkDAfgwdTd+vz/wnVhDYeztxvGyZ8HnC3wfEjT//a+R6Hj+fEhMNJKHeb0tPSppjw4nDKtj5rkiWZiWbIvUSMGziIhIEJyV2J1Qq5mdBwvZtD8/OJ2c8CsIjYLMX2DLvOD0IUFz++2wbRvcey9Mnw6/+lVLj0jao/oHz+Uzz/EKnkVqouBZRESkAaZ8MaVe10WEWTl1oFEq5tuf9wVnMDYHnHCLcfzDM8HpQ4IqOhr+9S945hmYPRt27GjpEUl7U69l2z4f7F1jHCvTtkiNFDyLiIg0wJ68PfW+9pxh3QH4duP+YA0Hxk4BsxV2LoXdKcHrRwKmuhX248YZz2VBqm4mHVNRqZcSj/EPrlNELTPP2dugJBesNuia2EyjE2l7FDyLiIiUi7XHYrPaqpyzWW3E2mMb1d6Zid0xmWD9Hjd7c4oCMcRjOeKN5GEAy54OTh8SUC4XnH02fPABbNwIixYZS7eTkmDgwJYenbQnFbPOIRYTEaGWmi+sWLLdYyRYrM0wMpG2ScGziIhIOafDSfrUdFImpzDeOZ6UySmkT03H6XDWfXM1YiPDOKFPJwDmBXP2edydxnPa58YMkrRqnTsby7XvvhuOPx5uugkGDYIvvzSqkIkEyuEl26GYavvHtUf7nUXqQ8GziIjIEZwOJ0lxSThsDpLikhodOFc4Z2gPAL7dGKR9zwDdh8HAs8Dvgx+fC14/EhDR0fDhh7B7N5SUwM6d8MIL0KNHS49M2puUnYcA6BZVR+kpZdoWqRcFzyIiIkFUse95+bZs3IVB3NA67vfG809vQsHB4PUj0ta5XJCaCm63cdxOlXp8/G/hVgCuGd275gu9HshYaxwrWZhIrRQ8i4iIVCM+Kj4g7fTpEsGQHlF4fX6+Tw/i0u1+4yHuOPAUwaoXg9ePSFvmchmFtZOTYfFi4/ioALq+GfVbu49/2s1edzFdo8K4+oRaguesdOP/jdAo6KJN9yK1UfAsIiJSjZkTZwasrXOGGrPP32wIYvBsMsHJdxnHK16A0sLg9SXSVmVlQWEhzJljPAoLjXNHaEhG/dbK4/XxfPms823j+2MLqSVZWMV+556jwKzQQKQ2+gkRERGpB5fbRWpGKu5iN6kZqbjc9V/uec4wYzProk2ZFJd5gzVESLwEYpxQlA0rZwWvHxFp1b5Yl8HOg4V0jgjl+rF15G2oyLStJdsidVIuehERkTq43C4SZiRQ7CkGIHlWMjarrd6ZuIf1jKanw8ZedzFLN2dxVvlMdMBZrHDa/fDpHbDkCTj+RojoEpy+RNqi2Fiw22HSJONru9041474fH5mLNgCwK2n9MMeWsfb/YpkYcq0LVInzTyLiIjUIaswqzJwrlDsKSarMKuGO6oymUyVs89BzboNcNy10GMElLhh8f8Fty+RtsbphLQ0SEmB8eONY2fTMuo3SUXysopHABKYff3zPrYcyCfaZuWmk/rUfrGnBPZtMI418yxSJwXPIiIidYi1x2Kz2qqcs1ltxNqPmLGaUn2SoYrkQxX7nuenHcDr8wdnoABmC5zziHG86iU4uDV4fYm0RU4nJCWBw9HygXNF8rKKRzUJzBrCDzz7vTHrfMvJ/YiyhdR+w/4N4CuD8M4QU0egLSIKnkVEROridDhJn5pOyuQUxjvHkzI55dgl23uqJhmq2CO9MXMjLreL0f064wgPIbugtLL2atD0Px0GnQM+D8z/R3D7kkbbuHEjWVn1W70g7dCRyctSUmpMYFZFHTPV30X2IS0jl4hQC78+uW/dY6is73y8kXRQRGql4FlERKQenA4nSXFJOGwOkuKSat3rXLFHOnlWMotdi0mYkUBG/m7OHNINgG9/DvLSbYCzHwKTGdI+h50/Br8/abBrr72Wp556qqWH0f7VsCqk1UhMNGbCExOP+VaVsll1zFT7/X6e7Wosvb7xpL7E2EPr7nuP9juLNISCZxERkaaomAlyuytngo7eI12xP/qcYcbS7W837sfvD+LSbYBuiZB0k3H87V8h2P1Jg5WUlFBSUtLSw2j/9rTd0lNVymbVMVO9dEsWa8O7Ywsx85tT+9Wvg8pM2wqeRepDwbOIiEhjHTkTtHjx4ZmgjIxqLx8/uCthVjOu7ELS9+cFf3yn/wVCImBPCvz8UfD7E5Hgq2Gm+tnvjL3O141xEhsZVnc7pQWQ+YtxrGRhIvWi4FlERKSxKmaCHnwQXnzReC4shJycai+3h1o5dZCRZOyr9c2wdDuqO5xyt3E8f5qRWVdE2p0Ne9ys3JFNqM/LbeMH1O+mjHXg90FUHETHBXeAIu2EgmcREZHGqqgZ+8gj8NvfGs92O8TE1HjLxJE9AXjjxx3kFpcFf4wn/c54c5zjgpWzgt+fiDS7z9buBeBsz356OGx1XF3uyGRhIlIvCp5FREQa6+iasSkpxtdxNc/iTBwZx4CuEeQUlvHi4m3BH2NoBEz4m3G8+DEozA5+nyLSbHx+P1+UB88X/fqi+t+o/c4iDabgWUREpCmOrBmblFRn3Virxcx95yYA8NKS7WTmNcNS6uOuhe7DodgNK14Ifn8i0mxSD5ax111MZJiV0xO61v/GPeXBc7xmnkXqS8GziIhIMzt3WA+O6+WgqMzLjO83B79DswXG32ccr5gJxbnB71NEmsVnriIAzhnWHVuIpX43FeVA9lbjOE7Bs0h9KXgWERFpZiaTiT+fNwSAt1a62JVdGPxOEy+G2ARj9nn1y8HvT0SCzmMy89UuoyzeRcf1rP+NFfudY/pARJcgjEykfVLwLCIiEmCx9lhs1sNJe2xWG7H22CrXjBsYy6mDYinz+nly3qbgD8pshlPvNY6XzYDSZgjYRSSoljtHkFXio5M9hFMGxtZ9Q4UdS4zn3mODMzCRdkrBs4iISIA5HU7Sp6aTMjmF8c7xpE9Nx+k4di90xd7nj9fs4Zd9zbCUeviVxkxTYRakvh78/kRakssFqangdhvHbYjL7SI1IxV3sZvUjFRc7urH/3nieADOHxFHiKUBb+u3fm88D5jQ1KGKdCgKnkVERBogPiq+Xtc5HU6S4pJw2BzVBs5TvpjCyF4xXDgiDr8f/vtNeqCHeiyLFU65xzj+4RnVfZb2y+WCxERITobFi43jNhJAuwozSJiRQPKsZBa7FpM8K5mEGQlk5GVUua7U62duwskAXDSyAUu2Cw7C3jXG8YAzAjRqkY5BwbOIiEgDzJw4s0n3V8wobczciMvt4t5zBmMxm5ifdoDVO5qhjNSo6yGqJ+TthTVvBb8/kZaQlQWFhTBnjvEoLDTO1eLI2d6aZnqbQ1ZpDsWe4irnij3F5BTnVDm3eH8JubZIutnMjOnXuf4dbF8I+KHbMIjq0eTxinQkCp5FRESC6MiZapfbVWVGKWFGAiGh2Vx9Qi8A/u/rdPx+f3AHZA2Dk+8yjpc+CV5PcPsTaUmJicajDtX9bLZUAB0bGlMlZwIYeRNibDFVzn1enmV7Yu9wLGZT/TuoXLKtWWeRhlLwLCIiEkRHzlRnFWZVmVEq9hSTVZjF788cRJjVzMod2SxMzwz+oJJuAnss5OyEDR8Evz+RVq6mn82W4LTHVcmZkDI5hfSp6cRFxVVeU1TqZd5eY9vFRU5bTU0dy++HrQuMY+13FmkwBc8iIiItLM4Rzs3j+gLwxLxNwZ99DrXDuKnG8ZLHwecNbn8i0iBH5kxIiks6Jm/C978coNDjp1fOPkZ1Dql/w1mbIXcPWMKgz7gAj1qk/VPwLCIi0grcNr4/9lAL6/e4+f6XA8Hv8IRbweaArE2Q9lnw+xORgPls7R4ALkpbgsnUiCXbfU6CkPAgjEykfVPwLCIi0gp0iQzjppP6AvDU/M3Bn322RcPYKcbx4seN5Zwi0urllvpYUL694+K0RQ27WSWqRJpEwbOIiEgr8dtT+zXv7PPY2yA0Evavh83fBr8/EWmyeXuLKfX4GBhtZUjmjvrf6CmBHUuMYwXPIo2i4FlERKSVaPbZZ3tnOOFXxvGSJ4Lbl4gExOcbjURmF9nyaMCCbdi1EsoKIaKbUaZKRBpMwbOIiEggxMfXfU09NPvs84m/A0so7FoOO5cFvz+Rcvn58Le/waBBYLNB585w663NOIAA/cw2m9hYdnXvw1K3ETJf9MhdYLdDbGz97j+yRJVZIYBIY+gnR0REJBBmzqz7mnpo9tnn6DgYdb1xrNlnaUZXXw0ffwxPPgmrV8OcOdCrVzMOIEA/s83G6eTfD8/BY7Fycs4O+s//HNLSwOms+17QfmeRAFDwLCIi0ppMmdL8s88n3wUmM2yZBxnrgt+fdHgHD8LcufDvf8PEiTB8OFxwAfzzny09stZrxbaDfLnVjdkEDx5cBUlJ9Q+cCw5CxlrjuP/pQRujSHun4FlERKQ12bOn+WefO/eHYZcbx0ufDG5fIkB4OFitsHJlS4+kbfD6/Dz0xUYArh3jJLEku2ENbF8I+I29zlE9Aj4+kY5CwbOIiEgr1Oyzz6fcYzxv/AQObg1+f9Kh2e1w113w8MNw7bWwfXtLj6gOU6a0aPcfpu7m5725RIVZ+cPZgxvewJH7nUWk0RQ8i4iItELNPvvcYzgMOhf8Pvjh6eD2JQL897/GtuP58yEx0Uge5vW29KhqsGdPi3WdX+LhsW/SAfj9mYPoEhnWsAb8fti6wDjWfmeRJlHwLCIi0kodOfu8IL0ZZp9Pvdd4Xvs25GYEvz/p8G6/HbZtg3vvhenT4Ve/aukRtT7PL9hCZl4JfbvYuXlc34Y3kLUJcveAJQz6jAv4+EQ6EgXPIiIirVSXyDCuG2MkBPootRlmvpwngnMceEvhxxnB708EiI6Gf/0LnnkGZs+GHTtaekStR2lJFC8tNda0//XCoYRaG/HWvWLJdp9xEBIewNGJdDwKnkVERFqxi47rCcD3vxyguKwZ1rRWzD6vfhUKG5iUSKSefL5jz40rnxQtK2vesbRmGRljKfX4OGVgLGcldmtcIypRJRIwCp5FRERaseN6OejpsFFY6mXxpszgdzjwLOgxAsoKYOWLwe9POiSXC84+Gz74ADZuhEWLjKXbSUkwcGAjG23hpF6BtnzbQdzuAUZpqomJmEymhjfiKYEdS41jJQsTaTIFzyIiIq2YyWTivOFxAHy9YV9zdHg48/aK/0FJXvD7lA6nc2djufbdd8Pxx8NNN8GgQfDll8Y/wUZpwaRegebx+njoc6M01fVjnQzpEd24hta9C2WFENHNKFMlIk2i4FlERKSVO3+EUZd1Xtp+Sj3VrHcNtKGXQpdBUJQNPz4X/P6kw4mOhg8/hN27oaQEdu6EF16AHipBDMCsJdvYmJGLxVLCPWc1ojQVQI4Lvv6LcTxuKpj1tl+kqfRTJCIi0solOzvRLSqMvGIPP2zNCn6HZgtMeNA4XvYs5DfDcvEOpLqyY0EvRSZtxmZ3GU/N2wxAz54/NLw0FRibyj/9HZTmQe+xcNLUAI9SpGMKePDsdru55ZZbiImJITo6mt/85jcUFhYec53X68VsNmMymSofkZGRgR6OiIhIm2c2mzh3mDEl9/X6Zli6DTD0Euh5PJTmw5LHm6fPDuDJJ5/ksssuqxIsZ2RkMGjQIDZs2NCCI5PWwGMy88dVbkq9PiYM6UZMp83VXxgfX3tDq1+G7YvBGg6XzjQ+EBORJrMGusGbbrqJbdu28dlnn+F2u5k8eTJ+v5+XX365ynWHDh3C7/ezcOFC4sv/AzBrOYmIiEi1zh/eg9nLd/Ltxn086h2O1RLk35kmE5w1Dd64xHgjfuIU6NQnuH12AIMGDeLee+9lwYIFlef+85//cPDgQXr37t2CI+s4Nu/P44/vr8UWYsHZ2Y6zsx2vuYhQ32A8pgx8ppbb5//imMtYm11GlM3Kvy4bweSvarhw5syaGzm4Feb93Tg++yHoMiDg4xTpqAIaPG/cuJHPPvuMlStXMnr0aAAef/xxbr75Zh577DE6d+5ceW12tlH+IikpiaioqEAOQ0REpN0Z068znSNCyS4oZcX2bE4eGBv8Tvufbjy2LYQF/4LLXwh+n+3chRdeSHJyMtOmTcPv95Ofn89rr73GX/7yFxwOR0sPr0N4ddkO1u52A7Bi++FybHE8AUCBZTGHrK80+7g2u8t48pRJAPx94lB6OGwNb8TnhU+mGEnC+o2H0b8J8ChFOraAfmy9caORFXDUqFGV504//XQ8Hg+pqalVrs3OziY0NFSBs4iISD1YLWbOGdodgK/WZzRfx2f+w3he9y7s/7n5+m2nTCYT06ZNY8mSJRQVFbFy5UpsNhu///3vW3poHYLf72dRurGHf8rpA/jD2YO5KrkXw+JD8ZgOABDhHU/Pkv/x3oq85qmtjpFd+4+r3JRaQzi9RxhXJvdqXEM/zoBdKyA0Ci55TknCRAIsoD9RXbp0AcDlclWey83NBeDAgQNVrj148CClpaWEh4fTp08frr/+enbs2FFj2yUlJeTm5lZ5iIiItBsuF6SmgtttHFfjvOHGvudvft6P19dMCabik4zs2/jhu4eap892rmL2OTMzk3Xr1nHvvfdq1rkpjvzZSU2t8ecHYGtmPntyigi1mvn9hEHceeYgHrvqOB6+sgt7bL9mb9idFJs3YMbGO8vzOeuJRXy9IYNg/7S9vHS7sVy7pIDpJzgaV9P5QBp8/4hxfN50iHEGdpAiEtjgeezYsfTq1Yt7772XgwcPcuDAAe65x6gVabFYjrl25cqVrFixgscee4y1a9dyxhlnkJdX/T6T6dOn43A4Kh/aFyQiIu2GywWJiZCcDIsXG8fVBADjBsQSbbOSlV9Cys5DDe9nypTGjW/C38BkgU1fw84fG9eGVKqYfS4pKcFisWjWuSmO/tlJTja+zqh+dcbC8lnnsf06Ex56+L1prD0Wm9VGmXk7+0PvJ8f2BF2jQth9qIjb56QyyXkhGe6ioLyELbkeHp+3CYC/ffcScfZGJPfylMLHt4O3FAadA8dPCvAoRQQCHDzb7XY+/PBDNmzYQGxsLE6nkzFjxgDQrVu3KtfGxsYyevRoRo4cydVXX83cuXPZuXMnX3/9dbVtP/DAA7jd7srHrl27Ajl0ERGRlpOVBYWFMGeO8SgsNM4BLreL1IxUUjNS2Vewm7OasnR7z57GjS92ICTdaBzPnwYqq9RkF154If369WPSpEkdetb5/fff59NPP218A0f+7KSkHP75ycmp9vJFm4zg+bTBXaucdzqcpE9NJ2VyCuP7jGfdPa+y6L4J3DlhIKFWMz9E9uLaWcvZ5y5u/Fhr8NcUN6UeH+N7hHHV+nmNa+T7hyBjDdhi4KJnjIR/IhJwAd8IMWbMGLZu3cru3bvJyspi4sSJmM1mRo4cWet9TqeTLl26sHPnzmq/HxYWRnR0dJWHiIhIu5KYaDzKudwuEmYkkDwrmeRZySTMSOCE/kauz29+3oevuZZuA5z2Z7DaYNdy2PRN8/XbTplMJrZt28ZLL73U0kNpUa+88gqzZ89uekOJiZCUVOXn52iFHh8rthkJwk5P6HbM950OJ0lxSQztOhSnw4k91Mofzkng27vH07s0l50HC7nuxeXszw1cAL0vsgsrMksxmeBfydE0KuTd9I1Rjx2Mfc7RcQEbn4hUFbQsAvHx8URGRvL8889z9tlnV+6Hrsm2bdvIyspi4MCBwRqSiIhIm5JVmEWx5/Ab9WJPMf27lxARaiHDXcza3TnNN5jonjD2duP4u38aWX1F2pDlB0op9fro1SmcAV0jarxu5sSqZaD6xkbw9s7PiY8JZ3tWAde9uJwDAQqgF/dLAuC4XjH0imhEERz3bvj4NuN47O2QODEg4xKR6gU8eH7rrbdYtWoVa9as4U9/+hMffPABjz32GD6fj/POO4+PPvoIgFdffZUXX3yRtWvX8sUXX3DxxRczcuRIJk7UD72IiLRPFfsqK9isNmLtDSs5FWo1MSHRWLo9d8O+gI6vTqfcDTYHHNgIa95q3r5byJQvGrlPXFqdhftKAGPJdkMTcvUqy+edyScSHxPOtszyADqvuPF5BMot6p9UOabqxEfF13yztww+uBWKDkHcKKOms4gEVcCD54ULF3LmmWdyyimnkJqaysKFCxkxYgRlZWWkpaWxd+9eABwOB9OnT2fs2LHceeednHbaaSxatAirNaClp0VERFqNKvsqneNJn5qO09HwjLjnl2fdnrshA39z7j8O7wTj7zOOFzwKpQXN13cL2ZPXyH3i0jqkpUFaGn5gYYYRPFe3ZLs+ene28/ZvT6Snw8bWzAJueHEFWfsONnpoHp+fJX2PB+C0hOqD56NnwatY8C9jG0VYNFz1KljDGj0WEamfgAfPs2bNIjc3l/z8fObPn09ycjJg7FneuXMnU6dOBeDyyy9n27ZtFBcXs337dp577jliYmICPRwREZFW5eh9lY1xekJXbCFmdmUXsX6PO8AjrMOYyUYJnLwM+PG55u07mJo4gyitTEwM2O0waRJMmsSOuP64CryEWEyMG1D7VsLaOLvYeXvyifSItrH5QD7X97kId2HZMdfVZ8XC2uwycm2ROEJNHNcrpmED2TIflj5hHF/8DHTu37D7RaRRVDldRESkBdQ6o1QHe6iVs8qXbk/77Gc8Xl+ghlU3axic+Q/j+IenIf9A8/UdTI3NRC6tU1ycMeuckgLjx7Nw1vsAjO7bmYiwBq5yzMg4XEM6I4M+XSJ4Z/KJdI8OY5OtMy8t3XbMLfVZsbCofBn5qd3DsJgbsIw8NwM+Kt/nfMKtMOyy+t8rIk2i4FlERKQNuv/8IUSFWUl15TBz4dbm7Xz4FdAzCUrzYeH05u1bpL6cTiMDt8PBwgMewFi10WAvvHC4hvQLLwBGErF/XDQMgHdW7aLU0/APsCqC59N6NGC5dVkxfHgrFGZB9xFw7r8a3K+INJ6CZxERkTaoVyc7D186HICnvtvM2l05zde5yQTnPGIcp7wOmenN17dIAxWbLCzfZuxNbtR+59tug/HjjVns226rPH320O50KysgM6+Eb35uWPK+7IJS1mUby73rHTyXFcO7k2DnDxAaCVe9BiG2Om8TkcBR8CwiItJGXTKqJxNHxuH1+bnn3TUUlnqar/O+J0PCheD3wrx/NF+/geZyGctxK5blulwtPSIJsOX2npR4fMQ5bAzqFtnwBuLiwOEwZrHjDtdQDrGYuS4nDYDZy3c2qMklmzPxA4n7t9Et3FL3DWXF8O4NsGUeWMPhurchVuVdRZqbgmcREZE26o4v7+DRS0fQI9rGtqwCHv0yrXkHcPY/wWSBTXNh+5Lm7TsQXC5ITDSW41Ysy01MVADdziyM7A0YS7YbWqKqLtcdSsNiNrFyezbp+/Lqfd+iTZkAjN+eWvfFZcXwzvVGkrAQO9zwPvQb39ghi0gTKHgWERFpLcrL6tTXnrw9OOwhPH71cQC8ucLF97/sD9bojhU7CE74lXH87YPga8bEZYGQlQWFhTBnjrEkd84c4+usrJYemQTQ4vLg+bTBjStRVZsenkLOGWok75tTz9lnn8/P4k3Gv7HTtqfUfnFZEbxzHWz97ojA+dQmjVlEGk/Bs4iISEuLja1SVge7HWJjibXHYrMe3tNos9qItccec/vJA2O59ZR+APzpg3Vk5Zc029A57X4IjYKMNbDhw+brN5ASE40luYmJzd+3SmQFletgIdvCYrCaTZw8sPElqmpz44l9APgodTf5JXVvndiYkUtWfgl2q4kTdtfyYVlZEbx9HWz9/nDg3PeUQA1bRBpBwbOIiEhLczqrlNUhLQ2cTpwOJ+lT0xnvHE/K5BTSp6bXWBv6vnMTSOgeRVZ+KX98fy0rt2ez5UA+hwpK8fn8wRt7ZFc45W7j+Lt/Gm/4pf5UIiuoFm4ySqkl9+lElC0kKH2cNKAL/btGUFDq5ePU3XVeX7Fke1y3UEJ9NQTbRTnw1jWwbQGERMANHyhwFmkFGljoTkRERILC6TQeDofxXHHa4cRhc5AUl1Tr7bYQC09eM4pLn/uBhemZLEzPrPyexWyikz2UROcFzCzxENnQOrd1OfEOWP0quHfBshlw2n2BbV+kkRaV/xw0Kst2PZlMJm48sQ///Hwjs5fvZFL5THS1pkxh0aibgFqybOfvgpcmw8HNRuA86QPoMy4IIxeRhtLMs4iISDsxtGc0z15/PCf06UTfLnaibEaQ7PX5ycovYUlkb77+cnngE2KF2o3kYQBLn4DcvYFtvwW43C5SM1JxF7tJzUjF5VYSsbamuMzLsq1GiarTBjeivnMDXJHci/AQC5v25zMnZXmN/25yMzJJ3XnIGFN1wXN/Cyy5wwico3vBr79W4CzSiih4FhERaUfOHdaDD6aMY+F9Z7B+2rlsum0YK165jV+t/hSA+bO/Ck5G6eFXQO+xUFYI86cFtu1m5irMIGFGAsmzklnsWkzyrGQSZiRUCYSmfKG9yq3d6h2HKCrz0j06jMS4qKD2FW0L4axhDgDu/fiTGv/dLIvoicfnp39sBM7II1aA+P2w/WO4wQ5l+dBrDExeAHEjgzpuEWkYBc8iIiJtkMvtqtesaOihbLpn7uHyaycAsDhxHMUlZYHPKG0ywXnTjeN178KuVYFtvxllleZQ7Cmucq7YU0xW4eE/sz152qvc2i3ZYizZPnVQ4EtUAZCRcbg+eEYGpw813lbbveOw+DsBx/67WVSe+Xv8kTPhPg98cQ+sfwbMJuh1DtzyBUQGb6m5iDSOgmcREZE2xuV2kTAjocbZreoMP34Q3aPDKPSZ+LFPkGaz4pNh1A3G8df3t73SVdKu/LDFCFpPGXhshvqAeOGFw/XBX3iBfl1DKDZvxISVSM+5x1zu9/tZHFFeNiuhPHjuZIJl90DKq4AJvi2G4+8Haw37oUWkRSl4FhERaWOyCrOqzIwePbtVHZPJxFmJRj3a+QPHBm9wZ/4dQiNhz2pY/37w+hGpxaESHz/vzQVgXJBKVHHbbUZ2/JQU4xjIt3wFQKTnPMz+mCqXb83MZ09oFKFWMyf27QzbP4XbIyF7g1Hubcyj8GOpsYpDRFolBc8iIiIdxFlDDwfPfn+QyldF9YBT7zWO50+D0oLg9CNSix8PlOD3w+DukXSLstV9Q2PExRnZ8ZOSjGOgwLIUL26sxNK7eA49i//H8/Nz+Ch1Nx+lGkv9L+jtIfzdK2D9UxBqgi7HwZSl0OOk4IxTRAJGwbOIiEhrEh8ftKZP6t8Fu9XE/qgubDhUQ33ZQDjxdxDTB/L2wtKngtePSA1+OFAKwMnBWrJdE5OHg6EzKDVtx4+PEH8v5v9cxL3vreX5hVu4yrKQ/ztwO2xbCJYwmFsM456ATn2bd5wi0iiq8ywiItKazJwZtKZtIRbGdw/j6z3FzNtbzIhgdRRig3MehvdugmXPQNKNEOOs+742wOV2kVWYVZmsLdYei9PRPl5be/LD/hIATh4QxODZ5wNbAWz9HvL20X1vKk/5w4gzryE2bC12LNj9NuLCOmPylBLiLcZhKgAv0Gs0DL4T/n4pmDSXJdJW6KdVRESkAzkr3khENH/v4T3TQSm7lHgx9DkFPMUw7++Bbz+IYkNjsFmrLvW1WW2UeErqLGHVkbTWcl27o7uyI9+LxWxibP/OwenkQBq8fBYMXQ2zL4NPphC/8kXuIoyrCWECVk7ExEhTCV1LM4j1HcRhKsDvM8FZ/4RffwPlmbdFpO3QzLOIiEgHckaPMMw+LxtzYJVrC5aQXDZmbgz8LGpF6apZp8HPH8OYydBnXGDaDjKnPY70qelkFWZxz9f38OR5TxJrjz0mURscTtbWEWefW2u5rmV9RgFwXC8HUbaQwDbuLYOlT8Ki/wNfGfjM0D0Borpz0BrKi5s+JwM/mfgoAArxM+Pil0mgO+QVYfrn43DpeNjdOv/sRKR2Cp5FRERaufioBu6DnjKlxuXfXWwWkvf8wqrewzj35fs4ZP4EgORZydisNtKnpgcuEIwbCUk3QcprRumq3y4Ec9tY9OZ0OHE6nDhsDpLikgDqzGgurcMPfY4DArzfOT4e9q6BT6fC/vXGucHnw4eH4KFvANiZkcoDmz845tZSbyycfgUUFhonkpPBbof33gvc+ESkWbSN32AiIiId2MyJDdwHvaf2Wa2ztqwAIKQsqcr5+pS8arAzHoSwaMhYC2veDGzbIkfx+/2BD549JXBlHLw4wQicwzvDFS/DdW9DWd31mK3ZOUbgPGeOUdZqzhzj65ycwIxPRJqNgmcREZEOpiJ4tvlGYPKHB7ezyK5w2p+M4+8eguLc4PYnHdqmXA9ZkZ2wWeB4Z0zTGywrhreuhiWPg98Lwy6D362EEVc2vB5zYqJR1ioxsenjEpEWoeBZRESkgxmQvYeedi8mQgj3JdV9Q1ONuQ06D4CCA0YQIhIkP+w3SlSN6RpGmNXStMY8JfDuJKOsVEgEXD0brnrN+EBIRDokBc8iIiId0OiuZQCEe8cGvzNrKJz7qHG8/HnI3hb8PqVDqixR1S20aQ15SuH9W2DLPLCGww3vw9CLmz5AEWnTFDyLiIh0QKO7VQTPo8HfDG8HBp8HAyaAtxS+/Vvw+5MOp8zrY0WmMfN8cve69yLXyOuBD2+F9K/AaoPr34G+JwdolCLSlil4FhERaS9cLkhNBbfbeHbVXH84weHFixsLUYT5hgZ/bCYTnDsdTBb45QvYtij4fUqHsm53DvkeP50K3QyNaWRBGZ8XPp4MaZ+BJRSueRP6nx7QcYpI26XgWUREpB0I2Z1hJCJKTobFi43nxETIyKj2eosZiiyrAbD7Grh0e8qUxg2y2xAYfatx/PUDxgxfK9fgMmHtUWP/vpvZD1sOAjDOtQ5zQ5N5Afh8RimqDR+C2QpXvwGDzgrwKEWkLVPwLCIi0g40phxOkcXIuh3uHQv+BnRWRymsWp3+ANhi4MDPkPpa49tpJg0uE9beuFywcWOdKxkaIi8vj40bNx5zfsOGDRQUFDS63aVbjDJr43asbVwD3z8Ma98yVkdc+SoknN/osYhI+6TgWUREpD1pQDmcIvNP+CkjxN+TUP+AZhgcYO8MZ/zVOP7275C1uXn6lYZzuYx/R0euZAhAAP3+++9zwgknsG/fvspzO3fuJCkpiS+//LLuBqqZCS/0+PjJdQiAk3c2InjetgiWPmkcXzqz/snB4rUyQaQjUfAsIiLSQflNRRRafgAgynNR83U8+lboeyqUFcB7N0NpYfP1LfWXlXV4NUPFSoasLFxuF6kZqbiL3aRmpOJyNyygvvzyywkNDeWxxx6rPPevf/0Lh8PBhRdeWPONFXv6q5kJX5m6lTKvn/gQH31yqt+qUKOCg/DRZMAPSTfDcdfU/96ZHXxlgkgHo+BZRESkA8u1fgZAhPc0zP6Y5unUbIErXoaIbsby7bl/ap5+pXESEytXMrgKM0iYkUDyrGQWuxaTPCuZhBkJDQqgY2JiuPvuu5k5cyYlJSUUFBTwyiuv8Kc//YmIiIjqb6qYBT96T39JCdjtLHv3GwBOTpmPyW6H2Nj6Dcbvh09/B/n7IHYwnDe93q9DRDoeBc8iIiIdWKl5EyWmXzARQpTnvObrOKo7XPEiYIKfZsPad5qvb2m0rNIcij3FVc4Ve4rJKsxqUDt33303oaGhbN++nfT0dGJiYrjjjjtq6Tir+j39YWGQlsbSc43Z4pMnXw1paeB01m8gq16CTXONzNpXvgKhNQTvIiIoeBYREenwKmafIz0XUOZtSOawJup/Opx+v3H8xT2Qmd58fUuLqph9drlc7Nixo/ZZ5yNVs6c/u0sPNuYYmdvHnXlC/QPn/T/DN+X7789+CHqMaOjLEJEORsGziIhIR5CWZuwTTUs75luFlmV4OIiVzizbXFzNzUE0/j7odxqUFWr/cwdz9913YzabCQkJqX3WuQ4VWbYTig/SNSqsfjeVFcEHt4K3BAadA2Nvb3T/ItJxNLKCvIiIiLQJMTFgt8OkSYfP2e3G+QomD/nWr4jx3MiXawq4+3Q/psbUyW0MswWueAn+dwpkpsFX98GlzzVP39KiYmJiePjhhwkPD6/frHMNFqYfAOD0/F31v+mbvxr/3iK6wSXPQ3P9exeRNk0zzyIiIu1ZXJwx25ySAuPHG89pacb5I+RZ5+KnlC37y/hpV07zjjGymxFAm8ywZg6sfqV5+5cWc//993PXXXc1+n6f38+i9EwATs+vZ9KyTd/C6peN48v+B5FdG92/iHQsCp5FRETaO6fT2CfqcBjP1ewJ9ZlyKbAsAuDVH3Y08wCBfuPh9L8Yx1/cYyRyamFz587l/vvvb+lhSC3WHyrjYEEpkWFWTijcX/cNnpLD2d1P/B0MPDO4A6yPWrZUiEjrouBZRESkjYm1x2Kz2iq/tlltxNhi6r4xPr7Wb1ckDpu7PoN97mbe+wxw6h8O7z398g+w7NnmH0M5v9/PTTf/iv/85z9s2rS52fuf8sWUZu+zLVqQUQLAqYNiCcFX9w3Ln4dD2yGyB5zxQJBHV4cjt1QkJxvPDSmzJSLNTsGziIhIG+N0OEmfmk7K5BRSJqeQPjWduKi4um+cObPWb5eZtzM0PhSPz8+c5TsDNNoGMJvhvH/DKfcaX3/7ICz8t1GLt5m9++EnZGXuB0xceft9+HzNO4Y9eXuatb+2qiJ4PiOhW90X5+2Dxf81js/+J4RFBXFk9VDTlor6ZgsXkWan4FlERKQNcjqcJMUlkRSXhNMRuDfbFx5nB+CtlS6Ky7wBa7feTCY46x8w4W/G1wunw7y/N2sA7ff7+cMDf8MSFYs5PIr1Cz9n6gtf4W+BIL5ZTWlbs91ZdgfrsssAOC2hHvuW50+D0nyIPwFGXB3cwdVXxZaKoUNr3FIhIq2HgmcRERGpNGaAjfiYcLILSvlsBxKkVwAAMDhJREFUzd6WG8j4Pxqz0ADLnoGv/gi+eizLDYDX5i9i75afCYsfSlR0FJaIGN54/ileWpgL7Tl+3tO2ZrsX90vCDwzrGU33aFvtF+9eDWvfNo7P/z9jlUNrUseqEBFpHVrZ/xwiIiLSkixmEzed1AeAh+Yupai0BWafK5w4BS56BjAZCcT+OgwKsoLapR+4//kXCOs9nD59nMRG2rjp9rso+Pl7Plu0hU5lk9t3AN2GLOh/AlCPJds+3+EkYaNugF7JQR6ZiLRXCp5FRESkimtHO4kMs5JfGM1lz/+A62Bhyw0m+WajjJUlDML2wsxxsOW7oHX3Qad4Drg24Tj5Ok4aYCRueu6h++jUJZb89d8R7b2YTmW/bXoA7XIZGZbdbuPZVc8ySwKAx+dncb8kAM4YUseS7XXvwJ4UCI2EM//eDKMTkfZKwbOIiIhU4bCH8OqvRmO1FvLLvjwumrGUxZsyW25AI66EyQugyA75+2HO5fD1A0bZoQD76Mzf0P2G/+Oai8+ja1QYAOHh4Sz/YQm/u/MaAKK9l9Cp7DfgNzWuE5cLEhONDMuLFxvPiYnNF0C3sb3N1VmTXYY7PIqYUBOjencyTlaXTb4kz9jrDDD+PojqEbQxHZ0FHxqQCV9E2gQFzyIiInKM0X07M3DQRxzXOwZ3URm3vLqSmQu3tlzSrO7D4JckGDPZ+Hr58/DiBDjwS8C6WLyvhB8HjCbaOZT7zh1S5XuDBw/mytP7cDDEKJ8V7b2UrqV/xeS3N7yjrCwoLIQ5c4wMy3PmGF9nBXdJeqU2tre5OgsyjFJq43uEYTGXf4hR3b7hxf81PnDp3N/YBhBER2bBH+8c37BM+CLSJih4FhERkWqFhhbw3m0ncs0JvfH54T9f/8Lv3kqlwGRtmQH5LXDBY3D9e2CPhf0bYNZpsPqVJmfj9vn8TF+XC8CNAyPo3bn6oDjf+g1ZIY/jpxS770TiSh5nd7ancZ0mJhoZlhMTGzvsdqExNa0rS1TFhdV8UdYW40MWgHP/BdZarg2Qiiz4Dpsj4JnwRaTlKXgWERGRGoVZLfz7ihE8etlwQiwmvlq/j2v6XkyJpwUTiQ0+F6Ysg4FngacYvrgHPvg1FOc2uslP1uwhLcdDVHE+UxMja722wLqAfWF/wmPKJMTfmz+/m8U3P+9rdN8dXUNrWu9zF7Mxx4PJ72N89xoC4rIi+OAW8JYa/04Gn9f0gYpIh6fgWURERGplMpm4YWwf3pl8Ep3sIWwI78qM77e07KCiusP178PZD4PZCj9/BC+Mh70/NbipYq+fx7/dBMCU5R/QKazut0el5i1khN1NsXk9RaV+bpudwuPfpuPzKRV3sC3adACA4zI20cVmqf6ir++HfeuNFQoXP2vUDxcRaSIFzyIiIlIvyX068ehlIwB4fuFWNuxxt+yAzGY4+ffwq6/B4YRD2+Hlc2DFCw1axv3G5gL25BQRF27m1ymf1XptjB/O8Fu4wR/CjRQyMXQa0/ss4UbLt+QvepYX//cEvoJDTX1lUosFvxjJ687Yurr6C9a9BymvASa4fBZE92y2sYlI+9ZCm5ZERESkLbpgRBzn525jbnR//vTBOj6dejIhlhb+LL73aLh9MXw6FX75wqjpu32xMeNo71zn7W9uM0px3T0sCpun9PA3PCXG8t/Fj0HGWobtXsUhoo9tYP9MCCk/PgC+/z4KvcfAoLNh4NnQY0SbnfnMDo9mxe4iIq1mOnXrT1axCZM/DL8p8JnO66PU62fpFiOx2hnbqgmeM9Ph87uN49P+BAPPbL7BiUi7p+BZREREGuShjKX82COBjRm5vLBoK1MnDAKMxE8zJ1aT8bg5hHeCa+bAylnw7YNGEL1rJVz0FAy5sMbbdkd3ZWe+F4vZxAW9y8sMFWUabax6CfIK4PtHAKjYXbuV/2/vvqOjLNM+jn9nMkkmAykmIRACI0hHaiKggIANEVEUUFlstGUXKe8C6opl10UUpegqLAgooiIiKLogihRpisiSIIiEUAIkoSaEFAipM+8fDwmpJIFJQvl9znkOycxT7nmYcybXXPd9XQ6icZCFk2ygS7078bP5cyDhHNnHd9PEHAcxvxjb2glQvRbUbgO2AGOcSekQ6g5HN0CIFZyOirwzl8zpdDK07ytEbE4yHhj0HmwEO1/hIJ1s0wnOuK3hrGVVgeMq8n2w7VQmZzKyCfQ00+L4gYJPZp6FxU9D1lmo3wW6/r1CxiAi1y8FzyIiIlIuNXLO8eoDN/O3L37jvbX76X5zLRrX9C534SeXM5mgw1+MrO/Sv0BCFCwaAC0fhfveKjYL/Yu9FQCt6vjinR4DD1phzQBwZhvFptzcjeODW7PXw4sO3w4lqVASOfzeCYQGh3JjjoNe038i9Xg0zzaI4eFqu+HgBjhzHPauLHjQA16w7VVjs1SDx22wZz4n3aI45htCcnoyEcciCLQFVlnF5rXHMogIaYaHGW7ytpAYd4JEH3+ynSbMWPFw3oh/9hD8sh9n1tokxt6VQrNgnwp9H6zfdRSw0tUrHTOFpuZ/9xzER0L1mtDnAzCXsB5aROQSKXgWERGRcuvdpjbLdxxl7Z6TPPflTpYO71jVQ7qgdlv4y0ZYPwk2vwe/LzaC2F7/hqY9L+yXdZa4ZrV52LyJEVm7YN0maOthBM43doLm3nA8HPrOBeDMsYgigXN+Fjczrz3UgkfeT2XMgRrc+MwoQh/xMjLQSYchLRHOJULsPli9nHPtW+N57jDmrLPQ0AJ7PyZo78cE4uQ/ONg8uxPb3My8+sQKatfrWqlTvx0OJ1N/TwVgSONq/L2VD4T14PDGb2m24QmysjyxOlrjnf0AHs76rN51jtW7NtG+nj/J1MPpdGJy5XgDA8FmY92eExB4I3d8/DbYbMbjANsXwG+fgckMfT80CsqJiLiYgmcREZFrSWRkwX8riMlk4vWHW7L17Q3siE1i3k8HK/R65eZuhXv+Bc0egG+GQ8JeWPQnYw1yTgYk7IPUY4xpfn7/0wAmiMyEP8+Bu5+AqBeA8HJdtl09f/qF1eHL8Dhe+WYX/x3RCUuDOwrs4wwPZ+YHTqbUe5oALzOftk6k+RuDODWoK6dPbqEhZlrgRgvcGJYDfNzbmO5dpx007QUt+4FHNRfcpJJ9t+sYe5Kz8c44y1+aXAhEb6wWzJ5RO0hIM9YdO51OjibaWLnzHCt3HWfroUTgXgbP/x+vP9yS2n5erhmQ3c7hLb+x77O9uDkd3P7RO1A7COx2iFoJK8YZ+93xItS/3TXXFBEpRNW2RURErgXnM3M88QSEhRn/5s/MVYBavlZe7tUMgKmrosjIKKaYVlWrcwv8ZRN0HG1kJfevNoqJpR4D4KTTjy2O5mS3/yvcOR8WnwP/5hc/58UMH84L9zXFx2rhj6MpLNhyuMDTOQ4nr0SkMKXr0wCcOuegzzY/1pxuy+GmA2lkOkMQqfQmjTfJYCPZONw84Nxp2LcKlo+Gac1g5XjjC4AyjKe8snMcvL3aaN01dOvX+BVq3WX3tRMaHEpocChhtcN4oEUz/jMglJ//fifPdGuAyZTDuqh4ur+zkc9+Peyy9l1z9qcD0OnsEXxvvQXqhMDaCWQueJQak+J5dWcIdB7nkmuJiBRHmWcREZFrgd1uZJsTEmDMGHjnHSNwtpd/vWxMcgwJaQlF193GxBjnT06GiAgIDOTRW+qyfMcxftqfQGxsN3IcTtzMV1hlaXcrdH8Nbn4Y9q8F3xAIbMyiTfG8sMPBbUEefN7zHuM1Xa4jRwis7snzPZry8je7mLZqLz1bBRPkbeVcZg6jPt/OmgNpmJwOxrf25ddML9buOcmwPi/xlzijgnW8yckysllGNgARg36krckC0Rsg4mM4fQi2zDS2+l2h3VBo3AMsHsWOp0xiYvL+X79JcCM6/iw3eJgYvO2/wHMlH5edaazpTjmK5fhOBpiiCfD/HnI6cjTVTOSy75nzyw307dCAGsH1jS8zLJ4ln68ER5POsXhbLAAjEyLgTDx8NRgObuSj7VkkpDmZvv44L+Xk4G5WbkhEKoaCZxERkWuF3W5svr4QGnpJpzh25hid5nUiPdvI8oXNCcNqsbK/94+EdLgb0oy2ToSFgc2GKTKSSX1a0uPfGzl7NpiZ6/Yz6q5GrnpFrhUSamznbTy1FkinY1AxQWcxAm2BWC3WvHsDYLVYCbQVze7/qb2dxdti2RmXzKTv9vBKr+YM+fh/bI9JwsMM7371Jvc9Np3Brdvw4gfrWXzwHLN22/C19CfZsgjyff/gdHOH4FAICYNOf4MDPxqVwPeuNNZyH9wAnj7QqLtRWbzh3WAtxyyAmBho1gzS0shs14F/D5sDvkH8tWl1vDPPXdjPEzi5FX78HuK2wondcDYezhfuyr0LQwA4dKF9VxLww/mfLV5gvxVu6moE/sGtyzTE9zccICvHyW03BdD+cBTMvh1Sj5FpsvF6uDtmcyaJp0/zySefMGTIkLK/dhGRclDwLCIiInmS0pMKBIcA6dnppByJJiQtDRYsMAKtyEhjanhCAnXtdl57qAVjF+/g32v30bFhIGE33lBFr6BsHA4nv5w0Mr0dg8qWCbX72okaGZW33hcosRq2m9nExIda0Ps/P/P19iNsiT7FseR0fL3c+eBWH9pN2gwYRcbeusWXmp99xPSO/fHLfgI3ZwCJ7rPAVEwLK7MZGt1tbKcPQ/h8o1jW2ZOw60tjc/MwAtOmPcF6BnKywe0if/IlJBhfiixYwOJTHsQdtVHDauapG51wswV2TIXEP+Dv3rClmPZPbh5k2AL4NTWWOJwk48QTsGLizjp3cDQ+g/S0szQyH6FGdjJErzM2AKsfL1rcYPN0sHeE4FZGhfN8jiens3TrAW42xfFWUDg03gGpTghszEfpvYg78S+sViuNGzdm4sSJPPXUU7i7uxcdp4jIZVLwLCIiIpetT2gdJqz6kqSkRvzfou1893+342N1UQCTf7p4TMwlTUUvbM/xVE5nOqmWkUYr/1plPs7uay++dVQxU9pb2e083sHOgi0xHEtOJ8TPi48Ht6NhXMG1yiaTiXGbFpAx5EFmR1rxzrkPL0db0s27yTBHcig+i9Y1i5kOf8ONcPc/4c6XIW4bRK2AyG8h8YCxtnv/amgOTAqBoOZGYBrcGqolG1O/vW4wMtbnpTdswvJ9vzHMbS2Dbb/jtWY39LPB4RW5AwVbbWjYxWgHVrst+NYFWwB/nPiNrnPCityW8Ptfo03Ntrz1wx4GbDhAS/djfNjlLEHxW+Dwz5CexK1g9NUGcLcZU7vtHY3rnfgDS/Rv7LDE4mZywm8YWfmb+5DZYyqv39yW/v37s2zZMrp3786UKVOUfRaRCqPgWUREREqV7e93oSBZrkIFyULqbMLb3IrYxHO8/PUu3u3f5vLbFeWbUgxcyHrnBtDDh8OsWUUOG/7tcGb1Kvp4rs0HjOxx+7g/cDc3cO0Yz09pJzKS57o3JeJwEt5WC9P/1JYgHyvEFX+aHnUzmXjgHQIzx2Fx1qJ6Ti2q59zJ2IUJ/MNzFW3tfoy8oyEdbgooeKDZDewdjO3ufxmVxfd8a6zvjt4CpMPRCGMDaAK8e366tMkN3KvDiGpkre7PF8Qb063PvxTic6BDfwhoBY/9DTZ+VuySgItNaTebTTx/b1P+WPMrP1GbP+2sxrKRI6lmAY5uZ/5//8xA/yYQswXSk4yCbgc3Xjg3gAmyPPxwD24BG0/CP+fx0Zw5xMXF8corr7Bs2TJq165Nv379lH0WkQqjigoiIiJSqqw6wUbQGh4OXboY/+YPYgE3tyze7d8WN7OJZTuOsjSijMWqLiZ3SvHLLxtbWprxGBhB6+7dRqY3JibvkJjkGHbH7ybiWAQxyTHFnnbzgVMAdDy807VjnDu3wDiTs47x2iM2/v6gJ3Fnd5c4nlzn3H4hzvoUJzz+QZJlIefM27G6mziTkc2mfQkMnv8/9p9MLfkEJhPUaAK3j4NB38GOTjAqAvp9BJ3HQEhHSHcHt/NT1Z05kJkMgW54Z8eT4bRw7IQvtBgFd30GM8+C2x2QFABnS66anTulPXxYOF3sXQgfFk7UyKi8LL2b2cS70d9Ry8fKgfizvLD0d5xmN6jbjqUBdhjwBTx/EIb/Ave/Da36Q5vHWVVnNE9mvsCQwM+wvHAQBq2AU8FgMrF//36GDx9Os2bN8sbx6quvApCYmFi2/7vKFBlpvFcruI2ciFQcZZ5FRESkbHILkjVvXmJBslD7DYy5uxFTV+3lH//dRdiNN1AvsJw9ifNnk3NbcE2caPyem+3On+3Nl+mN8YUmM5qQnp2eV+wsfxAHkJXj4NdoI3i+7fCOct+GIgqP8fw4Y6wZeWPJZbVYie78JcEXOZ3TlEa6WwTpbkam+Nuh26hmasS/lv/BrwcTGfZpOP8d0QnvfNPiS860myCggbH53AK9p1zIkHvbYOtG4qIPsGjhd4Tbm3PaqzHfzu4Lf+5TsP3Z+dd0sdZnuVPafa2+hAYXfX8E5KQzY0Bb+s/ZwvIdR2lX7waeuq3ehR3MZqjZ3NjaDSE+NYPRk38k3eHgk+7tMRWqoj158uQi17j55puJjo6+/BkPrlT4PkKFt5ETkYqhzLOIiIiUT6Fp0jHJMUQci8hrbXV/Ww861PfnbGYOoxdtJzO7mMJXF5O/vVJuC67cjHdutruEjHRCWkKBYDU9O71AgS+A348kczYzB18PE81PHiz3yy+i8BjPZ+UTbvAstvhaUnpSuU7vZjbRPDuJ/7S0EJyRQnT8WZ5dsgOn80Im+EhqGbL8+QqDsWABkdYgRq1Nosumasyo8wi/OG7mb61rYHE6Cr6uYmYaOJ1O0nKD8HK4pZ4/L9zXFIDXvt3Nb7FJJe47d1M06VkO2tT14/ZGRQNNk8lUbJB8RQXOUOL7wxVr90Wkcil4FhERkUsWkxxDkxlNCJsTxsaYjYTNCaP5zKY827MGvl7u7IxL5t9r9l7eRex2I9Pt63sh4Mif7Z04sVyZvF/OT9m+rYYnZkqeinzJYwwNdWlg5B53DJo1I7BTe2Z98SoeOVn88McJZm04cEnniwhqwNBTNblv8AyWx6bjcMJd+7fy1Z0B9KjjVXDn/K3P8r2mPn364Ofnx9mzZ8t9/SGd63Nfi1pk5TgZ8VkE2dlFq52fOpPBpxuM983/3dXoyguIy6sC3x8iUnkUPIuIiMglK5zpBSO76mZJ4c0+LQGYszGagwllCLJiYow1oblVtUvgdDr5zeTDP2avodeID1j535/LlcnLLRZW1v7OVc2SmJSXMW4z5R9MWGVk/qf+EMXGvfHFH1ToXsadTmNW5BnuG/gefdaeYs3RDExOB/fXtfJd90A+/GoCYYFlux8JCQl89913ZGVl8f7775d+QP6xRERgio1lcr9W1A+sxpGkcxzY35vnluzgvbX7WBoRx9aDiUz/cT/nTBZa1fGlW5MaZbxTIiIVS2ueRUREpELc1zKYbk1qsD4qnre+38P7TxZtZZSntKraQKx7db5Zu4+vtx8hOjcYr16L4b+c5mX/YIaUIXZOz8ph26HTAHSseXUEz3nOF8bqv3MVO0b8nc+jzzF60XaWj+xccL/z9zIeD1Y07cyyV74kIriJ8VzNm7CY4OEbvfjrS0/RYPWycg/j7bffBsDT05PJkyfz17/+lWrVSljXXkIlcu/ISGY+HkqfmZs5l3EDS8KLL0E++s5rIOssItcMBc8iIiJSYV7s2Yz1USdY+cdxth5MpH19/+J3zL8eF4ziSgkJYLezIzaJN76L5NdGj8NqYyqv1d3MvTfXwn39Or70a8Jr3+4mNjGN+8MuPg07IuY0GdkOgrw9aeB9lf0ZlK9K86ttfdmd6cGOuGSe+mgTZzwCmbo6gtNn3Dl48DgHn/4Px3wuZGxNwK1BHjz4yTR6zJrIDZ5mSDxS4vlLkpCQwPTp0+nQoQO7d+8mMTGR999/n3HjxpV0wIW16TfeCIcPG9PsExKo1gDefTKA8d/N4556/UlJcyfprJm4+FSOpmbSKTGau87VgpgMTXMWkSvCVfapISIiIq5QUl9eP6ufS6/TuKY3/v57SExszusrdvP1M50wmy+SSczXdsjhdPLhxmjeWrmHbIcTk9PJbQ0D6RNahx4talHd04Jz4T9pPKA3b3y3h/mbD7H7hCcmpydOU0axp89d79yxQQAmk4vWO1ewIj22bTY8a9bg5d6Z9J0Zw8F4X6AvM44cu3DQ+cC5jb87Dy7+D/e/+zI1vdxg3A/g+UbBC5RWVTskJO/Ht99+G6fTSefOnYmKimLQoEEXzz6XsRJ5xI73sVqs7O/9IyEdHsKZloYJYA55ldQVQItIVdOaZxERketQSX15g6tfrInSpalZaxvVPNzYEZfM8p1Hy3RMgs2XwZtO8/p3kWQ7nNzXohY/7/uMhX++lX5hdajuaXz/bwKGdWnAjAFt8bCY2Xogg5oZr2N2+hZ73rz+zg2unjZBRXpsnw8k3SwpnHR/gyzTcTJN0Zx120S/dtWZ1t6XpZ+O47feNfnm7kAGhy8zAueSXKSqNpBXXf306dNMnz6dkSNH5gXKL774IomJicyePfvi5y5jJfKUI9GQloZpwQJj3wULCvb2FhGpQgqeRURErlN2XzuhwaF5fXnz90J2JXf3cwzv1gCAySujSM/Kuej+m09k0HPge6w/noGnxczrD7dg5uOh1M4uuehYr1a1+WxoB6pbTXg6m1IrYyrVsu/B7Kyet8+ZjGx2nG+NdFuDgMt/YZWpuIrjQIbbHxy1DuWYdTQJHm8xoKM3fevZCD0ahZ9nOf7MK6Gqdn6ZmZncc889PPvss3mP1atXj9dffx1f3+K/rCgy9rJWmm7WzNg330wEEZGqpuBZRETkOhfifWFabu507vysFiuBtsvL1A7pfBO1fKwcSTrHRz8fAmD4t8ML7JPhZmHq76k8viGRk94BNPSx8N+RnXi8w41lKhrVrp4/kx4NJMt0HHdnMIFZ/0ed9AW89k0ii7fFsjbyBNkOJ3X9vajrb7us11OifFOcyyvQw69C7r2r1KxZk6VLlxJYqCXY888/z5AhQ6poVCIilUdrnkVERK5zs3rNyvs5dzp3QtqFabKBtsDLzkp7ebjx3L1NGLdkBzPX7efRW+pwJNUoWOVwOFl2+BxTh75PXOQZAP7020r+MWEgXrV8ynWdkBssHPccg3d2T2w5nfFw1mf74Qy2H96Zt0/HmyowGJ01q/R9SmC3Befd+zErx/BOj3dccu9FRMQ1FDyLiIhIAXZfe4UEbA+3DWHezwf542gK767dB8DGvfG8+f0edh9LAb9aBFnN/KOtD73emgFvDCr9pMVkeh2mVJLdvyDZ/QssjhCeDf2M7QdN7DmeCsBdzYJc+rpcKffe506lFxGRK4eCZxEREakUZrOJl+5vxoC5v/LZrzFYvXrx1I6tAHi7m/jrmvkMfvfveFnK0de3lExvtvkIj7T3ZlLvUPafTOVkaga33XSVrXd2tTK0pBIRkaK05llERORacxnrbsurvGukOzYI5O5mQeQ4nJw9G4K7m4nBneqzoWcQI7YsKV/gXE4Ng7zpOPWVMq2fviblb0n1xBMXbUklIiJFKfMsIiJyrbmMdbfllX+NdOF1ujHJMSSkJZCcnkzEsYi8x1/p1ZyTqRnEnP0fy//8F6N4V0RExQ82JgZ27zauFXhlFOGqVLlto558Et55x7gHxbSkEhGR4il4FhERkctS3DrdmOQYmsxoktfLN2xOGFaLlaiRUdwYYGfZyM48+Plk6vqPqZxBxsQYbY/S0iAszMi6Ll580UPuv//+IpWlr3r5W1KJiEi5KHgWERERl0tIS8gLnHOlZ6fnVfHOzUjHJMdUTjXphAQjcF6wwPj9iScgKcn4OXcNcKG1wLfffju33357xY9NRESuCgqeRUREpNIcO3OMTvM65QXWTWY0IWpkFGUKn12xJrdZsws/+/ldWAOcq/A64MuQux48/5cIVosVP6tfqcfm773tclrbXHV070WuaioYJiIiIpUmKT2pQDCZPxtdKlevyQ0ONrLN4eHQpYvxb2RkwXXAlyF3PXj4sHC62LsQPiycqJFRBHsHl3ps/t7bLqe1zVVH917kqqbMs4iIiFw/CrdpstsrdB1w8X2bEwqORa2jRESuCgqeRURExOUuZ8qyK6+b1zYrkIJTtF04Pbv8gwys0OniV6x8U5ar6v0hInI5NG1bREREXK7EKcvVS5+y7Irr5r+m3dd+oU1TBUzPLv8g7RU6XfyKlW/K8uVMaRcRqSoKnkVERMQlChe5svvaCQ0OzZuyXClVtc9ft9hr5p+efbFAdeBAMJmMzcMDWrWCpUtdPEi7MY6yjCe/YrK3ufKy7FeJqnp/iIhcKk3bFhEREZeo0CJXla1jR/j4YzhzBj78EB57zMgQt2pVteMqJnubW3At0BZ4PgAttKZaRERcQsGziIiISOEWQl5e0LCh8fN778Gnn8L69ZccPFdU66ncgmQFFF5TfT2spxYRqQSati0iIiJysRZCOTmQmQlBQZd++srMyhdeU309rKcWEakEyjyLiIhIhaqorGulXDMlBV58EerXh4cecs058yuc8XaV/C24rrXAuaLumQtVxXteRCqegmcRERGpUFWxFvqyr7luHVitkJEB1arB3LnG7652sYy3FO8quGfX1Pp/Ecmj4FlERESksA4dYN48SEuDbdvgmWfg0CEYP76qRyYiIlVEwbOIiIhIYTYbNG1q/BwaCvHxRuEwBc8iItctBc8iIiIipXE4jJ7PUjly22yp3ZaIXEEUPIuIiIgUdu4c7N9vVNneuhWmTYPRo6t6VNe+wm22QK22ROSKoeBZREREpLDNm6FRI7BYjErb48fDs89W9aiufbltthISYMwYeOcdI3AupWK4p6cnnp6elTRIEbleKXgWERERyW/+fGOTSlGkrVP+NluhoWU6x5o1a2jYsGEFjE5E5AIFzyIiIiJSZVzR1qlt27YuGImIyMWZq3oAIiIicv0ItAVitVzol2y1WAm0aT1rhQkJKX0fEREpE2WeRUREpNLYfe1EjYwiIS0BMIJpu68dSKjagV2rZl1+VldERAwuzzwnJyczcOBA/Pz88PHxYejQoaSlpRW778aNGwkLC8NqtXLzzTfzww8/uHo4IiIicoWx+9oJDQ4lNDj0fOBciZSJFRGRS+Ty4Pmpp54iPDycZcuW8dlnn7FixQpGjRpVZL+DBw/Ss2dP7r77bv73v//RtWtXHn74YQ4dOuTqIYmIiIgYlIkVEZFL5NLgeffu3Sxbtox58+bRpUsXHnjgAaZNm8Ynn3xCYmJigX2nT59Ow4YNeeutt2jZsiXvvfce/v7+fPTRR64ckoiIiFxNIiONTURE5Arj8uAZoE2bNnmPdevWjezsbCIiIgrsu27dOnr06JH3u8VioUuXLmzZssWVQxIREZGrQWAg2GzwxBPGZrMZj4lczMCBYDIZm4cHtGoFS5dW9ahE5Brl0uA5ICAAgJiYmLzHUlJSADh58mSBfaOjo6lfv36Bx+x2O3FxccWeOyMjg5SUlAKbiIiIXCPsdiPjHB4OXboYP9sreT20lFlAQEDe331VrmNH2LcPtm6Frl3hscdg586qHpWIXINcGjx36NCBOnXqMHbsWE6dOsXJkycZM2YMAG5ubgX2PXPmDDabrcBjNpuNjIyMYs89adIkfH1987a6deu6cugiIiJS1ex2CA0FX18Fzle4oUOHljhbMMS7kouyeXlBw4bQpg289x5Uqwbr11fuGETkuuDS4Nlms/HVV1+xa9cuAgMDsdvttG/fHoCgoKAC+3p6epKZmVngsfT09CIBda7x48eTnJyct8XGxrpy6CIiIiJSRhaLBR8fn2Kfm9WrCouy5eRAZiYU+rtTRMQVXF5tu3379hw4cIC4uDgSEhLo1asXZrOZVq1aFdgvJCSkSAAcGxvLTTfdVOx5PT098fHxKbCJiIjINUjtpORSpKTA3/4G9evDQw9V9WhE5Brk8uA5V0hICNWrV2fmzJncc889RdbFdO7cmdWrV+f9npOTw/r167nrrrsqakgiIiJyNVA7KSmPdevAajWm+8+fDy+/bPwuIuJiFlefcOHChTRq1Ah3d3cWLlzIl19+yebNm3E4HPTs2ZNhw4bRp08fRo8eTfv27ZkwYQJ9+vRh5syZOBwOBg4c6OohiYiIiMi1qkMHmDcP0tJg2zZ45hk4dAjGj6/qkYnINcblmefc7HHnzp2JiIhg/fr1tGzZkqysLCIjIzl69CgAbdu25fPPP+eTTz7hlltuYdeuXaxatQpvb29XD0lERERErlU2GzRtahSbGzYMnn3WKBwmIuJiLs88z5kzhzlz5hR53NPTk8OHDxd4rF+/fvTr18/VQxARERGRijBwIHz8sfGzu7sRtL76KvTpU5WjKsjhMHo+i4i4WIWteRYRERGRa9CV1lf53DnYvx927zbWPE+bBk8/XXXjEZFrlsszzyIiIiJyDcvtqwzG9OhPPzX6KhfqrFJpNm+GRo3AYjEqbY8fb0zdFhFxMQXPIiIiInJpKrKvcllals2fb2wiIpVA07ZFREREpPwquq+yWpaJyBVGmWcRERERKbvcvsoZGVCtGsydq77KInJdUPAsIiIiImWnvsoicp1S8CwiIiIiZZfbVxmM3srx8UbhMAXPInKN05pnEREREbl06qssItcJZZ5FREREpOxy+ypnZhq9nqdNg9Gjq3pUIiIVTsGziIiIiJSd+iqLyHXK5HQ6nVU9iEuRkpKCr68vycnJ+Pj4VPVwREREREREpJJURTyoNc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKBc8iIiIiIiIipVDwLCIiIiIiIlIKS1UP4FI5nU4AUlJSqngkIiIiIiIiUply48DcuLAyXLXBc2pqKgB169at4pGIiIiIiIhIVUhNTcXX17dSrmVyVmao7kIOh4OjR4/i7e2NyWSq6uGIiIhUqpSUFOrWrUtsbCw+Pj5VPRwREZHLVp7PNqfTSWpqKrVr18ZsrpzVyFdt5tlsNlOnTp2qHoaIiEiV8vHxUfAsIiLXlLJ+tlVWxjmXCoaJiIiIiIiIlELBs4iIiIiIiEgpFDyLiIhchTw9PfnnP/+Jp6dnVQ9FRETEJa70z7artmCYiIiIiIiISGVR5llERERERESkFAqeRUREREREREqh4FlERERERESkFAqeRUREREREREqh4FlEROQS7dy5k+7du2Oz2ahVqxaDBg3i1KlTec/PmjWL+vXr4+XlxZ133kl0dHTec4mJiQwePJiAgAB8fX2555572LlzZ97zhw4dolevXgQGBuLn50fv3r05fPhwmceWmZnJxIkTue+++4o89/7779O4cWOsVitNmzZl9uzZZTpnVFQUPXr0yHu9zz//PNnZ2WUek4iIXF0q8nMuv6SkJAICArj77rvLNb7s7GyaNGlCw4YNy3yMw+Fg9uzZtGnTplzXAgXPIiIil2zkyJF069aNLVu28OGHH7JhwwaeeuopAJYsWcKYMWOYMGECP/30E1lZWTz44IM4HA4A3njjDcxmMytWrGDlypUA9OjRg5SUFABiYmJo164dK1euZOnSpURHR/Poo4+WOqacnBwmT55Mo0aNeO2118jKyirw/O+//87MmTN588032bp1K4MGDWL48OEsWbLkoud1OBz07NkTd3d3fvrpJ2bPns1HH33ExIkTy33fRETk6lCRn3P5TZo0icTExHKPb+7cuezdu7dc+7ds2ZLRo0dz5syZcl9PrapEREQuUWxsLHXr1s37feHChTz55JOkpqbSuXNnbr/9dt59910A9uzZQ7Nmzfjxxx+54447ihx79OhRQkJCWLlyJffee2+Ray1ZsoRHH32UpKQkfH19SxzTmTNnaNeuHePGjWPTpk0cOXKENWvW5D2fmJiIp6cn1apVy3use/fuBAQE8Pnnn5d43vj4eIKCgti5cyctW7YEYOzYsURFRbFixYoy3C0REbnaVMbn3O7du+natSuhoaHk5OQU+My6mJMnT9KiRQs6derE77//zv79+0s9JiwsjP79+5OUlMQXX3xRpmPyU+ZZRETkEuX/owDAarXicDhISkpi+/btBaZMN23alODgYLZs2VLisWBkjouTk5OD1WotEPQWp3r16kRGRjJ06FBMJlOR5/39/Yucw2q1lnjdXDVq1OCOO+5gzpw5pKens3//fpYvX85jjz120eNEROTqVdGfcw6Hg6FDh/LSSy8RHBxcrrGNGjWKAQMG0Lp16zIfs23bNp577jnc3d3Lda1cCp5FRERcwOl08uGHH9KhQwdOnDgBQP369QvsY7fbiYuLK/b4uXPn4uXlxa233lrg8ZycHCIiInjttdd47rnnsFgsLh33oUOHWLNmDXfeeWep+3788ccsXrwYm81Go0aNuPXWW/Om74mIyLWtIj7n3njjDdLT0xk5cmS5xrJgwQI2b97MhAkTynVccV8ql4eCZxERkcuUlZXFsGHDWLduHTNmzMhbR2Wz2QrsZ7PZyMjIKHL8Bx98wEsvvcSUKVPw9/fPe/zPf/4zHh4ehIWFERYWxgsvvODSce/du5fu3bsTGhrK4MGDL7pvWloaPXv25NZbb+Xnn39m0aJFrFq1irfeesulYxIRkStPRXzO/fzzz0yePJkFCxaU64vhffv2MWrUKObPn4+Pj89lvKryU/AsIiJyGeLi4ujWrRvffvstP/74I7fccguenp6AUfE6v/T09AJ/aKSnpzNs2DBGjBjBzJkzGTFiRIH9J0yYwPbt2/nmm2+IiYkhLCwsr9DKhAkTsFgseVt5v33/+uuvad++PU2bNuX777/Hw8MDgA0bNhQ471133QUYWefjx4+zaNEibrvtNh577DGmTZvGhAkTii3+IiIi14aK+JyLi4ujb9++vPPOOzRv3rzY6xb3OZeamkrv3r0ZOXJk3udTYZ988kmB44YMGeKK2wCAa+d+iYiIXEf27t3LHXfcQcuWLdmxYwdBQUEAhISEAEahlQYNGuTtHxsbm1cx+9y5c9x7770cO3aMX375hdDQ0CLnDw4OJjg4mFatWtG9e3f8/f1ZtGgRw4YN45lnnilQfTswMLDM454xYwbjxo1j0qRJjBkzpsA0tnbt2rFr166833P/CNq5cyeNGzfGy8sr77nQ0FDS0tLYv39/seMXEZGrW0V9zn344YecOHGCESNG5AXUud0hrFYrq1atKvZzbunSpURGRjJlyhSmTJkCGO2qHA4HVquVuXPn0rt37wKfYxcrslleCp5FREQu0YABA7jttttYvHgxZvOFyVwhISHUq1eP1atX061bN8D4AyQuLi7vm/JXXnmFo0ePsnXr1gJTtUtiMpkwm815hVYCAwPLFTDn2rFjB2PGjGHJkiU89NBDRZ632Ww0bdq0yOMhISEsXbqUzMzMvCz177//jslkKneRFxERuTpU1OfciBEjihScHD9+PCdOnGDevHnY7XZsNluRz7mHHnqIyMjIAo/NmDGD5cuX88MPPxAcHIyvr69LA+b8FDyLiIhcgr179xIeHs4LL7xAdHR0gedq1KjB2LFjGT9+PG3atKF+/fqMGTOGXr165bV5WrhwIY8//jiJiYkFeltWr16dWrVq8corr9CoUSNat25NcnIyb775Jl5eXvTp0+eyxv3FF19Qt25dWrRoUaRFR4MGDUospvL0008zZcoUBg4cyNixY4mLi2Ps2LH07dtXwbOIyDWooj/nCgfGvr6+pKamFvsFbv59CgfGgYGBuLu7X/Q4V1HwLCIicgmOHz8OwCOPPFLkuenTpzNy5Eji4+N55plnSE9Pp3fv3syYMaPA8VOnTmXq1KkFju3bty9ffvkl9erV44033uDw4cPccMMNdOnShV9++YWaNWte9rgPHjxIo0aNijyXmppK9erViz2ubt26rF27lmeffZYuXbpQvXp1HnvsMd58883LGo+IiFyZKvpz7mpkcjqdzqoehIiIiIiIiMiVTNW2RUREREREREqh4FlERERERESkFAqeRUREREREREqh4FlERERERESkFAqeRUREREREREqh4FlERERERESkFAqeRUREREREREqh4FlERERERESkFAqeRUREREREREqh4FlERERERESkFAqeRUREREREREqh4FlERERERESkFAqeRURERERERErx/2gWzCCwKu1iAAAAAElFTkSuQmCC", "text/plain": [ "
" ] @@ -130,7 +135,7 @@ "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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VXNzs42nxWQ88sgjcrlc+sEPfmB9jPc1v7W1tem2225TdXW1iouL9fGPf9x6xnubn3p7e/X5z39eVVVVKisr0yc+8QmdPHnSes77ml9efPFFLV++XHv37h3x8Ynex8cff1xLly5VUVGRVqxYoYMHD2bz2EjCaO/tu+++qz/6oz9SeXm5qqurdeONN+rdd98d8Xm8t7ltrN+zpmeffVYul0t/8zd/M+LjvK9jMKapv/u7vzOqqqqMn/3sZ8Zzzz1nLF261Lj66qvtPhZSdOWVVxpf/epXjRdffNHYuXOn0djYaKxfv94wDMP4yU9+Yvh8PuORRx4xDhw4YHzoQx8y3vOe9xiRSMTmUyNZg4ODxvz58w1Jxve//33DMHhf811PT4+xZMkS4yMf+YixZ88e49ChQ8ZPfvITwzB4b/PZZz/7WWPRokXGU089ZTz33HPGihUrjA9/+MOGYfC+5pODBw8aN910k1FcXGxIMp599lnr2UTv429+8xvD6/Ua3/zmN40XXnjBuPHGG43a2lqjp6fHrl8Ohhnvvb355puN//k//6dx8OBB4+mnnzYuu+wy45JLLuG9zQPjva+maDRqrFy50pBk3HfffdbHeV/HNi0DcCQSMWpqaoxvfOMb1seefPJJQ5LR3Nxs48mQqmPHjo349x/+8IeG2+02+vv7jcsuu8y46667rGevv/66Icl4+umns31MTNI999xj/PEf//GIAMz7mt/uueceY9GiRUYwGDzvGe9t/rrooouMb33rW9a///SnPzX8fr9hGLyv+eQv//Ivjc985jPGU089dd4fpid6H2+44Qbjuuuus553dnYaPp/P+O53v5u9XwDGNN57e+6fpZ577jlDkvHGG28YhsF7m8vGe19N//zP/2y8//3vNxoaGkYEYN7XsU3LFuiXX35Z7e3tuvrqq62PffjDH5bb7da+fftsPBlSVV9fP+Lfi4qKFI1G1dXVpUOHDo14j5csWaLZs2fzHueJl156STt27NA3v/lN62O8r/nvX/7lX3TXXXepsLBwxMd5b/PbzTffrB//+Mc6ffq0urq69N3vflc333wz72ueue+++/T9739fTU1NIz6ezPu4e/fuEc8rKyu1fPly3uccMdZ7K43+ZylJikQiknhvc9l476sknTx5Ulu2bNG3v/1tuVyuEc94X8c2LQOweWelsbHR+lhx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", "text/plain": [ "
" ] @@ -169,49 +174,51 @@ "累计借入现金: 0.00\n", "累计借入资产: 0.00\n", "累计红利: 0.00\n", - "现金余额: 299050.00\n", - "未平仓头寸净值: 0.00\n", - "当前总资产: 299050.00\n", + "现金余额: 289640.00\n", + "未平仓头寸净值: 10640.00\n", + "当前总资产: 300280.00\n", "已平仓交易总成本: 0.00\n", - "已平仓净利润总额: -950.00\n", - "单笔交易最大占用现金比例%: 4.32\n", - "交易平均占用现金比例%: 3.99\n", - "已平仓帐户收益率%: -0.32\n", - "帐户年复合收益率%: -0.64\n", - "帐户平均年收益率%: -0.65\n", - "赢利交易赢利总额: 160.00\n", - "亏损交易亏损总额: -1110.00\n", + "已平仓净利润总额: 200.00\n", + "单笔交易最大占用现金比例%: 3.83\n", + "交易平均占用现金比例%: 3.40\n", + "已平仓帐户收益率%: 0.07\n", + "帐户年复合收益率%: 0.16\n", + "帐户平均年收益率%: 0.16\n", + "赢利交易赢利总额: 1220.00\n", + "亏损交易亏损总额: -1020.00\n", "已平仓交易总数: 6.00\n", - "赢利交易数: 2.00\n", - "亏损交易数: 4.00\n", - "赢利交易比例%: 33.33\n", - "赢利期望值: -158.33\n", - "赢利交易平均赢利: 80.00\n", - "亏损交易平均亏损: -277.50\n", - "平均赢利/平均亏损比例: 0.29\n", - "净赢利/亏损比例: 0.14\n", - "最大单笔赢利: 120.00\n", - "最大单笔亏损: -450.00\n", - "赢利交易平均持仓时间: 10.50\n", - "赢利交易最大持仓时间: 20.00\n", - "亏损交易平均持仓时间: 4.50\n", - "亏损交易最大持仓时间: 9.00\n", - "空仓总时间: 141.00\n", - "空仓时间/总时间%: 78.00\n", - "平均空仓时间: 23.00\n", - "最长空仓时间: 60.00\n", - "最大连续赢利笔数: 2.00\n", + "赢利交易数: 1.00\n", + "亏损交易数: 5.00\n", + "赢利交易比例%: 16.67\n", + "赢利期望值: 33.33\n", + "赢利交易平均赢利: 1220.00\n", + "亏损交易平均亏损: -204.00\n", + "平均赢利/平均亏损比例: 5.98\n", + "净赢利/亏损比例: 1.20\n", + "最大单笔赢利: 1220.00\n", + "最大单笔盈利百分比%: 13.08\n", + "最大单笔亏损: -320.00\n", + "最大单笔亏损百分比%: -3.40\n", + "赢利交易平均持仓时间: 50.00\n", + "赢利交易最大持仓时间: 50.00\n", + "亏损交易平均持仓时间: 5.20\n", + "亏损交易最大持仓时间: 7.00\n", + "空仓总时间: 135.00\n", + "空仓时间/总时间%: 63.00\n", + "平均空仓时间: 22.00\n", + "最长空仓时间: 57.00\n", + "最大连续赢利笔数: 1.00\n", "最大连续亏损笔数: 1.00\n", - "最大连续赢利金额: 160.00\n", - "最大连续亏损金额: -990.00\n", - "R乘数期望值: -0.01\n", - "交易机会频率/年: 12.23\n", - "年度期望R乘数: -0.12\n", - "赢利交易平均R乘数: 0.00\n", + "最大连续赢利金额: 1220.00\n", + "最大连续亏损金额: -810.00\n", + "R乘数期望值: 0.00\n", + "交易机会频率/年: 10.43\n", + "年度期望R乘数: 0.00\n", + "赢利交易平均R乘数: 0.13\n", "亏损交易平均R乘数: -0.02\n", - "最大单笔赢利R乘数: 0.01\n", + "最大单笔赢利R乘数: 0.13\n", "最大单笔亏损R乘数: -0.03\n", - "最大连续赢利R乘数: 0.00\n", + "最大连续赢利R乘数: 0.13\n", "最大连续亏损R乘数: -0.08\n", "\n" ] @@ -286,13 +293,14 @@ "name": "stdout", "output_type": "stream", "text": [ - "Wall time: 16 s\n" + "CPU times: total: 469 ms\n", + "Wall time: 1.86 s\n" ] }, { "data": { "text/plain": [ - "6055" + "6412" ] }, "execution_count": 7, @@ -329,7 +337,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/hikyuu/examples/notebook/007-SystemDetails.ipynb b/hikyuu/examples/notebook/007-SystemDetails.ipynb index 3b30cece..9c889d5f 100644 --- a/hikyuu/examples/notebook/007-SystemDetails.ipynb +++ b/hikyuu/examples/notebook/007-SystemDetails.ipynb @@ -5,24 +5,29 @@ "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-04-02 16:51:52,026 [INFO] hikyuu version: 1.3.5_202404021336_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:91) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "warning: can't import TA-Lib, will be ignored! You can fetch ta-lib from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib\n", - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2023-10-14 02:24:15.633 [HKU-I] - Using SQLITE3 BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2023-10-14 02:24:15.633 [HKU-I] - Loading market information... (StockManager.cpp:499)\n", - "2023-10-14 02:24:15.634 [HKU-I] - Loading stock type information... (StockManager.cpp:512)\n", - "2023-10-14 02:24:15.634 [HKU-I] - Loading stock information... (StockManager.cpp:426)\n", - "2023-10-14 02:24:15.694 [HKU-I] - Loading stock weight... (StockManager.cpp:529)\n", - "2023-10-14 02:24:16.103 [HKU-I] - Loading KData... (StockManager.cpp:134)\n", - "2023-10-14 02:24:16.107 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:157)\n", - "2023-10-14 02:24:16.108 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:160)\n", - "2023-10-14 02:24:16.108 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:163)\n", - "2023-10-14 02:24:16.124 [HKU-I] - 0.02s Loaded Data. (StockManager.cpp:145)\n", - "Wall time: 1.19 s\n" + "2024-04-02 16:51:52.422 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-04-02 16:51:52.448 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", + "2024-04-02 16:51:52.460 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", + "2024-04-02 16:51:52.472 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", + "2024-04-02 16:51:52.631 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", + "2024-04-02 16:51:53.811 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", + "2024-04-02 16:51:54.466 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", + "2024-04-02 16:51:54.467 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", + "2024-04-02 16:51:54.467 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", + "2024-04-02 16:51:54.472 [HKU-I] - 0.66s Loaded Data. (StockManager.cpp:150)\n", + "CPU times: total: 484 ms\n", + "Wall time: 3.27 s\n" ] } ], @@ -67,10 +72,9 @@ "metadata": {}, "outputs": [], "source": [ - "def TurtleSG(self):\n", + "def TurtleSG(self, k):\n", " n1 = self.get_param(\"n1\")\n", " n2 = self.get_param(\"n2\")\n", - " k = self.to\n", " c = CLOSE(k)\n", " h = REF(HHV(c, n1), 1) #前n日高点\n", " L = REF(LLV(c, n2), 1) #前n日低点\n", @@ -89,7 +93,7 @@ "source": [ "my_sg = crtSG(TurtleSG, {'n1': 20, 'n2': 10}, 'TurtleSG')\n", "\n", - "my_mm = MM_FixedCount(1000)\n", + "my_mm = MM_Nothing()\n", "\n", "s = sm['sz000001']\n", "query = Query(Datetime(200101010000), Datetime(201705010000))\n", @@ -106,24 +110,103 @@ "outputs": [ { "data": { + "image/png": "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9PX1cejQIdStWxdNmzZFjx49Pqt9mUyGS5cuCRtB1a1bt8iylpaWOH36NBo1aoSGDRsKn0UUycvLk1u6Ji8vDwCQm5srV17RwvAbNmzA4MGDERQUhGHDhqFTp05wdnbG0aNH5WaaFOf27dtYvnw55syZA2traxw9ehRDhw6Fvr4+PDw8AORvfvX69WucOHECz549g6enpxD4AfI3Ezt27BiCg4ORm5uLfv36QSqVYvny5QD+t5nYxo0bYWBggEGDBsHT0xNHjhwBAOzevRtjxozBmjVrUL16dYwePRpubm64desWlJSUcPHiRfTo0QNBQUFwdXVFYGAg2rZti4cPH4p2Xiy4j+3atYOZmRnCwsJw7do1DBkyBJaWlujWrVuJrod9o77kgls/2iLuRESpqamkqalJERERdOLECbKzs1NYTiaT0b1794TH169fp3r16lFubq6ojIeHBwGgHj16lKj9fv36UaNGjYiIaMuWLaSqqiq6hw8fPiQAFBERQefPnycAFBMTI+RnZWWRiooK/fXXX0REZGdnJ1r0PDQ0lABQfHx8kWlPnjwhZWVlhUfTpk2F86ytrWnatGmi/vfp04c6duwoPC54jbx586ZE1//48WMCQMePHy9R+a5du1K7du2IiOi3336jChUqiPJPnTpFAOjt27clqo8xxr5l3+t7K2OMsdL7ERdxX7FiBRkaGlKDBg2Ew9zcnMzNzUVpOjo6dPToUdG5169fF33WKtC7d2+aMGGCXHq/fv2oWbNmpKysTK1ataLFixeTTCYjPT09evnypVDO0dGRLl68WGy/ly5dSrVr16a8vLwiyygrKxOAEh+FP7ekp6fTwIEDSSqV0oYNG4T0zMxM6tKlC+nr69PWrVuL7WNhb968ofT0dFFaixYthM+kH9v86p/YTKxOnTo0cuRIIf/evXsEgE6fPk1ERO7u7uTm5ibkJyUlkVQqpfXr18tdz/79+0lVVZVevXolpHXv3p0aN25couth3y6eQviZTp06BVVVVVSvXh0REREoX768wnISiUQ0SunQoUNQVVUVRdF9fHzg7OwMAKhevTomTJjw0fZVVFSESL2TkxNycnJw5swZIT8kJARGRkaoWbMmHBwcoKGhgZMnTwr5Z8+ehUQiERaKNzIywuvXr0t+A5A/hTAyMlLhsWHDBqGcs7OzqO28vDycOXNGtOXr69evoaKigrJly5ao7YJRVwX3oDg5OTmIiIiAtbW10J/Y2FhER0cLZUJCQuDg4AA9Pb0Stc8YY4wxxhj797Rr1w7h4eHCUXg93oKj8GyR1NRUJCcnw8rKSuEaWEVp27YtOnbsCBUVFXTt2hUODg4ICQmBsbExjI2NhXLq6uqIjY0ttq74+HjcvHkT27dvL7JMbm7+5mEFR05ODuzt7SGRSODh4SHKIyI8efIEQP5IJTs7O4SHh+PixYvw8vIS9S04OBhTpkyBl5cXmjVrhnPnzgEA6tevjzFjxijsi76+vmgd5YK6Cj5jfWzzq8/dTCw5ORkRERGifFtbW5iamgrTNUNDQ0X5ZcuWFZb0+VBoaCgcHBxQrlw5UX8uXboEIuLNvL5jHMD6TAcPHkTbtm2hpqaGo0ePwsnJqUTnHTp0SPgBfP/+PQYNGoSEhAT8+uuvAIARI0bg/v37GDp0KHJycgDkB1emTJmC8+fP48aNG5g7dy42bdqEwYMHAwAqVqyIzp07Y+TIkTh//jxOnDiBadOmYcKECVBWVoaGhgaGDh2KqVOn4vjx4/j7778xatQoeHt7C9MGHRwccPny5VLdg4IphIqOgkXRAWDkyJG4fPkyAgMDERkZiREjRkAmk4l+6V6+fBn29vZFvtkMHz4cwcHBuH37Nk6cOIGuXbvC2toarq6uAPIXMJw0aRIA4OrVq5g+fTouX76M8+fPo1u3bnj9+jVGjBgBIH+BeUdHRwwYMABXr15FcHAwFi9eDD8/v1JdP2OMMcYYY+zfceDAAdjb26NWrVpQVlbGkiVLsG/fPtEaWFevXhXKN2rUCLa2ttDT08OrV68wbNgwqKioCMe2bdswb948UVpgYCC6d++Ovn37QkVFBYMGDRKmuQUEBIj6079/fwwcOBASiQQHDx5U2Ofdu3eje/fuCAwMLNEX7UD+FDtHR0eoqalBSUkJhw8flitz+PBheHl5YdCgQdDQ0ICDgwMkEoncMWbMGEycOBF6enoYOXIkcnNzUbVqVVSqVKlEfXny5AlOnTolLHXzsc2vPraZWMFaVkVtJhYTEyOXX1DH06dPkZSUJAQlFeV/qKj+ZmdnIzEx8aPXw75hX3K41482hTAzM5PKlClDO3fupBs3bpCysjI9evToo+e9evWKJBIJXbt2je7cuUP16tWjbt26UWZmJuXk5BAASkpKotTUVGrdujXVqVOHbt++TQ8ePCBXV1fS1dUlHR0dql+/Pu3cuVNUd1JSEnXv3p00NTXJ2NiYAgICSCaTCflZWVk0dOhQKlOmDOnp6dGIESMoKytLyD9+/DhVrFhRGO5akimEpbF7926qXLkySaVS+vnnnykyMlKU37dvX5o4cWKR58+cOZMqVqxIUqmULC0t6ZdffqHnz58L+R4eHuTh4UFERA8ePCB7e3vS1NQkIyMjcnNzo/v374vqi4uLo5YtWwr1rVixotTXxBhj36rv8b2VMcbYp/kRpxAmJibSmTNn6I8//qDu3bvThg0bKD09ndLT0+no0aPUqlUrOnv2LEVHR1NqaqroXAD04sULuTqLmkJIlP9ZSktLi968eUNHjx6l5cuXl7rP58+fp7Jly9K7d++oRo0aJZrKd+bMGapcuTLFxMSQVCql2NhYsrKyotjYWLmyhaczFggLC6OqVasqrLvwZ72SePDgAdnY2JCTkxNlZ2cTEdGAAQNES8MQEcXGxhIACgsLo8DAQKpUqZJcXRKJhP7880/avHkzKSkpyeVbWVnRjBkz6Ny5cwSA4uLiRPmurq40cOBAiouLIwB07tw5UX7//v2pWbNmcvU2bdqUBgwYIEo7e/as8Bn2Y9fDvl3f7CLu34OLFy/i/fv3aNOmDbp3747+/fuXKKp9+PBhmJqawsHBAUFBQWjSpAnmzp0LiUQiWrBPR0cHBw8exMSJE6Grq4vy5cvj9OnTxdZdtmxZ7Nixo8j8goX0ChbT+1CLFi2gqqqKgwcPomPHjmjSpInc4oKK0kqqa9eu6Nq1q8K8V69eYc+ePbh7926R5/v5+RU7QmrXrl3C/6tUqSLsOFiU8uXL4/jx4x/pNWOMMcbYF5KXAyipABLJ1+4JY1+dgYEBpk+fjufPn8PExATr1q2Dt7c3AMDDwwPe3t6oX78+1NXVP7utx48fY8+ePcjIyICVlRU2btyISZMmYeTIkaJd3IkIGRkZRX4e8vf3R58+faCpqYkpU6bA398fXbt2hZqamsLyDx48QM+ePREcHAxtbW0A+aOBpk6dCjc3N5w5c0a0vErh6Ywl8eEO9MX566+/0L9/fzRu3Bhbt24V+vyxza8+dzOxgj4WteHXx/IVXXNp+8ubeX0feArhZ3B1dcXjx4+ho6ODFStWYP78+SU6T0NDQ5j2N27cOMybNw+SIv5IUVFRwe+//17k2lr/NIlEgqCgIPj5+SE7O/uLtFnAz88PgwcPRoUKFb5ou4wxxhhj34T374CFtYDtn7drGmM/ktDQUOzcuRN16tSBqqoqZs2ahfj4eGzZsgXR0dFwdXXFq1evPqsNmUwGe3t7nD17Furq6nj+/Dnc3d1x/fp1WFlZISUlBenp6UhPT0dgYCA6duyosJ5Dhw7h+vXr8Pf3B5AfZNPS0sLcuXMVln/06BGaNWuGyZMn46effhLleXl5oWHDhnB1dUVCQoKQ/vLlS7kpgz///DMePHggl17UwAFFli5dih49emDq1KnYv3+/aGc/c3NzxMfHi8oXPLaysoK5uTlevXolLH0DAHFxcQCASpUqwdzcHIB4el52djYSEhJE+YraqFSpEgwNDSGVSovM/1BR/dXV1YW+vv5Hr4d9uziA9ZlMTU0BABUqVJDbvrMoPXr0EG37WpiKigqIqMSLmP8bOnbsiFGjRuHBgwdfrM2UlBRYWVlhzpw5X6xNxhhjjLFvSlw4kPYciDoGJBW/SDRj/wUHDhxAhQoVUKdOHZw8eRJly5bFsWPH4ObmhmrVqmHp0qW4d+8eGjVqJNqYqbSUlJTw4sULbNmyBUpKSsJaTpUqVYKtrS22bdsGAIiNjcWsWbMUBqTi4+MxePBgTJ48GSYmJkK9q1evxqxZs+TWGb527RqcnZ3Rv39/YR3kDy1fvhxVqlRB3bp1cf78eQCAiYkJIiMjhbWtiAhhYWGoWrWqsOD7hQsX8OuvvyI4OLhE13/z5k2MGTMGO3fuhI+Pj9zgio9tfvW5m4mZm5ujYsWKovyoqCg8ffoUzZo1g5KSEho1aiTKT0lJwdWrV0UbghXu7+XLl5GSkiLqT0FZ3szrO/Yl5yv+aGtgMcYYY98yfm9l7DsTf5VoWpn/HdnpHz+Hsf/3I66B9fz5c9q6dSuFhIRQeHg4RUZG0saNG6lhw4aUmZkplJszZw798ssvonPx/2tg3bp1i6ZPny6kl2QNrMIePnxI5ubmdPDgQbK2tqbVq1fLnZeWlkb29vbUpk0bys3NlcsfO3YsmZqaCuslv3nzhvT09Gjq1Kl09OhR0tLSEg4AosevXr2ikSNHUt26dSknJ4eI8tdidnFxoa5du1JOTo5oDaxbt26RmZkZHTx4UNSHfv360ZIlSxRe96RJk8jKyoqio6PlDplMRjKZjBwdHcnZ2ZmuXLlCu3fvJi0tLQoODhbq6Ny5M9na2lJYWBgdP36cTExM6PfffxfyfXx8yMTEhI4dO0bnz58nW1tbGj58uJC/ePFi0tLSol27dtGVK1fI2dmZ2rdvL+QfOHCAlJWVaeXKlXTjxg1yd3en2rVrC/d74sSJwlrK7969IwsLC3J3d6cbN27QqlWrSE1NjcLDw4mISnQ97NvEASzGGGPsB8XvrYx9Z+IuiQNYiQ+/do/Yd+RHDGAR5S9Q3qJFC5o5cyYRER09epScnJyIiGjUqFHUp08fOn36tLDgOBEJG2ONGjWKtLS0aNGiRUJeaQNYREQTJkwgANS6dWu5AFVUVBTZ2dmRnZ1dkfc+JyeHWrduTSYmJnTq1CkiIoqJiZErl5CQQFKpVGEdhQN2RETJyck0efJkevfunSiAtW/fPtq7d6/c+fXq1SNfX1+Fdffv358AKDzS0tKI6OObX33uZmIymYymTJlChoaGpK2tTb1796akpCRRG0uXLiUzMzPS0NCgdu3aiTYVK7yZFxHR7du3qWHDhqSmpkZVq1alPXv2iOrizby+TxKiT1yN+xOkpqZCV1cXKSkpKFOmjCgvJycHCQkJMDIygqqq6pfqEmOMMfbD4vdWxr4zT84DG9v97/Hwq4ChzdfrD/uuFPdZ63t08eJFTJw4EVFRURg3bhzc3d1haGiIP/74A5cvX8aRI0eQkJCADRs2YOXKldDT08O6detgb2+P0NBQNG3aFLVr18bmzZtRq1Ytod4+ffrAwsJCbumS58+fIyYmBu3bt0dSUhLS0tJw4sQJLFy4EK9fv8bo0aOxYcMGJCUlwdvbG66urrC1tYWNjQ2qV6+O3bt3Fzv9LD09HT179sS5c+dw7949mJmZyZVJTEyEhYWFsKC4IioqKsjLyyvxffT09MTGjRtLXJ6xbxnvQsgYY4wxxti3IC9H/JhkX6cfjH0D8vLy0KtXL3h5eUFFRQXa2trIyspC2bJl8eeffwIAjIyMMH78eIwdOxY7duyAkZERAKB69erw9/fH1KlTi9z970Pdu3fHhQsXMHDgQDx8+BC1a9eGo6Mj+vXrBy8vL6iqqmLIkCHYu3cvduzYgZkzZ+L06dM4cOAA7O3tRbvrKaKtrY0DBw4gKipKYfCqpArvWs/Yf02pR2ClpaXBx8cHe/bsQU5ODtq0aYNFixYJi5kXh0dgMcYYY18Ov7cy9p2JOgFs8/jf46EXAOMaX68/7Lvyo43AUiQvLw/KyspfpK3k5ORiN9b6kn1hjOUr9S6EI0aMwLlz5xAcHIzjx4/jyZMn6Nmz57/RN8YYY4wxxv478t6LH+dmf51+MPaN+pIBo4/tCs/BK8a+vFJPIbxy5QqGDx+Opk2bAgAmT57MAaz/mNzcXCgrK8ttr8oYY4wxxj7DhwGsDx8zxhhj/2GlHoHVvXt37Ny5E69evUJycjLWr1+P7t27/xt9++6lpaXhyZMnuHLlCg4dOoTr16+L8vfu3Ytdu3aJ0t6/fw93d3fEx8d/ya5+lIWFBR4+fAgA2LJlC1xdXT96zoEDB4q8jgMHDiAuLu6jdURERCAyMrJ0nVXg6dOnkEgkOHPmzGfX9TU8f/4cBgYG2Llz57/azuLFi2FhYYF37979q+38E5o3bw4vLy8A+T835cuXx++///51O4Xv/7VWlCZNmmDQoEGfdG7Xrl3RsWPHz2p/y5YtHDRnjP34PlwDi0dgMcYYY4JSB7D8/PygpKQEExMT6OvrIyoqCosWLVJYNjs7G6mpqaLjRzNo0CC0b98eTZo0Qd26dWFjY4Ny5cpBTU0NZmZmcHV1xbBhw7BixQpcunRJOI+IMGXKFEilUlF9ampqsLCwwOTJkxW216NHD1haWsLe3l44tLW1YW1tLUqTSCTC7hV5eXl48+YNoqKicPr0aRw+fPizrjk4OBhOTk4fLffo0SPUq1cPJ0+eFKWfO3cOffr0EQJixTl9+jT69+8Pmaz4RUxTUlJgaWmJCxcufLTOkoqKivrH6ips+vTpkEgkkEgkkEqlqFatGhYuXFjsNZqZmSEqKgrdunX77PY3btxY5CKTw4YNQ2RkJLS0tD5aT1ZWlsIAZXBwMGrVqoWcnBwFZ8mbM2cO2rdvX6KyRVFTU8OtW7fg4+PzWfV8bcnJyejTpw/KlCkDQ0NDjB8//qOv/a8lNzcXMTExJSq7efNm7Nix41/u0b/j3LlzcHR0hLq6OmrUqIHjx48DAJKSklC+fHnR73VFCn7WPzwGDhxY5DkymQyrVq2Cvb29XN7du3fRvHlzaGhowNzcHH/88cdnXR9j7BvDI7AYY4yxIpU6gPXLL7/g9evXOHr0KEJCQqClpVXkCKzZs2dDV1dXOMqXL//ZHf7WdO3aFYMHD8bUqVOxZMkS1KxZExoaGnj+/DnS0tIQExODs2fP4vDhwxg6dKhw3r59+6Cjo4OOHTsiICAA6urqwrF69Wps375dlFaw0wYABAYG4saNG8JRt25drF27VpRWQCaTQV9fH7a2trC1tcXUqVOxe/duHDt2DKqqqjA0NBQd2trawqiWqVOnIjg4WHS9qampOHXqFLp27frRezNmzBisWLEC0dHRQlpERAS6dOmCP//8U5iGCuRvk6utrS13+Pn54d69eyhTpoxcXkE/AWDSpEno0KEDfvrpp5I+dcXavXs3qlat+o/UpYilpSWio6Nx5coVDBkyBBMnTsTChQuLPcfAwOBfH4GirKz80fn+BRwdHbFu3TpRWnp6OoYNG4a1a9eWeMHocePGIS4u7rNHl+np6UFJqdS/0r4pvXr1wv3793HixAmsWrUKy5cv/yZGlSlS8LurJDQ1NaGhofEv9+ifFxMTg7Zt26J58+a4cuUKXFxc4O7ujidPnkBPTw/z58/HgAEDig0yRkdHi47Lly9DKpWiR48eCsuvWbMGNWvWxMiRI5Geni7KS09PR8uWLaGvr4+wsDBMmDABEydOFL0/MMa+c7wGFmOf7e3btwgICCjy/blPnz6YOHGiwrzk5GRoa2t/Vvu3bt0qUTlFX/ZGRkbi2bNnpW4zKioKf/31V6nPY+y7Q6Vw584dAkCXL18W0uLi4kgikdDJkyflymdlZVFKSopwxMfHEwBKSUmRK/v+/Xt69uwZvX//vjRd+mZERkZSgwYNqGfPnpSQkCCkX7lyhWxsbGjhwoVCWm5uLtnZ2dHZs2fp77//pqNHjxIRUVpaGlWuXJn27NlDREQymUyune7du1OlSpWoQYMGwqGjo0PVqlUTpQGgzMxMUT1aWlqUlpZGRERHjx4lFxcXufpXrFhBnp6eRETk6elJK1asICIic3Nzio6OpkWLFhEAhYeWlhalp6eTlpZWkYeKigopKSmJ0lxdXYmIKCIigrKysoS+vHr1iqpUqUKXL1+m7OxsUT+vXbsm/D8hIYG0tLTo8ePHRT4/Ba+90NDQIssU9ueff1IpfzxKbNq0aVS5cmVR2ogRI6hatWoKyyt6HXyODRs2kLKy8mfXU7lyZZo2bZoobcmSJcLzWRqbNm0iR0fHUp3TrFkz4bX6NRT1vJT2tVbg5s2bBICuX78upE2YMIEsLS0/qz//FBcXFxo4cKDw+Evf/3/zZ7IoY8aModq1awuPc3JyyNzcnKZOnUpERHl5eWRtbU0HDx4scZ2TJ0+mn376qch8BwcHmjdvHvn5+cn9nli2bBkZGBhQRkaGkDZ48GCqU6dOkfV97++tjP3nhK8kmlbmf8ft4K/dI/YdSUlJKfKz1vdqxYoVpKenR7Vr1xYOY2NjMjY2FqVpaWkJn6nevHlDVapUocGDByuss3fv3jRhwgSFeUlJSaSlpSVKq1GjBmloaAifXZSUlOjixYsKz4+NjSULCwtasGDBR69tzpw51Lp1a4qPjxfSHB0daceOHR8990Pnzp0jXV1dev36danPLRAXF0ft2rUjTU1NMjU1paCgoCLLymQymj59OpmampKmpiZ16tRJru3ly5dTxYoVSV1dnVxdXenRo0ef3DfGCpRquMLt27cBQDStoXz58jA0NFQYaZZKpShTpozo+NHExMRg7Nix6NixI7y9vYURQwcOHMDUqVPRrFkzDB06FCNHjhTOWbp0KWrUqIHGjRtjzJgxePz4MQBg5MiRsLa2hru7O4D86Ylr1qyRa3PKlCkIDw8XDgcHByxfvlyUVtg/NWonNzcX8+fPR5s2bUBEwvHTTz9h//79SE9Ph5aWFtLT04Xj7du3OHXqlPA4MzMTe/fuRVpampB2+vRpAMCGDRvQuHFjvHr1CnFxcejQoQMWLFiAypUr4+eff8aOHTuQnZ2NkSNHwtPTEykpKQCAPXv2oH79+rCyshL6eu3aNTg5OUFdXR02NjY4ceKE6FqysrIwbtw4VKpUCZqamqhfvz6uXbsGAPDy8kLfvn2Fe1exYkUAEHbcNDU1ha6uLjp16oSEhIR/5N5Wq1ZNWBOsYIrfkSNHYGpqCg8PD4XrKt27dw8dOnQQRqf1799fyDt06BBq1qwJTU1N2Nvb49ixYyXqx2+//SZcLwBcv34dTZs2hY6ODgwMDBAUFIQnT55AIpHg0aNHCAgIgEQiwcaNGwEAW7duFY2MK6nu3bvjzp07ePDggcL8jIwMeHt7Q19fH7q6uvDx8QERicqoqKgI/ZDJZJg6dSosLS2hrq6OmjVrip6rjRs3ws7ODlKpFGZmZjhw4AAA4OzZs3B1dUXZsmVhYmKCsWPHCu2cOXMGEokE+/btQ+XKlVG/fn0AH3+tlVRoaCiMjY1Rp04dIa1p06aIi4vDixcv5Mp7eXmhefPm+O2336ClpYVly5YBANavXw9ra2toaWnhp59+wuXLl4VzFD2fBffjw2mlxU01lUgkCAkJwaZNmyCRSDB9+vRir63wemUF569atQpeXl7Q0dGBlZUVdu/eLTpn27ZtqFKlCtTV1eHs7KxwumJR13rs2DFIJBL8/fffQtmmTZuWaORoYaGhoWjdurXwWEVFBY0bNxZ+xyopKaFv377YunVriepLSkrC4sWLMW3atCLLXL16FePGjVM4gvHu3buwtbUVjWZr0qQJbt68iexsHqXB2A9BbgphyabjM/Yjc3NzE800GTJkCIYMGSI3I6WAvr4+Dh06hJ07d/4jSxjIZDJcunRJ+OxS+G+1D1laWuL06dP47bff5D6TfcjX1xf169dHy5YtkZubi7CwMNy5cwdjx46FqakplJSUYGFhAQsLC2hqamL8+PGIjY0VzdIpOFq0aIF3797BwsJCLs/Q0PCj15iXl4d27doJ/QgICMCECRPk1msuEBQUhMWLF2PVqlU4efIkHjx4AE9PTyF/9+7dGDNmDAIDA3H+/Hnk5OTAzc3tm10ag30/ShXAMjc3B5D/R3SBFy9eIDExUcj7xxAB7999neODD8bF2bVrFxYvXozMzEwsWbIEY8eOxaBBg9CxY0dcunQJERERGDNmjCiItHfvXuzfvx8aGhrIzs6Gt7c3duzYgVOnTokWKvby8oKPj49oCh6QP7XP3t4e1tbWkEgkiImJwejRo0VrYBWIi4uDtrY2tLS08O7dO5iYmEBbWxtv3rwp9VOybds2JCUlyX2QfP78ucLn/8yZM6hduzZ8fHyQl5cHAHj8+DHGjBmDunXryn3QX7RoEVq1aoVTp05h1qxZSEtLw6hRo1CxYkX4+vqiR48e2LdvHzIzM3H58mXo6uoCAMLCwuDi4iLUk5SUhBYtWqBcuXI4c+YM5s+fj1mzZonaevXqFR4/fowVK1bg/Pnz0NLSQp8+fQAA8+bNE6ZtRUdH4+zZswDypzlaWlpi165d2LdvH65cuYKpU6cKdf75558K31DU1dXx22+/FXtvo6KiRAE4IsLixYuxZ88eTJo0Sa78kydP4OTkBJlMhoMHD+L06dNwdHQEAFy+fBkeHh4YMmQILl68iC5dusDNza1Ea44VJpPJ0KZNG5QvXx4XLlzArl27YGlpCQsLC0RHR8PS0hIjRoxAdHQ0unTpgszMTFy7dk30XJSUVCpFw4YNERYWpjB/2LBhOHjwINasWYOQkBA8e/asyLIAsHz5cixZsgQrV67EpUuXRNN3Fy1aBG9vb/Tu3Rvh4eFYtWqVEBA4cuQIOnfujJCQEMybNw8LFiyQe+OeO3cuVq9ejcWLF5fotQYALVu2LPK1ERsbCyD/Z6PwawDI/yMIyF8YXpH79+/j7t27QqAlODgYY8eOxbRp0/D333+jTp06aNOmDZKSkop8Pj9FdHQ0fvrpJ3Tu3BnR0dGiAH1JzZ49Gw0bNsSZM2dQq1YteHl5Cb+Xzpw5g759+6Jbt264cOECevfujXnz5onOL+5aW7duDXd3d/j5+QHID+hevXpVWK+xqD/+1NXV0bJlS6GNop6Tws+Hi4tLsa/FwlatWgVzc3NRGx8q7gsHAwMDxMfHi4K3qampkMlkn/Q7nTH2DfowgCXL+zr9YOwbcvjwYTRs2FA41q5di7Vr14rSPtwsy8bGBmfOnIGHh0eJ2/H09ETXrl2RlZWF1q1bY8mSJSAivHz5EuXKlStxPTY2NggICMCQIUOKDdgoKysjICAAV65cQVxcHAYMGID169fj6dOnWLp0KapXr46nT5/izp070NbWRvfu3VGhQgVkZWUhKysLz549Q//+/ZGSkoKsrCzk5OQgOzsbt2/fxtSpU5GRkYGsrCwkJiZ+tM+HDx/G/fv3sXnzZjg4OGDw4MHw8PAQviAtTCaTISgoCJMnTxaWcJk/fz6OHj0qfE6cPXs2vL290bdvXzg6OmLNmjW4c+eO8LmKsU+l+Ov1Ijg5OcHBwQFeXl6YP38+1NTUMGHCBFhaWn72IsxycjKAWWb/bJ0l5fccUPv4ItYA4OPjA19fXygrK+PJkyfw8/NDbGws9u3bh44dO6Jnz55o2bKlaHTMsWPHIJPJUKtWLSxfvhxXr15F7969YWZmhpYtWyIjI0M4srOzMXDgQJw9exYSiQRz5sxBdHQ0QkJC8PLlS6xYsQIuLi7IyMhAcHAwtm/fjp07d+Lp06eQSqWwtLREeno69u/fj06dOuHly5fQ1tYu8YicwnJzc7Fw4UKMGDECb968gYGBAd6+fYtnz57B1tZWKBcdHY2JEyfi5MmTmD59OkaNGgVlZWUAQJUqVXDv3j0sWLAAnTt3houLCxYsWIAqVaoAANq1a4cJEyZgypQpaNCgAQ4dOoSgoCBs27YNly5dwp07d5CTk4O3b98KQbOHDx+KXn8rVqyAmpqasI4YkD9SonCZChUqYM+ePcLjsWPHokOHDkhOTka5cuVgbGwMALC2thbK9OzZEz179hQ9PnXqlPDYzc0N9erVU3jvivrmIysrC0eOHMHq1atFbxAymQx+fn7Cml4fBjDmzp0LQ0ND7Nu3TxipUTAiaOrUqRg1ahSGDRsGAKhduzZ27tyJnTt3wt/fX2E/FElOTsbr16/RqlUr1KxZU5RnbW0NVVVV6OvrC/fo/v37UFJSQoUKFUrcRmE2NjZ49OiRXPrTp0+xefNm7NmzB506dQKQHyz8MLBQWFRUFExNTdGqVSsoKyujdu3aAPJ3K5w2bRr8/f2FwGDhb9Hmzp0r/N/R0RGLFy/GxYsXRev8DR06FM2aNQMAzJo166OvNQBYu3YtMjIyFPbVzCz/91x6ejo0NTVFeQWPixpdk5qaitWrVwtrNbRv3x6zZ88WRhAuXrwYW7duxZEjR9CmTZsin8/Ssra2hoaGBnR0dEQ/I6XRoUMHDBkyBADw+++/o0qVKrh27RpatmyJWbNmoUOHDkLg18HBAY8fPxatBzZ58uQir7V3795YsGABqlWrhsOHD2PChAn47bffhN8ZZmZmorUCCyv8HBT1nBR+PmxsbPDs2TNkZ2fLbcpRWMHC7J+z0YC7uzt+++03zJw5E76+voiKihICewW/Yxlj37kPR1zJcr9OPxj7hrRr104YZQ9AGPldeAR4kyZNhP8XfLljZWVVqvVR27Zti9evX+P8+fPo2rUrqlWrhpCQEBgbGwufDQAIX0A2bNiwyLri4+Nx8+ZNbN++Hb179y6yXGJiIvr27YuyZcti+vTp6Nmzp/CFv46ODmbNmoVBgwYhKChI+LK6gK6uLiIiIjBp0iTMnz9fSB89ejS0tbXlrr1+/fpwcnLCggUL5PoRGhoKBwcHUaCuadOmGDlyJIhI9AXb7du3kZiYiDZt2ghpLi4uUFJSQnh4OPT09BARESH6UtfW1hampqYIDw8v0W72jBWlVCOwJBIJjh49iurVq6Nz585o3bo19PX1cfr06RLtWvYjUlVVRUxMDEaMGIEWLVrAxcUFt2/fFraMHzduHMaNGyeMBAAADQ0NzJgxA25ubvjpp5/g6OiIQ4cOwdfXF+rq6jh48CCqV6+O+fPnC4vBFyzmp6WlhYEDB+Lo0aOIjY3FhAkTIJVKYW1tjejoaEyePBmGhobCToQF1q1bB4lEAjc3N2E60t9//y23iLuvr2+R1zpgwAB4eXmhcePGwpSrU6dOwcHBQXj+MzIy0LBhQ+jo6ODBgwfw8fGR+2AllUoxceJE3L17F6qqqpgzZw4ePXqEVq1aoXHjxmjdujWmTJkCb29vvHr1Cl26dMGaNWvQpUsXdOzYEa9fv0aVKlWEgE9SUhL09fWF+m/duoWffvpJCCgAQK1atUR9ICJs3boVHh4eqFGjhvDtTHJycpHX/+7dO8ybNw+tW7dGpUqVsHDhQlF5XV1dYbH8D48PA1iPHz+Guro6NDU1MXjwYMyZM0du6l1BQEqRCxcuoG3btgqnGV27dg3z588XjSq5d+8enjx5UmR9iujr62PQoEHw8vLCgAEDhCnERfnweSgtQ0NDvH37Vi49MjISMplMtOi/mpqaKGj6oYEDB+L169eoWbMm1qxZI+zIeefOHaSkpMDNzU3heU+ePMG4cePQuHFjmJqa4tq1a3KvicLPS0lea0D+qJ2iXhsFz6FUKsX79+Jv3gv6/WEQpUD16tWF4NW7d+8QFRWFUaNGCc+7lpYWUlNT8eTJk1I/n/82BwcH4f8FwciCaZ63bt0SPd+A+L5+7FqB/CC1n58fevfuDXV1dSGgC+T/3i7q+Sg8Kq2o56Tw81Hws63otVvYsWPH8Pz5cyHg9ins7e2xdu1azJs3D5qammjevDk6duwIFRWVz/rZY4x9Q65tEj/mABZjOHDgAOzt7VGrVi0oKytjyZIl2Ldvn2j2ydWrV4XyjRo1gq2tLfT09PDq1SsMGzYMKioqwrFt2zbMmzdPlBYYGIju3bujb9++UFFRwaBBg2BgYIBBgwbJbVrTv39/DBw4EBKJBAcPHlTY5927d6N79+4IDAwUZqJ86PDhw6hVqxbKly+PdevWoVGjRpgyZQrq168Pf39/XLp0CRcvXkTnzp2hp6cn94WmiooKNmzYgJcvXwojvQ4dOoQ7d+5g1apVcu1VrVoVlSpVUtiXokadZ2dny43gKlj+pnB5DQ0NGBkZ4enTp8IorI+NYmfsU5RqBBYAlCtXrsTrfXwWVc38kVBfg6riD4uKjB8/HkFBQZBIJKhVqxaWLl2KefPm4d27d8jJyYGSkhJ0dHSwcuVKPHv2DOvWrcO5c+ewYMECDBkyBF27dkX9+vUxfvx4vHr1ChYWFrCxsYG6ujqUlJRgaGiI69evC8EoIyMjxMTE4NSpU9i0aRO0tbUxduxYdOnSBVKpFO3bt8f27duxbNky4YPx48ePhZEEzZs3R7du3TBhwgQ4OTmJ1lQCgJUrV8rN175x4wbS0tIA5Acx+/Tpg99//x1eXl5Yu3YtevXqJZTV1NTEgwcPEBwcjEqVKkFLSwsZGRlQUlKCuro6UlNToampCRUVFSQlJSE2NhblypVDSkoKEhMTcenSJdjb2+P9+/cYMmQIypcvD4lEgsaNG2PJkiV49OgRDh06hGnTpgl9UlNTE/1CT09Pl9s95MNf+AEBAVi9ejXmzJmDGTNmID4+vthpPUD+tz/v37+Hv78/atSogY0bN4q+Ddq8eTMGDBig8NypU6eKphuWL18ex48fF0bJfRjkU1ZWFgVFPkRERa5PlJOTg0mTJolGiwEQplyWxpo1a9C7d2/Mnz8ftWvXxu+//17kCJIPn4fSysrKUjiCpWAntg+DdcW1Vbt2bURHR2PFihWYNm0agoKCEBYWJky9UnTvEhMTUbduXWHkT9WqVeXuIQDRa6skrzUAaNasWZFDph89eoQKFSrA3NwcR44cEeXFx8cDkP8DQFFfcnNzQURYsmQJGjduLCpXEGQp6vlUUlKSW1Ps316joPDzWfB8FPQhPT0dampqovKF72tJrhXI35kyIyND+H1aIDY2FpUrV1bYLxcXF4SEhADInzZf8BwUiI+PF/3xVxBkLG70FZD/h2yzZs1KvMtnUfr3749+/frh2bNnMDU1xcKFC1GjRo0S7/rJGPuG5WQBaR/87ctTCNl/nIeHB6pVq4Zr167h8uXL8PHxEb54DgsLw8KFC+Hn5wczMzNhlNSdO3cA/G9a/rJly0QzHfr06QMLCwvMmTOnyHbfvn2LJ0+eYMKECejWrZsob+DAgRg4cGCR5/799994+/Yt1q9fj/r162Pnzp2iz0sAkJKSgnHjxmHp0qXo3Lkzrl+/Dnd3d3h4eODGjRuwsLAAABw8eBB79+7FvHnzMG/ePISFhSEvLw+qqqrQ1NQUrvHDtaYLzgfy11VeuHBhsbsWp6eny33hXtRMgPT0dCgpKcn97VMwSr3gb/ePjWJn7FOUOoD1xUgkJZ7G9zV5enqiTp06MDc3R9myZVGmTBns3LkTt2/fxpYtW5CZmQkNDQ08efIEAwYMQHJyMm7evAk7OzukpKSgfv36aNu2LYD8BeHLly8v10bhkVTTp0/H4sWLkZKSAicnJ+jp6WHdunVYsGABUlNTkZ6ejnPnziE2NhZ79+6FlpYWxo8fj/79++Py5csYOXIkYmNjYWtri/379yM3N1f0Yd7Ly0tYCyo1NRULFy7E69evRaMQunXrhoCAAPTr1w8RERFyawQV/PLz9vbGwoUL4evrC2trawwZMgStW7fG5MmT4ezsLEw9UlNTg5GREa5evSpca0Gw58yZMxg+fDgiIyMxcuRIIX/GjBnCh10jIyO8fv1aaN/a2lru2j5co2bv3r3w9PREv379hHYKK/iwW1DH27dvcfbsWRw7dgytWrUCkD9lrrCOHTsiMjJS7vkrfE8KFIwA+VQ1a9bE6dOn5Yb0AvlDdB89evRZ9RfWpEkTNGnSBFOmTMHMmTOFAJaSkhJyc//3zbCRkRGSk5ORk5PzSR+mC0bWfajgdXLhwgVh6l5KSgpu375d7PQ1PT09+Pn54ddffxXWLhswYADU1NQQEhICOzs7Uflz587hzZs3WLlyJaRSKfLy8hAVFVXsOlElea0B+RsUfGwKobOzMyZPnozo6GjY2NgAAEJCQuDg4AA9Pb0i+1BAV1cXxsbGiIuLK/a5V/R8litXDjKZDK9evRL++Ptw7b0Pffj8/5Osra3x999/C1MMAfF9Lcm1xsXFYdKkSdi1axcGDRqEtWvXYvDgwQDy73lRP6uF/9hydnYWpkID+QucnjlzBuPHjxfKvH79GioqKsUGpvLy8nDw4EGF66N9CmVlZVhaWiI3Nxdr1qwpMnDOGPvOfLj+FcAjsNh/noGBAaZPn47nz5/DxMQE69atg7e3N4D84Ja3tzfq169f7Be/JfX48WPs2bMHGRkZsLKywsaNGzFp0iSMHDlSFKwhImRkZMh9+VfA398fffr0gaamJqZMmQJ/f3907dpV9OWcrq4u7ty5g9OnT4v+hihY3+tDV65cQaVKlUR/9z9+/Fg0tfFzlGYmgFQqhUwmk/scWTBKveBefWwUO2OfolRTCJm8gt0ER4wYgbS0NFSsWFE0ndLFxQWTJ08WdqQwNDTEmDFjcO3aNWzcuBG+vr5CxPzcuXPCWj2F7du3TwiwuLm5Yd26dQgJCcH8+fMxd+5c2NraomnTprhz5w6eP3+O5ORkqKmp4cCBA3jz5g1u374tGkmyatUqWFtb4/jx46L54p07d8bBgweFUR137tyBjY0NIiMjYWBgIJRTUVHBb7/9hi1btmDQoEGfPaIAAFavXg0dHR1oa2uLjjZt2uDevXvQ1taWyy8IQDg4OIh2Whs8eDDi4uIwcOBAXL16FTt27MAff/whas/Y2BhHjhzBlStXsG/fPixevFiUXxBI3Lx5M65fvw4dHR1oampi8+bNuHXrFhYuXCiM0ihQmimEn2vSpEm4ffs2+vbti4sXL+LChQvC+kA+Pj7Yvn07pk+fjoiICJw4cQJdu3YVLfJMRDhz5ozoePfunaiN58+fw9fXF3///Tdu3LiB69evC4GVgnt08uRJ3Lx5E/Hx8bC0tISBgYFoCLenpyeWLl0KIH/3zYLdSS5evAhnZ2dhJ0kgf/H5grn9W7ZsEQJH9vb2aNCgAby9vXHs2DFcvHgR3bp1K/YPlQULFmDHjh2IjIzEiRMnkJmZCRsbG2hpacHHxweTJ0/G0qVLcfPmTQQHB+PIkSPCHwDLli1DREQEvL29iww6FSjJaw0o2RTCxo0bw9HREQMGDMDVq1cRHByMxYsXC9OPnz17BolEIgzbVsTHxwfz58/H4sWLcfPmTRw8eBAdOnQAUPzzWa9ePWhpaWHWrFnIyspCeHg4Nm/eXOy1FywGf+3aNYVrl32OIUOGYPv27QgKCkJERARmz54tWm/uY9cKAL/88guaNWuGTp06YerUqZgwYYIQ6C7pFMKRI0fi8uXLCAwMRGRkJEaMGAGZTCaa7nv58mXY29sXu8bG7du38ebNG/z8889yeZMmTVK4UYMi2dnZWLduHW7duoXz58+jU6dOUFFRwYgRI0p0PmPsG6dox0EOYDGG0NBQ7Ny5E3Xq1IGqqipmzZqF+Ph4bNmyBdHR0XB1dcWrV68+qw2ZTAZ7e3ucPXsW6urqeP78Odzd3XH9+nVYWVkhJSVF2IUwMDBQWC7mQ4cOHcL169eFdWc9PDygpaUlWme1gEQiQU5ODmxtbZGcnCysPzt+/HjhcXJyMtTV1ZGXlyfM2CgYxfRPrn9Z1KhzXV1duWUKCtYULTwdMDs7GwkJCahUqZKQ/7FR7Ix9EvqCUlJSCAClpKTI5b1//56ePXtG79+//5Jd+mxRUVFkZWVFEyZMIJlMRkRES5Ysod69ewv5VapUodatWwvXff78eZoxYwa1atWKypQpQ927d6cLFy6QlpYWvXnzhoiIOnbsSH/++ScREY0dO5Z8fX2JiCg3N5e2bNlC1apVo/PnzxMR0YQJE8jf359kMhm5uLiQr68v3bx5U+jj3bt3iYhIS0uL0tLSiIjoypUrpK+vT2FhYUK58+fPk7a2tlBvXFyccE3m5uYUHR1NREQXL14kCwsLat++PWlpadH06dMpMzNTdF9WrFhBo0aNEvq/YsUKIiJq1aqV0GblypUpPj6+2PsbGhpKNWrUKLbM8ePHqWLFipSXlyekbdu2jaysrEgqldLPP/9MZ8+eJQAUGhpKRES3b98mR0dHUldXp59//pm2bt1KACgmJoaIiGQyGfXr1480NTWpYcOGRES0a9cuqlChAmlqalL//v1p8uTJVKFChWL7psi0adOocuXKReZv2LCBlJWVRWnx8fGi/hMRnTx5kurVq0dqampkbGxMfn5+Qt6iRYuoYsWKpKamRlZWVuTr6yv8bG3YsIEAyB23b9+mGTNmCNf09u1b+vnnn0lLS4v09PSoY8eOwv0hyn+9WFlZkYaGBu3fv5+IiHr27ElTpkwRytSrV0947fr6+lK9evWIiGj//v2kr69Pz58/JyKihw8fkrq6OiUnJxMR0YgRI8jb21uo5/Hjx9SsWTOSSqVkaWlJ69ato1atWpGnp6dQRllZmTZs2EBERCtXriQLCwuSSqVUpUoVWrp0qVAuJyeHpkyZQubm5iSVSsnOzo7OnDlDREQTJ04kfX19MjQ0pAULFlCzZs2ENkJDQwmA3Gv2Y6+10oiLi6OWLVsK11nwc0NEtGfPHqpatarw2NPTk5o1ayY6Pzc3lyZPnkympqakrq5OVapUoVmzZhHRx5/PvXv3krW1NWloaFDr1q1p9uzZotehi4sLDRw4UHh87949srOzI6lUKrq/ihS+j0REAITfb4rS8vLyaMKECWRgYEDa2trUt29fWrNmDRV+yyruWjds2EBqamr08OFDIsp/f7GxsRF+L5fG7t27qXLlysLzGxkZKcrv27cvTZw4sdg61q5dS5qampSbmyuX5+HhQR4eHnLpin5PZGVlkYODA6mrq5O+vj55enrSy5cvi237e31vZew/KeU50bQy4uPcH1+7V+w7Utxnre/V/v37ydLSkgBQ9erVyd3dnZo3b04NGjQgW1tbKl++POnq6pKVlRVFRUWJzgVAL168kKuzd+/eNGHCBLn09PR0SkpKIi0tLVF6hw4daPPmzURE9OTJE9LX16f79+/LnR8XF0cmJiY0d+5cUXp4eDipq6vTpUuX5M45evQoNWjQQHj8+vVr0tTUFJUxNjame/fuCY9fvHhBACgjI0Ouvk+1ceNG0tDQEP4WJ8r/u75z585yZTMyMkhDQ4NWr14tpB0/fpxUVVWFz7IVK1YUfTZ58OABAaBbt279Y31m/00cwPoMp06dIkNDQ1qyZAkR5f/Cef/+PY0cOZIGDRoklHv79i05OztTjRo1KD4+ntzc3Khdu3a0ZMkSunfvHl26dIlMTExo0aJFwjm9evWi3377jXJzc6lVq1a0fPly2rRpE1WuXJlq165N27dvp5cvX1JycjK1aNFC+EV57949Gj16NJUtW5Y6duwo+iVUEMC6efMmGRkZ0a5du+SuadGiRWRkZERPnjwRpZubm9O1a9do3LhxpKGhQYsXLyYiosuXL5OVlRWZmZnR7NmzacuWLaSlpUVSqZRUVVVJS0uLVFVVSSqVkpaWFikrK5OGhgZpaWmRRCIhTU1N0tXVLfIelySAJZPJyMbGhvbt21dsOfbvCwsLI2NjY8rOzi7VeaNGjRL9zDRo0EAIirF8EyZMoDFjxnztbrBCXr58SZqamnK/L78l3+N7K2P/WW+fyAewzsz72r1i35EfMYD1/Plz2rp1K4WEhFB4eDhFRkbSxo0bqWHDhqIv0OfMmUO//PKL6NyCANatW7do+vTpQnpRASwiUhjAevjwIZmbm9PBgwfJ2tpaFLgpkJaWRvb29tSmTRuFX1iNHTuWTE1N6dGjR6L0DwNYoaGhZGlpKbq2DwNY586dIwMDA4X9L06/fv2Ez60fevfuHVlYWJC7uzvduHGDVq1aRWpqahQeHk5ERBcuXCAnJyfhs6WPjw+ZmJjQsWPH6Pz582Rra0vDhw8X6lu8eDFpaWnRrl276MqVK+Ts7Ezt27cvdZ8Z+xAHsD7DggULKDg4WHhsb29PAMjAwIBOnz4tKpuZmUn+/v6Unp4uSt+/fz9pamrS/PnzRenHjh2jsmXLkqqqKlWsWJHi4+Npw4YNdOTIESLKH/1SMHLG2tpaNJKCiCg5OZmWLVsmjKAi+l8A6+jRo7Rx48Zir6sgel7A3NycRo8eTU5OTnIjELKysmj+/Pnk5uam8Bf25yhJAIuIaN++fVS9enXKysr6R9tnpdepUyeaMWNGics/ePCAjI2NKTY2lojyfxeUKVNG7mflv65p06Z08uTJr90NVsiAAQOEkabfqu/xvZWx/6yEaPkA1ulZX7tX7DvyIwawiPK/IG3RogXNnDmTiPKDPk5OTkSU/yVonz596PTp06IvUHNycggAjRo1irS0tEQDBUobwCLK/yIRALVu3Vru805UVBTZ2dmRnZ1dkfc+JyeHWrduTSYmJnTq1Ckh/dGjR8IMgqioKKpVqxbVrFmT9PX1qW/fvnTo0CGaN28eJSYmCudMnDiROnXqVNwtU6jwzAhFbt++TQ0bNiQ1NTWqWrUq7dmzR8j7cPZEVlYWDR06lMqUKUN6eno0YsQI0ecwmUxGU6ZMIUNDQ9LW1qbevXtTUlJSqfvM2IckREWsPvcvSE1Nha6uLlJSUuR2SsjJyUFCQgKMjIy+692UZDJZsWuhfCg3Nxfx8fFF7jL2sbaI6B+d/1ycgpfKh4uGfytWr16Nhg0bolatWl+7K/9pr1+/xrp16zBu3Lgid0os7MCBA1BXV//oLpCMfUtSUlKwZMkS+Pr6/iMLx/5bfpT3Vsb+E17dBVY0Eqc1Hgc0nfx1+sO+O8V91voeXbx4ERMnTkRUVBTGjRsHd3d3GBoa4o8//sDly5dx5MgRJCQkYMOGDVi5cqWwuZW9vT1CQ0PRtGlT1K5dG5s3bxZ9PihqF8Lnz58jJiYG7du3R1JSEtLS0nDixAlhU6vRo0djw4YNSEpKgre3N1xdXWFrawsbGxtUr14du3fvLnbjnfT0dPTs2RPnzp3DvXv38Pz5czx+/BiRkZEICwvDtWvXMGbMGEyfPh2vX7/G1q1bsXHjRrx8+RI9e/bEL7/8Ag0NDdSpUwfBwcFo0aLFv3bvGftWcQCLMcbYZ4mNjUXlypUV5j179uwf2yGHlR6/tzL2HXkeAaxuIk5zHgM0n/41esO+Qz9aAOv8+fO4c+cOvLy8oKKiAm1tbWRlZaFs2bL4888/0b59e6FsXl4eduzYgSZNmsDc3ByvXr3CkiVLMHXqVNHuf0DRAayff/4ZFy5cwMCBAzF+/HjUrl0bjo6O6Nu3L7y8vKCqqgoiwt69e7Fjxw6cOnUKp0+fRl5eHuzt7Uv0xS0RISoqClWrVsXw4cNx+/Zt2NnZoUmTJmjVqpXC5+3ixYtYvXo1PDw8ULVqVSxbtgzz58//xLvK2PeNA1iMMcY+S05OTpE7EdrY2HyxUaJMHr+3MvYdib8MrPtgRMVPI4CWv32d/hRFlgco8e/1b9GPFsBSpPBufP+25OTkYndb/5J9YYzl+3iYmDHGGCuGqqoqbG1tv3Y3GGPs+5b3Xj5Nlvfl+1GcO/uAfUOBLmsB23ZfuzfsP+hLBoyKC14BX7YvjLF8JV+siTHGGGOMMfbvKAhgGdvlr30FALLcr9cfRXZ7AjkZwI5eX7snjDHG/oO+uQDWF5zRyBhjjP3Q+D2Vse9IXk7+v8pqgNL/T/n91gJYjP1H8PsnY9+mb2YKoYqKCiQSCZKSklCmTBkoKyt/s7vdMcYYY98yIkJeXh5SU1MhkUhKtLAsY+wrKwhgKan8b40pDmAxVmpv377FkiVLMGXKFIW7wxe1iHuBe/fuoWPHjrh7966wePyTJ09gaGiI8+fPY/To0bh69WqR7d+6deuL74o+adIkzJkzBxcuXECjRo0+fgJj36lv5i9aiUQCIyMjJCcnIykp6Wt3hzHGGPvuqampwcDAgL8QYux7QLL8fyVK+UEs4NtbA4uxL2zlypXw8/ODpaWlkPby5UsAgImJiZD28OFDBAcHo3Xr1gCAbdu24dmzZ1i9enWp29yxYwdq1qz5SV/+xMXFoV27dhg7dixGjx5d6vMrVqyI2NhYhXlJSUkK1+XauXMn5s2bh9q1a6Nfv364ePEiDA0NS912fHw8hg4ditDQUOjq6sLHxwe+vr4KyxIRAgMDsWrVKqSkpKBly5ZYvXo1jIyMhDIrVqzAvHnz8PLlSzRq1Ahr165FpUqVSt0vxgr7ZgJYQP4oLAMDA8hkMshksq/dHcYYY+y7paSkBCUlJQ5eMfbd+P8pSxLJ/wJYihZ2Z+w/xs3NDRs3bhQeT58+XfQvADRp0kT4v76+Pg4dOoS6deuiadOm6NGjR4nbysnJwdq1a7Fu3Tr07t0bx48fx7t371C1alVIJBLk5ubi3bt3MDQ0RKVKlXD58mXR+ZaWljh9+jQaNWqEhg0bomHDhgrbiY2NReXKlQH8bzdDFxcXAEBERATs7e1F5Yt6L9+wYQMGDx6MoKAgDBs2DJ06dYKzszOOHj0KKyurEl93Xl4e2rVrBzMzM4SFheHatWsYMmQILC0t0a1bN7nyQUFBWLx4MTZu3AgDAwMMGjQInp6eOHLkCABg9+7dGDNmDNasWYPq1atj9OjRcHNzw61btxSOimOspL6pABaQ/8OprKzMuzowxhhjjLH/joI1dyRKgNb/j554l/D1+sPYN+Lw4cOiQNDTp08BAMeOHRPS7t69KzrHxsYGZ86cKfVUvm3btkFNTQ2tWrUSRnNpa2vjwYMHJZ5CaGNjg4CAAAwZMgTXr19XGLCpUKECcnNzcf78eQwZMgSRkZEA8kdgFVi6dCl++eUXqKmpyZ3/7t07jBo1Clu2bMHatWvh5eUFAPjrr7/Qp08f1K1bF0uWLEGvXiXbcOHw4cO4f/8+Tp06hXLlysHBwQEhISFYtmyZXABLJpMhKCgIkydPRocOHQAA8+fPR5s2bRATEwMrKyvMnj0b3t7e6Nu3LwBgzZo1qFatGs6ePQtXV9cS9YkxRb65ABZjjDHGGGP/OQVTCCEBdEzz//s8AggeWPQ5ehUAV///rZn1b1NS4XW52BfXrl27Uo3ASk1NhUwmg5WVValG++Tk5CAgIADGxsYgImRnZwt52dnZyMrKwvv370FEyMrKApA/VV9RG/Hx8bh58ya2b9+O3r17l7gPhY0YMQJ9+vSRC2Dt3r0b48ePh5aWFi5evIg6deoIeerq6ggODsbChQvh5eWFdevWYdq0aWjcuDHq168PJycnLFiwQK6t0NBQODg4oFy5ckJa06ZNMXLkSBCRaATY7du3kZiYiDZt2ghpLi4uUFJSQnh4OPT09BAREYFZs2YJ+ba2tjA1NUV4eDgHsNhn4QAWY4wxxhhjX12hKYQGlQFIgKwUIDK4+NNsWgKWiqcp/eOU1TiAxb64AwcOwN7eHjKZDHfu3EHZsmVRvnx57Nu3Tyjz8OFD4f+NGjXCmzdv8OrVK7x48QIzZszAqlWrhPyCpWp+//13IW3q1KlQUVHBq1evUK5cOVy8eBEdO3YEkB8Uql27tqhPFhYWAPLX6Oratatcn3fv3o3u3bsjMDAQPXr0+OTZRR9OHTx8+DC8vLzg5+eHffv2wcHBochzp0yZgrt372LkyJG4evUqqlatWuQaVI8fP5abcmhpaYns7GwkJiaK1rZ6/PgxAIjKa2howMjICE+fPkVMTIxcfkF9BaPnGPtUHMBijDHGGGPsa6NCASxdC6DvXuD1/aLLX1gCpD0HstO/TP8AQEn1y7XFGAAPDw9Uq1YN165dw+XLl+Hj4wMPDw8AQFhYGBYuXAg/Pz+YmZnB2NgYAHDnzh0A/wv+LFu2DMuWLRPqLGoXQnNzcwQEBCA4OBhOTk5ITEz8pD7//fffePv2LdavX4/69etj586dclP58vLyIJVKhf+rqKhg1KhRQv779/nr3xWUKdCuXTs8fvwYxsbG8Pf3F9LPnz+PQYMG4f59+d8Z2dnZUFFRwZ9//llkn9PT0+UWftfU1BTO/7CskpKSXN80NTWRnZ2N9PR00fkf5jP2OTiAxRhjjDHG2NdWEMDC/4+4qNw0/yhK5J78ANaXXOhdmT86sC/LwMAA06dPx/Pnz2FiYoJ169bB29sbQH5wy9vbG/Xr14e6uvpntxUcHIzs7GwEB+ePerx58yaaNWtWZPm7d++KptwV8Pf3R58+faCpqYkpU6bA398fXbt2FU0FVFZWVrgG1p49ewAAycnJkEqlCq+rIFBXUh8GmooqUxA0K1AwTfLDQJRUKoVMJkNubq5op8asrCxoamoK7Smq78O6GCst3gKAMcYYY4yxr67QCKySUPn/D6V5X3BEg7L8YtKM/dtCQ0Oxc+dO1KlTB6qqqpg1axbi4+OxZcsWREdHw9XVFa9evfrsdho1aiR6nJOTAxMTEyQmJsodubm5wlTEwg4dOoTr168Lo6M8PDygpaWFuXPnlqovUVFRqFChAtLS0uTyXr58CYlEIjp+/vlnPHjwQC5d0fRGRczNzREfHy9Ki4+Ph66uLvT19eXKAhBNB8zOzkZCQgIqVaok5Cuqr6gpjIyVFAewGGOMMcYY+9oKFnGXlPDPc+X/n86Xl/Pv9Ke4Nhn7Qg4cOIAKFSqgTp06OHnyJMqWLYtjx47Bzc0N1apVw9KlS3Hv3j00atQI0dHRX7Wv8fHxGDx4MCZPngwTExMAgJKSElavXo1Zs2bh8uXLxZ4fExODFStWwMrKCidOnAARYfDgwQCA6OholClTBgBgYmKCyMhIjBw5Erm5uSAihIWFoWrVqiAiEBEuXLiAX3/9VRhN9jHOzs64fPkyUlJShLSQkBCFI9AcHBygoaGBkydPCmlnz56FRCJB48aNYW5ujooVK4ryo6Ki8PTp02JHtDFWEhzAYowxxhhj7Gv7cArhxxSMhsrlEVjsx1WvXj3Mnj0bISEhWL9+PWbMmIE+ffpAIpEgIiICcXFxSE5Ohre3t2hR9sJu376NgICAT2r/wYMHsLCwkDtSU1NF5dLT0+Hm5oY6depg7NixorwGDRpg2LBh6NSpk7AAeoGUlBQ8f/4cdnZ26NChA9q0aYO8vDxs3LgRXl5eQjlra2t06dIFL1++BABUrlwZN2/eRI8ePZCbK95Y4fbt2+jatatol0AA8PT0xNKlSxVep4eHBwwMDNC/f3/cvHkTq1evxp49ezB+/HgAwMWLF+Hs7IyUlBRoaGhg6NChmDp1Ko4fP46///4bo0aNgre3tzBay8fHB4sWLcLu3btx9epVDBw4EO3bt0fNmjVLeOcZU4wDWIwxxhhjjH11BVMISzoC6/+DSV9yDazCi7jL8r5cu+w/y9TUFJaWlpgzZw5CQkJQo0YNGBsbQ1lZGerq6hg9ejT69u2L+vXrY8mSJcJ5BUGdOXPmoFGjRtDT0/uk9qtWrYqnT5/KHQWjoYD80VGNGjVCbm4uduzYoXDHwTlz5qB27dpwcnJCSEgI4uLi0KhRI3To0AEVKlSAt7c3zp49i4yMDHTr1g0jR46EmZmZqI7Dhw8jMzMTQP7OiPv374etra3cWlOPHz/G0qVL0b59e1H6vXv3EBsbq/A6NTU1cfToUbx48QL169fH/PnzsX37djRo0AAAkJCQgHv37iEjIwMAMGvWLLi7u6Nbt27o0KEDWrRoIQogDh8+HD4+Pvj111/h6uqKChUqFLuIPGMlxSsxMsYYY4wx9rUJUwhLOQLriy7iXiiAlZMJSLW/XNvsP+fixYuYOHEioqKiMG7cOLi7uyMtLQ3h4eFCAMnf3x8bNmzAwIEDoaenh3Xr1sHe3h5hYWEAgDNnzuDChQuoVavWJ/Xh/v37crvzARBGYGVmZsLFxQXVq1fH7t27RYGtwlRUVLB792707NkTnTt3xu3bt+Hk5ISVK1eidu3aAPJ3TywIxvn6+mLHjh14/fo1ZDIZbt68CYlEAgsLC6ioqCAv738B5N9++034v0TB7w9PT09s3Ljxo1MY7ezscPHiRYV5bm5uePPmjfBYKpVi+fLlWL58ucLyEokEgYGBCAwMLLZNxkqLA1iMMcYYY4x9baWdQigs4v6VAli5WRzAYv+qvLw89OrVC15eXlBRUYG2tjaysrJQtmxZYTSPkZERxo8fj7Fjx2LHjh0wMjICAFSvXh3+/v6YOnWqaPe/0rK1tRV2CCysbNmyAAANDQ0cOHAA9vb2oh35FNHW1saBAwcQFRUFS0tL0Yil3bt3Y8CAAQgICICPjw8AoFmzZpg5cybKlCkDiUSCCRMmQFVVVW7KIGP/JRIi4d3yX5eamgpdXV2kpKQUGZ1mjDHGGGPsP+faJuDgSKBKG6DXjo+XPzACuL4ZaDoZaDzu3+8fAKxuAjyPyP//r5cALSNAy+DLtM0+6r/wWSsvL0/hFL3vXXZ2NuLj42Ftbf21u8LYN41HYDHGGGOMMfbVFayBVdIphP8/Aiv3C47AkhUa+bE8f20cTHoKSHW+XB/Yf9qPGLwC8qfkcfCKsY/jRdwZY4wxxhj72j51F8IvOYVQJpNPe/Pwy7XPGGPsP40DWIwxxhhjjH1tpV3EXeVrBLAUrL3DuxGyH9AXXGWHMVYKHMBijDHGGGPsqyvtFMKvEMAiBcGqrOQv1z5jJfD27VsEBARApmjEIIA+ffpg4sSJRZ5/7949VK1aVVgsXVtbG4mJiQCA8+fPo27dusW2f+vWrRL1MycnRy4tMjISz549K9H5hUVFReGvv/4q9XmMfW84gMUYY4wxxtjXVuophP+/I+DXHoGVlfLl2mf/OStXroS+vj7s7e2Fw8TEBCYmJqI0bW1tHDt2TDhv27ZtGDJkyCe1uWPHDtSsWfOjuwoqEhcXh3bt2mHhwoUfLTt//ny0adMGT58+FdK8vLxw/vz5Urf76tUr9O/fHwkJCaU+t0B8fDzat28PLS0tmJmZiXZJ/BARISAgAGZmZtDS0oK7u7tc2ytWrICVlRU0NDTQtGlTPH78+JP7xlgBDmAxxhhjjDH2tRUEsCQl/PP8qyzirmgEVuqXa5/9J7m5ueHGjRvCMWTIEAwZMkSUVnhUlL6+Pg4dOoSdO3dix44S7OhZSE5ODtauXYvBgwejd+/eMDQ0xLt371C1alUYGhqiffv2uHnzJgwNDVG/fn258y0tLXH69Gn89ttvCA8PL7YtX19f1K9fHy1btkRubi7CwsJw584djB07FqamplBSUoKFhQUsLCygqamJ8ePHIzY2Furq6nJHixYt8O7dO1hYWMjlGRoafvS68/Ly0K5dO6EfAQEBmDBhAnbt2qWwfFBQEBYvXoxVq1bh5MmTePDgATw9PYX83bt3Y8yYMQgMDMT58+eRk5MDNze3IkfFMVZSvAshY4wxxhhjX913MIVQUQArJ/PLtc/+kw4fPoyGDRsKjwtGLBUecXX37l3ROTY2Njhz5gxq1apVqra2bdsGNTU1tGrVCq1btwaQP4XwwYMHMDQ0xPnz5zF69GhcvXq1yDpsbGwQEBCAIUOG4Pr161BSUhyUVlZWRkBAAMaPH4+4uDgMGDAA69evR8+ePbFnzx5MmzYNkZGRSElJgY2NDbp3744KFSogKysLAPDmzRtMnjwZCxcuhFQqFeqNjo7G7t27MXHixCLb/tDhw4dx//59nDp1CuXKlYODgwNCQkKwbNkydOvWTVRWJpMhKCgIkydPRocOHQD8bzRZTEwMrKysMHv2bHh7e6Nv374AgDVr1qBatWo4e/YsXF1dS9QnxhThEViMMcYYY4x9bQWLuJd0CuG3soh7Lgew2L+rXbt2CA8PF45BgwZh0KBBojQHBwehfGpqKpKTk2FlZVXiAA6QP/oqICAAxsbGICJkZWUJwaLs7GxkZWXh/fv3oryiRhTFx8fj5s2b2L59e7FtJiYmomvXrvD398f06dPRs2dPPH78GGPGjAERYdasWcjOzkZQUBAcHR1F5+rq6iIiIgKTJk0SpY8ePRo3b96Uu/b69etjzJgxCvsRGhoKBwcHlCtXTkhr2rQpLl26JLeg/e3bt5GYmIg2bdoIaS4uLlBSUkJ4eDiSk5MREREhyre1tYWpqelHR6Ux9jEcwGKMMcYYY+xrK/UUwm9kEXcegcX+ZQcOHIC9vT1q1aoFZWVlLFmyBPv27ROtgVV4RFSjRo1ga2sLPT09vHr1CsOGDYOKiopwbNu2DfPmzROlBQYGIigoCK9evQIAXLx4UZi+p66ujtq1a8PCwgLdunVDbGyskLd3716Ffd69eze6d++OwMBA5OUp3qnz8OHDqFWrFsqXL49169ahUaNGmDJlCurXrw9/f39cunQJFy9eROfOnaGnp4fs7GzR+SoqKtiwYQNevnwpBNIOHTqEO3fuYNWqVXLtVa1aFZUqVVLYl8ePH8PKykqUZmlpiezsbGEB+8JlAYjKa2howMjICE+fPkVMTIxcfkF9hdf7YuxT8BRCxhhjjDHGvrrSTiEsWAMru/hy/yRFI7A4gMX+RR4eHqhWrRquXbuGy5cvw8fHBx4eHgCAsLAwLFy4EH5+fjAzM4OxsTEA4M6dOwAAyf//LC1btgzLli0T6uzTpw8sLCwwZ84cUVvm5uYICAhAcHAwnJyc5AI3JfX333/j7du3WL9+PerXr4+dO3eiV69eojIpKSkYN24cli5dis6dO+P69etwd3eHh4cHbty4AQsLCwDAwYMHsXfvXsybNw/z5s1DWFgY8vLyoKqqCk1NTeEay5QpI6q/4HwAGDRoEBYuXIg///yzyD6np6fLrZWlqakJAHKBs/T0dCgpKYmmLRaUz87ORnp6uuj8D/MZ+xwcwGKMMcYYY+xrK5hCWOIRWAW7EOb8O/1RRNEaWJdWAa1mfrk+sP8UAwMDTJ8+Hc+fP4eJiQnWrVsHb29vAPnBLW9vb9SvXx/q6uqf3VZwcDCys7MRHBwMALh58yaaNWtWZPm7d++KptwV8Pf3R58+faCpqYkpU6bA398fXbt2hZqamlBGV1cXd+7cwenTp1G2bFkhfe3atVi7dq1cnVeuXEGlSpWEgBWQPxKqIGj3uaRSKd6/F4/mLJg++WEgSiqVQiaTITc3V7RTY1ZWFjQ1NYXAlqL6PqyLsdLiKYSMMcYYY4x9bcI6M9/ZIu6ynEJ9Z+yfFxoaip07d6JOnTpQVVXFrFmzEB8fjy1btiA6Ohqurq7C1L/P0ahRI9HjnJwcmJiYIDExUe7Izc1VuP7VoUOHcP36dfj7+wPID7JpaWlh7ty5cmUlEglycnJga2uL5ORkJCcn4/Xr1xg/frzwODk5Gerq6sjLy4OysjKA/42IKnj8TzA3N0d8fLwoLT4+Hrq6utDX15crC0A0HTA7OxsJCQmoVKmSkK+ovqKmMDJWUhzAYowxxhhj7GsTRmCVdhH3rzyFEADep3+5PrD/lAMHDqBChQqoU6cOTp48ibJly+LYsWNwc3NDtWrVsHTpUty7dw+NGjVCdHT0V+1rfHw8Bg8ejMmTJ8PExAQAoKSkhNWrV2PWrFm4fPnyR+tISUnBzJnFj2hMS0sDAGhpaX1+p/+fs7MzLl++jJSUFCEtJCRE4Qg0BwcHaGho4OTJk0La2bNnIZFI0LhxY5ibm6NixYqi/KioKDx9+rTYEW2MlQQHsBhjjDHGGPvqSruI+1dYA0vRIu4AkJn05frA/lPq1auH2bNnIyQkBOvXr8eMGTPQp08fSCQSREREIC4uDsnJyfD29sbvv/+usI7bt28jICDgk9p/8OCBsGB74SM1NVVULj09HW5ubqhTpw7Gjh0rymvQoAGGDRuGTp06CQugF+XOnTswNDQUpu8pEh0dDQMDA2hoaJTqWjw9PbF06VKFeR4eHjD4P/buOjyKqwvg8G/jSgIJwZ3i7pIiQYtbKG6BQinFrcVdWyjuri1SIDiB4P7hBHcIEiAJ8Wx2vj+2GbLsJiRYkPM+zzzZvffOzN1Jacjh3HNdXGjfvj3nzp1j3rx5rF+/nv79+wP6ovbu7u4EBQVha2vLzz//zNChQ9m5cyeHDx+mR48edO7cWc3W6t27N3/99Rf//PMPp06dwsvLizp16lCwYMEkzVmIN0kASwghhBBCiOSW1CWEVv/VkvlURdQV5XWW2JskgCU+knTp0pE5c2bGjx+Pj48P+fPnJ02aNJibm2NjY0PPnj1p3bo1pUqVYvr06ep5Wq0+W3D8+PGULVuWlClTvtP9c+fOzYMHD4yOuEXTr1+/TtmyZdFqtaxZs8bk0r7x48dTuHBhypcvj4+Pj9qeK1cuunTpol6nR48eODk5kSFDBtq0acPWrVvp06cPqVOnVs/Ztm0b33//fZI/i5+fH3fv3jXZZ2dnx/bt2/H396dUqVL8+eefrF69mtKlSwPw7Nkz/Pz8CAsLA2Ds2LE0bNiQpk2bUrduXapVq2YQQOzWrRu9e/ema9euVK5cmSxZsiRYRF6IxJIi7kIIIYQQQiQ3dQlhIsdb/rd8KCr0o0zHyJv1r36YCKcWwzM/CH32aeYgvilHjx5l4MCBXLt2jX79+tGwYUNevXrFsWPH1ADSoEGDWLx4MV5eXqRMmZKFCxdSpEgRDh48CICvry9HjhyhUKFC7zSHK1euGO3OB6gZWOHh4VSsWJF8+fLxzz//GO0GGMvCwoJ//vmH5s2b06hRI/z8/Hj06BG3bt3i1q1bVK5cmdOnT9OrVy+GDx/O06dPWblyJb/99huPHz/mwYMH/PTTT9ja2jJr1iy10HxSvG0JY4ECBTh69KjJvnr16vH8+XP1vbW1NbNmzWLWrFkmx2s0GkaOHMnIkSOTPE8hEiIBLCGEEEIIIZJdEpcQWv63fCg67ONM501x618NuAu2znB9tz6AdWOvYSF3l5yQKtunmdfXSFH0WW12qd4+9isWExNDixYtaNeuHRYWFjg4OBAREYGzs7OazZM6dWr69+9Pnz59WLNmjZqplC9fPgYNGsTQoUMNdv9Lqjx58nDx4kWj9tidA21tbdm8eTNFihQx2JHPFAcHBzZv3sy1a9dInz49Y8eO5cKFCxQoUICuXbtSo0YNNQCWJk0aevfuTe/evTl69Cjz5s3j7t275M6dGy8vL6pVq/bOn0mIL5lGUT7dtiHBwcE4OTkRFBQUb3RaCCGEEEKIb87+SbBvNBRrC/WmvX18aABMyqF/PfQFmH24HclMigyBcfrdxfjdX7+EcWsfOLnA9Phf/wcuOT7unL5W3r3g1CKoMQ7Kdk30ad/C71pxd+MTQnx7pAaWEEIIIYQQyS2puxBa2r1+ff/tu5u9t7gF3GODZcXaQhZ3SFfk9RFrerGPP6evUYxWH7wCuLI1eefyGZLglRDfNllCKIQQQgghRLJ7xyWEAEemQZayH35KccWtgWX2368Q6QpB+zeCLMOdPu48vnbaOLvPRQYl3zyEEOIzJBlYQgghhBBCJDd1h79EZmDFzdTKVOqDT8dI3ABWYoNsIum0ka9fR4Yk3zyEEOIzJD99hBBCCCGESG5KEjOwAPI30n81t/7w83lTbBF3jXnCyxxLdnz9+uHpjzunr1FMnADWy9uyjFAIIeKQAJYQQgghhBDJLjaAlcgMLABzS/3XuDsEfiyx9zB7SwWSoq1ev7664+PN52sVdwkhwJoWEPkqeebyhXrx4gUjRoxAp9OZ7G/VqhUDBw6M9/xHjx5x9+7dd7p3QEAAhw4dYuHChcyePduov2fPngwfPvydri2EkACWEEIIIYQQyS+pSwjhdTApboH1jyX2Hm/b7dDS/vVru1Qfbz5fq8cXjNue3/z08/hMzJkzh1SpUlGkSBH1SJs2LWnTpjVoc3BwYMeO1wHTVatW0aVLl3e6Z69evRg5ciQAISEhBAYGmjyCg4MBGDBgAOXKlcPFxYXUqVPz/fff07FjR4YNG/Zen33//v2ULVsWW1tbMmXKRO/evQkLCzM5dtmyZWg0GlasWKG23b17l8aNG5MiRQpcXFxo1KiRUWAuOjqaESNGkCVLFqytrfnuu+8IDAwE9Ds+mpmZodFo1MPBwSHBOZ85c4aqVatib2+Pk5MTEydOVPsiIyPp3r07Li4upEiRAi8vr3g/jxDxkSLuQgghhBBCJLd3WUIYG0z6JBlYsQGst/z6EDfAZfsFBbA2/gy6aGg0P2lZcB/a322M2+ZVhD7XwDHNp5/PZ6BevXosWbJEfR+bwRQ3k6lSpUrq61SpUuHt7U2JEiXw8PCgWbNmib7XqVOn2Lt3L35+fup1T582vRQ2Q4YMPHjwgDRp0lCuXDkaNWrE+PHjmTZtGuXLlydjxozqNQYOHEjNmjUTPQ+An376ie7duzNv3jwuX75M165diYyMZObMmQbjIiIiGDp0qNH5AwYMIHv27Pj6+hIUFESfPn2oW7cuZ8+excxM//+Z9u3bc+zYMf766y9y5szJpUuXsLDQ/xl/+fIliqLg6+tLhgwZANTzTDl79qwavJs0aRKRkZGEhoaq/b169WLHjh2sW7cOrVZLmzZtsLa2ZtasWUl6LuLbJgEsIYQQQgghkt07LCHUxAawPmIGli4G7h4BK7v/7vmWAFuq7K9fxy5x/NxFBMO5VfrX1UZBinTJOx9THpyEvHWSexbJYuvWrZQpU0Z9/+DBAwCDjKvLly8bnPPdd9/h6+tLoUKFEn0frVZLt27d+OOPP7C0tGTZsmUcP34cc3P9n7OBAwcSEBDAggULDM7r3bu3+nrGjBkUK1aMLFmyJP4DxmPPnj1kypQJgIIFC3Lz5k2mT59uFMAaNWoUJUuWNMqumjRpknp+7NzKly/P9evXyZ07N7t372bt2rVcuXKFHDlyAFCgQAF1/IsXLwAoVqwYjo6Ob53vL7/8gqenJ1OnTjXqe/HiBfPnz+fvv/+mcuXKAAwbNoxevXrxxx9/YGtra3SOEKZIAEsIIYQQQojk9j5LCD9mAOv0EtjaGxzSGt4zPhoNZK8Et3w/7rw+pLhLMD9FNlt8Aq7H3/fi1qebx2emdu3aScrACg4ORqfTkS1btgQzht40evRo0qVLR5s2bWjdujUvX76kTRsTGXFxvHjxwiD76cWLF4wePRpnZ2fAMLj1pv79+7Nnzx7+97//meyPG3wCsLGxISbG8M/U+fPnmT17NufPn2fdunVvPR9Qr7F48WIaNGigBq9MfTYrK6tEBa+uXbvGkSNHmDdvnsn+gwcPotPpqF69utrm4eFBREQEZ8+epWzZsm+9hxAgASwhhBBCCCGSn/IOGVhqAOsjBl3+t1T/NeSx4T0TovmESxs/hJg48/wU9cTis3+i4XvX3BBwVf864BpEhRqfE9fb+r9QmzdvpkiRIuh0Oi5duoSzszOZMmXi33//VcfcuHFDfV22bFmeP3/OkydP8Pf3Z9SoUcydO1ftjy3uPnnyZLVt6NChzJkzh4iICFxcXLC0tOT8+fNvnZulpSV58uQB4MqVK7x69Yps2bKRJo1+uWdCmUUZM2Y0yHhKSFRUFMuWLcPDw0Nti4iIoGXLlgwePFhdrpiQ+fPnkzFjRnLlygXAsWPHaNGiBW3btsXb25t06dIxcOBAWrXSb8Tw/PlzoqKisLW1xc3NjfLlyzN27FiyZs1qdO1jx45hbW3NnTt3aNq0KQEBAVSsWJHp06eTJk0abt26hZubG/b2r2vkZc6cGXidUSdEYkgASwghhBBCiOQWm4GVpADWJwgU2ac2fc+EfIrA2ocUE/X6tTYq/nHv4uIGffZUhb5vH2vr/Pq1xhyarYQZJfTvzyzXHwmJVN55mp8rT09P8ubNy+nTpzlx4gS9e/fG09MT0Gf1TJ06ld9//5306dOrQaNLly4BoPnvz9LMmTMNlt21atWKjBkzMn78eIN79ejRg+DgYEqWLMnSpUtxc3N76/wcHR3p1q0bAE2aNAGgYsWKVKlSRb1/fLp3756YR8CrV69o0aIFDx8+NAja9erVC1dXV3r27Jng+YqiMHLkSObNm8e///6r1rjy9/dn8eLF9O7dm19//ZWNGzfSunVrMmbMSKVKlShdujQnTpzA2tqaK1euMGLECCpXrsz58+eNsrL8/f2xtLTkjz/+YNasWURERPDrr7/SrFkz9u3bR0hICHZ2dgbn2NjYoNFoiIyMTNRzEAIkgCWEEEIIIcRn5B0CWOryw08gKQGs5MxmSgpd9OvX2oj3v15EEPxvORRuDuva69uyV4KMJRI+L+a/eaTOCy3WQsos0OsSzPkewl+8/7y+QC4uLgwfPpxHjx6RNm1aFi5cSOfOnQF9cKtz586UKlVKXR73PmxsbKhTpw6//vorNWrUSNK5586d48qVK2TKlIlevXpRqFAhlixZgqXl+9WBu3TpEk2aNEGn03H48GE1+2nBggVs2LDBoCC7KYGBgbRt25Z9+/bx77//UrduXbVPq9VSo0YN+vTpA0CJEiXYtWsXy5Yto1KlSri6uuLq6gpAoUKFKFOmDFmzZmXHjh1qEDHutUJCQliyZImaWTVlyhTq1KnDvXv3sLa2JirKMDgcHR2NoihGgS0hEpKEbU6EEEIIIYQQH8U77UL4CTKdot8I6CRmCeGn3B3xQ4iJG8D6ANkgO36DXYPAu8frttBnCZ8TGaIPfAEUb6cPXgE4ZYR+N+B3/7cffa69/9w/Q/v27WPt2rUULVoUS0tLxo4dy/3791mxYgXXr1+ncuXKPHny5L3v07lzZ86dO8fNmzepWLEi6dKlIyLi7QFNnU5Hjx49GDJkCGZmZixZsoSHDx/SoEEDwsLC3nk+R44coVy5cpQsWZLTp0+rS/8AxowZw/Pnz8mWLRs2NjZqAK9Dhw7kzp0bgGfPnlG+fHkePXrEmTNnDIJXAG5ubuTMmdOgLVeuXPE+y8yZM+Pi4mJULD72WlZWVgZ1t2Ln++TJEzJkyMCTJ0+Ijn79Z+3evXsAZM+eHSESSwJYQgghhBBCJLd3WkL4CQJYb2YkaZKyhPALycCKG8B6efv9r3d2pf6r35bXbQkFJq/thHEZ4NIG/XubFIb9Zub6XSATc3xlNm/eTJYsWShatCi7d+/G2dmZHTt2UK9ePfLmzcuMGTPw8/OjbNmyXL+eQBH8RMidOzedOnWidOnSDBs2jJMnTyYqs2vUqFEEBQWpWUn29vZs2bKFbNmyoSjvtqwzIiKCpk2b4uXlxbJly3BwcDDo37NnDxcvXuTs2bPqAfrA1rZt2wD4+eefcXJy4uDBgyYLtZcrV45jx44ZtF2+fNkgUBbXrVu3CAgIMAp6xV4rKiqKM2fOqG2XLl1Co9GQM2dOypcvT3R0NL6+vmq/j48PqVOnpmDBgol6JkKALCEUQgghhBDiM/AOGVhqsfSPGCh6MyMpURlYX3ANrA2doFDT97uextzE8skEApMbfjJ8n0oyUmKVLFmScePGkTZtWuzt7XFwcODUqVPMmTOHM2fOqAGmCRMmMHnyZINi7bEuXLjAhg0bGDZsWIL3+u233wD9LobHjh1j4cKFdO/enZQpU8Z7TmBgIBMnTuTo0aMGS/kcHR2ZMWMGAA0bNiRLlixG586YMYOTJ0+ydOlSo74DBw7w8OFDPD09DQrUA6RPnz7enQPTpUtHjhw5CAsLY9OmTUyaNMmoSHrKlClxcXGhV69eVKxYkTFjxlCrVi1WrlzJlStX1N0MFy9ejFarpVSpUty/f5+BAwdSqFAh6tSpYzT//PnzU7NmTdq1a8f06dOJjIykV69edOzYkZQpU5IyZUoaNWpE9+7dmT9/PmFhYQwbNowBAwZgbp6IoLgQ/5EMLCGEEEIIIZKbWsfqMyvirg03fc+EfHEBrOi3j0kKS1OZUEnIxElf7INN5UuXLl06MmfOzPjx4/Hx8SF//vykSZMGc3NzbGxs6NmzJ61bt6ZUqVJMnz5dPU+r1f+3N378eMqWLZtgEAr0hc5//vlnChYsiIuLC/369ePxY/3OmxEREURERBATE0NMTIz6PiIiAkdHR7Zs2UKhQoXivXaPHj3Imzevep9Yd+/exc/Pz+Q5sfcuV64c3333ncGxd+/etz63gIAAtFotvXr1Mjp/3Lhx6rXXrFnDsmXLKFOmDD4+Pnh7e6vBMScnJ8aNG0fp0qX59ddfqVixIvv371eLwL85/1WrVlGgQAHq1KlDs2bNqFmzJn/99Zfav3DhQgoXLkyNGjVo06YNP//8M717937rZxEiLsnAEkIIIYQQIrmpNbDeZQnhR8zAinz1xj0TE8D6b8zNvVC+R8JjPwe6NwJYIU/BzhUSKI6dIHMTv2IlVBw+IvD160YLwMLq3e77lTl69CgDBw7k2rVr9OvXj4YNG/Lq1SuOHTtGihT6ZZaDBg1i8eLFeHl5kTJlShYuXEiRIkU4ePAgAL6+vhw5ciTBABPodyz87rvvqFGjBhUrViRlypQMHz6cVKlSGY1dsmSJ+nr27Nl06dIlwWsHBwfj5+eHtbU1Bw8epHXr1gBMmjQp3nPatGlDmzZtErzum+IGxzJnzpyo5YuNGzemcePGJvsaNWpEo0aN4j33zfmnTJmSVatWxTve2dmZNWvWvHVOQiREAlhCCCGEEEIku/co4v6xdvu7fwJC3ijonJglhLEBmVu+8OoJOKb50DP7sGIMd0fDZwScWQkt18F3VZN+PVOF4N8shh/rzQBhwSZJv99XKiYmhhYtWtCuXTssLCxwcHAgIiICZ2dnli9fDkDq1Knp378/ffr0Yc2aNaROnRqAfPnyMWjQIIYOHYqVVeICgm9mAw0ePJiBAwcmeE5idhmMiIjghx9+APR1tpo1a5ao+QghjEkASwghhBBCiOT2OS4hXFjNuC0xRdyfXX39+tWjLyCA9cbzO7NC/9W7J/S6mLRrKYrpbKv4MrBePX79Oot70jLwvnLu7u64u7ur78PDw4mJiTFZM8nc3JyWLVuq79OkScPo0aNNXnfFihWJur+FhYW6XC6x7ty5Y9Tm5ubGixcvknQdIYRpUgNLCCGEEEKI5PZeSwg/Ya2pxGRgRYW9fh0R/PHm8qG8mYEVK+j+6+9Loq8VHScYGUd8Aazru/VfXXJC+61Ju9c3SAp+C/FtS1IAy9fXF41GY/KITeMUQgghhBBCJNF7ZWB9xBpY8d0zIdGhr19HfsEBLICXd5J2rTeL3seK7zncP67/amGTtPsIIcQ3KEk5kaVLl+b69esGbb6+vvTu3ZvatWt/0IkJIYQQQgjxzQgN0H+1My4aHS/z/2r7JBSAeVdvLquLlZgA1peWgZVQBpv/WUiVLfHXCn9puj0iyHR74D3911I/Jf4eQgjxjUpSAMvW1pacOXMatHXo0IFff/3V5A4NQgghhBBCiEQIuq//6pQp8efEZu28uPU6EPKhvLhluj0xSwjNLSHmv0LmUSEfbk4fS0IBwAN/6HclNMXOBfI1MNx18OFp/dc0BSEsAF7569/HF8CKfc4ZSyRpyiJpHj58SNGiRSlatCg7d+5M9Hm3bt2iWLFi1KpVK8Ed9gBWrlxJdHQ07dq1e8/ZCiHi8141sHx8fDhz5gx9+vT5UPMRQgghhBDi2xP2XP/VwS3x58RmYD2/AVMLfthjWf3X9yne/vXrxOyS2HzN69efcnnju4qJjvNGA6W7QJ9rYGELTy7A9v6mj/Ve4LfJ8Fph/xXrdskO3U5B9f8Kid/cB+s6GB5/t329Y2PKrB/5Q34dlixZEm9Jm549e5o859WrVzRu3BgXFxcOHDjA1KlTE3WvsLAwfvzxR0JCQli9ejUbN25McHyOHDkYP368+v7w4cO4urri6uqKtbU1jo6O6vvjx48n9iPj6+tL9uzZefDgQbxjli1bhkajMShQ7+PjQ+nSpXF0dCRdunR0796diIjXtdjie45eXl6Avmh+r169SJcuHQ4ODpQrV479+/er5+t0OkaPHk2WLFmws7OjSJEibNiwQe2PiYmhU6dOZMyYEXt7e0qVKmVw/puCgoJo164dzs7OpEiRgo4dOxIWFmYwZvbs2WTLlg1bW1s8PDy4dSueQLv4ar3XLoSTJk2idevW8WZfRUZGEhn5ehvZ4OAvIIVYCCGEEEKITy020JOYDKdYcesmmVslbofAxFBiXmclZasA1UbA6cX/9ZkoUP6mbN9DoR/h/Fr9tT53sZ81bz1oMAusHfXvG82Dy/+aLuT++Lw+cPjUz7A98pX+q5UjWDuAy3f690H3X2fZvSlVDrCyf++P8bXx8vJi6dKl6HQ6NbjSunVr6tevz7///mswdvjw4QQGBhpdIyAggFq1ahEcHIyvry/nzp2jQYMGhIaGMmjQoHjvHRERQf369bl06RJ79+5l0aJFtGrVik2bNlG1alWDsWXKlOHly5dYWloCUKBAAV68eEGnTp0ICAhQx4wbN47KlSsn+vPv27ePCRMm4Ovra/A7tam5Dh061Kjdz8+Pn376iRIlSuDn50fnzp2xtbVlwoQJAEalgV6+fMn3339Ps2bNAFiwYAF37txh9erVODo68scff1C7dm0uXrxI1qxZ2bp1K7t27WLu3LmkTZuWZcuW4enpyeHDhylTpgxarZbIyEhWrFiBg4MD48ePp27duly/fp00aYx3Jm3Tpg23bt1i8+bNBAUF8dNPP6EoCgsXLgTgn3/+oVevXsyfP598+fLRs2dP6tWrx/nz5zEzk73pvhXvHMC6efMmu3bt4uzZs/GOGTduHCNGjHjXWwghhBBCCPFtiA0MJabGVCwL69evO+6BdIU/zFye+sGsMvrX9m5gHuc+ic2oig2mJSbgldxia2CZW74OXgHkq6c/TDk0BfYMN166GfVfAfvYgNR31aDxQgh9Fv/9c3i807S/dgsXLmThwoVUrVqVLl260KRJE5YsWaIGr06cOEFUVBTu7u4mz9+7dy+tW7cmR44cbN26ldSpU5M2bVo2b95M06ZNOXXqFLNnzyZt2rQG5z19+pQmTZpw5swZNmzYQIUKFShfvjzR0dHUrVuXefPm0bp1a3W8lZUVM2fOZNOmTUyfPh2AadOmERQUxJMnT1AUhatXr+Lm5sbjx4+xtLTExcXlrZ9/+fLl5MiRgw4dOvDjjz/GO27UqFGULFmSu3fvGrR369ZNfV24cGEOHjzI7t271QDWm6WBhgwZQvHixalWrRoADRo04Ndff1X7ly5dirOzMzt37qRz586UKFGCvXv3YmGhDykUKVIEb29vNm3aRJkyZbC2tmbZsmXq+bNnz8bNzY3jx49Tr57hn6vLly+zefNmTpw4QcmSJQH4448/aNu2LZMmTSJVqlSMGzeOzp07q89+/vz55M2bl/379ycpMCi+bO8cwJo7dy6lSpWiUKFC8Y757bff6N27t/o+ODiYTJmSsK5fCCGEEEKIb0FsplJilujFipuBZeXw4eZin/r1a3Or10sVIfFLHGM/xxexhPC/DKy4n/NtHNPpv4Y8MWyPrfkVG8AyM4eCTd5vfsKkbdu2ERgYaBTA8vf3Z/DgwSxfvpx+/foxfPhwNTsKoFq1apw5c4amTZuSN29eevfuTffu3XFycmL//v20bt0ac3NzDh48SIECBdBq9QHOpUuXkjt3btq2bcuuXbuYNGkSadOm5cCBA/z444/s3r2bffv2AZA9e3Y2b96MlZUVxYoVI3fu3Hh5eREZGYmiKJw9e5b+/fuzZ88e/ve//5n8fAsXLkSj0eDr6xvvMzh//jyzZ8/m/PnzrFu3LsHnFRMTE2/g7OXLl0ybNo1//vlHbXvz93YLCwssLCyIidH/mU6XLp3RdWxsbNT+N+l0+mC2qTlcvnwZ0AfBYlWqVAmtVsv//vc/SpQowZkzZxg7dqzanydPHtKlS8exY8ckgPUNeecA1rp16+jSpUuCY6ytrbG2tk5wjBBCCCGEEN+82EylJAWw4gRcLO0+3Fxs45QH0YaDmRlUHgT+56DWpMRdI3ZJz4fKwIrRQtQrsE35Ya5ncO3YAJZlwuPisnPVf73lC/7nId1//6gfG8Cy/oABRREvjUZj1Fa/fn0sLS0ZM2YM/fv3Nwh6xJU1a1bGjRvHyJEjyZs3L3v37mXu3Lm0aNGCFStWULRoUZPn1atXjwsXLpAzZ0527NjB48ePuX//Pnfu3CFFihS0adNGDf5YWVlx7NgxTpw4gbOzM2ZmZjRpog9oZsyYkQIFCiTps8UVERFBy5YtGTx4MBkzZox3XGRkJDt37mTt2rWsWbPG5Ji5c+eSIUMGqlevHu91/v77b4KDg+MNFh0/fpwLFy4wceJEo7579+7Ru3dvKlasSLly5Yz6Y4Na9+7dI0eOHMDr8kNPnz7l9u3bAGTLZrgjaObMmROsDSa+Pu8UwDp79iy3b9+mQYMGH3g6QgghhBBCfIPUANY71rH6kDWU4taTiZ1Xxf5Ju4bmAwewltWHByegxzlIkf7DXFMbBdFh+uAYgFkSAlj2cbJItveHDjv0r9UlhBLAel/Dhg1jzJgx6HQ69u3bh0aj4Y8//lD7o6KiTCZLeHt7kzp1ajQaDf369VPbq1atSseOHdUaT7Hat2+PtbU1z549Y//+/bi7u7N8+XK1v06dOjRo0ICOHTuqbTExMaxatYrSpUtTtWpVfv31VwoUKMD333/PtWvXmDt3rsE9Vq1aRb58+fDweL1ctHv37u/+cIBevXrh6uoab/F6gNy5c3Pt2jWsrKwYN24cNWrUMBqj0+mYO3euwcqpN3l7e+Pl5UW/fv3ImzevUX/sskBPT09q1qypti9fvpwOHTqg1WopWbIka9euNRmYK126NBkzZqR3794sWrSImJgYevXqBYC5uTkhIfrAsJ2dYaDezs4uwfpg4uvzTtXOfH19cXNzI1euXB96PkIIIYQQQnx7dLEBrISzLkyeAx+vCPi7BqA+ZA0sXQzcPaTPlLq++/2vF2tFI5iQ5XVx9aQsIXTO8vr1vaMQ9d9uaZFvLCEU72zEiBFotVo8PDxYu3YtWq0WJycntT8wMBBnZ2ej89zc3N6avRRXbBCsc+fOavHxuIeiKOh0OqO2Fi1aYGlpia+vLzVr1qRkyZKcOHGChw8fMmvWLMLDw9V7KIqi1or6EBYsWMCGDRtYtWpVggXMt23bxsmTJ5kzZw7Tp0+nRYsWRmN27NjBo0ePDOp6xdLpdAwdOpQGDRrQt29ftX5WXLNnz6ZixYrUr1+flStXGvTVq1ePs2fP4uPjQ+HChSlSpAinTp0yuoadnR3r16/n4sWLuLq6kjlzZkqVKgXov5+x36OoqCiD8yIiIoyCWuLr9k4BrNOnT8ebUimEEEIIIYRIondZQpgyThAlKcXfk8LUDnyJ8SFrYL288/q1vev7Xy/WnYP6r2f/+6XbPAkBBrtU4BUnmBb03zImycD6ZK5du0bq1KlN1lzq0qWLunNh7OHj40Pz5s2N2i9evAjAyJEjsbS0NDq2bdtG586djdqtra0JDQ2lV69eFClShCJFiuDn58eePXvw8fExqCcVEhKCvf2HC2qOGTOG58+fky1bNmxsbLCx0dfD69ChA7lz51bH5ciRgxIlStC+fXsWLVrE6tWruXbtmsG1/vnnH6pUqWIUDNTpdDRv3py5c+eyfft2hg8fbhQY7Nu3L/3792fRokXMmzfPoNYYgJOTE/nz58fDw4P58+dTpEgR/vzzT5OfqVSpUty8eZMHDx4QEBBAnTp1MDMzo1ChQmTIkAGA+/cNd/K8f/8+2bNnT/yDE1+8dwpgLV++nB07dnzouQghhBBCCPFtepddCO1docth6H72o0wJePcMqtjPobxDAOvVYwh6+Pr9U7/Xr7UfaLmQqcBcUjKwADKVgpRZ9a/DX+q/Rr3Sf5UA1gcXHBxMwYIFGTBgACEhIVy4cIFBgwZx/fp1unfvzuDBg9Wxc+bMYeDAgRw5cgRFUVAUhSpVqrB69WoURSEmJoYuXbpw4sQJtQ7V8OHD1bHHjx9XX9euXZv58+ejKApnzpxR27VaLfb29hQqVAhzc3NWrlxJgQIFaNCgAblz56ZNmzbqfB4+fGi02+H72LNnDxcvXuTs2bPqAfrA1rZt20yeE5sBFjfgFxMTw5YtW0yWBpo1axY+Pj4cPXpU3ZkwLm9vb2bMmIGPj4/JzK745hBfkfdYGTJkwMHBgVmzZlGtWjVcXFzIkCEDWbNmZffu10Hja9eu8eDBA6pUqZKoe4uvwzsFsIQQQgghhBAf0LvsQgiQtgCkyvb2ce/qXTOo3rUGlk4Hf+SGKfleL8cLipN1ER1u+rykMnWdpAaw4HVReTWAFZuBJUsIPwStVktYWBjjx48nbdq03L59m7Jly/Lnn3/y448/qplHqVKlYvv27cycOVM9t1y5ctSuXZuDBw8aXFNRFLp168a1a9coWLCg0T3nzJlDzZo1efTokUF7aGgonp6etGvXjoiICLW9Q4cOREVFceXKFa5cucK8efN49uwZoN/xMCwsjGPHjhEYGIi9vT3ly5cHYMaMGbRt2/adnkuOHDnIkyePwQH6nQFji6C3bt0ab29vLl68yL///kvHjh1xd3c3qGF14cIFnj9/zvfff290j1WrVlGtWjV0Oh03btxQj9ii6atWraJUqVKkSpXKoD+24PqqVauYNGkSx48f5/Tp0/Tt2xdfX1+8vLwAOHr0KO7u7gQFBanjT548qe7QuG7dOiZNer1pRO/evfnrr7/4559/OHXqFF5eXtSpU8fk91B8vSSAJYQQQgghRHJ73yLuH8s718B6xyWEMXEyrALv6b/G7uwHcGLeu83nTXGvGet9Alh7R8PBPyFEH7iQXQjf38SJE0mTJg0XLlygUKFC7Ny5kyZNmuDt7c3ixYsZMWKEwfj79+9z4sQJ9X3dunWZMWMG0dHRBuNCQ0NxdHRk8+bNagAs1vr16+nXrx+bNm0ifXrDzQLs7e05cuQIfn5+uLu74+/v/9bPsHHjRgYOHEiRIkXo3Lkz8+bNY/r06QDcvXsXPz+/t1zh3Tk7O9OlSxdKlChBnz59qF+/Pt7e3gZjTp8+jZ2dncna1o8fP2bNmjV89913Bkdsttbjx485ePCgUX/JkiUB/Y6B69evp2rVqlSrVo1Tp06xc+dOdafDZ8+e4efnR1iYvn6cr68vVapUwd3dnf/973/4+voaBKe6detG79696dq1K5UrVyZLliwGxfbFt0GjKO+6sD3pgoODcXJyIigoiBQpUnyq2wohhBBCCPF5G+4MKNDnGjimSe7ZwKScEPoMav8BJTu+ffybdg2BI9OgbDeoMSbx50UEwfjM+tdeu/XL9PaMgENx6uYMD0r6fN70/CZML2bYVmcKlOiQtOvsHgaHpxq397oEThnfeXrv4mv7Xevvv/8mLCwMT09P7O3t0Wq1TJw4kRkzZrBz504KFixInjx51CLiTZo0oXDhwpibmzNkyJAk3ev27dscP36cDh068Pfff1O5cmUiIyPRarWULVuWsWPH0rRpUwDCwsJo1qwZV69e5eLFi1haWuLm5kaqVKnU/goVKjBt2jR69erF7t27OXXqFMHBwdSoUYN8+fKxatUqUqZM+cGfmRBfuw+3FYIQQgghhBAi6RQF+O/flJO6hPBj6XII7p+APLXf7Xyzd9yFMCZOtkxksL6A+yHTRZ/fi6kMLBsn47a3+b436LQQEfi6LV2RTx68+hrFBoxAv5SwTJkymJmZceTIEbJmzQrodw6sV68eiqKQKVMmJk+eTPbs2Q3qYSWWk5MTu3fvply5chw5coTy5cuj0WgoXbo0P/zwgzrOzs6OjRs3cvnyZbVouZWVFVeuXAH0mURz586lRIkSWFhYsG/fPtKnT0/69Onx9fWlQoUKnDp1ymRdKSFEwiQDSwghhBBCiOSk08HI/7Ix+t/W73D3pfMZCQf/gNJd4IcJiT8v2B/+1NfzoWRHOLnAeMyHyMC6cxiW1DJsa7UeclZ9/2snk6/9d60rV66QK1cuzMw+XZBXp9Ml6n6PHz82KtJ+5MgRihQpgp2dnUH7o0ePjJYnCiESRzKwhBBCCCGESE5xd+p7Y5v6L1ZsLa8k18CKev3aVPAqxQfKbIotth6X9dcX9PmaxBYq/5QSGywztcNguXLlTI6V4JUQ7+4zyVEWQgghhBDiGxV3md3nVsT9Xb3rLoQx0W/pj0y4P7Eu/2vclr6YcZsQQojPhgSwhBBCCCGESE5xs5Q+lxpY70utgfUeGVjv0p8Yka/g7ErDtkLNwFwWpwghxOfsK/kJKYQQQgghxBfKIAPrK/nreexSyCRnYL0tgPWWDK3EMLV80N71/a8rPolu3boxevToT3IvPz8/bGxs6NGjx1vHnjlzhuDgYPX9q1ev0Gg03L9//53uvWfPHpNLE03RarU8evToreNiYmKoXbs2QUGGdeTq1KnDunXr3mmeQnxKX8lPSCGEEEIIIb5QcYM8Zl/LEsLYGlhJDGDptAn3f4gMLG2EcZttyve/rvgs3LhxA41Gk+gjvtpaUVFRtG3bljJlyrBgwQLOnz+f4H1XrVpFgQIF2LFjB6AvAA9gbv5h/0wHBgYatXXv3h1PT8+3nnv8+HGuXr2Kk9M77LgZj4sXL1KhQgVsbW3Jnj07y5cvj3dsZGQk3bt3x8XFhRQpUuDl5UVYWJjarygKI0aMIH369Njb29OwYUOePXv2weYqvnwSwBJCCCGEECI5KV/hEsJ3roH1lgCVTpv0oFhcT/3gyHTjdglgfTVy5MhBeHi40VG5cmWmT59u0BYaGmoyMKXT6WjTpg1arZYdO3bQt29fGjRowIMHD+K976RJk5gwYQKenp78+uuvREXp/1v+0AGsKlWqsGbNGvX9rFmzmDt3LpcuXcLZ2RlnZ2csLCzo3bu30bkbNmygSZMm/Pzzz7i6uqrHrl276NChg0Fb37593zqX4OBgqlWrRrZs2Th+/DgdOnSgXbt2HDt2zOT4Xr164e3tzbp161i/fj3btm0zuM+kSZOYNm0ac+fOZffu3Vy9epW2bdu+w1MSX6uv5CekEEIIIYQQXyhFef36awlgfawaWAC691hGOKuM6d0N7VK9+zXFR1OiRAmjjKmZM2cyZMgQo/ZKlSoBoNFouHTpErVq1UKj0WBjY4ONjQ1mZmZYWFio7w8fPoy7u7vRPaOiomjXrh1nzpxh27Zt2NjYMGzYMNzd3alcuTK3bt0yOufJkycANG/enCNHjqjZV2A6gBUQEPDWzLBq1arx5MkTgzaAv/76i59//pk7d+4wZcoUxo0bx/79+wkMDOT58+f06NGDxo0bM2TIEIN7xsTEsGrVKpo0acLs2bMJCAhQj+rVq7No0SKDtsmTJ7/1+7NkyRJiYmKYP38+hQoVYvDgwZQsWZLZs2cbjX3x4gXz58/njz/+oHLlylSrVo1hw4axePFiwsPD0el0TJo0icGDB1O3bl3KlSvHn3/+yfbt27l9+/Zb5yK+DV/JT0ghhBBCCCG+UF9lDazYANYH2IXQNRd8Hycb5EMsI3yTQ5oPf03x3k6dOoWiKAbHL7/8wqhRo4zafX191fMKFy5MWFhYvLWrXr16RceOHWnUqBFWVlZqu7+/Px4eHpw/f569e/eqNajMzMxYvHgx5cuXp3jx4mzcuFE958WLF+TPn5+BAwcSERFBwYIFmTlzJtHR+v+WLS0tje7v6urKq1evEjy2bNmCm5ubQRuAu7s7ixcvxsLCgu3btxMQEEDNmjVxcHDA0dGRkSNHsnXrVjJlykT16tXVe65btw5/f39KlCgBQP78+cmYMSMZM2bkwIEDdO3alYwZM5IuXTp69eoF6Jf8ZcmShalTp5p8jvv27cPDw8PgGXp4eJjMwDp48CA6nc5gTh4eHkRERHD27FkuXLhAQEAAP/zwg9pfsWJFzMzM4s3oEt8e2WpDCCGEEEKI5KTuQqh5Xfz8SxcbiNMlMQNLG2nc1u2k/joH/8sI+bstmFsZj3sf9qk/7PVEsrKwsGDVqlUUKlSIH374gfr166t9Op2Oli1bkjNnTn7//Xe1fdGiRfTp04fKlSuzc+dO7O3tDa5pbm7OkiVLmDRpEk2bNqV69erMnj2bzJkzc+TIEZo3b463tzerV6+mYMGCam0nOzs7k3N0cHDgwYMHLF682ChbCsDGxgaNRoODg4NRX4MGDQDYtWuXQXuePHlYsmQJZcqUYc+ePQwfPhzQ15aaMGGCwdjnz59z9uxZo0Lxc+bM4dSpU+pzzJMnD5kyZTL5GW7dukWtWrUM2jJnzmxyqeWtW7dwc3MzeK6ZM2cG4MGDB1hY6EMT2bJlU/ttbW1JnTp1gks3xbdFAlhCCCGEEEIkp9gspa+lgDvEWUKYxAwsUzsExl7P3g1Cn8JNn/ebmykp0n/4a4r30rFjRxYuXBhvv6mgz7Bhw9SgTfbs2Rk5ciSjR482CGB5e3vj5+fH8ePHMTPTB1q1Wi2LFy9mwoQJBAcHmwwaxVq4cCEnT57k999/V8flypWLI0eO0K1bN2bNmsXs2bMJCwvDwsLCZAZWLK1Wy9SpU9HpdAwbNizB5/E+li1bhoWFhdFyxpIlSxq1vXr1ioYNGwL6oN3OnTvjvW5ISIhRgM7Ozo7ISONAtKmxsUG6yMhIIiIiMDMzw9raOlHXE98mCWAJIYQQQgiRnGLrRH0tywfhdSZZUmtgRb2Kv6/dVrj/nkuJdvxueI8uh/VfrexNjxfJZsGCBSxYYFivbM6cOXTt2hULCwu2bdtG1apVjc4LDw8nJkb/352XlxdNmjQhJCSEmJgYIiMj8fDwYO/evVhZWRESEgKAtbU1Bw4cQKPRMHnyZNq2bcuSJUuMrl2lShVcXFwoUqQI27ZtM+iztrZm/vz5aLX6nTSDgoLizb6KlTVrVlavXk2tWrXIlCkTHTp0SNSzGTBgAC1atKB27do8fvxYbY+JiVHreimKgk6nw8LCgj///JN58+apywdjnTx5MsEMrLextrZWi9XHioiIMPm5TY2Njo5GURTs7OzQarXodDq0Wq2ajZXQ9cS3SQJYQgghhBBCJKfYLKWvKoAVm4GlJDzuTZEh8felzqU/3kf2yjC1gP61lQOkLfB+1xOfzI4dO5g6dSotWrTAzs6OLl26sHnzZvLly2cw7ocffmD//v0mr+Hr60vPnj2N2idNmhTvrnuhoaHY2try4sULLl++TJYsWQz6bWxsTJ6n1WpRFMVk/5EjRyhWrBgA1atXZ8SIEXTu3JmMGTMa1IgyJTo6mlmzZtGuXTujpXWmlhAeOnQInU6nZpvFVbVqVYNgEeiXFtaoUSPBOcTKkCED9+/fN2i7f/8+2bNnNzn2yZMnREdHq1lp9+7dA/TZcqGh+uzLBw8ekDVrVkBfg+vZs2cmrye+TV/RT0khhBBCCCG+QGoA6ytaQviuNbDiW0L4oThngrLd9K9rTfq49xIfzL///kuHDh1Yt24dzs7OZM6cmb/++osqVapw4sQJg7G+vr5GBd4VRaFKlSrMnj3bZF98wSuAOnXqYG5ujpubG8WKFaNgwYJMnDiRFStWAPoMoTePsLAwypQpQ7du3Uz2xwavYv3+++/06tWLAgXeHlA9fPgwLi4u5M2bl6NHj+Lg4KAe165dw8PDAwcHB+rWraueYyp4BbBnzx7Onj1rcAwaNOitc4jl7u7O3r171Yw3AB8fH6pUqWI0tnz58kRHRxsU2/fx8SF16tQULFiQYsWKYWtry+7du9X+/fv3o9FoqFChQqLnJL5ukoElhBBCCCFEctJ9hUsI37kGVgIZWB9KtVFQsiOkzPrx7yXei06nY9SoUcyYMYPt27cbBHhq167NtGnTqFKlCoMHD6Zv375G9Zw+hD179hAZGYm1tbV6/UuXLsVbJysqKoquXbvi5+fHhQsXMDMzY9CgQbi6usZ7D41Gw8SJExM1n3Xr1qk79ZUtW1ZdBgmGGVj+/v6cPXs2wWu9rQaWTqejVq1a/PTTTzRq1Mjo/I4dO/LHH3/QrVs3fvnlFzZu3MiFCxdYvXo1ABs2bGDevHls27aNrFmz0qhRI7p37878+fMJCwtj2LBhDBgwAHNzc2xtbfn5558ZOnQomTNnxsHBgR49etC5c2dSpUqVqGcjvn5f0U9JIYQQQgghvkCxy+ziyZL4IsUG45JaAysiSP+1Qn8o1Ax+XPlh5wX655wq29ez4+NX6tKlS7i7u7Nq1SqOHDliVL8JwNPTk127djF9+nTy5ctnVJfqQzA3N8fOzs4g0HPz5k1y5TJczqrT6di4cSNFihTh0KFDHD9+nHPnzuHv70/OnDkZPXq0ukwuMSIjI7GyMtxtU6fTsWHDBjWAlZB06dK9ddzJkye5c+eOwTFmzBi1Pzo6Gj8/Px49emTy/AwZMrBlyxYOHTpE8eLF2bhxI9u2bVOXWT569Ag/Pz+io6MBfQH8woULU6NGDdq0acPPP/9M79691euNHTuWhg0b0rRpU+rWrUu1atWYPHnyWz+r+HZIBpYQQgghhBDJ6Wss4m6dQv81/GXSzgsN0H91zgQeiV/KJL4+mzZtIk+ePEybNi3BXQHLli3L5cuXGTx4ME5OTu99XxcXF06ePMnDhw9xcXEx6NPpdBw4cIALFy5QqlQpAE6fPs2///7LihUrCAoKokePHgwYMECtfbVmzRqOHTvGL7/8wsyZMxk5ciSdOnUyuu/hw4eJjIwkRYoUWFhYMGfOHHLnzm0wJioqiqZNm+Lh4aG2BQcHExERQWRkJI8ePfogzyCWtbU1d+/eTXBMxYoVuXDhgsm+bt260a1bN/W9s7Mza9asSfB+s2bNYtasWe82YfHVkwCWEEIIIYQQyelrLOKeIr3+6/ObcGRG4s7JVgFCn+lf26f+OPMSX4zff/890WNTpEjBtGnTPsh9GzduzLJly8iWLZuaORRXpkyZmD9/PilS6IO0Q4cO5dWrVwwYMIBWrVqZDLaVKVOGkydPMmfOHF6+NB3UXb58OfPmzUNRFMzMzMiTJw/Lli0zGGNjY8PUqVMN2g4cOEDdunWxtLSkQYMG5MmTJ8HPt3fvXpo2bcqLFy/Inz8/mjcyESMiItBqtZw4cYLz588neC0hPjWNoiR1a5B3FxwcjJOTE0FBQeofeCGEEEIIIb5pTy7B7HJg7wb9rif3bD6MkGcwOWfSzrF20mejRYVARx/IaLxkTMRPftdKHoqiGAWBhBAfh2RgCSGEEEIIkZy+xiLuDqmh1mS4f+LtY1Hgwj8QGfS6yTVX/MOF+IxI8EqIT0cCWEIIIYQQQiSnr3EJIUCpTvojMYIfwd3D+tcpMoKNZBAJIYQw9JX9lBRCCCGEEOILExvAMjNPeNzXzCHN69eOaZNvHkIIIT5bEsASQgghhBAiOakZWN/wUiS7OLu9SQBLfECmSj5/wjLQQogPSAJYQgghhBBCJCc1gPUNZ2BZO75+HTeYJb554eHhBu/btWvH8OHDDdrCwsJMnuvr60ulSpWIiIhQ2wIDA8mfPz9nzpyJ9553794lODjYoE2r1XLx4kWjsYqiEBMTY9B28+ZNbty4Ee/14/Po0SOWL1+e5POE+FZIAEsIIYQQQojk9DUWcU+quAEsqX8l/nPixAlKlSrFq1ev4h3j7+9P3rx5TQak3N3diY6OZt68eWrb8OHDKViwIEWLFo33mtOmTaNZs2YGgalJkybRrFkztFqtwdi///6bsmXLcvnyZbVtwIABrF+/PlGfMa6wsDC6dOlicK13tX79etKnT5/gmJEjR6LRaDh06JDatnbtWgoVKoSdnR2ZM2dm5MiRasbanTt30Gg0Jo9Ro0YB8OLFCzp06ICLiwtOTk5Uq1aN8+fPq9cPDw+nV69epEuXDgcHB8qVK8f+/fvV/sDAQJo1a0aaNGlwdHTEw8ODCxcuxPsZHj58SMOGDXFwcMDV1ZUBAwag0+nUfkVRGDFiBOnTp8fe3p6GDRvy7NmzpD1M8dn4hn9KCiGEEEII8Rn4Wou4J0XcAJa1U/LNQ3xWSpQoQdq0aWnbtq3JZX8xMTG0atWKTJkyUaBAAQB++OEHnJ2dcXZ2xtXVlbNnzzJ06FC1bf78+Wzbtk197+zszPjx4w2uO27cOAA1kHTz5k3++OMP1q5di4WF4T5oP/74I+3bt6d69eoEBgZy69Yttm3bxvTp08mYMSMajYYMGTKQMWNGHB0dadq0KQA2NjZGR4ECBYiKiqJo0aIm+58/f/7WZ7ZhwwbKlClDixYtePr0abzjnj59yuTJk43ar1y5wu+//86xY8cYNGgQI0eOZM6cOQBkzJiR69evGxzbtm3D3NwcT09PAMaOHYuZmRlbt25lx44dANSsWVPNaFuwYAF37txh9erV7N+/n6xZs1K7dm3u3LkDQEBAAGnTpuXff/9l586d6HQ6ateuTVRUlNFcY2JiqFu3Li9fvsTHx4epU6cya9YsxowZo46ZNGkS06ZNY+7cuezevZurV6/Stm3btz5H8ZlSPqGgoCAFUIKCgj7lbYUQQgghhPh83TqgKMNSKMqMUsk9k+Rzdo3+GQxLoSjH5iT3bL5IX+vvWs+ePVPSpk2rjBo1SlEURWnbtq0ybNgwRVEUpWvXrkr69OmVR48efZB7/fPPPwqgmJubx3uYmZkpbdu2NTo3NDRUuX//vuLu7q6MHTtWURRFOXXqlJIiRQolJiZG0Wq1St68eZX169cbnBcREaF06dJFef78uUH78+fPlYEDByrh4eFJ+gw//PCD8vvvvyvTp09XzM3N4x3XokULpUmTJgqgHDx4MN5xtWrVUho2bBhvf6tWrZQWLVqo7+/du2fQ//DhQwVQduzYYbI/KipKsbOzU+bMMf3n/sSJEwqgnDt3zqhv69atirm5ucH3f8yYMYqrq6sSExOjxMTEKK6ursqff/6p9m/fvl0BlFu3bsX7mcTn6xv+Zx4hhBBCCCE+A4osIcQ+Tt0ra1lCKF5zdXVlwYIFHDlyxKD96dOneHt7s23bNtKlS2fQ9+zZMx4/fmx0KG9kcZmqVVWxYkW0Wm28x4QJE4zmGBYWxs8//0yLFi3o3Lkz/fr14/nz53To0IFMmTLRrVs3Xr16Rffu3WnUqJHBudbW1jx79oyOHTsatA8ZMoT9+/cbZXw1bdqUhg0bxvu8tm7dypgxY3BwcIh3zPbt29m/fz/Dhg2Ld0ysmJgYXFxM16W7du0aa9euZciQIWpbpkyZDMbY2Nio1zHVb2FhgYWFhVEdsbj3B0iVKpVR3+XLl0mbNq3B979SpUoEBARw69YtLly4QEBAAD/88IPaX7FiRczMzDh27Fi8n1l8vr7hn5JCCCGEEEJ8BqSIO2QuCzbOYGEDWcol92zEZ6Z27dps2bLFoM3NzY2rV69SuHBho/ElS5akefPmdOzYUT0yZszI8+fP+ffff6lZsyYAixcvZsGCBe81t2PHjlG0aFGCgoLYuHEjHh4eTJ06lXz58tGgQQPOnj1LVFQUFSpUwNHRkZCQEKNrzJ49G51Op/adO3eONWvWsGrVKqMAVvbs2cmdO3e889G8ZTfTgIAAOnTowLRp0xIMcoWGhrJw4UKOHz9Ot27dTI75888/qVq1Knny5In3OvPnz8fW1pYyZcqY7P/7778JDg6mcuXKBu2KonD16lUGDBhA69atyZgxo9G5Li4uBAQEGBTxj12q+PTpU27dugVAtmzZ1H5bW1tSp07NgwcP4p2z+HxZvH2IEEIIIYQQ4qOJLTj8ll88v2pW9tBpLygKpMyS3LMRn4ncuXNz9+5dk31x61ZptVpatWrFkiVL1LbZs2cbBFYSCta8q5iYGLp3706PHj3o2rUr9+/fp2LFilSrVo0DBw6ogaYFCxawb98+JkyYwIgRIzh//jw2NjZkzJiRFy9eYGamzytJmzatwfVj63oBVKtWjY0bNxrV60oKRVFo164d1atXp1GjRmrdqTfZ2NgQGRlJihQpmD17tskgYXBwMCtXrmTVqlXx3m/BggUMGjSIv/76y2QGlbe3N15eXvTr14+8efOq7aNHj2b48OHExMRQs2ZN/vzzT5PXr1mzJpaWlvTp04dJkyYREBDA4MGDATA3NyckJAQzMzOsra0NzrOzsyMyMjLeeYvPlwSwhBBCCCGESE5SxF3PJUdyz0B8Zq5evZqoccOHDzcKxtSqVQsrKyv1fdwsnYQcOHBAXfZmSkxMDC1btgT0QZLjx49z48YNnJ2d1TFr165l7dq1RueuX7+eSpUqYW7+Otty3759lC5dOlFze1+DBw/m5s2brFmzJsFxZ8+eJSgoiFOnTtG9e3cuXrzI2LFjDcasWLGCFClSUKtWLaPzIyIi6N69O0uXLmXWrFn89NNPBv06nY7hw4czduxYBg8ebLSUsUuXLjRo0IAHDx4wb948ChYsyPHjx8mcObPBuHTp0rF27Vrat2/P3Llzsbe359dff+X06dO4ublx9+5ddDodWq3WIJMtIiICOzu7RD0z8XmRAJYQQgghhBDJKTaAZfYNLyEU4gNbu3Yt3333nfre1BK0N+XJk4c5c+aoAZeLFy9y/PhxvLy81DEHDx7k8ePH6nuNRkNMTAw2NjYG7X/88QedOnUiRQp9TbcyZcoQHR1tELyKjIw0eP+xjR07FgsLC1xdXQHUmmBVqlShYsWK7Nq1C0DNXCtdujR2dnZ06tSJIUOGYGtrq17rn3/+oX79+kbzDw8Pp0aNGvj7+3P06FGKFStm0K/T6WjevDm+vr5s376datWqGc3T1dUVV1dXChQoQPXq1cmRIwdz5swxCqKBPlD5+PFjHjx4QOrUqdmyZQspU6Yka9asPHr0CIAHDx6QNWtWQP/Mnz17Rvbs2d/lEYpkJgEsIYQQQgghkpMUcRfCpFmzZtG/f/+3jouKiqJFixYGbStXrlQDNQDR0dFvvc6dO3do3rw5hw4dIlWqVFhbW9OnTx+KFi2qBmLCw8NJnTr1W681dOhQ2rRpk+CYkJAQ7O3t33qtD8XPz8/g/cOHD6latSqLFy/G3d3d5DkWFhYoimJQZP358+ccPHiQ3377zWj8kCFDePToESdOnDC5bHDWrFn4+Phw4sSJRAWRNBoN5ubm8RZ5jx0TWxx+9uzZNG3aFI1GQ7FixbC1tWX37t106tQJgP3796PRaKhQocJb7y0+PxLAEkIIIYQQIjlJEXchTOratStdu3Z96zhTSwjt7OySVPfq9u3bNGnShAcPHrBgwQKKFClCz549GTZsGO3atePcuXP4+/tjZmZGs2bNOH78OFmymK7XdufOHaKjow2WML7p/v37REREJCozLK7YoNG4ceOSdB5gVGw9dqlk5syZyZw5M8HBwXTr1o1WrVqRPn16zp07R//+/WnevLnBszx48CCKolCunPGGC6tWraJly5a8ePGCFy9eqO0ODg6kTZuWVatWUa1aNXQ6ncEOkLE1wf766y+srKwoVaoU0dHRzJo1i4cPH9K6dWsANmzYwLx589i2bRtmZmYsXLiQYsWKodVqmTVrFn5+fqxYsQLQF2z/+eefGTp0KJkzZ8bBwYEePXrQuXNnk8E18fmTAJYQQgghhBDJSScZWEJ8aG3atDEI2MQW965Zs6bJbKM5c+ZQq1Ytg6wtgB49elCtWjU0Gg3Nmzenf//+ao2mw4cPY2dnR+rUqRk4cCAA/v7+eHl5kTNnTrJly0blypX58ccfadOmDbly5VKvu23bNooUKYKjo2OSPtfNmzeTND4pbGxsiI6Opk2bNgQFBZElSxZ+/fVX+vTpYzDu9OnTfPfddyYDhI8fP2by5MlMnjzZoL1x48asW7eOx48fc/ToUaM6XMWLF+fUqVNkz56dwYMH07dvX+zt7SlZsiSHDh1SC9o/evQIPz8/oqOjsba2Zt26dXTv3h0LCws8PDw4dOgQ6dOnV687duxYwsPDadq0Kebm5rRq1YpJkyZ9qEcmPjGNErvw9RMIDg7GycmJoKAgdS2wEEIIIYQQ37SLG2Bde8jiDu23JvdsxBfqa/tdy9fXlzp16iRqbOwSwthdCLNmzcqOHTvUAFbsjnpPnz7FycmJGzdu4OTkRPv27SlZsiSDBg0iffr0rFu3jgoVKtCtWzd0Oh2zZs1S73Hnzh0KFSrE9evXcXV1pVOnTgwdOpSQkBCuXbuGn58fhw8fZv/+/bRt25YpU6YQERHB2rVrWbJkCZcuXaJJkyZ06tSJ3LlzU6RIEQYPHqwubRNCvJ1kYAkhhBBCCJGc1CLukoElRKxKlSoREhKSqLGmlhCCvmB4lSpVCAsLo02bNjg5OQFQoUIF/P39yZQpE+PGjcPCwoLDhw+rRd87dOhAkyZNsLOzw+y/P5fm5ub069ePNGnSALBo0SJAv5Rvy5Yt5M+fn2bNmrF06VK1Rpa1tTU//fQTP/30ExcvXmTu3LlcvHiR7777jmrVqhkUhxdCvJ1kYAkhhBBCCJGczv8NGzpB9krQZlNyz0Z8oeR3LSHE107+mUcIIYQQQojkpBZxl7+aCyGEEPGRn5JCCCGEEEIkJ9mFUAghhHgrCWAJIYQQQgiRnGQXQiG+OjExMck9BSG+OvJTUgghhBBCiOSkFnGXDCwhPid+fn7Y2NjQo0ePJJ23adMm3N3d0el0H2lmeufPnzdqu3nzJsHBwYm+Rnh4OBqNhsePH9OpUyd1J8eETJ48mS5duiRlqkJ8EBLAEkIIIYQQIjkpkoElhClp06ZFo9Ek6tBqtQDcuHEj0edoNBry5Mlj8t5RUVG0bduWMmXKsGDBApPBovjUqFGDgIAAjh49muC44cOHY2VlhYODQ7yHlZUVw4cPNzo3JCSEevXqsXr1arUtOjqaBg0aMHPmzETP1cbGRn3dsmVLevTowejRoxN9/rs6cOAAxYsXx8bGhvz587Nz5854xwYGBtKqVStSpEiBq6sr/fv3NwgORkZG0r17d1xcXEiRIgVeXl6EhYV99M8gPj35KSmEEEIIIURyUmtgaZJ3HkJ8Zh4/foyiKAket2/fNjgnR44chIeHGx2VK1dm+vTpBm2hoaEmA1M6nY42bdqg1WrZsWMHffv2pUGDBjx48MBorKmgmK2tLTdu3MDd3T3BYBtA9+7dCQkJiffo2rWryWfj4ODA2rVrWbt2LYqiADBx4kTc3NwYMGBAop+xRqPB2tqa6OhoKlWqhK+vL8WLFwdAURS0Wq3RodPpEux7m9u3b1OrVi2qVq3KyZMnqVixIg0bNuTOnTsmx7do0YIrV66wa9cu5s6dy6xZs5g8ebLa36tXL7y9vVm3bh3r169n27Zt9O3bN9HPQHw5JIAlhBBCCCFEcvL/7xdoKeIuhIGoqChCQkKSdI5Go+HSpUvUqlULjUaDjY0NNjY2mJmZYWFhob4/fPgw7u7uJu/Zrl07zpw5w7Zt27CxsWHYsGG4u7tTuXJlbt26ZTA+bjDt5cuXRkG3a9euERAQYNBmYWGhnj9t2rQEM7BmzZplNMc6depgbm5O+fLl8fb2xtLSEgsLC4YNG8b+/fuxsrJCo9Hg6+urnnPnzp14s9AiIyPJnDkzGo2GYsWKUatWLUqUKMHKlSuxtLQ0OgYMGMC8efNM9o0cOfKt36Pp06eTM2dOJkyYQMGCBZk2bRqpUqVi8eLFRmPPnz/P9u3bmT9/PmXKlKFx48Z069ZNzTJ78eIF8+fP548//qBy5cpUq1aNYcOGsXjxYsLDw986F/FlkQCWEEIIIYQQyUUbCf9b+t/riOSdixCfmUWLFlGzZs0kn1e4cGHCwsLirV316tUrOnbsSKNGjbCyslLb/f398fDw4Pz58+zdu5e0adMCYGZmxuLFiylfvjzFixdn48aNRtd88eIFVapUMVrut3z5ckqUKMG5c+dMzuVdM7AWLlxoMgMq9sifP7/ROdbW1iaz2DJlysS5c+cM2k6dOkWrVq2Mxh47dgwLCws0Gg3e3t5G/cOHD8ff3x8XFxfWr19vcu779u0z+L5aWFhQoUIFjh07ZnJsmjRpKFq0qNrm4eHBvXv38Pf35+DBg+h0OqpXr27QHxERwdmzZ03eX3y5JIAlhBBCCCFEcomMk10SEZR88xDiK2JhYcGqVatYsWIFmzZtMujT6XS0bNmSnDlz8vvvv6vtixYtIl++fLi5uXH48GEyZMhgcJ65uTlLlizh999/p2nTptSuXZt79+4B8ODBAzw8PMiePTt//vmnwXl9+vShR48eVKhQwWguAIsXLyZPnjzxHsuWLftQjyVeKVKkIDAwENAH8WbOnKkuS4wrKiqKn3/+mZYtW9KgQQN+//13QkNDjcbZ2tqSJ08e0qVLZ/J+t27dIlu2bAZtmTNnNrlEM76xoH/ut27dws3NDXt7e5P94usiASwhhBBCCCGSS3ScX/6ipeiwEG86fPhwgkXY3wxuxMqePTsjR440Kkju7e2Nn58fa9euxcxM/+uwVqtl8eLFTJgwgXLlyuHg4BDv/VxcXDh58iSKouDg4MD+/fspXrw4pUuXxt3d3Sjrq3379gQGBrJw4UKaNWvGokWL1L7evXtz6tQpypUrx5o1a9izZ4/RMXr0aAoUKPBBnmVkZKTBZ8mVKxcAbm5uPHnyBIApU6awc+dONCZq8v3yyy9UrlyZAgUK4ObmRtu2bfHy8jIa5+zszOHDhylXrpzJeYSEhGBnZ2fQZmdnR2RkZKLHxn4eU/02Njbq0kjxdbF4+xAhhBBCCCHERxEVJ4AV+Sr55iHEZ6p8+fIcOnQo3v6wsDB27dqFubm+hlx4eDgxMfqdPb28vGjSpAkhISHExMQQGRmJh4cHe/fuxcrKSq2vZW1tzYEDB9BoNEyePJm2bduyZMkSo3tVqVIFFxcXihQpwrZt2wC4fPkyv/32Gz179uTRo0cMGTKE7t27U6BAAU6fPs3u3buZPXs2adKkwc7OTl2WCODo6EjXrl1ZtWoVf//9d4LPIXXq1FSsWFF936lTJ7p06RLv+KioKKPnlDp1ap4+fQrodwHs3r07oM9YunPnDk+fPmXevHns37/f6HpTpkzh9OnTHD16lOnTpwPQs2dPtebUiBEjEpx/XNbW1kbzi4iIMApEJTQW9IEsU/3R0dEoimLyeuLLJhlYQgghhBBCJJeoOFlXlvLLlhBJZWdnR4MGDXj06BGKovDDDz/g6OiIo6Mjzs7OZMmSBUdHR3x9fenZsyeOjo5kzpxZHePo6Mhff/1lMuMIIDQ0FJ1OR0BAAJcvXyZLliwG/T///DM9e/YEIH369PTv3x9PT08ePHhAy5YtGTp0KGnSpAGgVq1aFCtWDNDXzPL09CRbtmzodDrGjBlD2bJlCQkJ4fTp0zg4OBAcHExISAjp06fHxsZGvaenpyfHjh0jIiKCiIgI/vzzTy5evKi+j22LXUoH+kymVKlSGbx3cHAAIH/+/Jw/f55u3brRqFEjChcubPAZ58yZw8SJE1m3bh3W1tZqu5mZGStXrmTp0qUMGjQo0d+zDBkycP/+fYO2+/fvkz179kSPBciWLRsZMmTgyZMnREdHq/2xSztNXU982SSAJYQQQgghRHKJilMDq3i7ZJuGEF+6bt26MWrUKHx9fU0WKq9SpQqzZ8822de3b994rxu745+bmxvFihWjYMGCTJw4kRUrVpgcP2jQILJnz07evHnJly8f/fr1MxoTGBhI4cKFyZUrF6NGjeL69euMHj1aHbtq1Srq16+vLnEMDw/H0dERgKdPn5IxY0aKFy/OkiVLiIqK4uXLl7Rq1QqtVgvo63xFRESogTOAu3fvkj59evX9y5cvSZkyJQDff/8969ev59SpU/z1118Aam2rMWPGMHDgQLZu3WoyIJQ2bVr27NnDokWLaNKkCS9fvoz3WcZyd3dn9+7d6vuYmBh8fX2pUqWKybF3797l+vXrapuPjw/FihUjZcqUlC9fnujoaIMdF318fEidOjUFCxZ861zEl0UCWEIIIYQQQiSXuEsISxjXkhFCJM7Vq1eNsqM+hD179hAaGkp0dDRbt27F3NycS5cuERwcbDRWp9OxZs0azp8/T+rUqTlz5gwbNmxAp9MZjHN2dsbb25uxY8dy9uxZqlevTuvWralevTq3b99mxowZBkG1Fy9e4OTkBMDs2bOZOXMmoK+vFRYWxoABA9BqtUydOpWYmBiePXuGn58f7du3V69x4cIFihQpQmBgIFqtliNHjpAxY0ZAX0sqPDycAQMG4OjoSHBwMOnSpWPhwoVMmTKFPXv2MGXKFBwcHHBwcGDQoEEsXLhQff/bb79x9OhRrly5wtKlSwkKCsLd3Z2jR4+afKbdu3fnxIkTjBw5kosXL/Lrr7+i0+lo164dADNmzKBt27YAVKhQgeLFi9OhQwdOnTrFunXrmDZtmlqAP2vWrDRq1Iju3btz6NAhdu3axbBhwxgwYIC6rFR8RZRPKCgoSAGUoKCgT3lbIYQQQgghPk+nlijKsBSKsqJJcs9EfOG+xt+11q5dq7i6uir379+Pd0x0dLSyfv16RaPRKFeuXIl3XJUqVZTZs2e/9Z6TJk1S2rZtm+CY8uXLK7t371bfX79+XRk1apSSPXt2JVu2bMry5csVRVGUlStXKlmyZFFy5MihDB48WDl69KgSERGhKIqihISEKKNHj1asra2VHj16KFqtVnny5IlSuHBhZcSIEeq1g4KCFDMzMyUiIkKJjo5WMmTIoGzatElRFEUBlJcvXyqKoii3bt1SgoKClFu3bimurq5KaGioki9fPmXs2LGKoihK0aJFlY0bNyoeHh4KoKRNm1Y5c+aMcvbsWSVVqlRKkyZNlLJlyyo6nU45e/askiNHDiUmJka5e/euyWfUuXNno/bw8HBFURTl0aNHSqpUqdR5mvLPP/8oOXLkUKytrZXvv/9euXjxotrXt29fpWTJkur7e/fuKdWrV1esra2VzJkzG30fX758qfz444+KnZ2dkiZNGmXEiBGKTqeL997iyyVF3IUQQgghhEguwY/0X1OkT3icEN+gWrVqkT59ejJlypTgOI1Gwy+//ELu3Lnf+56xuww+fPgQFxcXgz6dTseBAwe4cOECpUqVQqfTUalSJY4ePYqHhwfjx4+nYcOGWFjof81u0aIFnp6erFu3jkWLFjFhwgRKlCjB4cOHqVatGo8fP2blypU0btyY/fv34+XlRbVq1ejUqRNPnz7F1taWGTNmUKBAAaytrdm8eTM2NjbUqVMHAHt7e65du0apUqXU3Rjnzp1L6dKlsbOzY9WqVSxYsIDDhw9z584datWqRb169YiMjMTW1pbdu3fj6enJsGHD6N69O+XLl6dfv37Y2tpSvHhxzMzMDOpovU1sna506dLx/PnzBMc2adKEJk2amOybNGmSwftMmTKxc+fOeK/l7OzMmjVrEj1P8eWSAJYQQgghhBDJJUS/dT0OaRMeJ8Q3yMHBgbNnz/Lo0SPCw8NNjtFoNLi4uODs7PxB7tm4cWOWLVtGtmzZDAqDx8qUKRPz588nRYoUgL7AeZo0aYyCXbEsLS1p3rw5zZs3Jzg4mIiICDQaDWvXriVNmjRYWVlx8OBBatWqxZgxY+jRowedOnVi4cKFAOTIkYNFixYBULt2bQoUKKDWxho4cCBVq1ZVlygqikLatGnVHQ0LFy7M9OnTmTFjBkOGDMHKygoAW1tbAF69esWECRPo3LkzAN7e3nTo0IGzZ8+yevXqD/I8hfiQNIqiKJ/qZsHBwTg5OREUFKT+gRdCCCGEEOKbtfFnOLcKqo4A957JPRvxBZPftb5sT548MSi6/qHFxMRITSjxxZMi7kIIIYQQQiQXnX7XMMxkYYQQ37KPGbwCJHglvgoSwBJCCCGEECK5SABLCCGESBT5SSmEEEIIIcSn5t0L7h0Hpwz69+by13IhhBAiIfKTUgghhBBCiE/tlL4oM08v6b9KBpYQQgiRoHdaQvjs2TPatWuHi4sLtra2NGjQ4ANPSwghhBBCiK9UVJhxmwSwhPjs+Pn5YWNjQ48ePZJ87r179xI91t/fH19fX+bMmcOqVauM+hs0aMCSJUsAOHz4ME+fPlX7ZsyYwcmTJxN9L0VRuHDhglG7n58fkZGRJs+JjIykYcOGJndlFOJTSnIA69WrV1SoUAF/f382btzI0aNHadmy5ceYmxBCCCGEEF+fK97GbRLAEsJI2rRp0Wg0iTq0Wn09uRs3biT6HI1GQ548eUzeOyoqirZt21KmTBkWLFjA+fPnTY6LjIwkIiLCoO3vv/+mcOHCPHv2LN7P1r59e0qWLEmKFClInz49lStX5ueff2bq1KkJPpPly5fj7u7O/fv3AQgNDaVu3brcvn0bV1dXg8PBwYF27doZnP/w4UM8PDzYv3+/2hYYGIiHhwcbNmwweU9ra2vCw8NZt26d2la/fn1cXV1JmTIl5ubm6j0bN26c4PzjevXqFT169KBTp04G7TqdjtGjR5MlSxbs7OwoUqSIwdxiYmLo1KkTGTNmxN7enlKlShl8nuHDh8f7/Y59bvv376ds2bLY2tqSKVMmevfuTVjY639cOH/+PNWrV8fOzo60adPSvn17nj9/rvb7+PhQunRpHB0dSZcuHd27dzf67yCuTZs2UaBAAaytrcmfPz+7du0y6L9//z516tTB3t6e9OnTM3ny5EQ/x29JkgNY48ePR6fTsWXLFipUqECRIkXw9PT8GHMTQgghhBDi6+N/zrhNAlhCGHn8+DGKoiR43L592+CcHDlyEB4ebnRUrlyZ6dOnG7SFhoaaDEzpdDratGmDVqtlx44d9O3blwYNGvDgwQOjsb/99hs9e/ZU3586dYr27duj0+n47rvvcHZ2xsbGhmLFihmclz59ejw8PBgxYgQAvr6+PHjwgGPHjgGQNWtWrly5YnS/mTNnUqBAAapUqUJ0dDR9+vShVKlSODk5ERAQYHCYCoJkzJiROXPmsGbNGrWtX79+1KhRg+bNmxuM3bZtGy4uLhQoUIB79+4xatQoChQogJWVFRs2bCAgIIDVq1fj7u6u3nP9+vVG93xTSEgIQ4cOJXv27MyZMwdFUQz6t27dyq5du5g7dy5HjhzBw8MDT09P9dlotVoiIyNZsWIF+/fvJ3PmzNStW5cnT54A0L17d65fv25wdOzYkXLlypEpUyYAfvrpJ1q1asWJEyeYPHkyS5cupV+/fuocunXrRqVKlTh27BgLFy5k//79tGnTRu338/Pjp59+4tChQ0yZMoWlS5cybNgwk5/31KlTNG7cmObNm3PkyBHc3d2pX78+169fB/QBudq1a6PVajl48CAjRoxgwIAB/P333299lt8cJYnSp0+vzJgxI6mnKYqiKEFBQQqgBAUFvdP5QgghhBBCfPH2jFSUYSkMj4sbk3tW4gv3Nf6uFRkZqbx69SrBMbdv31YAJTo6Wm07deqUUrlyZSUiIkJtq1KlijJ79mz1/Z49e5SiRYsqkZGRRvds3bq1kitXLsXf319RFEWJiYlRWrdureTMmVO5efOmwfjnz58radKkUTZs2KD4+voqbm5uyqxZs9T+9evXK99//71y8eLFeD8DoISHhxu0ZcmSRfHz81MURVHq16+vLF68WO2LiIhQvL29leHDhyvjxo2L97qzZ89W2rZtq77Pnz+/YmZmppibm8d7AMrt27cVRVGU7du3K507d1b69++vnDlzRr1OmjRplMDAQMXf318ZPXq00q5dO8Xf31/x9/c3eObxuXjxolKiRAll/fr1SsWKFRUvLy+D/kePHhl8PxVFUb777jtl4MCBJq/39OlTBVA2bdpksv/FixdKihQplF27dqlt9+7dMxgzZswYJW3atPH2r1y5UjEzM1NCQ0NN3qNr165K0aJFTfZ5enoqtWrVUt/rdDold+7cSq9evRRFUZRNmzYplpaWypMnT9QxP/74o1KhQgWT1/uWJSkD6969ezx69IgUKVJQqVIlXFxc+P777zl16pTJ8ZGRkQQHBxscQgghhBBCfNNioozbJANLCCOLFi2iZs2aST6vcOHChIWFxVu76tWrV3Ts2JFGjRphZWWltvv7++Ph4cH58+fZu3cvadOmBcDMzIzFixdTvnx5ihcvzsaNG9VzUqVKxfr16ylYsCBLlizBxsaGfv364eDggIODA40bN+bkyZOULl0aBwcHYmJiuH79Ot26dVMPgF69eqnvAwMDTc47JCSE+fPnY2VlRe3atQkPDyciIoKGDRuSM2dOXF1dSZUqFTlz5qRWrVomr+Hj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", "text/plain": [ - "[Datetime(2001,1,2,0,0,0,0,0), Datetime(2001,1,3,0,0,0,0,0), Datetime(2001,1,4,0,0,0,0,0), Datetime(2001,1,5,0,0,0,0,0), Datetime(2001,1,8,0,0,0,0,0), Datetime(2001,1,9,0,0,0,0,0), Datetime(2001,1,10,0,0,0,0,0), Datetime(2001,1,11,0,0,0,0,0), Datetime(2001,1,12,0,0,0,0,0), Datetime(2001,1,15,0,0,0,0,0), Datetime(2001,1,16,0,0,0,0,0), Datetime(2001,1,17,0,0,0,0,0), Datetime(2001,1,18,0,0,0,0,0), Datetime(2001,1,19,0,0,0,0,0), Datetime(2001,2,5,0,0,0,0,0), Datetime(2001,2,6,0,0,0,0,0), Datetime(2001,2,7,0,0,0,0,0), Datetime(2001,2,8,0,0,0,0,0), Datetime(2001,2,9,0,0,0,0,0), Datetime(2001,2,12,0,0,0,0,0), Datetime(2001,2,13,0,0,0,0,0), Datetime(2001,2,14,0,0,0,0,0), Datetime(2001,2,15,0,0,0,0,0), Datetime(2001,2,16,0,0,0,0,0), Datetime(2001,2,19,0,0,0,0,0), Datetime(2001,2,20,0,0,0,0,0), Datetime(2001,2,21,0,0,0,0,0), Datetime(2001,2,22,0,0,0,0,0), Datetime(2001,2,23,0,0,0,0,0), Datetime(2001,2,26,0,0,0,0,0), Datetime(2001,2,27,0,0,0,0,0), Datetime(2001,2,28,0,0,0,0,0), Datetime(2001,3,1,0,0,0,0,0), Datetime(2001,3,2,0,0,0,0,0), Datetime(2001,3,5,0,0,0,0,0), Datetime(2001,3,6,0,0,0,0,0), Datetime(2001,3,7,0,0,0,0,0), Datetime(2001,3,8,0,0,0,0,0), Datetime(2001,3,9,0,0,0,0,0), Datetime(2001,3,12,0,0,0,0,0), Datetime(2001,3,13,0,0,0,0,0), Datetime(2001,3,14,0,0,0,0,0), Datetime(2001,3,15,0,0,0,0,0), Datetime(2001,3,16,0,0,0,0,0), Datetime(2001,3,19,0,0,0,0,0), Datetime(2001,3,20,0,0,0,0,0), Datetime(2001,3,21,0,0,0,0,0), Datetime(2001,3,22,0,0,0,0,0), Datetime(2001,3,23,0,0,0,0,0), Datetime(2001,3,26,0,0,0,0,0), Datetime(2001,3,27,0,0,0,0,0), Datetime(2001,3,28,0,0,0,0,0), Datetime(2001,3,29,0,0,0,0,0), Datetime(2001,3,30,0,0,0,0,0), Datetime(2001,4,2,0,0,0,0,0), Datetime(2001,4,3,0,0,0,0,0), Datetime(2001,4,4,0,0,0,0,0), Datetime(2001,4,5,0,0,0,0,0), Datetime(2001,4,6,0,0,0,0,0), Datetime(2001,4,9,0,0,0,0,0), Datetime(2001,4,10,0,0,0,0,0), Datetime(2001,4,11,0,0,0,0,0), Datetime(2001,4,12,0,0,0,0,0), Datetime(2001,4,13,0,0,0,0,0), Datetime(2001,4,16,0,0,0,0,0), Datetime(2001,4,17,0,0,0,0,0), Datetime(2001,4,18,0,0,0,0,0), Datetime(2001,4,19,0,0,0,0,0), Datetime(2001,4,20,0,0,0,0,0), Datetime(2001,4,23,0,0,0,0,0), Datetime(2001,4,24,0,0,0,0,0), Datetime(2001,4,25,0,0,0,0,0), Datetime(2001,4,26,0,0,0,0,0), Datetime(2001,4,27,0,0,0,0,0), Datetime(2001,4,30,0,0,0,0,0), Datetime(2001,5,8,0,0,0,0,0), Datetime(2001,5,9,0,0,0,0,0), Datetime(2001,5,10,0,0,0,0,0), Datetime(2001,5,11,0,0,0,0,0), Datetime(2001,5,14,0,0,0,0,0), Datetime(2001,5,15,0,0,0,0,0), Datetime(2001,5,16,0,0,0,0,0), Datetime(2001,5,17,0,0,0,0,0), Datetime(2001,5,18,0,0,0,0,0), Datetime(2001,5,21,0,0,0,0,0), Datetime(2001,5,22,0,0,0,0,0), Datetime(2001,5,23,0,0,0,0,0), Datetime(2001,5,24,0,0,0,0,0), Datetime(2001,5,25,0,0,0,0,0), Datetime(2001,5,28,0,0,0,0,0), Datetime(2001,5,29,0,0,0,0,0), Datetime(2001,5,30,0,0,0,0,0), Datetime(2001,5,31,0,0,0,0,0), Datetime(2001,6,1,0,0,0,0,0), Datetime(2001,6,4,0,0,0,0,0), Datetime(2001,6,5,0,0,0,0,0), Datetime(2001,6,6,0,0,0,0,0), Datetime(2001,6,7,0,0,0,0,0), Datetime(2001,6,8,0,0,0,0,0), Datetime(2001,6,11,0,0,0,0,0), Datetime(2001,6,12,0,0,0,0,0), Datetime(2001,6,13,0,0,0,0,0), Datetime(2001,6,14,0,0,0,0,0), Datetime(2001,6,15,0,0,0,0,0), Datetime(2001,6,18,0,0,0,0,0), Datetime(2001,6,19,0,0,0,0,0), Datetime(2001,6,20,0,0,0,0,0), Datetime(2001,6,21,0,0,0,0,0), Datetime(2001,6,22,0,0,0,0,0), Datetime(2001,6,25,0,0,0,0,0), Datetime(2001,6,26,0,0,0,0,0), Datetime(2001,6,27,0,0,0,0,0), Datetime(2001,6,28,0,0,0,0,0), Datetime(2001,6,29,0,0,0,0,0), Datetime(2001,7,2,0,0,0,0,0), Datetime(2001,7,3,0,0,0,0,0), Datetime(2001,7,4,0,0,0,0,0), Datetime(2001,7,5,0,0,0,0,0), Datetime(2001,7,6,0,0,0,0,0), Datetime(2001,7,9,0,0,0,0,0), Datetime(2001,7,10,0,0,0,0,0), Datetime(2001,7,11,0,0,0,0,0), Datetime(2001,7,12,0,0,0,0,0), Datetime(2001,7,13,0,0,0,0,0), Datetime(2001,7,16,0,0,0,0,0), Datetime(2001,7,17,0,0,0,0,0), Datetime(2001,7,18,0,0,0,0,0), Datetime(2001,7,19,0,0,0,0,0), Datetime(2001,7,20,0,0,0,0,0), Datetime(2001,7,23,0,0,0,0,0), Datetime(2001,7,24,0,0,0,0,0), Datetime(2001,7,25,0,0,0,0,0), Datetime(2001,7,26,0,0,0,0,0), Datetime(2001,7,27,0,0,0,0,0), Datetime(2001,7,30,0,0,0,0,0), Datetime(2001,7,31,0,0,0,0,0), Datetime(2001,8,1,0,0,0,0,0), Datetime(2001,8,2,0,0,0,0,0), Datetime(2001,8,3,0,0,0,0,0), Datetime(2001,8,6,0,0,0,0,0), Datetime(2001,8,7,0,0,0,0,0), Datetime(2001,8,8,0,0,0,0,0), Datetime(2001,8,9,0,0,0,0,0), Datetime(2001,8,10,0,0,0,0,0), Datetime(2001,8,13,0,0,0,0,0), Datetime(2001,8,14,0,0,0,0,0), Datetime(2001,8,15,0,0,0,0,0), Datetime(2001,8,16,0,0,0,0,0), Datetime(2001,8,17,0,0,0,0,0), Datetime(2001,8,20,0,0,0,0,0), Datetime(2001,8,21,0,0,0,0,0), Datetime(2001,8,22,0,0,0,0,0), Datetime(2001,8,23,0,0,0,0,0), Datetime(2001,8,24,0,0,0,0,0), Datetime(2001,8,27,0,0,0,0,0), Datetime(2001,8,28,0,0,0,0,0), Datetime(2001,8,29,0,0,0,0,0), Datetime(2001,8,30,0,0,0,0,0), Datetime(2001,8,31,0,0,0,0,0), Datetime(2001,9,3,0,0,0,0,0), Datetime(2001,9,4,0,0,0,0,0), Datetime(2001,9,5,0,0,0,0,0), Datetime(2001,9,6,0,0,0,0,0), Datetime(2001,9,7,0,0,0,0,0), Datetime(2001,9,10,0,0,0,0,0), Datetime(2001,9,11,0,0,0,0,0), Datetime(2001,9,12,0,0,0,0,0), Datetime(2001,9,13,0,0,0,0,0), Datetime(2001,9,14,0,0,0,0,0), Datetime(2001,9,17,0,0,0,0,0), Datetime(2001,9,18,0,0,0,0,0), Datetime(2001,9,19,0,0,0,0,0), Datetime(2001,9,20,0,0,0,0,0), Datetime(2001,9,21,0,0,0,0,0), Datetime(2001,9,24,0,0,0,0,0), Datetime(2001,9,25,0,0,0,0,0), Datetime(2001,9,26,0,0,0,0,0), Datetime(2001,9,27,0,0,0,0,0), Datetime(2001,9,28,0,0,0,0,0), Datetime(2001,10,8,0,0,0,0,0), Datetime(2001,10,9,0,0,0,0,0), Datetime(2001,10,10,0,0,0,0,0), Datetime(2001,10,11,0,0,0,0,0), Datetime(2001,10,12,0,0,0,0,0), Datetime(2001,10,15,0,0,0,0,0), Datetime(2001,10,16,0,0,0,0,0), Datetime(2001,10,17,0,0,0,0,0), Datetime(2001,10,18,0,0,0,0,0), Datetime(2001,10,19,0,0,0,0,0), Datetime(2001,10,22,0,0,0,0,0), Datetime(2001,10,23,0,0,0,0,0), Datetime(2001,10,24,0,0,0,0,0), Datetime(2001,10,25,0,0,0,0,0), Datetime(2001,10,26,0,0,0,0,0), Datetime(2001,10,29,0,0,0,0,0), Datetime(2001,10,30,0,0,0,0,0), Datetime(2001,10,31,0,0,0,0,0), Datetime(2001,11,1,0,0,0,0,0), Datetime(2001,11,2,0,0,0,0,0), Datetime(2001,11,5,0,0,0,0,0), Datetime(2001,11,6,0,0,0,0,0), Datetime(2001,11,7,0,0,0,0,0), Datetime(2001,11,8,0,0,0,0,0), Datetime(2001,11,9,0,0,0,0,0), Datetime(2001,11,12,0,0,0,0,0), Datetime(2001,11,13,0,0,0,0,0), Datetime(2001,11,14,0,0,0,0,0), Datetime(2001,11,15,0,0,0,0,0), Datetime(2001,11,16,0,0,0,0,0), Datetime(2001,11,19,0,0,0,0,0), Datetime(2001,11,20,0,0,0,0,0), Datetime(2001,11,21,0,0,0,0,0), Datetime(2001,11,22,0,0,0,0,0), Datetime(2001,11,23,0,0,0,0,0), Datetime(2001,11,26,0,0,0,0,0), Datetime(2001,11,27,0,0,0,0,0), Datetime(2001,11,28,0,0,0,0,0), Datetime(2001,11,29,0,0,0,0,0), Datetime(2001,11,30,0,0,0,0,0), Datetime(2001,12,3,0,0,0,0,0), Datetime(2001,12,4,0,0,0,0,0), Datetime(2001,12,5,0,0,0,0,0), Datetime(2001,12,6,0,0,0,0,0), Datetime(2001,12,7,0,0,0,0,0), Datetime(2001,12,10,0,0,0,0,0), Datetime(2001,12,11,0,0,0,0,0), Datetime(2001,12,12,0,0,0,0,0), Datetime(2001,12,13,0,0,0,0,0), Datetime(2001,12,14,0,0,0,0,0), Datetime(2001,12,17,0,0,0,0,0), Datetime(2001,12,18,0,0,0,0,0), Datetime(2001,12,19,0,0,0,0,0), Datetime(2001,12,20,0,0,0,0,0), Datetime(2001,12,21,0,0,0,0,0), Datetime(2001,12,24,0,0,0,0,0), Datetime(2001,12,25,0,0,0,0,0), Datetime(2001,12,26,0,0,0,0,0), Datetime(2001,12,27,0,0,0,0,0), Datetime(2001,12,28,0,0,0,0,0), Datetime(2001,12,31,0,0,0,0,0), Datetime(2002,1,4,0,0,0,0,0), Datetime(2002,1,7,0,0,0,0,0), Datetime(2002,1,8,0,0,0,0,0), Datetime(2002,1,9,0,0,0,0,0), Datetime(2002,1,10,0,0,0,0,0), Datetime(2002,1,11,0,0,0,0,0), Datetime(2002,1,14,0,0,0,0,0), Datetime(2002,1,15,0,0,0,0,0), Datetime(2002,1,16,0,0,0,0,0), Datetime(2002,1,17,0,0,0,0,0), Datetime(2002,1,18,0,0,0,0,0), Datetime(2002,1,21,0,0,0,0,0), Datetime(2002,1,22,0,0,0,0,0), Datetime(2002,1,23,0,0,0,0,0), Datetime(2002,1,24,0,0,0,0,0), Datetime(2002,1,25,0,0,0,0,0), Datetime(2002,1,28,0,0,0,0,0), Datetime(2002,1,29,0,0,0,0,0), Datetime(2002,1,30,0,0,0,0,0), Datetime(2002,1,31,0,0,0,0,0), Datetime(2002,2,1,0,0,0,0,0), Datetime(2002,2,4,0,0,0,0,0), Datetime(2002,2,5,0,0,0,0,0), Datetime(2002,2,6,0,0,0,0,0), Datetime(2002,2,7,0,0,0,0,0), Datetime(2002,2,8,0,0,0,0,0), Datetime(2002,2,25,0,0,0,0,0), Datetime(2002,2,26,0,0,0,0,0), Datetime(2002,2,27,0,0,0,0,0), Datetime(2002,2,28,0,0,0,0,0), Datetime(2002,3,1,0,0,0,0,0), Datetime(2002,3,4,0,0,0,0,0), Datetime(2002,3,5,0,0,0,0,0), Datetime(2002,3,6,0,0,0,0,0), Datetime(2002,3,7,0,0,0,0,0), Datetime(2002,3,8,0,0,0,0,0), Datetime(2002,3,11,0,0,0,0,0), Datetime(2002,3,12,0,0,0,0,0), Datetime(2002,3,13,0,0,0,0,0), Datetime(2002,3,14,0,0,0,0,0), Datetime(2002,3,15,0,0,0,0,0), Datetime(2002,3,18,0,0,0,0,0), Datetime(2002,3,19,0,0,0,0,0), Datetime(2002,3,20,0,0,0,0,0), Datetime(2002,3,21,0,0,0,0,0), Datetime(2002,3,22,0,0,0,0,0), Datetime(2002,3,25,0,0,0,0,0), Datetime(2002,3,26,0,0,0,0,0), Datetime(2002,3,27,0,0,0,0,0), Datetime(2002,3,28,0,0,0,0,0), Datetime(2002,3,29,0,0,0,0,0), Datetime(2002,4,1,0,0,0,0,0), Datetime(2002,4,2,0,0,0,0,0), Datetime(2002,4,3,0,0,0,0,0), Datetime(2002,4,4,0,0,0,0,0), Datetime(2002,4,5,0,0,0,0,0), Datetime(2002,4,8,0,0,0,0,0), Datetime(2002,4,9,0,0,0,0,0), Datetime(2002,4,10,0,0,0,0,0), Datetime(2002,4,11,0,0,0,0,0), Datetime(2002,4,12,0,0,0,0,0), Datetime(2002,4,15,0,0,0,0,0), Datetime(2002,4,16,0,0,0,0,0), Datetime(2002,4,17,0,0,0,0,0), Datetime(2002,4,18,0,0,0,0,0), Datetime(2002,4,19,0,0,0,0,0), Datetime(2002,4,22,0,0,0,0,0), Datetime(2002,4,23,0,0,0,0,0), Datetime(2002,4,24,0,0,0,0,0), Datetime(2002,4,25,0,0,0,0,0), Datetime(2002,4,26,0,0,0,0,0), Datetime(2002,4,29,0,0,0,0,0), Datetime(2002,4,30,0,0,0,0,0), Datetime(2002,5,8,0,0,0,0,0), Datetime(2002,5,9,0,0,0,0,0), Datetime(2002,5,10,0,0,0,0,0), Datetime(2002,5,13,0,0,0,0,0), Datetime(2002,5,14,0,0,0,0,0), Datetime(2002,5,15,0,0,0,0,0), Datetime(2002,5,16,0,0,0,0,0), Datetime(2002,5,17,0,0,0,0,0), Datetime(2002,5,20,0,0,0,0,0), Datetime(2002,5,21,0,0,0,0,0), Datetime(2002,5,22,0,0,0,0,0), Datetime(2002,5,23,0,0,0,0,0), Datetime(2002,5,24,0,0,0,0,0), Datetime(2002,5,27,0,0,0,0,0), Datetime(2002,5,28,0,0,0,0,0), Datetime(2002,5,29,0,0,0,0,0), Datetime(2002,5,30,0,0,0,0,0), Datetime(2002,5,31,0,0,0,0,0), Datetime(2002,6,3,0,0,0,0,0), Datetime(2002,6,4,0,0,0,0,0), Datetime(2002,6,5,0,0,0,0,0), Datetime(2002,6,6,0,0,0,0,0), Datetime(2002,6,7,0,0,0,0,0), Datetime(2002,6,10,0,0,0,0,0), Datetime(2002,6,11,0,0,0,0,0), Datetime(2002,6,12,0,0,0,0,0), Datetime(2002,6,13,0,0,0,0,0), Datetime(2002,6,14,0,0,0,0,0), Datetime(2002,6,17,0,0,0,0,0), Datetime(2002,6,18,0,0,0,0,0), Datetime(2002,6,19,0,0,0,0,0), Datetime(2002,6,20,0,0,0,0,0), Datetime(2002,6,21,0,0,0,0,0), Datetime(2002,6,24,0,0,0,0,0), Datetime(2002,6,25,0,0,0,0,0), Datetime(2002,6,26,0,0,0,0,0), Datetime(2002,6,27,0,0,0,0,0), Datetime(2002,6,28,0,0,0,0,0), Datetime(2002,7,1,0,0,0,0,0), Datetime(2002,7,2,0,0,0,0,0), Datetime(2002,7,3,0,0,0,0,0), Datetime(2002,7,4,0,0,0,0,0), Datetime(2002,7,5,0,0,0,0,0), Datetime(2002,7,8,0,0,0,0,0), Datetime(2002,7,9,0,0,0,0,0), Datetime(2002,7,10,0,0,0,0,0), Datetime(2002,7,11,0,0,0,0,0), Datetime(2002,7,12,0,0,0,0,0), Datetime(2002,7,15,0,0,0,0,0), Datetime(2002,7,16,0,0,0,0,0), Datetime(2002,7,17,0,0,0,0,0), Datetime(2002,7,18,0,0,0,0,0), Datetime(2002,7,19,0,0,0,0,0), Datetime(2002,7,22,0,0,0,0,0), Datetime(2002,7,23,0,0,0,0,0), Datetime(2002,7,24,0,0,0,0,0), Datetime(2002,7,25,0,0,0,0,0), Datetime(2002,7,26,0,0,0,0,0), Datetime(2002,7,29,0,0,0,0,0), Datetime(2002,7,30,0,0,0,0,0), Datetime(2002,7,31,0,0,0,0,0), Datetime(2002,8,1,0,0,0,0,0), Datetime(2002,8,2,0,0,0,0,0), Datetime(2002,8,5,0,0,0,0,0), Datetime(2002,8,6,0,0,0,0,0), Datetime(2002,8,7,0,0,0,0,0), Datetime(2002,8,8,0,0,0,0,0), Datetime(2002,8,9,0,0,0,0,0), Datetime(2002,8,12,0,0,0,0,0), Datetime(2002,8,13,0,0,0,0,0), Datetime(2002,8,14,0,0,0,0,0), Datetime(2002,8,15,0,0,0,0,0), Datetime(2002,8,16,0,0,0,0,0), Datetime(2002,8,19,0,0,0,0,0), Datetime(2002,8,20,0,0,0,0,0), Datetime(2002,8,21,0,0,0,0,0), Datetime(2002,8,22,0,0,0,0,0), Datetime(2002,8,23,0,0,0,0,0), Datetime(2002,8,26,0,0,0,0,0), Datetime(2002,8,27,0,0,0,0,0), Datetime(2002,8,28,0,0,0,0,0), Datetime(2002,8,29,0,0,0,0,0), Datetime(2002,8,30,0,0,0,0,0), Datetime(2002,9,2,0,0,0,0,0), Datetime(2002,9,3,0,0,0,0,0), Datetime(2002,9,4,0,0,0,0,0), Datetime(2002,9,5,0,0,0,0,0), Datetime(2002,9,6,0,0,0,0,0), Datetime(2002,9,9,0,0,0,0,0), Datetime(2002,9,10,0,0,0,0,0), Datetime(2002,9,11,0,0,0,0,0), Datetime(2002,9,12,0,0,0,0,0), Datetime(2002,9,13,0,0,0,0,0), Datetime(2002,9,16,0,0,0,0,0), Datetime(2002,9,17,0,0,0,0,0), Datetime(2002,9,18,0,0,0,0,0), Datetime(2002,9,19,0,0,0,0,0), Datetime(2002,9,20,0,0,0,0,0), Datetime(2002,9,23,0,0,0,0,0), Datetime(2002,9,24,0,0,0,0,0), Datetime(2002,9,25,0,0,0,0,0), Datetime(2002,9,26,0,0,0,0,0), Datetime(2002,9,27,0,0,0,0,0), Datetime(2002,10,8,0,0,0,0,0), Datetime(2002,10,9,0,0,0,0,0), Datetime(2002,10,10,0,0,0,0,0), Datetime(2002,10,11,0,0,0,0,0), Datetime(2002,10,14,0,0,0,0,0), Datetime(2002,10,15,0,0,0,0,0), Datetime(2002,10,16,0,0,0,0,0), Datetime(2002,10,17,0,0,0,0,0), Datetime(2002,10,18,0,0,0,0,0), Datetime(2002,10,21,0,0,0,0,0), Datetime(2002,10,22,0,0,0,0,0), Datetime(2002,10,23,0,0,0,0,0), Datetime(2002,10,24,0,0,0,0,0), Datetime(2002,10,25,0,0,0,0,0), Datetime(2002,10,28,0,0,0,0,0), Datetime(2002,10,29,0,0,0,0,0), Datetime(2002,10,30,0,0,0,0,0), Datetime(2002,10,31,0,0,0,0,0), Datetime(2002,11,1,0,0,0,0,0), Datetime(2002,11,4,0,0,0,0,0), Datetime(2002,11,5,0,0,0,0,0), Datetime(2002,11,6,0,0,0,0,0), Datetime(2002,11,7,0,0,0,0,0), Datetime(2002,11,8,0,0,0,0,0), Datetime(2002,11,11,0,0,0,0,0), Datetime(2002,11,12,0,0,0,0,0), Datetime(2002,11,13,0,0,0,0,0), Datetime(2002,11,14,0,0,0,0,0), Datetime(2002,11,15,0,0,0,0,0), Datetime(2002,11,18,0,0,0,0,0), Datetime(2002,11,19,0,0,0,0,0), Datetime(2002,11,20,0,0,0,0,0), Datetime(2002,11,21,0,0,0,0,0), Datetime(2002,11,22,0,0,0,0,0), Datetime(2002,11,25,0,0,0,0,0), Datetime(2002,11,26,0,0,0,0,0), Datetime(2002,11,27,0,0,0,0,0), Datetime(2002,11,28,0,0,0,0,0), Datetime(2002,11,29,0,0,0,0,0), Datetime(2002,12,2,0,0,0,0,0), Datetime(2002,12,3,0,0,0,0,0), Datetime(2002,12,4,0,0,0,0,0), Datetime(2002,12,5,0,0,0,0,0), Datetime(2002,12,6,0,0,0,0,0), Datetime(2002,12,9,0,0,0,0,0), Datetime(2002,12,10,0,0,0,0,0), Datetime(2002,12,11,0,0,0,0,0), Datetime(2002,12,12,0,0,0,0,0), Datetime(2002,12,13,0,0,0,0,0), Datetime(2002,12,16,0,0,0,0,0), Datetime(2002,12,17,0,0,0,0,0), Datetime(2002,12,18,0,0,0,0,0), Datetime(2002,12,19,0,0,0,0,0), Datetime(2002,12,20,0,0,0,0,0), Datetime(2002,12,23,0,0,0,0,0), Datetime(2002,12,24,0,0,0,0,0), Datetime(2002,12,25,0,0,0,0,0), Datetime(2002,12,26,0,0,0,0,0), Datetime(2002,12,27,0,0,0,0,0), Datetime(2002,12,30,0,0,0,0,0), Datetime(2002,12,31,0,0,0,0,0), Datetime(2003,1,2,0,0,0,0,0), Datetime(2003,1,3,0,0,0,0,0), Datetime(2003,1,6,0,0,0,0,0), Datetime(2003,1,7,0,0,0,0,0), Datetime(2003,1,8,0,0,0,0,0), Datetime(2003,1,9,0,0,0,0,0), Datetime(2003,1,10,0,0,0,0,0), Datetime(2003,1,13,0,0,0,0,0), Datetime(2003,1,14,0,0,0,0,0), Datetime(2003,1,15,0,0,0,0,0), Datetime(2003,1,16,0,0,0,0,0), Datetime(2003,1,17,0,0,0,0,0), Datetime(2003,1,20,0,0,0,0,0), Datetime(2003,1,21,0,0,0,0,0), Datetime(2003,1,22,0,0,0,0,0), Datetime(2003,1,23,0,0,0,0,0), Datetime(2003,1,24,0,0,0,0,0), Datetime(2003,1,27,0,0,0,0,0), Datetime(2003,1,28,0,0,0,0,0), Datetime(2003,1,29,0,0,0,0,0), Datetime(2003,2,10,0,0,0,0,0), Datetime(2003,2,11,0,0,0,0,0), Datetime(2003,2,12,0,0,0,0,0), Datetime(2003,2,13,0,0,0,0,0), Datetime(2003,2,14,0,0,0,0,0), Datetime(2003,2,17,0,0,0,0,0), Datetime(2003,2,18,0,0,0,0,0), Datetime(2003,2,19,0,0,0,0,0), Datetime(2003,2,20,0,0,0,0,0), Datetime(2003,2,21,0,0,0,0,0), Datetime(2003,2,24,0,0,0,0,0), Datetime(2003,2,25,0,0,0,0,0), Datetime(2003,2,26,0,0,0,0,0), Datetime(2003,2,27,0,0,0,0,0), Datetime(2003,2,28,0,0,0,0,0), Datetime(2003,3,3,0,0,0,0,0), Datetime(2003,3,4,0,0,0,0,0), Datetime(2003,3,5,0,0,0,0,0), Datetime(2003,3,6,0,0,0,0,0), Datetime(2003,3,7,0,0,0,0,0), Datetime(2003,3,10,0,0,0,0,0), Datetime(2003,3,11,0,0,0,0,0), Datetime(2003,3,12,0,0,0,0,0), Datetime(2003,3,13,0,0,0,0,0), Datetime(2003,3,14,0,0,0,0,0), Datetime(2003,3,17,0,0,0,0,0), Datetime(2003,3,18,0,0,0,0,0), Datetime(2003,3,19,0,0,0,0,0), Datetime(2003,3,20,0,0,0,0,0), Datetime(2003,3,21,0,0,0,0,0), Datetime(2003,3,24,0,0,0,0,0), Datetime(2003,3,25,0,0,0,0,0), Datetime(2003,3,26,0,0,0,0,0), Datetime(2003,3,27,0,0,0,0,0), Datetime(2003,3,28,0,0,0,0,0), Datetime(2003,3,31,0,0,0,0,0), Datetime(2003,4,1,0,0,0,0,0), Datetime(2003,4,2,0,0,0,0,0), Datetime(2003,4,3,0,0,0,0,0), Datetime(2003,4,4,0,0,0,0,0), Datetime(2003,4,7,0,0,0,0,0), Datetime(2003,4,8,0,0,0,0,0), Datetime(2003,4,9,0,0,0,0,0), Datetime(2003,4,10,0,0,0,0,0), Datetime(2003,4,11,0,0,0,0,0), Datetime(2003,4,14,0,0,0,0,0), Datetime(2003,4,15,0,0,0,0,0), Datetime(2003,4,16,0,0,0,0,0), Datetime(2003,4,17,0,0,0,0,0), Datetime(2003,4,18,0,0,0,0,0), Datetime(2003,4,21,0,0,0,0,0), Datetime(2003,4,22,0,0,0,0,0), Datetime(2003,4,23,0,0,0,0,0), Datetime(2003,4,24,0,0,0,0,0), Datetime(2003,4,25,0,0,0,0,0), Datetime(2003,4,28,0,0,0,0,0), Datetime(2003,4,29,0,0,0,0,0), Datetime(2003,4,30,0,0,0,0,0), Datetime(2003,5,12,0,0,0,0,0), Datetime(2003,5,13,0,0,0,0,0), Datetime(2003,5,14,0,0,0,0,0), Datetime(2003,5,15,0,0,0,0,0), Datetime(2003,5,16,0,0,0,0,0), Datetime(2003,5,19,0,0,0,0,0), Datetime(2003,5,20,0,0,0,0,0), Datetime(2003,5,21,0,0,0,0,0), Datetime(2003,5,22,0,0,0,0,0), Datetime(2003,5,23,0,0,0,0,0), Datetime(2003,5,26,0,0,0,0,0), Datetime(2003,5,27,0,0,0,0,0), Datetime(2003,5,28,0,0,0,0,0), Datetime(2003,5,29,0,0,0,0,0), Datetime(2003,5,30,0,0,0,0,0), Datetime(2003,6,2,0,0,0,0,0), Datetime(2003,6,3,0,0,0,0,0), Datetime(2003,6,4,0,0,0,0,0), Datetime(2003,6,5,0,0,0,0,0), Datetime(2003,6,6,0,0,0,0,0), Datetime(2003,6,9,0,0,0,0,0), Datetime(2003,6,10,0,0,0,0,0), Datetime(2003,6,11,0,0,0,0,0), Datetime(2003,6,12,0,0,0,0,0), Datetime(2003,6,13,0,0,0,0,0), Datetime(2003,6,16,0,0,0,0,0), Datetime(2003,6,17,0,0,0,0,0), Datetime(2003,6,18,0,0,0,0,0), Datetime(2003,6,19,0,0,0,0,0), Datetime(2003,6,20,0,0,0,0,0), Datetime(2003,6,23,0,0,0,0,0), Datetime(2003,6,24,0,0,0,0,0), Datetime(2003,6,25,0,0,0,0,0), Datetime(2003,6,26,0,0,0,0,0), Datetime(2003,6,27,0,0,0,0,0), Datetime(2003,6,30,0,0,0,0,0), Datetime(2003,7,1,0,0,0,0,0), Datetime(2003,7,2,0,0,0,0,0), Datetime(2003,7,3,0,0,0,0,0), Datetime(2003,7,4,0,0,0,0,0), Datetime(2003,7,7,0,0,0,0,0), Datetime(2003,7,8,0,0,0,0,0), Datetime(2003,7,9,0,0,0,0,0), Datetime(2003,7,10,0,0,0,0,0), Datetime(2003,7,11,0,0,0,0,0), Datetime(2003,7,14,0,0,0,0,0), Datetime(2003,7,15,0,0,0,0,0), Datetime(2003,7,16,0,0,0,0,0), Datetime(2003,7,17,0,0,0,0,0), Datetime(2003,7,18,0,0,0,0,0), Datetime(2003,7,21,0,0,0,0,0), Datetime(2003,7,22,0,0,0,0,0), Datetime(2003,7,23,0,0,0,0,0), Datetime(2003,7,24,0,0,0,0,0), Datetime(2003,7,25,0,0,0,0,0), Datetime(2003,7,28,0,0,0,0,0), Datetime(2003,7,29,0,0,0,0,0), Datetime(2003,7,30,0,0,0,0,0), Datetime(2003,7,31,0,0,0,0,0), Datetime(2003,8,1,0,0,0,0,0), Datetime(2003,8,4,0,0,0,0,0), Datetime(2003,8,5,0,0,0,0,0), Datetime(2003,8,6,0,0,0,0,0), Datetime(2003,8,7,0,0,0,0,0), Datetime(2003,8,8,0,0,0,0,0), Datetime(2003,8,11,0,0,0,0,0), Datetime(2003,8,12,0,0,0,0,0), Datetime(2003,8,13,0,0,0,0,0), Datetime(2003,8,14,0,0,0,0,0), Datetime(2003,8,15,0,0,0,0,0), Datetime(2003,8,18,0,0,0,0,0), Datetime(2003,8,19,0,0,0,0,0), Datetime(2003,8,20,0,0,0,0,0), Datetime(2003,8,21,0,0,0,0,0), Datetime(2003,8,22,0,0,0,0,0), Datetime(2003,8,25,0,0,0,0,0), Datetime(2003,8,26,0,0,0,0,0), Datetime(2003,8,27,0,0,0,0,0), Datetime(2003,8,28,0,0,0,0,0), Datetime(2003,8,29,0,0,0,0,0), Datetime(2003,9,1,0,0,0,0,0), Datetime(2003,9,2,0,0,0,0,0), Datetime(2003,9,3,0,0,0,0,0), Datetime(2003,9,4,0,0,0,0,0), Datetime(2003,9,5,0,0,0,0,0), Datetime(2003,9,8,0,0,0,0,0), Datetime(2003,9,9,0,0,0,0,0), Datetime(2003,9,10,0,0,0,0,0), Datetime(2003,9,11,0,0,0,0,0), Datetime(2003,9,12,0,0,0,0,0), Datetime(2003,9,15,0,0,0,0,0), Datetime(2003,9,16,0,0,0,0,0), Datetime(2003,9,17,0,0,0,0,0), Datetime(2003,9,18,0,0,0,0,0), Datetime(2003,9,19,0,0,0,0,0), Datetime(2003,9,22,0,0,0,0,0), Datetime(2003,9,23,0,0,0,0,0), Datetime(2003,9,24,0,0,0,0,0), Datetime(2003,9,25,0,0,0,0,0), Datetime(2003,9,26,0,0,0,0,0), Datetime(2003,9,29,0,0,0,0,0), Datetime(2003,9,30,0,0,0,0,0), Datetime(2003,10,8,0,0,0,0,0), Datetime(2003,10,9,0,0,0,0,0), Datetime(2003,10,10,0,0,0,0,0), Datetime(2003,10,13,0,0,0,0,0), Datetime(2003,10,14,0,0,0,0,0), Datetime(2003,10,15,0,0,0,0,0), Datetime(2003,10,16,0,0,0,0,0), Datetime(2003,10,17,0,0,0,0,0), Datetime(2003,10,20,0,0,0,0,0), Datetime(2003,10,21,0,0,0,0,0), Datetime(2003,10,22,0,0,0,0,0), Datetime(2003,10,23,0,0,0,0,0), Datetime(2003,10,24,0,0,0,0,0), Datetime(2003,10,27,0,0,0,0,0), Datetime(2003,10,28,0,0,0,0,0), Datetime(2003,10,29,0,0,0,0,0), Datetime(2003,10,30,0,0,0,0,0), Datetime(2003,10,31,0,0,0,0,0), Datetime(2003,11,3,0,0,0,0,0), Datetime(2003,11,4,0,0,0,0,0), Datetime(2003,11,5,0,0,0,0,0), Datetime(2003,11,6,0,0,0,0,0), Datetime(2003,11,7,0,0,0,0,0), Datetime(2003,11,10,0,0,0,0,0), Datetime(2003,11,11,0,0,0,0,0), Datetime(2003,11,12,0,0,0,0,0), Datetime(2003,11,13,0,0,0,0,0), Datetime(2003,11,14,0,0,0,0,0), Datetime(2003,11,17,0,0,0,0,0), Datetime(2003,11,18,0,0,0,0,0), Datetime(2003,11,19,0,0,0,0,0), Datetime(2003,11,20,0,0,0,0,0), Datetime(2003,11,21,0,0,0,0,0), Datetime(2003,11,24,0,0,0,0,0), Datetime(2003,11,25,0,0,0,0,0), Datetime(2003,11,26,0,0,0,0,0), Datetime(2003,11,27,0,0,0,0,0), Datetime(2003,11,28,0,0,0,0,0), Datetime(2003,12,1,0,0,0,0,0), Datetime(2003,12,2,0,0,0,0,0), Datetime(2003,12,3,0,0,0,0,0), Datetime(2003,12,4,0,0,0,0,0), Datetime(2003,12,5,0,0,0,0,0), Datetime(2003,12,8,0,0,0,0,0), Datetime(2003,12,9,0,0,0,0,0), Datetime(2003,12,10,0,0,0,0,0), Datetime(2003,12,11,0,0,0,0,0), Datetime(2003,12,12,0,0,0,0,0), Datetime(2003,12,15,0,0,0,0,0), Datetime(2003,12,16,0,0,0,0,0), Datetime(2003,12,17,0,0,0,0,0), Datetime(2003,12,18,0,0,0,0,0), Datetime(2003,12,19,0,0,0,0,0), Datetime(2003,12,22,0,0,0,0,0), Datetime(2003,12,23,0,0,0,0,0), Datetime(2003,12,24,0,0,0,0,0), Datetime(2003,12,25,0,0,0,0,0), Datetime(2003,12,26,0,0,0,0,0), Datetime(2003,12,29,0,0,0,0,0), Datetime(2003,12,30,0,0,0,0,0), Datetime(2003,12,31,0,0,0,0,0), Datetime(2004,1,2,0,0,0,0,0), Datetime(2004,1,5,0,0,0,0,0), Datetime(2004,1,6,0,0,0,0,0), Datetime(2004,1,7,0,0,0,0,0), Datetime(2004,1,8,0,0,0,0,0), Datetime(2004,1,9,0,0,0,0,0), Datetime(2004,1,12,0,0,0,0,0), Datetime(2004,1,13,0,0,0,0,0), Datetime(2004,1,14,0,0,0,0,0), Datetime(2004,1,15,0,0,0,0,0), Datetime(2004,1,16,0,0,0,0,0), Datetime(2004,1,29,0,0,0,0,0), Datetime(2004,1,30,0,0,0,0,0), Datetime(2004,2,2,0,0,0,0,0), Datetime(2004,2,3,0,0,0,0,0), Datetime(2004,2,4,0,0,0,0,0), Datetime(2004,2,5,0,0,0,0,0), Datetime(2004,2,6,0,0,0,0,0), Datetime(2004,2,9,0,0,0,0,0), Datetime(2004,2,10,0,0,0,0,0), Datetime(2004,2,11,0,0,0,0,0), Datetime(2004,2,12,0,0,0,0,0), Datetime(2004,2,13,0,0,0,0,0), Datetime(2004,2,16,0,0,0,0,0), Datetime(2004,2,17,0,0,0,0,0), Datetime(2004,2,18,0,0,0,0,0), Datetime(2004,2,19,0,0,0,0,0), Datetime(2004,2,20,0,0,0,0,0), Datetime(2004,2,23,0,0,0,0,0), Datetime(2004,2,24,0,0,0,0,0), Datetime(2004,2,25,0,0,0,0,0), Datetime(2004,2,26,0,0,0,0,0), Datetime(2004,2,27,0,0,0,0,0), Datetime(2004,3,1,0,0,0,0,0), Datetime(2004,3,2,0,0,0,0,0), Datetime(2004,3,3,0,0,0,0,0), Datetime(2004,3,4,0,0,0,0,0), Datetime(2004,3,5,0,0,0,0,0), Datetime(2004,3,8,0,0,0,0,0), Datetime(2004,3,9,0,0,0,0,0), Datetime(2004,3,10,0,0,0,0,0), Datetime(2004,3,11,0,0,0,0,0), Datetime(2004,3,12,0,0,0,0,0), Datetime(2004,3,15,0,0,0,0,0), Datetime(2004,3,16,0,0,0,0,0), Datetime(2004,3,17,0,0,0,0,0), Datetime(2004,3,18,0,0,0,0,0), Datetime(2004,3,19,0,0,0,0,0), Datetime(2004,3,22,0,0,0,0,0), Datetime(2004,3,23,0,0,0,0,0), Datetime(2004,3,24,0,0,0,0,0), Datetime(2004,3,25,0,0,0,0,0), Datetime(2004,3,26,0,0,0,0,0), Datetime(2004,3,29,0,0,0,0,0), Datetime(2004,3,30,0,0,0,0,0), Datetime(2004,3,31,0,0,0,0,0), Datetime(2004,4,1,0,0,0,0,0), Datetime(2004,4,2,0,0,0,0,0), Datetime(2004,4,5,0,0,0,0,0), Datetime(2004,4,6,0,0,0,0,0), Datetime(2004,4,7,0,0,0,0,0), Datetime(2004,4,8,0,0,0,0,0), Datetime(2004,4,9,0,0,0,0,0), Datetime(2004,4,12,0,0,0,0,0), Datetime(2004,4,13,0,0,0,0,0), Datetime(2004,4,14,0,0,0,0,0), Datetime(2004,4,15,0,0,0,0,0), Datetime(2004,4,16,0,0,0,0,0), Datetime(2004,4,19,0,0,0,0,0), Datetime(2004,4,20,0,0,0,0,0), Datetime(2004,4,21,0,0,0,0,0), Datetime(2004,4,22,0,0,0,0,0), Datetime(2004,4,23,0,0,0,0,0), Datetime(2004,4,26,0,0,0,0,0), Datetime(2004,4,27,0,0,0,0,0), Datetime(2004,4,28,0,0,0,0,0), Datetime(2004,4,29,0,0,0,0,0), Datetime(2004,4,30,0,0,0,0,0), Datetime(2004,5,10,0,0,0,0,0), Datetime(2004,5,11,0,0,0,0,0), Datetime(2004,5,12,0,0,0,0,0), Datetime(2004,5,13,0,0,0,0,0), Datetime(2004,5,14,0,0,0,0,0), Datetime(2004,5,17,0,0,0,0,0), Datetime(2004,5,18,0,0,0,0,0), Datetime(2004,5,19,0,0,0,0,0), Datetime(2004,5,20,0,0,0,0,0), Datetime(2004,5,21,0,0,0,0,0), Datetime(2004,5,24,0,0,0,0,0), Datetime(2004,5,25,0,0,0,0,0), Datetime(2004,5,26,0,0,0,0,0), Datetime(2004,5,27,0,0,0,0,0), Datetime(2004,5,28,0,0,0,0,0), Datetime(2004,5,31,0,0,0,0,0), Datetime(2004,6,1,0,0,0,0,0), Datetime(2004,6,2,0,0,0,0,0), Datetime(2004,6,3,0,0,0,0,0), Datetime(2004,6,4,0,0,0,0,0), Datetime(2004,6,7,0,0,0,0,0), Datetime(2004,6,8,0,0,0,0,0), Datetime(2004,6,9,0,0,0,0,0), Datetime(2004,6,10,0,0,0,0,0), Datetime(2004,6,11,0,0,0,0,0), Datetime(2004,6,14,0,0,0,0,0), Datetime(2004,6,15,0,0,0,0,0), Datetime(2004,6,16,0,0,0,0,0), Datetime(2004,6,17,0,0,0,0,0), Datetime(2004,6,18,0,0,0,0,0), Datetime(2004,6,21,0,0,0,0,0), Datetime(2004,6,22,0,0,0,0,0), Datetime(2004,6,23,0,0,0,0,0), Datetime(2004,6,24,0,0,0,0,0), Datetime(2004,6,25,0,0,0,0,0), Datetime(2004,6,28,0,0,0,0,0), Datetime(2004,6,29,0,0,0,0,0), Datetime(2004,6,30,0,0,0,0,0), Datetime(2004,7,1,0,0,0,0,0), Datetime(2004,7,2,0,0,0,0,0), Datetime(2004,7,5,0,0,0,0,0), Datetime(2004,7,6,0,0,0,0,0), Datetime(2004,7,7,0,0,0,0,0), Datetime(2004,7,8,0,0,0,0,0), Datetime(2004,7,9,0,0,0,0,0), Datetime(2004,7,12,0,0,0,0,0), Datetime(2004,7,13,0,0,0,0,0), Datetime(2004,7,14,0,0,0,0,0), Datetime(2004,7,15,0,0,0,0,0), Datetime(2004,7,16,0,0,0,0,0), Datetime(2004,7,19,0,0,0,0,0), Datetime(2004,7,20,0,0,0,0,0), Datetime(2004,7,21,0,0,0,0,0), Datetime(2004,7,22,0,0,0,0,0), Datetime(2004,7,23,0,0,0,0,0), Datetime(2004,7,26,0,0,0,0,0), Datetime(2004,7,27,0,0,0,0,0), Datetime(2004,7,28,0,0,0,0,0), Datetime(2004,7,29,0,0,0,0,0), Datetime(2004,7,30,0,0,0,0,0), Datetime(2004,8,2,0,0,0,0,0), Datetime(2004,8,3,0,0,0,0,0), Datetime(2004,8,4,0,0,0,0,0), Datetime(2004,8,5,0,0,0,0,0), Datetime(2004,8,6,0,0,0,0,0), Datetime(2004,8,9,0,0,0,0,0), Datetime(2004,8,10,0,0,0,0,0), Datetime(2004,8,11,0,0,0,0,0), Datetime(2004,8,12,0,0,0,0,0), Datetime(2004,8,13,0,0,0,0,0), Datetime(2004,8,16,0,0,0,0,0), Datetime(2004,8,17,0,0,0,0,0), Datetime(2004,8,18,0,0,0,0,0), Datetime(2004,8,19,0,0,0,0,0), Datetime(2004,8,20,0,0,0,0,0), Datetime(2004,8,23,0,0,0,0,0), Datetime(2004,8,24,0,0,0,0,0), Datetime(2004,8,25,0,0,0,0,0), Datetime(2004,8,26,0,0,0,0,0), Datetime(2004,8,27,0,0,0,0,0), Datetime(2004,8,30,0,0,0,0,0), Datetime(2004,8,31,0,0,0,0,0), Datetime(2004,9,1,0,0,0,0,0), Datetime(2004,9,2,0,0,0,0,0), Datetime(2004,9,3,0,0,0,0,0), Datetime(2004,9,6,0,0,0,0,0), Datetime(2004,9,7,0,0,0,0,0), Datetime(2004,9,8,0,0,0,0,0), Datetime(2004,9,9,0,0,0,0,0), Datetime(2004,9,10,0,0,0,0,0), Datetime(2004,9,13,0,0,0,0,0), Datetime(2004,9,14,0,0,0,0,0), Datetime(2004,9,15,0,0,0,0,0), Datetime(2004,9,16,0,0,0,0,0), Datetime(2004,9,17,0,0,0,0,0), Datetime(2004,9,20,0,0,0,0,0), Datetime(2004,9,21,0,0,0,0,0), Datetime(2004,9,22,0,0,0,0,0), Datetime(2004,9,23,0,0,0,0,0), Datetime(2004,9,24,0,0,0,0,0), Datetime(2004,9,27,0,0,0,0,0), Datetime(2004,9,28,0,0,0,0,0), Datetime(2004,9,29,0,0,0,0,0), Datetime(2004,9,30,0,0,0,0,0), Datetime(2004,10,8,0,0,0,0,0), Datetime(2004,10,11,0,0,0,0,0), Datetime(2004,10,12,0,0,0,0,0), Datetime(2004,10,13,0,0,0,0,0), Datetime(2004,10,14,0,0,0,0,0), Datetime(2004,10,15,0,0,0,0,0), Datetime(2004,10,18,0,0,0,0,0), Datetime(2004,10,19,0,0,0,0,0), Datetime(2004,10,20,0,0,0,0,0), Datetime(2004,10,21,0,0,0,0,0), Datetime(2004,10,22,0,0,0,0,0), Datetime(2004,10,25,0,0,0,0,0), Datetime(2004,10,26,0,0,0,0,0), Datetime(2004,10,27,0,0,0,0,0), Datetime(2004,10,28,0,0,0,0,0), Datetime(2004,10,29,0,0,0,0,0), Datetime(2004,11,1,0,0,0,0,0), Datetime(2004,11,2,0,0,0,0,0), Datetime(2004,11,3,0,0,0,0,0), Datetime(2004,11,4,0,0,0,0,0), Datetime(2004,11,5,0,0,0,0,0), Datetime(2004,11,8,0,0,0,0,0), Datetime(2004,11,9,0,0,0,0,0), Datetime(2004,11,10,0,0,0,0,0), Datetime(2004,11,11,0,0,0,0,0), Datetime(2004,11,12,0,0,0,0,0), Datetime(2004,11,15,0,0,0,0,0), Datetime(2004,11,16,0,0,0,0,0), Datetime(2004,11,17,0,0,0,0,0), Datetime(2004,11,18,0,0,0,0,0), Datetime(2004,11,19,0,0,0,0,0), Datetime(2004,11,22,0,0,0,0,0), Datetime(2004,11,23,0,0,0,0,0), Datetime(2004,11,24,0,0,0,0,0), Datetime(2004,11,25,0,0,0,0,0), Datetime(2004,11,26,0,0,0,0,0), Datetime(2004,11,29,0,0,0,0,0), Datetime(2004,11,30,0,0,0,0,0), Datetime(2004,12,1,0,0,0,0,0), Datetime(2004,12,2,0,0,0,0,0), Datetime(2004,12,3,0,0,0,0,0), Datetime(2004,12,6,0,0,0,0,0), Datetime(2004,12,7,0,0,0,0,0), Datetime(2004,12,8,0,0,0,0,0), Datetime(2004,12,9,0,0,0,0,0), Datetime(2004,12,10,0,0,0,0,0), Datetime(2004,12,13,0,0,0,0,0), Datetime(2004,12,14,0,0,0,0,0), Datetime(2004,12,15,0,0,0,0,0), Datetime(2004,12,16,0,0,0,0,0), Datetime(2004,12,17,0,0,0,0,0), Datetime(2004,12,20,0,0,0,0,0), Datetime(2004,12,21,0,0,0,0,0), Datetime(2004,12,22,0,0,0,0,0), Datetime(2004,12,23,0,0,0,0,0), Datetime(2004,12,24,0,0,0,0,0), Datetime(2004,12,27,0,0,0,0,0), Datetime(2004,12,28,0,0,0,0,0), Datetime(2004,12,29,0,0,0,0,0), Datetime(2004,12,30,0,0,0,0,0), Datetime(2004,12,31,0,0,0,0,0), Datetime(2005,1,4,0,0,0,0,0), Datetime(2005,1,5,0,0,0,0,0), Datetime(2005,1,6,0,0,0,0,0), Datetime(2005,1,7,0,0,0,0,0), Datetime(2005,1,10,0,0,0,0,0), Datetime(2005,1,11,0,0,0,0,0), Datetime(2005,1,12,0,0,0,0,0), Datetime(2005,1,13,0,0,0,0,0), Datetime(2005,1,14,0,0,0,0,0), Datetime(2005,1,17,0,0,0,0,0), Datetime(2005,1,18,0,0,0,0,0), Datetime(2005,1,19,0,0,0,0,0), Datetime(2005,1,20,0,0,0,0,0), Datetime(2005,1,21,0,0,0,0,0), Datetime(2005,1,24,0,0,0,0,0), Datetime(2005,1,25,0,0,0,0,0), Datetime(2005,1,26,0,0,0,0,0), Datetime(2005,1,27,0,0,0,0,0), Datetime(2005,1,28,0,0,0,0,0), Datetime(2005,1,31,0,0,0,0,0), Datetime(2005,2,1,0,0,0,0,0), Datetime(2005,2,2,0,0,0,0,0), Datetime(2005,2,3,0,0,0,0,0), Datetime(2005,2,4,0,0,0,0,0), Datetime(2005,2,16,0,0,0,0,0), Datetime(2005,2,17,0,0,0,0,0), Datetime(2005,2,18,0,0,0,0,0), Datetime(2005,2,21,0,0,0,0,0), Datetime(2005,2,22,0,0,0,0,0), Datetime(2005,2,23,0,0,0,0,0), Datetime(2005,2,24,0,0,0,0,0), Datetime(2005,2,25,0,0,0,0,0), Datetime(2005,2,28,0,0,0,0,0), Datetime(2005,3,1,0,0,0,0,0), Datetime(2005,3,2,0,0,0,0,0), Datetime(2005,3,3,0,0,0,0,0), Datetime(2005,3,4,0,0,0,0,0), Datetime(2005,3,7,0,0,0,0,0), Datetime(2005,3,8,0,0,0,0,0), Datetime(2005,3,9,0,0,0,0,0), Datetime(2005,3,10,0,0,0,0,0), Datetime(2005,3,11,0,0,0,0,0), Datetime(2005,3,14,0,0,0,0,0), Datetime(2005,3,15,0,0,0,0,0), Datetime(2005,3,16,0,0,0,0,0), Datetime(2005,3,17,0,0,0,0,0), Datetime(2005,3,18,0,0,0,0,0), Datetime(2005,3,21,0,0,0,0,0), Datetime(2005,3,22,0,0,0,0,0), Datetime(2005,3,23,0,0,0,0,0), Datetime(2005,3,24,0,0,0,0,0), Datetime(2005,3,25,0,0,0,0,0), Datetime(2005,3,28,0,0,0,0,0), Datetime(2005,3,29,0,0,0,0,0), Datetime(2005,3,30,0,0,0,0,0), Datetime(2005,3,31,0,0,0,0,0), Datetime(2005,4,1,0,0,0,0,0), Datetime(2005,4,4,0,0,0,0,0), Datetime(2005,4,5,0,0,0,0,0), Datetime(2005,4,6,0,0,0,0,0), Datetime(2005,4,7,0,0,0,0,0), Datetime(2005,4,8,0,0,0,0,0), Datetime(2005,4,11,0,0,0,0,0), Datetime(2005,4,12,0,0,0,0,0), Datetime(2005,4,13,0,0,0,0,0), Datetime(2005,4,14,0,0,0,0,0), Datetime(2005,4,15,0,0,0,0,0), Datetime(2005,4,18,0,0,0,0,0), Datetime(2005,4,19,0,0,0,0,0), Datetime(2005,4,20,0,0,0,0,0), Datetime(2005,4,21,0,0,0,0,0), Datetime(2005,4,22,0,0,0,0,0), Datetime(2005,4,25,0,0,0,0,0), Datetime(2005,4,26,0,0,0,0,0), Datetime(2005,4,27,0,0,0,0,0), Datetime(2005,4,28,0,0,0,0,0), Datetime(2005,4,29,0,0,0,0,0), Datetime(2005,5,9,0,0,0,0,0), Datetime(2005,5,10,0,0,0,0,0), Datetime(2005,5,11,0,0,0,0,0), Datetime(2005,5,12,0,0,0,0,0), Datetime(2005,5,13,0,0,0,0,0), Datetime(2005,5,16,0,0,0,0,0), Datetime(2005,5,17,0,0,0,0,0), Datetime(2005,5,18,0,0,0,0,0), Datetime(2005,5,19,0,0,0,0,0), Datetime(2005,5,20,0,0,0,0,0), Datetime(2005,5,23,0,0,0,0,0), Datetime(2005,5,24,0,0,0,0,0), Datetime(2005,5,25,0,0,0,0,0), Datetime(2005,5,26,0,0,0,0,0), Datetime(2005,5,27,0,0,0,0,0), Datetime(2005,5,30,0,0,0,0,0), Datetime(2005,5,31,0,0,0,0,0), Datetime(2005,6,1,0,0,0,0,0), Datetime(2005,6,2,0,0,0,0,0), Datetime(2005,6,3,0,0,0,0,0), Datetime(2005,6,6,0,0,0,0,0), Datetime(2005,6,7,0,0,0,0,0), Datetime(2005,6,8,0,0,0,0,0), Datetime(2005,6,9,0,0,0,0,0), Datetime(2005,6,10,0,0,0,0,0), Datetime(2005,6,13,0,0,0,0,0), Datetime(2005,6,14,0,0,0,0,0), Datetime(2005,6,15,0,0,0,0,0), Datetime(2005,6,16,0,0,0,0,0), Datetime(2005,6,17,0,0,0,0,0), Datetime(2005,6,20,0,0,0,0,0), Datetime(2005,6,21,0,0,0,0,0), Datetime(2005,6,22,0,0,0,0,0), Datetime(2005,6,23,0,0,0,0,0), Datetime(2005,6,24,0,0,0,0,0), Datetime(2005,6,27,0,0,0,0,0), Datetime(2005,6,28,0,0,0,0,0), Datetime(2005,6,29,0,0,0,0,0), Datetime(2005,6,30,0,0,0,0,0), Datetime(2005,7,1,0,0,0,0,0), Datetime(2005,7,4,0,0,0,0,0), Datetime(2005,7,5,0,0,0,0,0), Datetime(2005,7,6,0,0,0,0,0), Datetime(2005,7,7,0,0,0,0,0), Datetime(2005,7,8,0,0,0,0,0), Datetime(2005,7,11,0,0,0,0,0), Datetime(2005,7,12,0,0,0,0,0), Datetime(2005,7,13,0,0,0,0,0), Datetime(2005,7,14,0,0,0,0,0), Datetime(2005,7,15,0,0,0,0,0), Datetime(2005,7,18,0,0,0,0,0), Datetime(2005,7,19,0,0,0,0,0), Datetime(2005,7,20,0,0,0,0,0), Datetime(2005,7,21,0,0,0,0,0), Datetime(2005,7,22,0,0,0,0,0), Datetime(2005,7,25,0,0,0,0,0), Datetime(2005,7,26,0,0,0,0,0), Datetime(2005,7,27,0,0,0,0,0), Datetime(2005,7,28,0,0,0,0,0), Datetime(2005,7,29,0,0,0,0,0), Datetime(2005,8,1,0,0,0,0,0), Datetime(2005,8,2,0,0,0,0,0), Datetime(2005,8,3,0,0,0,0,0), Datetime(2005,8,4,0,0,0,0,0), Datetime(2005,8,5,0,0,0,0,0), Datetime(2005,8,8,0,0,0,0,0), Datetime(2005,8,9,0,0,0,0,0), Datetime(2005,8,10,0,0,0,0,0), Datetime(2005,8,11,0,0,0,0,0), Datetime(2005,8,12,0,0,0,0,0), Datetime(2005,8,15,0,0,0,0,0), Datetime(2005,8,16,0,0,0,0,0), Datetime(2005,8,17,0,0,0,0,0), Datetime(2005,8,18,0,0,0,0,0), Datetime(2005,8,19,0,0,0,0,0), Datetime(2005,8,22,0,0,0,0,0), Datetime(2005,8,23,0,0,0,0,0), Datetime(2005,8,24,0,0,0,0,0), Datetime(2005,8,25,0,0,0,0,0), Datetime(2005,8,26,0,0,0,0,0), Datetime(2005,8,29,0,0,0,0,0), Datetime(2005,8,30,0,0,0,0,0), Datetime(2005,8,31,0,0,0,0,0), Datetime(2005,9,1,0,0,0,0,0), Datetime(2005,9,2,0,0,0,0,0), Datetime(2005,9,5,0,0,0,0,0), Datetime(2005,9,6,0,0,0,0,0), Datetime(2005,9,7,0,0,0,0,0), Datetime(2005,9,8,0,0,0,0,0), Datetime(2005,9,9,0,0,0,0,0), Datetime(2005,9,12,0,0,0,0,0), Datetime(2005,9,13,0,0,0,0,0), Datetime(2005,9,14,0,0,0,0,0), Datetime(2005,9,15,0,0,0,0,0), Datetime(2005,9,16,0,0,0,0,0), Datetime(2005,9,19,0,0,0,0,0), Datetime(2005,9,20,0,0,0,0,0), Datetime(2005,9,21,0,0,0,0,0), Datetime(2005,9,22,0,0,0,0,0), Datetime(2005,9,23,0,0,0,0,0), Datetime(2005,9,26,0,0,0,0,0), Datetime(2005,9,27,0,0,0,0,0), Datetime(2005,9,28,0,0,0,0,0), Datetime(2005,9,29,0,0,0,0,0), Datetime(2005,9,30,0,0,0,0,0), Datetime(2005,10,10,0,0,0,0,0), Datetime(2005,10,11,0,0,0,0,0), Datetime(2005,10,12,0,0,0,0,0), Datetime(2005,10,13,0,0,0,0,0), Datetime(2005,10,14,0,0,0,0,0), Datetime(2005,10,17,0,0,0,0,0), Datetime(2005,10,18,0,0,0,0,0), Datetime(2005,10,19,0,0,0,0,0), Datetime(2005,10,20,0,0,0,0,0), Datetime(2005,10,21,0,0,0,0,0), Datetime(2005,10,24,0,0,0,0,0), Datetime(2005,10,25,0,0,0,0,0), Datetime(2005,10,26,0,0,0,0,0), Datetime(2005,10,27,0,0,0,0,0), Datetime(2005,10,28,0,0,0,0,0), Datetime(2005,10,31,0,0,0,0,0), Datetime(2005,11,1,0,0,0,0,0), Datetime(2005,11,2,0,0,0,0,0), Datetime(2005,11,3,0,0,0,0,0), Datetime(2005,11,4,0,0,0,0,0), Datetime(2005,11,7,0,0,0,0,0), Datetime(2005,11,8,0,0,0,0,0), Datetime(2005,11,9,0,0,0,0,0), Datetime(2005,11,10,0,0,0,0,0), Datetime(2005,11,11,0,0,0,0,0), Datetime(2005,11,14,0,0,0,0,0), Datetime(2005,11,15,0,0,0,0,0), Datetime(2005,11,16,0,0,0,0,0), Datetime(2005,11,17,0,0,0,0,0), Datetime(2005,11,18,0,0,0,0,0), Datetime(2005,11,21,0,0,0,0,0), Datetime(2005,11,22,0,0,0,0,0), Datetime(2005,11,23,0,0,0,0,0), Datetime(2005,11,24,0,0,0,0,0), Datetime(2005,11,25,0,0,0,0,0), Datetime(2005,11,28,0,0,0,0,0), Datetime(2005,11,29,0,0,0,0,0), Datetime(2005,11,30,0,0,0,0,0), Datetime(2005,12,1,0,0,0,0,0), Datetime(2005,12,2,0,0,0,0,0), Datetime(2005,12,5,0,0,0,0,0), Datetime(2005,12,6,0,0,0,0,0), Datetime(2005,12,7,0,0,0,0,0), Datetime(2005,12,8,0,0,0,0,0), Datetime(2005,12,9,0,0,0,0,0), Datetime(2005,12,12,0,0,0,0,0), Datetime(2005,12,13,0,0,0,0,0), Datetime(2005,12,14,0,0,0,0,0), Datetime(2005,12,15,0,0,0,0,0), Datetime(2005,12,16,0,0,0,0,0), Datetime(2005,12,19,0,0,0,0,0), Datetime(2005,12,20,0,0,0,0,0), Datetime(2005,12,21,0,0,0,0,0), Datetime(2005,12,22,0,0,0,0,0), Datetime(2005,12,23,0,0,0,0,0), Datetime(2005,12,26,0,0,0,0,0), Datetime(2005,12,27,0,0,0,0,0), Datetime(2005,12,28,0,0,0,0,0), Datetime(2005,12,29,0,0,0,0,0), Datetime(2005,12,30,0,0,0,0,0), Datetime(2006,1,4,0,0,0,0,0), Datetime(2006,1,5,0,0,0,0,0), Datetime(2006,1,6,0,0,0,0,0), Datetime(2006,1,9,0,0,0,0,0), Datetime(2006,1,10,0,0,0,0,0), Datetime(2006,1,11,0,0,0,0,0), Datetime(2006,1,12,0,0,0,0,0), Datetime(2006,1,13,0,0,0,0,0), Datetime(2006,1,16,0,0,0,0,0), Datetime(2006,1,17,0,0,0,0,0), Datetime(2006,1,18,0,0,0,0,0), Datetime(2006,1,19,0,0,0,0,0), Datetime(2006,1,20,0,0,0,0,0), Datetime(2006,1,23,0,0,0,0,0), Datetime(2006,1,24,0,0,0,0,0), Datetime(2006,1,25,0,0,0,0,0), Datetime(2006,2,6,0,0,0,0,0), Datetime(2006,2,7,0,0,0,0,0), Datetime(2006,2,8,0,0,0,0,0), Datetime(2006,2,9,0,0,0,0,0), Datetime(2006,2,10,0,0,0,0,0), Datetime(2006,2,13,0,0,0,0,0), Datetime(2006,2,14,0,0,0,0,0), Datetime(2006,2,15,0,0,0,0,0), Datetime(2006,2,16,0,0,0,0,0), Datetime(2006,2,17,0,0,0,0,0), Datetime(2006,2,20,0,0,0,0,0), Datetime(2006,2,21,0,0,0,0,0), Datetime(2006,2,22,0,0,0,0,0), Datetime(2006,2,23,0,0,0,0,0), Datetime(2006,2,24,0,0,0,0,0), Datetime(2006,2,27,0,0,0,0,0), Datetime(2006,2,28,0,0,0,0,0), Datetime(2006,3,1,0,0,0,0,0), Datetime(2006,3,2,0,0,0,0,0), Datetime(2006,3,3,0,0,0,0,0), Datetime(2006,3,6,0,0,0,0,0), Datetime(2006,3,7,0,0,0,0,0), Datetime(2006,3,8,0,0,0,0,0), Datetime(2006,3,9,0,0,0,0,0), Datetime(2006,3,10,0,0,0,0,0), Datetime(2006,3,13,0,0,0,0,0), Datetime(2006,3,14,0,0,0,0,0), Datetime(2006,3,15,0,0,0,0,0), Datetime(2006,3,16,0,0,0,0,0), Datetime(2006,3,17,0,0,0,0,0), Datetime(2006,3,20,0,0,0,0,0), Datetime(2006,3,21,0,0,0,0,0), Datetime(2006,3,22,0,0,0,0,0), Datetime(2006,3,23,0,0,0,0,0), Datetime(2006,3,24,0,0,0,0,0), Datetime(2006,3,27,0,0,0,0,0), Datetime(2006,3,28,0,0,0,0,0), Datetime(2006,3,29,0,0,0,0,0), Datetime(2006,3,30,0,0,0,0,0), Datetime(2006,3,31,0,0,0,0,0), Datetime(2006,4,3,0,0,0,0,0), Datetime(2006,4,4,0,0,0,0,0), Datetime(2006,4,5,0,0,0,0,0), Datetime(2006,4,6,0,0,0,0,0), Datetime(2006,4,7,0,0,0,0,0), Datetime(2006,4,10,0,0,0,0,0), Datetime(2006,4,11,0,0,0,0,0), Datetime(2006,4,12,0,0,0,0,0), Datetime(2006,4,13,0,0,0,0,0), Datetime(2006,4,14,0,0,0,0,0), Datetime(2006,4,17,0,0,0,0,0), Datetime(2006,4,18,0,0,0,0,0), Datetime(2006,4,19,0,0,0,0,0), Datetime(2006,4,20,0,0,0,0,0), Datetime(2006,4,21,0,0,0,0,0), Datetime(2006,4,24,0,0,0,0,0), Datetime(2006,4,25,0,0,0,0,0), Datetime(2006,4,26,0,0,0,0,0), Datetime(2006,4,27,0,0,0,0,0), Datetime(2006,4,28,0,0,0,0,0), Datetime(2006,5,8,0,0,0,0,0), Datetime(2006,5,9,0,0,0,0,0), Datetime(2006,5,10,0,0,0,0,0), Datetime(2006,5,11,0,0,0,0,0), Datetime(2006,5,12,0,0,0,0,0), Datetime(2006,5,15,0,0,0,0,0), Datetime(2006,5,16,0,0,0,0,0), Datetime(2006,5,17,0,0,0,0,0), Datetime(2006,5,18,0,0,0,0,0), Datetime(2006,5,19,0,0,0,0,0), Datetime(2006,5,22,0,0,0,0,0), Datetime(2006,5,23,0,0,0,0,0), Datetime(2006,5,24,0,0,0,0,0), Datetime(2006,5,25,0,0,0,0,0), Datetime(2006,5,26,0,0,0,0,0), Datetime(2006,5,29,0,0,0,0,0), Datetime(2006,5,30,0,0,0,0,0), Datetime(2006,5,31,0,0,0,0,0), Datetime(2006,6,1,0,0,0,0,0), Datetime(2006,6,2,0,0,0,0,0), Datetime(2006,6,5,0,0,0,0,0), Datetime(2006,6,6,0,0,0,0,0), Datetime(2006,6,7,0,0,0,0,0), Datetime(2006,6,8,0,0,0,0,0), Datetime(2006,6,9,0,0,0,0,0), Datetime(2006,6,12,0,0,0,0,0), Datetime(2006,6,13,0,0,0,0,0), Datetime(2006,6,14,0,0,0,0,0), Datetime(2006,6,15,0,0,0,0,0), Datetime(2006,6,16,0,0,0,0,0), Datetime(2006,6,19,0,0,0,0,0), Datetime(2006,6,20,0,0,0,0,0), Datetime(2006,6,21,0,0,0,0,0), Datetime(2006,6,22,0,0,0,0,0), Datetime(2006,6,23,0,0,0,0,0), Datetime(2006,6,26,0,0,0,0,0), Datetime(2006,6,27,0,0,0,0,0), Datetime(2006,6,28,0,0,0,0,0), Datetime(2006,6,29,0,0,0,0,0), Datetime(2006,6,30,0,0,0,0,0), Datetime(2006,7,3,0,0,0,0,0), Datetime(2006,7,4,0,0,0,0,0), Datetime(2006,7,5,0,0,0,0,0), Datetime(2006,7,6,0,0,0,0,0), Datetime(2006,7,7,0,0,0,0,0), Datetime(2006,7,10,0,0,0,0,0), Datetime(2006,7,11,0,0,0,0,0), Datetime(2006,7,12,0,0,0,0,0), Datetime(2006,7,13,0,0,0,0,0), Datetime(2006,7,14,0,0,0,0,0), Datetime(2006,7,17,0,0,0,0,0), Datetime(2006,7,18,0,0,0,0,0), Datetime(2006,7,19,0,0,0,0,0), Datetime(2006,7,20,0,0,0,0,0), Datetime(2006,7,21,0,0,0,0,0), Datetime(2006,7,24,0,0,0,0,0), Datetime(2006,7,25,0,0,0,0,0), Datetime(2006,7,26,0,0,0,0,0), Datetime(2006,7,27,0,0,0,0,0), Datetime(2006,7,28,0,0,0,0,0), Datetime(2006,7,31,0,0,0,0,0), Datetime(2006,8,1,0,0,0,0,0), Datetime(2006,8,2,0,0,0,0,0), Datetime(2006,8,3,0,0,0,0,0), Datetime(2006,8,4,0,0,0,0,0), Datetime(2006,8,7,0,0,0,0,0), Datetime(2006,8,8,0,0,0,0,0), Datetime(2006,8,9,0,0,0,0,0), Datetime(2006,8,10,0,0,0,0,0), Datetime(2006,8,11,0,0,0,0,0), Datetime(2006,8,14,0,0,0,0,0), Datetime(2006,8,15,0,0,0,0,0), Datetime(2006,8,16,0,0,0,0,0), Datetime(2006,8,17,0,0,0,0,0), Datetime(2006,8,18,0,0,0,0,0), Datetime(2006,8,21,0,0,0,0,0), Datetime(2006,8,22,0,0,0,0,0), Datetime(2006,8,23,0,0,0,0,0), Datetime(2006,8,24,0,0,0,0,0), Datetime(2006,8,25,0,0,0,0,0), Datetime(2006,8,28,0,0,0,0,0), Datetime(2006,8,29,0,0,0,0,0), Datetime(2006,8,30,0,0,0,0,0), Datetime(2006,8,31,0,0,0,0,0), Datetime(2006,9,1,0,0,0,0,0), Datetime(2006,9,4,0,0,0,0,0), Datetime(2006,9,5,0,0,0,0,0), Datetime(2006,9,6,0,0,0,0,0), Datetime(2006,9,7,0,0,0,0,0), Datetime(2006,9,8,0,0,0,0,0), Datetime(2006,9,11,0,0,0,0,0), Datetime(2006,9,12,0,0,0,0,0), Datetime(2006,9,13,0,0,0,0,0), Datetime(2006,9,14,0,0,0,0,0), Datetime(2006,9,15,0,0,0,0,0), Datetime(2006,9,18,0,0,0,0,0), Datetime(2006,9,19,0,0,0,0,0), Datetime(2006,9,20,0,0,0,0,0), Datetime(2006,9,21,0,0,0,0,0), Datetime(2006,9,22,0,0,0,0,0), Datetime(2006,9,25,0,0,0,0,0), Datetime(2006,9,26,0,0,0,0,0), Datetime(2006,9,27,0,0,0,0,0), Datetime(2006,9,28,0,0,0,0,0), Datetime(2006,9,29,0,0,0,0,0), Datetime(2006,10,9,0,0,0,0,0), Datetime(2006,10,10,0,0,0,0,0), Datetime(2006,10,11,0,0,0,0,0), Datetime(2006,10,12,0,0,0,0,0), Datetime(2006,10,13,0,0,0,0,0), Datetime(2006,10,16,0,0,0,0,0), Datetime(2006,10,17,0,0,0,0,0), Datetime(2006,10,18,0,0,0,0,0), Datetime(2006,10,19,0,0,0,0,0), Datetime(2006,10,20,0,0,0,0,0), Datetime(2006,10,23,0,0,0,0,0), Datetime(2006,10,24,0,0,0,0,0), Datetime(2006,10,25,0,0,0,0,0), Datetime(2006,10,26,0,0,0,0,0), Datetime(2006,10,27,0,0,0,0,0), Datetime(2006,10,30,0,0,0,0,0), Datetime(2006,10,31,0,0,0,0,0), Datetime(2006,11,1,0,0,0,0,0), Datetime(2006,11,2,0,0,0,0,0), Datetime(2006,11,3,0,0,0,0,0), Datetime(2006,11,6,0,0,0,0,0), Datetime(2006,11,7,0,0,0,0,0), Datetime(2006,11,8,0,0,0,0,0), Datetime(2006,11,9,0,0,0,0,0), Datetime(2006,11,10,0,0,0,0,0), Datetime(2006,11,13,0,0,0,0,0), Datetime(2006,11,14,0,0,0,0,0), Datetime(2006,11,15,0,0,0,0,0), Datetime(2006,11,16,0,0,0,0,0), Datetime(2006,11,17,0,0,0,0,0), Datetime(2006,11,20,0,0,0,0,0), Datetime(2006,11,21,0,0,0,0,0), Datetime(2006,11,22,0,0,0,0,0), Datetime(2006,11,23,0,0,0,0,0), Datetime(2006,11,24,0,0,0,0,0), Datetime(2006,11,27,0,0,0,0,0), Datetime(2006,11,28,0,0,0,0,0), Datetime(2006,11,29,0,0,0,0,0), Datetime(2006,11,30,0,0,0,0,0), Datetime(2006,12,1,0,0,0,0,0), Datetime(2006,12,4,0,0,0,0,0), Datetime(2006,12,5,0,0,0,0,0), Datetime(2006,12,6,0,0,0,0,0), Datetime(2006,12,7,0,0,0,0,0), Datetime(2006,12,8,0,0,0,0,0), Datetime(2006,12,11,0,0,0,0,0), Datetime(2006,12,12,0,0,0,0,0), Datetime(2006,12,13,0,0,0,0,0), Datetime(2006,12,14,0,0,0,0,0), Datetime(2006,12,15,0,0,0,0,0), Datetime(2006,12,18,0,0,0,0,0), Datetime(2006,12,19,0,0,0,0,0), Datetime(2006,12,20,0,0,0,0,0), Datetime(2006,12,21,0,0,0,0,0), Datetime(2006,12,22,0,0,0,0,0), Datetime(2006,12,25,0,0,0,0,0), Datetime(2006,12,26,0,0,0,0,0), Datetime(2006,12,27,0,0,0,0,0), Datetime(2006,12,28,0,0,0,0,0), Datetime(2006,12,29,0,0,0,0,0), Datetime(2007,1,4,0,0,0,0,0), Datetime(2007,1,5,0,0,0,0,0), Datetime(2007,1,8,0,0,0,0,0), Datetime(2007,1,9,0,0,0,0,0), Datetime(2007,1,10,0,0,0,0,0), Datetime(2007,1,11,0,0,0,0,0), Datetime(2007,1,12,0,0,0,0,0), Datetime(2007,1,15,0,0,0,0,0), Datetime(2007,1,16,0,0,0,0,0), Datetime(2007,1,17,0,0,0,0,0), Datetime(2007,1,18,0,0,0,0,0), Datetime(2007,1,19,0,0,0,0,0), Datetime(2007,1,22,0,0,0,0,0), Datetime(2007,1,23,0,0,0,0,0), Datetime(2007,1,24,0,0,0,0,0), Datetime(2007,1,25,0,0,0,0,0), Datetime(2007,1,26,0,0,0,0,0), Datetime(2007,1,29,0,0,0,0,0), Datetime(2007,1,30,0,0,0,0,0), Datetime(2007,1,31,0,0,0,0,0), Datetime(2007,2,1,0,0,0,0,0), Datetime(2007,2,2,0,0,0,0,0), Datetime(2007,2,5,0,0,0,0,0), Datetime(2007,2,6,0,0,0,0,0), Datetime(2007,2,7,0,0,0,0,0), Datetime(2007,2,8,0,0,0,0,0), Datetime(2007,2,9,0,0,0,0,0), Datetime(2007,2,12,0,0,0,0,0), Datetime(2007,2,13,0,0,0,0,0), Datetime(2007,2,14,0,0,0,0,0), Datetime(2007,2,15,0,0,0,0,0), Datetime(2007,2,16,0,0,0,0,0), Datetime(2007,2,26,0,0,0,0,0), Datetime(2007,2,27,0,0,0,0,0), Datetime(2007,2,28,0,0,0,0,0), Datetime(2007,3,1,0,0,0,0,0), Datetime(2007,3,2,0,0,0,0,0), Datetime(2007,3,5,0,0,0,0,0), Datetime(2007,3,6,0,0,0,0,0), Datetime(2007,3,7,0,0,0,0,0), Datetime(2007,3,8,0,0,0,0,0), Datetime(2007,3,9,0,0,0,0,0), Datetime(2007,3,12,0,0,0,0,0), Datetime(2007,3,13,0,0,0,0,0), Datetime(2007,3,14,0,0,0,0,0), Datetime(2007,3,15,0,0,0,0,0), Datetime(2007,3,16,0,0,0,0,0), Datetime(2007,3,19,0,0,0,0,0), Datetime(2007,3,20,0,0,0,0,0), Datetime(2007,3,21,0,0,0,0,0), Datetime(2007,3,22,0,0,0,0,0), Datetime(2007,3,23,0,0,0,0,0), Datetime(2007,3,26,0,0,0,0,0), Datetime(2007,3,27,0,0,0,0,0), Datetime(2007,3,28,0,0,0,0,0), Datetime(2007,3,29,0,0,0,0,0), Datetime(2007,3,30,0,0,0,0,0), Datetime(2007,4,2,0,0,0,0,0), Datetime(2007,4,3,0,0,0,0,0), Datetime(2007,4,4,0,0,0,0,0), Datetime(2007,4,5,0,0,0,0,0), Datetime(2007,4,6,0,0,0,0,0), Datetime(2007,4,9,0,0,0,0,0), Datetime(2007,4,10,0,0,0,0,0), Datetime(2007,4,11,0,0,0,0,0), Datetime(2007,4,12,0,0,0,0,0), Datetime(2007,4,13,0,0,0,0,0), Datetime(2007,4,16,0,0,0,0,0), Datetime(2007,4,17,0,0,0,0,0), Datetime(2007,4,18,0,0,0,0,0), Datetime(2007,4,19,0,0,0,0,0), Datetime(2007,4,20,0,0,0,0,0), Datetime(2007,4,23,0,0,0,0,0), Datetime(2007,4,24,0,0,0,0,0), Datetime(2007,4,25,0,0,0,0,0), Datetime(2007,4,26,0,0,0,0,0), Datetime(2007,4,27,0,0,0,0,0), Datetime(2007,4,30,0,0,0,0,0), Datetime(2007,5,8,0,0,0,0,0), Datetime(2007,5,9,0,0,0,0,0), Datetime(2007,5,10,0,0,0,0,0), Datetime(2007,5,11,0,0,0,0,0), Datetime(2007,5,14,0,0,0,0,0), Datetime(2007,5,15,0,0,0,0,0), Datetime(2007,5,16,0,0,0,0,0), Datetime(2007,5,17,0,0,0,0,0), Datetime(2007,5,18,0,0,0,0,0), Datetime(2007,5,21,0,0,0,0,0), Datetime(2007,5,22,0,0,0,0,0), Datetime(2007,5,23,0,0,0,0,0), Datetime(2007,5,24,0,0,0,0,0), Datetime(2007,5,25,0,0,0,0,0), Datetime(2007,5,28,0,0,0,0,0), Datetime(2007,5,29,0,0,0,0,0), Datetime(2007,5,30,0,0,0,0,0), Datetime(2007,5,31,0,0,0,0,0), Datetime(2007,6,1,0,0,0,0,0), Datetime(2007,6,4,0,0,0,0,0), Datetime(2007,6,5,0,0,0,0,0), Datetime(2007,6,6,0,0,0,0,0), Datetime(2007,6,7,0,0,0,0,0), Datetime(2007,6,8,0,0,0,0,0), Datetime(2007,6,11,0,0,0,0,0), Datetime(2007,6,12,0,0,0,0,0), Datetime(2007,6,13,0,0,0,0,0), Datetime(2007,6,14,0,0,0,0,0), Datetime(2007,6,15,0,0,0,0,0), Datetime(2007,6,18,0,0,0,0,0), Datetime(2007,6,19,0,0,0,0,0), Datetime(2007,6,20,0,0,0,0,0), Datetime(2007,6,21,0,0,0,0,0), Datetime(2007,6,22,0,0,0,0,0), Datetime(2007,6,25,0,0,0,0,0), Datetime(2007,6,26,0,0,0,0,0), Datetime(2007,6,27,0,0,0,0,0), Datetime(2007,6,28,0,0,0,0,0), Datetime(2007,6,29,0,0,0,0,0), Datetime(2007,7,2,0,0,0,0,0), Datetime(2007,7,3,0,0,0,0,0), Datetime(2007,7,4,0,0,0,0,0), Datetime(2007,7,5,0,0,0,0,0), Datetime(2007,7,6,0,0,0,0,0), Datetime(2007,7,9,0,0,0,0,0), Datetime(2007,7,10,0,0,0,0,0), Datetime(2007,7,11,0,0,0,0,0), Datetime(2007,7,12,0,0,0,0,0), Datetime(2007,7,13,0,0,0,0,0), Datetime(2007,7,16,0,0,0,0,0), Datetime(2007,7,17,0,0,0,0,0), Datetime(2007,7,18,0,0,0,0,0), Datetime(2007,7,19,0,0,0,0,0), Datetime(2007,7,20,0,0,0,0,0), Datetime(2007,7,23,0,0,0,0,0), Datetime(2007,7,24,0,0,0,0,0), Datetime(2007,7,25,0,0,0,0,0), Datetime(2007,7,26,0,0,0,0,0), Datetime(2007,7,27,0,0,0,0,0), Datetime(2007,7,30,0,0,0,0,0), Datetime(2007,7,31,0,0,0,0,0), Datetime(2007,8,1,0,0,0,0,0), Datetime(2007,8,2,0,0,0,0,0), Datetime(2007,8,3,0,0,0,0,0), Datetime(2007,8,6,0,0,0,0,0), Datetime(2007,8,7,0,0,0,0,0), Datetime(2007,8,8,0,0,0,0,0), Datetime(2007,8,9,0,0,0,0,0), Datetime(2007,8,10,0,0,0,0,0), Datetime(2007,8,13,0,0,0,0,0), Datetime(2007,8,14,0,0,0,0,0), Datetime(2007,8,15,0,0,0,0,0), Datetime(2007,8,16,0,0,0,0,0), Datetime(2007,8,17,0,0,0,0,0), Datetime(2007,8,20,0,0,0,0,0), Datetime(2007,8,21,0,0,0,0,0), Datetime(2007,8,22,0,0,0,0,0), Datetime(2007,8,23,0,0,0,0,0), Datetime(2007,8,24,0,0,0,0,0), Datetime(2007,8,27,0,0,0,0,0), Datetime(2007,8,28,0,0,0,0,0), Datetime(2007,8,29,0,0,0,0,0), Datetime(2007,8,30,0,0,0,0,0), Datetime(2007,8,31,0,0,0,0,0), Datetime(2007,9,3,0,0,0,0,0), Datetime(2007,9,4,0,0,0,0,0), Datetime(2007,9,5,0,0,0,0,0), Datetime(2007,9,6,0,0,0,0,0), Datetime(2007,9,7,0,0,0,0,0), Datetime(2007,9,10,0,0,0,0,0), Datetime(2007,9,11,0,0,0,0,0), Datetime(2007,9,12,0,0,0,0,0), Datetime(2007,9,13,0,0,0,0,0), Datetime(2007,9,14,0,0,0,0,0), Datetime(2007,9,17,0,0,0,0,0), Datetime(2007,9,18,0,0,0,0,0), Datetime(2007,9,19,0,0,0,0,0), Datetime(2007,9,20,0,0,0,0,0), Datetime(2007,9,21,0,0,0,0,0), Datetime(2007,9,24,0,0,0,0,0), Datetime(2007,9,25,0,0,0,0,0), Datetime(2007,9,26,0,0,0,0,0), Datetime(2007,9,27,0,0,0,0,0), Datetime(2007,9,28,0,0,0,0,0), Datetime(2007,10,8,0,0,0,0,0), Datetime(2007,10,9,0,0,0,0,0), Datetime(2007,10,10,0,0,0,0,0), Datetime(2007,10,11,0,0,0,0,0), Datetime(2007,10,12,0,0,0,0,0), Datetime(2007,10,15,0,0,0,0,0), Datetime(2007,10,16,0,0,0,0,0), Datetime(2007,10,17,0,0,0,0,0), Datetime(2007,10,18,0,0,0,0,0), Datetime(2007,10,19,0,0,0,0,0), Datetime(2007,10,22,0,0,0,0,0), Datetime(2007,10,23,0,0,0,0,0), Datetime(2007,10,24,0,0,0,0,0), Datetime(2007,10,25,0,0,0,0,0), Datetime(2007,10,26,0,0,0,0,0), Datetime(2007,10,29,0,0,0,0,0), Datetime(2007,10,30,0,0,0,0,0), Datetime(2007,10,31,0,0,0,0,0), Datetime(2007,11,1,0,0,0,0,0), Datetime(2007,11,2,0,0,0,0,0), Datetime(2007,11,5,0,0,0,0,0), Datetime(2007,11,6,0,0,0,0,0), Datetime(2007,11,7,0,0,0,0,0), Datetime(2007,11,8,0,0,0,0,0), Datetime(2007,11,9,0,0,0,0,0), Datetime(2007,11,12,0,0,0,0,0), Datetime(2007,11,13,0,0,0,0,0), Datetime(2007,11,14,0,0,0,0,0), Datetime(2007,11,15,0,0,0,0,0), Datetime(2007,11,16,0,0,0,0,0), Datetime(2007,11,19,0,0,0,0,0), Datetime(2007,11,20,0,0,0,0,0), Datetime(2007,11,21,0,0,0,0,0), Datetime(2007,11,22,0,0,0,0,0), Datetime(2007,11,23,0,0,0,0,0), Datetime(2007,11,26,0,0,0,0,0), Datetime(2007,11,27,0,0,0,0,0), Datetime(2007,11,28,0,0,0,0,0), Datetime(2007,11,29,0,0,0,0,0), Datetime(2007,11,30,0,0,0,0,0), Datetime(2007,12,3,0,0,0,0,0), Datetime(2007,12,4,0,0,0,0,0), Datetime(2007,12,5,0,0,0,0,0), Datetime(2007,12,6,0,0,0,0,0), Datetime(2007,12,7,0,0,0,0,0), Datetime(2007,12,10,0,0,0,0,0), Datetime(2007,12,11,0,0,0,0,0), Datetime(2007,12,12,0,0,0,0,0), Datetime(2007,12,13,0,0,0,0,0), Datetime(2007,12,14,0,0,0,0,0), Datetime(2007,12,17,0,0,0,0,0), Datetime(2007,12,18,0,0,0,0,0), Datetime(2007,12,19,0,0,0,0,0), Datetime(2007,12,20,0,0,0,0,0), Datetime(2007,12,21,0,0,0,0,0), Datetime(2007,12,24,0,0,0,0,0), Datetime(2007,12,25,0,0,0,0,0), Datetime(2007,12,26,0,0,0,0,0), Datetime(2007,12,27,0,0,0,0,0), Datetime(2007,12,28,0,0,0,0,0), Datetime(2008,1,2,0,0,0,0,0), Datetime(2008,1,3,0,0,0,0,0), Datetime(2008,1,4,0,0,0,0,0), Datetime(2008,1,7,0,0,0,0,0), Datetime(2008,1,8,0,0,0,0,0), Datetime(2008,1,9,0,0,0,0,0), Datetime(2008,1,10,0,0,0,0,0), Datetime(2008,1,11,0,0,0,0,0), Datetime(2008,1,14,0,0,0,0,0), Datetime(2008,1,15,0,0,0,0,0), Datetime(2008,1,16,0,0,0,0,0), Datetime(2008,1,17,0,0,0,0,0), Datetime(2008,1,18,0,0,0,0,0), Datetime(2008,1,21,0,0,0,0,0), Datetime(2008,1,22,0,0,0,0,0), Datetime(2008,1,23,0,0,0,0,0), Datetime(2008,1,24,0,0,0,0,0), Datetime(2008,1,25,0,0,0,0,0), Datetime(2008,1,28,0,0,0,0,0), Datetime(2008,1,29,0,0,0,0,0), Datetime(2008,1,30,0,0,0,0,0), Datetime(2008,1,31,0,0,0,0,0), Datetime(2008,2,1,0,0,0,0,0), Datetime(2008,2,4,0,0,0,0,0), Datetime(2008,2,5,0,0,0,0,0), Datetime(2008,2,13,0,0,0,0,0), Datetime(2008,2,14,0,0,0,0,0), Datetime(2008,2,15,0,0,0,0,0), Datetime(2008,2,18,0,0,0,0,0), Datetime(2008,2,19,0,0,0,0,0), Datetime(2008,2,20,0,0,0,0,0), Datetime(2008,2,21,0,0,0,0,0), Datetime(2008,2,22,0,0,0,0,0), Datetime(2008,2,25,0,0,0,0,0), Datetime(2008,2,26,0,0,0,0,0), Datetime(2008,2,27,0,0,0,0,0), Datetime(2008,2,28,0,0,0,0,0), Datetime(2008,2,29,0,0,0,0,0), Datetime(2008,3,3,0,0,0,0,0), Datetime(2008,3,4,0,0,0,0,0), Datetime(2008,3,5,0,0,0,0,0), Datetime(2008,3,6,0,0,0,0,0), Datetime(2008,3,7,0,0,0,0,0), Datetime(2008,3,10,0,0,0,0,0), Datetime(2008,3,11,0,0,0,0,0), Datetime(2008,3,12,0,0,0,0,0), Datetime(2008,3,13,0,0,0,0,0), Datetime(2008,3,14,0,0,0,0,0), Datetime(2008,3,17,0,0,0,0,0), Datetime(2008,3,18,0,0,0,0,0), Datetime(2008,3,19,0,0,0,0,0), Datetime(2008,3,20,0,0,0,0,0), Datetime(2008,3,21,0,0,0,0,0), Datetime(2008,3,24,0,0,0,0,0), Datetime(2008,3,25,0,0,0,0,0), Datetime(2008,3,26,0,0,0,0,0), Datetime(2008,3,27,0,0,0,0,0), Datetime(2008,3,28,0,0,0,0,0), Datetime(2008,3,31,0,0,0,0,0), Datetime(2008,4,1,0,0,0,0,0), Datetime(2008,4,2,0,0,0,0,0), Datetime(2008,4,3,0,0,0,0,0), Datetime(2008,4,7,0,0,0,0,0), Datetime(2008,4,8,0,0,0,0,0), Datetime(2008,4,9,0,0,0,0,0), Datetime(2008,4,10,0,0,0,0,0), Datetime(2008,4,11,0,0,0,0,0), Datetime(2008,4,14,0,0,0,0,0), Datetime(2008,4,15,0,0,0,0,0), Datetime(2008,4,16,0,0,0,0,0), Datetime(2008,4,17,0,0,0,0,0), Datetime(2008,4,18,0,0,0,0,0), Datetime(2008,4,21,0,0,0,0,0), Datetime(2008,4,22,0,0,0,0,0), Datetime(2008,4,23,0,0,0,0,0), Datetime(2008,4,24,0,0,0,0,0), Datetime(2008,4,25,0,0,0,0,0), Datetime(2008,4,28,0,0,0,0,0), Datetime(2008,4,29,0,0,0,0,0), Datetime(2008,4,30,0,0,0,0,0), Datetime(2008,5,5,0,0,0,0,0), Datetime(2008,5,6,0,0,0,0,0), Datetime(2008,5,7,0,0,0,0,0), Datetime(2008,5,8,0,0,0,0,0), Datetime(2008,5,9,0,0,0,0,0), Datetime(2008,5,12,0,0,0,0,0), Datetime(2008,5,13,0,0,0,0,0), Datetime(2008,5,14,0,0,0,0,0), Datetime(2008,5,15,0,0,0,0,0), Datetime(2008,5,16,0,0,0,0,0), Datetime(2008,5,19,0,0,0,0,0), Datetime(2008,5,20,0,0,0,0,0), Datetime(2008,5,21,0,0,0,0,0), Datetime(2008,5,22,0,0,0,0,0), Datetime(2008,5,23,0,0,0,0,0), Datetime(2008,5,26,0,0,0,0,0), Datetime(2008,5,27,0,0,0,0,0), Datetime(2008,5,28,0,0,0,0,0), Datetime(2008,5,29,0,0,0,0,0), Datetime(2008,5,30,0,0,0,0,0), Datetime(2008,6,2,0,0,0,0,0), Datetime(2008,6,3,0,0,0,0,0), Datetime(2008,6,4,0,0,0,0,0), Datetime(2008,6,5,0,0,0,0,0), Datetime(2008,6,6,0,0,0,0,0), Datetime(2008,6,10,0,0,0,0,0), Datetime(2008,6,11,0,0,0,0,0), Datetime(2008,6,12,0,0,0,0,0), Datetime(2008,6,13,0,0,0,0,0), Datetime(2008,6,16,0,0,0,0,0), Datetime(2008,6,17,0,0,0,0,0), Datetime(2008,6,18,0,0,0,0,0), Datetime(2008,6,19,0,0,0,0,0), Datetime(2008,6,20,0,0,0,0,0), Datetime(2008,6,23,0,0,0,0,0), Datetime(2008,6,24,0,0,0,0,0), Datetime(2008,6,25,0,0,0,0,0), Datetime(2008,6,26,0,0,0,0,0), Datetime(2008,6,27,0,0,0,0,0), Datetime(2008,6,30,0,0,0,0,0), Datetime(2008,7,1,0,0,0,0,0), Datetime(2008,7,2,0,0,0,0,0), Datetime(2008,7,3,0,0,0,0,0), Datetime(2008,7,4,0,0,0,0,0), Datetime(2008,7,7,0,0,0,0,0), Datetime(2008,7,8,0,0,0,0,0), Datetime(2008,7,9,0,0,0,0,0), Datetime(2008,7,10,0,0,0,0,0), Datetime(2008,7,11,0,0,0,0,0), Datetime(2008,7,14,0,0,0,0,0), Datetime(2008,7,15,0,0,0,0,0), Datetime(2008,7,16,0,0,0,0,0), Datetime(2008,7,17,0,0,0,0,0), Datetime(2008,7,18,0,0,0,0,0), Datetime(2008,7,21,0,0,0,0,0), Datetime(2008,7,22,0,0,0,0,0), Datetime(2008,7,23,0,0,0,0,0), Datetime(2008,7,24,0,0,0,0,0), Datetime(2008,7,25,0,0,0,0,0), Datetime(2008,7,28,0,0,0,0,0), Datetime(2008,7,29,0,0,0,0,0), Datetime(2008,7,30,0,0,0,0,0), Datetime(2008,7,31,0,0,0,0,0), Datetime(2008,8,1,0,0,0,0,0), Datetime(2008,8,4,0,0,0,0,0), Datetime(2008,8,5,0,0,0,0,0), Datetime(2008,8,6,0,0,0,0,0), Datetime(2008,8,7,0,0,0,0,0), Datetime(2008,8,8,0,0,0,0,0), Datetime(2008,8,11,0,0,0,0,0), Datetime(2008,8,12,0,0,0,0,0), Datetime(2008,8,13,0,0,0,0,0), Datetime(2008,8,14,0,0,0,0,0), Datetime(2008,8,15,0,0,0,0,0), Datetime(2008,8,18,0,0,0,0,0), Datetime(2008,8,19,0,0,0,0,0), Datetime(2008,8,20,0,0,0,0,0), Datetime(2008,8,21,0,0,0,0,0), Datetime(2008,8,22,0,0,0,0,0), Datetime(2008,8,25,0,0,0,0,0), Datetime(2008,8,26,0,0,0,0,0), Datetime(2008,8,27,0,0,0,0,0), Datetime(2008,8,28,0,0,0,0,0), Datetime(2008,8,29,0,0,0,0,0), Datetime(2008,9,1,0,0,0,0,0), Datetime(2008,9,2,0,0,0,0,0), Datetime(2008,9,3,0,0,0,0,0), Datetime(2008,9,4,0,0,0,0,0), Datetime(2008,9,5,0,0,0,0,0), Datetime(2008,9,8,0,0,0,0,0), Datetime(2008,9,9,0,0,0,0,0), Datetime(2008,9,10,0,0,0,0,0), Datetime(2008,9,11,0,0,0,0,0), Datetime(2008,9,12,0,0,0,0,0), Datetime(2008,9,16,0,0,0,0,0), Datetime(2008,9,17,0,0,0,0,0), Datetime(2008,9,18,0,0,0,0,0), Datetime(2008,9,19,0,0,0,0,0), Datetime(2008,9,22,0,0,0,0,0), Datetime(2008,9,23,0,0,0,0,0), Datetime(2008,9,24,0,0,0,0,0), Datetime(2008,9,25,0,0,0,0,0), Datetime(2008,9,26,0,0,0,0,0), Datetime(2008,10,6,0,0,0,0,0), Datetime(2008,10,7,0,0,0,0,0), Datetime(2008,10,8,0,0,0,0,0), Datetime(2008,10,9,0,0,0,0,0), Datetime(2008,10,10,0,0,0,0,0), Datetime(2008,10,13,0,0,0,0,0), Datetime(2008,10,14,0,0,0,0,0), Datetime(2008,10,15,0,0,0,0,0), Datetime(2008,10,16,0,0,0,0,0), Datetime(2008,10,17,0,0,0,0,0), Datetime(2008,10,20,0,0,0,0,0), Datetime(2008,10,21,0,0,0,0,0), Datetime(2008,10,22,0,0,0,0,0), Datetime(2008,10,23,0,0,0,0,0), Datetime(2008,10,24,0,0,0,0,0), Datetime(2008,10,27,0,0,0,0,0), Datetime(2008,10,28,0,0,0,0,0), Datetime(2008,10,29,0,0,0,0,0), Datetime(2008,10,30,0,0,0,0,0), Datetime(2008,10,31,0,0,0,0,0), Datetime(2008,11,3,0,0,0,0,0), Datetime(2008,11,4,0,0,0,0,0), Datetime(2008,11,5,0,0,0,0,0), Datetime(2008,11,6,0,0,0,0,0), Datetime(2008,11,7,0,0,0,0,0), Datetime(2008,11,10,0,0,0,0,0), Datetime(2008,11,11,0,0,0,0,0), Datetime(2008,11,12,0,0,0,0,0), Datetime(2008,11,13,0,0,0,0,0), Datetime(2008,11,14,0,0,0,0,0), Datetime(2008,11,17,0,0,0,0,0), Datetime(2008,11,18,0,0,0,0,0), Datetime(2008,11,19,0,0,0,0,0), Datetime(2008,11,20,0,0,0,0,0), Datetime(2008,11,21,0,0,0,0,0), Datetime(2008,11,24,0,0,0,0,0), Datetime(2008,11,25,0,0,0,0,0), Datetime(2008,11,26,0,0,0,0,0), Datetime(2008,11,27,0,0,0,0,0), Datetime(2008,11,28,0,0,0,0,0), Datetime(2008,12,1,0,0,0,0,0), Datetime(2008,12,2,0,0,0,0,0), Datetime(2008,12,3,0,0,0,0,0), Datetime(2008,12,4,0,0,0,0,0), Datetime(2008,12,5,0,0,0,0,0), Datetime(2008,12,8,0,0,0,0,0), Datetime(2008,12,9,0,0,0,0,0), Datetime(2008,12,10,0,0,0,0,0), Datetime(2008,12,11,0,0,0,0,0), Datetime(2008,12,12,0,0,0,0,0), Datetime(2008,12,15,0,0,0,0,0), Datetime(2008,12,16,0,0,0,0,0), Datetime(2008,12,17,0,0,0,0,0), Datetime(2008,12,18,0,0,0,0,0), Datetime(2008,12,19,0,0,0,0,0), Datetime(2008,12,22,0,0,0,0,0), Datetime(2008,12,23,0,0,0,0,0), Datetime(2008,12,24,0,0,0,0,0), Datetime(2008,12,25,0,0,0,0,0), Datetime(2008,12,26,0,0,0,0,0), Datetime(2008,12,29,0,0,0,0,0), Datetime(2008,12,30,0,0,0,0,0), Datetime(2008,12,31,0,0,0,0,0), Datetime(2009,1,5,0,0,0,0,0), Datetime(2009,1,6,0,0,0,0,0), Datetime(2009,1,7,0,0,0,0,0), Datetime(2009,1,8,0,0,0,0,0), Datetime(2009,1,9,0,0,0,0,0), Datetime(2009,1,12,0,0,0,0,0), Datetime(2009,1,13,0,0,0,0,0), Datetime(2009,1,14,0,0,0,0,0), Datetime(2009,1,15,0,0,0,0,0), Datetime(2009,1,16,0,0,0,0,0), Datetime(2009,1,19,0,0,0,0,0), Datetime(2009,1,20,0,0,0,0,0), Datetime(2009,1,21,0,0,0,0,0), Datetime(2009,1,22,0,0,0,0,0), Datetime(2009,1,23,0,0,0,0,0), Datetime(2009,2,2,0,0,0,0,0), Datetime(2009,2,3,0,0,0,0,0), Datetime(2009,2,4,0,0,0,0,0), Datetime(2009,2,5,0,0,0,0,0), Datetime(2009,2,6,0,0,0,0,0), Datetime(2009,2,9,0,0,0,0,0), Datetime(2009,2,10,0,0,0,0,0), Datetime(2009,2,11,0,0,0,0,0), Datetime(2009,2,12,0,0,0,0,0), Datetime(2009,2,13,0,0,0,0,0), Datetime(2009,2,16,0,0,0,0,0), Datetime(2009,2,17,0,0,0,0,0), Datetime(2009,2,18,0,0,0,0,0), Datetime(2009,2,19,0,0,0,0,0), Datetime(2009,2,20,0,0,0,0,0), Datetime(2009,2,23,0,0,0,0,0), Datetime(2009,2,24,0,0,0,0,0), Datetime(2009,2,25,0,0,0,0,0), Datetime(2009,2,26,0,0,0,0,0), Datetime(2009,2,27,0,0,0,0,0), Datetime(2009,3,2,0,0,0,0,0), Datetime(2009,3,3,0,0,0,0,0), Datetime(2009,3,4,0,0,0,0,0), Datetime(2009,3,5,0,0,0,0,0), Datetime(2009,3,6,0,0,0,0,0), Datetime(2009,3,9,0,0,0,0,0), Datetime(2009,3,10,0,0,0,0,0), Datetime(2009,3,11,0,0,0,0,0), Datetime(2009,3,12,0,0,0,0,0), Datetime(2009,3,13,0,0,0,0,0), Datetime(2009,3,16,0,0,0,0,0), Datetime(2009,3,17,0,0,0,0,0), Datetime(2009,3,18,0,0,0,0,0), Datetime(2009,3,19,0,0,0,0,0), Datetime(2009,3,20,0,0,0,0,0), Datetime(2009,3,23,0,0,0,0,0), Datetime(2009,3,24,0,0,0,0,0), Datetime(2009,3,25,0,0,0,0,0), Datetime(2009,3,26,0,0,0,0,0), Datetime(2009,3,27,0,0,0,0,0), Datetime(2009,3,30,0,0,0,0,0), Datetime(2009,3,31,0,0,0,0,0), Datetime(2009,4,1,0,0,0,0,0), Datetime(2009,4,2,0,0,0,0,0), Datetime(2009,4,3,0,0,0,0,0), Datetime(2009,4,7,0,0,0,0,0), Datetime(2009,4,8,0,0,0,0,0), Datetime(2009,4,9,0,0,0,0,0), Datetime(2009,4,10,0,0,0,0,0), Datetime(2009,4,13,0,0,0,0,0), Datetime(2009,4,14,0,0,0,0,0), Datetime(2009,4,15,0,0,0,0,0), Datetime(2009,4,16,0,0,0,0,0), Datetime(2009,4,17,0,0,0,0,0), Datetime(2009,4,20,0,0,0,0,0), Datetime(2009,4,21,0,0,0,0,0), Datetime(2009,4,22,0,0,0,0,0), Datetime(2009,4,23,0,0,0,0,0), Datetime(2009,4,24,0,0,0,0,0), Datetime(2009,4,27,0,0,0,0,0), Datetime(2009,4,28,0,0,0,0,0), Datetime(2009,4,29,0,0,0,0,0), Datetime(2009,4,30,0,0,0,0,0), Datetime(2009,5,4,0,0,0,0,0), Datetime(2009,5,5,0,0,0,0,0), Datetime(2009,5,6,0,0,0,0,0), Datetime(2009,5,7,0,0,0,0,0), Datetime(2009,5,8,0,0,0,0,0), Datetime(2009,5,11,0,0,0,0,0), Datetime(2009,5,12,0,0,0,0,0), Datetime(2009,5,13,0,0,0,0,0), Datetime(2009,5,14,0,0,0,0,0), Datetime(2009,5,15,0,0,0,0,0), Datetime(2009,5,18,0,0,0,0,0), Datetime(2009,5,19,0,0,0,0,0), Datetime(2009,5,20,0,0,0,0,0), Datetime(2009,5,21,0,0,0,0,0), Datetime(2009,5,22,0,0,0,0,0), Datetime(2009,5,25,0,0,0,0,0), Datetime(2009,5,26,0,0,0,0,0), Datetime(2009,5,27,0,0,0,0,0), Datetime(2009,6,1,0,0,0,0,0), Datetime(2009,6,2,0,0,0,0,0), Datetime(2009,6,3,0,0,0,0,0), Datetime(2009,6,4,0,0,0,0,0), Datetime(2009,6,5,0,0,0,0,0), Datetime(2009,6,8,0,0,0,0,0), Datetime(2009,6,9,0,0,0,0,0), Datetime(2009,6,10,0,0,0,0,0), Datetime(2009,6,11,0,0,0,0,0), Datetime(2009,6,12,0,0,0,0,0), Datetime(2009,6,15,0,0,0,0,0), Datetime(2009,6,16,0,0,0,0,0), Datetime(2009,6,17,0,0,0,0,0), Datetime(2009,6,18,0,0,0,0,0), Datetime(2009,6,19,0,0,0,0,0), Datetime(2009,6,22,0,0,0,0,0), Datetime(2009,6,23,0,0,0,0,0), Datetime(2009,6,24,0,0,0,0,0), Datetime(2009,6,25,0,0,0,0,0), Datetime(2009,6,26,0,0,0,0,0), Datetime(2009,6,29,0,0,0,0,0), Datetime(2009,6,30,0,0,0,0,0), Datetime(2009,7,1,0,0,0,0,0), Datetime(2009,7,2,0,0,0,0,0), Datetime(2009,7,3,0,0,0,0,0), Datetime(2009,7,6,0,0,0,0,0), Datetime(2009,7,7,0,0,0,0,0), Datetime(2009,7,8,0,0,0,0,0), Datetime(2009,7,9,0,0,0,0,0), Datetime(2009,7,10,0,0,0,0,0), Datetime(2009,7,13,0,0,0,0,0), Datetime(2009,7,14,0,0,0,0,0), Datetime(2009,7,15,0,0,0,0,0), Datetime(2009,7,16,0,0,0,0,0), Datetime(2009,7,17,0,0,0,0,0), Datetime(2009,7,20,0,0,0,0,0), Datetime(2009,7,21,0,0,0,0,0), Datetime(2009,7,22,0,0,0,0,0), Datetime(2009,7,23,0,0,0,0,0), Datetime(2009,7,24,0,0,0,0,0), Datetime(2009,7,27,0,0,0,0,0), Datetime(2009,7,28,0,0,0,0,0), Datetime(2009,7,29,0,0,0,0,0), Datetime(2009,7,30,0,0,0,0,0), Datetime(2009,7,31,0,0,0,0,0), Datetime(2009,8,3,0,0,0,0,0), Datetime(2009,8,4,0,0,0,0,0), Datetime(2009,8,5,0,0,0,0,0), Datetime(2009,8,6,0,0,0,0,0), Datetime(2009,8,7,0,0,0,0,0), Datetime(2009,8,10,0,0,0,0,0), Datetime(2009,8,11,0,0,0,0,0), Datetime(2009,8,12,0,0,0,0,0), Datetime(2009,8,13,0,0,0,0,0), Datetime(2009,8,14,0,0,0,0,0), Datetime(2009,8,17,0,0,0,0,0), Datetime(2009,8,18,0,0,0,0,0), Datetime(2009,8,19,0,0,0,0,0), Datetime(2009,8,20,0,0,0,0,0), Datetime(2009,8,21,0,0,0,0,0), Datetime(2009,8,24,0,0,0,0,0), Datetime(2009,8,25,0,0,0,0,0), Datetime(2009,8,26,0,0,0,0,0), Datetime(2009,8,27,0,0,0,0,0), Datetime(2009,8,28,0,0,0,0,0), Datetime(2009,8,31,0,0,0,0,0), Datetime(2009,9,1,0,0,0,0,0), Datetime(2009,9,2,0,0,0,0,0), Datetime(2009,9,3,0,0,0,0,0), Datetime(2009,9,4,0,0,0,0,0), Datetime(2009,9,7,0,0,0,0,0), Datetime(2009,9,8,0,0,0,0,0), Datetime(2009,9,9,0,0,0,0,0), Datetime(2009,9,10,0,0,0,0,0), Datetime(2009,9,11,0,0,0,0,0), Datetime(2009,9,14,0,0,0,0,0), Datetime(2009,9,15,0,0,0,0,0), Datetime(2009,9,16,0,0,0,0,0), Datetime(2009,9,17,0,0,0,0,0), Datetime(2009,9,18,0,0,0,0,0), Datetime(2009,9,21,0,0,0,0,0), Datetime(2009,9,22,0,0,0,0,0), Datetime(2009,9,23,0,0,0,0,0), Datetime(2009,9,24,0,0,0,0,0), Datetime(2009,9,25,0,0,0,0,0), Datetime(2009,9,28,0,0,0,0,0), Datetime(2009,9,29,0,0,0,0,0), Datetime(2009,9,30,0,0,0,0,0), Datetime(2009,10,9,0,0,0,0,0), Datetime(2009,10,12,0,0,0,0,0), Datetime(2009,10,13,0,0,0,0,0), Datetime(2009,10,14,0,0,0,0,0), Datetime(2009,10,15,0,0,0,0,0), Datetime(2009,10,16,0,0,0,0,0), Datetime(2009,10,19,0,0,0,0,0), Datetime(2009,10,20,0,0,0,0,0), Datetime(2009,10,21,0,0,0,0,0), Datetime(2009,10,22,0,0,0,0,0), Datetime(2009,10,23,0,0,0,0,0), Datetime(2009,10,26,0,0,0,0,0), Datetime(2009,10,27,0,0,0,0,0), Datetime(2009,10,28,0,0,0,0,0), Datetime(2009,10,29,0,0,0,0,0), Datetime(2009,10,30,0,0,0,0,0), Datetime(2009,11,2,0,0,0,0,0), Datetime(2009,11,3,0,0,0,0,0), Datetime(2009,11,4,0,0,0,0,0), Datetime(2009,11,5,0,0,0,0,0), Datetime(2009,11,6,0,0,0,0,0), Datetime(2009,11,9,0,0,0,0,0), Datetime(2009,11,10,0,0,0,0,0), Datetime(2009,11,11,0,0,0,0,0), Datetime(2009,11,12,0,0,0,0,0), Datetime(2009,11,13,0,0,0,0,0), Datetime(2009,11,16,0,0,0,0,0), Datetime(2009,11,17,0,0,0,0,0), Datetime(2009,11,18,0,0,0,0,0), Datetime(2009,11,19,0,0,0,0,0), Datetime(2009,11,20,0,0,0,0,0), Datetime(2009,11,23,0,0,0,0,0), Datetime(2009,11,24,0,0,0,0,0), Datetime(2009,11,25,0,0,0,0,0), Datetime(2009,11,26,0,0,0,0,0), Datetime(2009,11,27,0,0,0,0,0), Datetime(2009,11,30,0,0,0,0,0), Datetime(2009,12,1,0,0,0,0,0), Datetime(2009,12,2,0,0,0,0,0), Datetime(2009,12,3,0,0,0,0,0), Datetime(2009,12,4,0,0,0,0,0), Datetime(2009,12,7,0,0,0,0,0), Datetime(2009,12,8,0,0,0,0,0), Datetime(2009,12,9,0,0,0,0,0), Datetime(2009,12,10,0,0,0,0,0), Datetime(2009,12,11,0,0,0,0,0), Datetime(2009,12,14,0,0,0,0,0), Datetime(2009,12,15,0,0,0,0,0), Datetime(2009,12,16,0,0,0,0,0), Datetime(2009,12,17,0,0,0,0,0), Datetime(2009,12,18,0,0,0,0,0), Datetime(2009,12,21,0,0,0,0,0), Datetime(2009,12,22,0,0,0,0,0), Datetime(2009,12,23,0,0,0,0,0), Datetime(2009,12,24,0,0,0,0,0), Datetime(2009,12,25,0,0,0,0,0), Datetime(2009,12,28,0,0,0,0,0), Datetime(2009,12,29,0,0,0,0,0), Datetime(2009,12,30,0,0,0,0,0), Datetime(2009,12,31,0,0,0,0,0), Datetime(2010,1,4,0,0,0,0,0), Datetime(2010,1,5,0,0,0,0,0), Datetime(2010,1,6,0,0,0,0,0), Datetime(2010,1,7,0,0,0,0,0), Datetime(2010,1,8,0,0,0,0,0), Datetime(2010,1,11,0,0,0,0,0), Datetime(2010,1,12,0,0,0,0,0), Datetime(2010,1,13,0,0,0,0,0), Datetime(2010,1,14,0,0,0,0,0), Datetime(2010,1,15,0,0,0,0,0), Datetime(2010,1,18,0,0,0,0,0), Datetime(2010,1,19,0,0,0,0,0), Datetime(2010,1,20,0,0,0,0,0), Datetime(2010,1,21,0,0,0,0,0), Datetime(2010,1,22,0,0,0,0,0), Datetime(2010,1,25,0,0,0,0,0), Datetime(2010,1,26,0,0,0,0,0), Datetime(2010,1,27,0,0,0,0,0), Datetime(2010,1,28,0,0,0,0,0), Datetime(2010,1,29,0,0,0,0,0), Datetime(2010,2,1,0,0,0,0,0), Datetime(2010,2,2,0,0,0,0,0), Datetime(2010,2,3,0,0,0,0,0), Datetime(2010,2,4,0,0,0,0,0), Datetime(2010,2,5,0,0,0,0,0), Datetime(2010,2,8,0,0,0,0,0), Datetime(2010,2,9,0,0,0,0,0), Datetime(2010,2,10,0,0,0,0,0), Datetime(2010,2,11,0,0,0,0,0), Datetime(2010,2,12,0,0,0,0,0), Datetime(2010,2,22,0,0,0,0,0), Datetime(2010,2,23,0,0,0,0,0), Datetime(2010,2,24,0,0,0,0,0), Datetime(2010,2,25,0,0,0,0,0), Datetime(2010,2,26,0,0,0,0,0), Datetime(2010,3,1,0,0,0,0,0), Datetime(2010,3,2,0,0,0,0,0), Datetime(2010,3,3,0,0,0,0,0), Datetime(2010,3,4,0,0,0,0,0), Datetime(2010,3,5,0,0,0,0,0), Datetime(2010,3,8,0,0,0,0,0), Datetime(2010,3,9,0,0,0,0,0), Datetime(2010,3,10,0,0,0,0,0), Datetime(2010,3,11,0,0,0,0,0), Datetime(2010,3,12,0,0,0,0,0), Datetime(2010,3,15,0,0,0,0,0), Datetime(2010,3,16,0,0,0,0,0), Datetime(2010,3,17,0,0,0,0,0), Datetime(2010,3,18,0,0,0,0,0), Datetime(2010,3,19,0,0,0,0,0), Datetime(2010,3,22,0,0,0,0,0), Datetime(2010,3,23,0,0,0,0,0), Datetime(2010,3,24,0,0,0,0,0), Datetime(2010,3,25,0,0,0,0,0), Datetime(2010,3,26,0,0,0,0,0), Datetime(2010,3,29,0,0,0,0,0), Datetime(2010,3,30,0,0,0,0,0), Datetime(2010,3,31,0,0,0,0,0), Datetime(2010,4,1,0,0,0,0,0), Datetime(2010,4,2,0,0,0,0,0), Datetime(2010,4,6,0,0,0,0,0), Datetime(2010,4,7,0,0,0,0,0), Datetime(2010,4,8,0,0,0,0,0), Datetime(2010,4,9,0,0,0,0,0), Datetime(2010,4,12,0,0,0,0,0), Datetime(2010,4,13,0,0,0,0,0), Datetime(2010,4,14,0,0,0,0,0), Datetime(2010,4,15,0,0,0,0,0), Datetime(2010,4,16,0,0,0,0,0), Datetime(2010,4,19,0,0,0,0,0), Datetime(2010,4,20,0,0,0,0,0), Datetime(2010,4,21,0,0,0,0,0), Datetime(2010,4,22,0,0,0,0,0), Datetime(2010,4,23,0,0,0,0,0), Datetime(2010,4,26,0,0,0,0,0), Datetime(2010,4,27,0,0,0,0,0), Datetime(2010,4,28,0,0,0,0,0), Datetime(2010,4,29,0,0,0,0,0), Datetime(2010,4,30,0,0,0,0,0), Datetime(2010,5,4,0,0,0,0,0), Datetime(2010,5,5,0,0,0,0,0), Datetime(2010,5,6,0,0,0,0,0), Datetime(2010,5,7,0,0,0,0,0), Datetime(2010,5,10,0,0,0,0,0), Datetime(2010,5,11,0,0,0,0,0), Datetime(2010,5,12,0,0,0,0,0), Datetime(2010,5,13,0,0,0,0,0), Datetime(2010,5,14,0,0,0,0,0), Datetime(2010,5,17,0,0,0,0,0), Datetime(2010,5,18,0,0,0,0,0), Datetime(2010,5,19,0,0,0,0,0), Datetime(2010,5,20,0,0,0,0,0), Datetime(2010,5,21,0,0,0,0,0), Datetime(2010,5,24,0,0,0,0,0), Datetime(2010,5,25,0,0,0,0,0), Datetime(2010,5,26,0,0,0,0,0), Datetime(2010,5,27,0,0,0,0,0), Datetime(2010,5,28,0,0,0,0,0), Datetime(2010,5,31,0,0,0,0,0), Datetime(2010,6,1,0,0,0,0,0), Datetime(2010,6,2,0,0,0,0,0), Datetime(2010,6,3,0,0,0,0,0), Datetime(2010,6,4,0,0,0,0,0), Datetime(2010,6,7,0,0,0,0,0), Datetime(2010,6,8,0,0,0,0,0), Datetime(2010,6,9,0,0,0,0,0), Datetime(2010,6,10,0,0,0,0,0), Datetime(2010,6,11,0,0,0,0,0), Datetime(2010,6,17,0,0,0,0,0), Datetime(2010,6,18,0,0,0,0,0), Datetime(2010,6,21,0,0,0,0,0), Datetime(2010,6,22,0,0,0,0,0), Datetime(2010,6,23,0,0,0,0,0), Datetime(2010,6,24,0,0,0,0,0), Datetime(2010,6,25,0,0,0,0,0), Datetime(2010,6,28,0,0,0,0,0), Datetime(2010,6,29,0,0,0,0,0), Datetime(2010,6,30,0,0,0,0,0), Datetime(2010,7,1,0,0,0,0,0), Datetime(2010,7,2,0,0,0,0,0), Datetime(2010,7,5,0,0,0,0,0), Datetime(2010,7,6,0,0,0,0,0), Datetime(2010,7,7,0,0,0,0,0), Datetime(2010,7,8,0,0,0,0,0), Datetime(2010,7,9,0,0,0,0,0), Datetime(2010,7,12,0,0,0,0,0), Datetime(2010,7,13,0,0,0,0,0), Datetime(2010,7,14,0,0,0,0,0), Datetime(2010,7,15,0,0,0,0,0), Datetime(2010,7,16,0,0,0,0,0), Datetime(2010,7,19,0,0,0,0,0), Datetime(2010,7,20,0,0,0,0,0), Datetime(2010,7,21,0,0,0,0,0), Datetime(2010,7,22,0,0,0,0,0), Datetime(2010,7,23,0,0,0,0,0), Datetime(2010,7,26,0,0,0,0,0), Datetime(2010,7,27,0,0,0,0,0), Datetime(2010,7,28,0,0,0,0,0), Datetime(2010,7,29,0,0,0,0,0), Datetime(2010,7,30,0,0,0,0,0), Datetime(2010,8,2,0,0,0,0,0), Datetime(2010,8,3,0,0,0,0,0), Datetime(2010,8,4,0,0,0,0,0), Datetime(2010,8,5,0,0,0,0,0), Datetime(2010,8,6,0,0,0,0,0), Datetime(2010,8,9,0,0,0,0,0), Datetime(2010,8,10,0,0,0,0,0), Datetime(2010,8,11,0,0,0,0,0), Datetime(2010,8,12,0,0,0,0,0), Datetime(2010,8,13,0,0,0,0,0), Datetime(2010,8,16,0,0,0,0,0), Datetime(2010,8,17,0,0,0,0,0), Datetime(2010,8,18,0,0,0,0,0), Datetime(2010,8,19,0,0,0,0,0), Datetime(2010,8,20,0,0,0,0,0), Datetime(2010,8,23,0,0,0,0,0), Datetime(2010,8,24,0,0,0,0,0), Datetime(2010,8,25,0,0,0,0,0), Datetime(2010,8,26,0,0,0,0,0), Datetime(2010,8,27,0,0,0,0,0), Datetime(2010,8,30,0,0,0,0,0), Datetime(2010,8,31,0,0,0,0,0), Datetime(2010,9,1,0,0,0,0,0), Datetime(2010,9,2,0,0,0,0,0), Datetime(2010,9,3,0,0,0,0,0), Datetime(2010,9,6,0,0,0,0,0), Datetime(2010,9,7,0,0,0,0,0), Datetime(2010,9,8,0,0,0,0,0), Datetime(2010,9,9,0,0,0,0,0), Datetime(2010,9,10,0,0,0,0,0), Datetime(2010,9,13,0,0,0,0,0), Datetime(2010,9,14,0,0,0,0,0), Datetime(2010,9,15,0,0,0,0,0), Datetime(2010,9,16,0,0,0,0,0), Datetime(2010,9,17,0,0,0,0,0), Datetime(2010,9,20,0,0,0,0,0), Datetime(2010,9,21,0,0,0,0,0), Datetime(2010,9,27,0,0,0,0,0), Datetime(2010,9,28,0,0,0,0,0), Datetime(2010,9,29,0,0,0,0,0), Datetime(2010,9,30,0,0,0,0,0), Datetime(2010,10,8,0,0,0,0,0), Datetime(2010,10,11,0,0,0,0,0), Datetime(2010,10,12,0,0,0,0,0), Datetime(2010,10,13,0,0,0,0,0), Datetime(2010,10,14,0,0,0,0,0), Datetime(2010,10,15,0,0,0,0,0), Datetime(2010,10,18,0,0,0,0,0), Datetime(2010,10,19,0,0,0,0,0), Datetime(2010,10,20,0,0,0,0,0), Datetime(2010,10,21,0,0,0,0,0), Datetime(2010,10,22,0,0,0,0,0), Datetime(2010,10,25,0,0,0,0,0), Datetime(2010,10,26,0,0,0,0,0), Datetime(2010,10,27,0,0,0,0,0), Datetime(2010,10,28,0,0,0,0,0), Datetime(2010,10,29,0,0,0,0,0), Datetime(2010,11,1,0,0,0,0,0), Datetime(2010,11,2,0,0,0,0,0), Datetime(2010,11,3,0,0,0,0,0), Datetime(2010,11,4,0,0,0,0,0), Datetime(2010,11,5,0,0,0,0,0), Datetime(2010,11,8,0,0,0,0,0), Datetime(2010,11,9,0,0,0,0,0), Datetime(2010,11,10,0,0,0,0,0), Datetime(2010,11,11,0,0,0,0,0), Datetime(2010,11,12,0,0,0,0,0), Datetime(2010,11,15,0,0,0,0,0), Datetime(2010,11,16,0,0,0,0,0), Datetime(2010,11,17,0,0,0,0,0), Datetime(2010,11,18,0,0,0,0,0), Datetime(2010,11,19,0,0,0,0,0), Datetime(2010,11,22,0,0,0,0,0), Datetime(2010,11,23,0,0,0,0,0), Datetime(2010,11,24,0,0,0,0,0), Datetime(2010,11,25,0,0,0,0,0), Datetime(2010,11,26,0,0,0,0,0), Datetime(2010,11,29,0,0,0,0,0), Datetime(2010,11,30,0,0,0,0,0), Datetime(2010,12,1,0,0,0,0,0), Datetime(2010,12,2,0,0,0,0,0), Datetime(2010,12,3,0,0,0,0,0), Datetime(2010,12,6,0,0,0,0,0), Datetime(2010,12,7,0,0,0,0,0), Datetime(2010,12,8,0,0,0,0,0), Datetime(2010,12,9,0,0,0,0,0), Datetime(2010,12,10,0,0,0,0,0), Datetime(2010,12,13,0,0,0,0,0), Datetime(2010,12,14,0,0,0,0,0), Datetime(2010,12,15,0,0,0,0,0), Datetime(2010,12,16,0,0,0,0,0), Datetime(2010,12,17,0,0,0,0,0), Datetime(2010,12,20,0,0,0,0,0), Datetime(2010,12,21,0,0,0,0,0), Datetime(2010,12,22,0,0,0,0,0), Datetime(2010,12,23,0,0,0,0,0), Datetime(2010,12,24,0,0,0,0,0), Datetime(2010,12,27,0,0,0,0,0), Datetime(2010,12,28,0,0,0,0,0), Datetime(2010,12,29,0,0,0,0,0), Datetime(2010,12,30,0,0,0,0,0), Datetime(2010,12,31,0,0,0,0,0), Datetime(2011,1,4,0,0,0,0,0), Datetime(2011,1,5,0,0,0,0,0), Datetime(2011,1,6,0,0,0,0,0), Datetime(2011,1,7,0,0,0,0,0), Datetime(2011,1,10,0,0,0,0,0), Datetime(2011,1,11,0,0,0,0,0), Datetime(2011,1,12,0,0,0,0,0), Datetime(2011,1,13,0,0,0,0,0), Datetime(2011,1,14,0,0,0,0,0), Datetime(2011,1,17,0,0,0,0,0), Datetime(2011,1,18,0,0,0,0,0), Datetime(2011,1,19,0,0,0,0,0), Datetime(2011,1,20,0,0,0,0,0), Datetime(2011,1,21,0,0,0,0,0), Datetime(2011,1,24,0,0,0,0,0), Datetime(2011,1,25,0,0,0,0,0), Datetime(2011,1,26,0,0,0,0,0), Datetime(2011,1,27,0,0,0,0,0), Datetime(2011,1,28,0,0,0,0,0), Datetime(2011,1,31,0,0,0,0,0), Datetime(2011,2,1,0,0,0,0,0), Datetime(2011,2,9,0,0,0,0,0), Datetime(2011,2,10,0,0,0,0,0), Datetime(2011,2,11,0,0,0,0,0), Datetime(2011,2,14,0,0,0,0,0), Datetime(2011,2,15,0,0,0,0,0), Datetime(2011,2,16,0,0,0,0,0), Datetime(2011,2,17,0,0,0,0,0), Datetime(2011,2,18,0,0,0,0,0), Datetime(2011,2,21,0,0,0,0,0), Datetime(2011,2,22,0,0,0,0,0), Datetime(2011,2,23,0,0,0,0,0), Datetime(2011,2,24,0,0,0,0,0), Datetime(2011,2,25,0,0,0,0,0), Datetime(2011,2,28,0,0,0,0,0), Datetime(2011,3,1,0,0,0,0,0), Datetime(2011,3,2,0,0,0,0,0), Datetime(2011,3,3,0,0,0,0,0), Datetime(2011,3,4,0,0,0,0,0), Datetime(2011,3,7,0,0,0,0,0), Datetime(2011,3,8,0,0,0,0,0), Datetime(2011,3,9,0,0,0,0,0), Datetime(2011,3,10,0,0,0,0,0), Datetime(2011,3,11,0,0,0,0,0), Datetime(2011,3,14,0,0,0,0,0), Datetime(2011,3,15,0,0,0,0,0), Datetime(2011,3,16,0,0,0,0,0), Datetime(2011,3,17,0,0,0,0,0), Datetime(2011,3,18,0,0,0,0,0), Datetime(2011,3,21,0,0,0,0,0), Datetime(2011,3,22,0,0,0,0,0), Datetime(2011,3,23,0,0,0,0,0), Datetime(2011,3,24,0,0,0,0,0), Datetime(2011,3,25,0,0,0,0,0), Datetime(2011,3,28,0,0,0,0,0), Datetime(2011,3,29,0,0,0,0,0), Datetime(2011,3,30,0,0,0,0,0), Datetime(2011,3,31,0,0,0,0,0), Datetime(2011,4,1,0,0,0,0,0), Datetime(2011,4,6,0,0,0,0,0), Datetime(2011,4,7,0,0,0,0,0), Datetime(2011,4,8,0,0,0,0,0), Datetime(2011,4,11,0,0,0,0,0), Datetime(2011,4,12,0,0,0,0,0), Datetime(2011,4,13,0,0,0,0,0), Datetime(2011,4,14,0,0,0,0,0), Datetime(2011,4,15,0,0,0,0,0), Datetime(2011,4,18,0,0,0,0,0), Datetime(2011,4,19,0,0,0,0,0), Datetime(2011,4,20,0,0,0,0,0), Datetime(2011,4,21,0,0,0,0,0), Datetime(2011,4,22,0,0,0,0,0), Datetime(2011,4,25,0,0,0,0,0), Datetime(2011,4,26,0,0,0,0,0), Datetime(2011,4,27,0,0,0,0,0), Datetime(2011,4,28,0,0,0,0,0), Datetime(2011,4,29,0,0,0,0,0), Datetime(2011,5,3,0,0,0,0,0), Datetime(2011,5,4,0,0,0,0,0), Datetime(2011,5,5,0,0,0,0,0), Datetime(2011,5,6,0,0,0,0,0), Datetime(2011,5,9,0,0,0,0,0), Datetime(2011,5,10,0,0,0,0,0), Datetime(2011,5,11,0,0,0,0,0), Datetime(2011,5,12,0,0,0,0,0), Datetime(2011,5,13,0,0,0,0,0), Datetime(2011,5,16,0,0,0,0,0), Datetime(2011,5,17,0,0,0,0,0), Datetime(2011,5,18,0,0,0,0,0), Datetime(2011,5,19,0,0,0,0,0), Datetime(2011,5,20,0,0,0,0,0), Datetime(2011,5,23,0,0,0,0,0), Datetime(2011,5,24,0,0,0,0,0), Datetime(2011,5,25,0,0,0,0,0), Datetime(2011,5,26,0,0,0,0,0), Datetime(2011,5,27,0,0,0,0,0), Datetime(2011,5,30,0,0,0,0,0), Datetime(2011,5,31,0,0,0,0,0), Datetime(2011,6,1,0,0,0,0,0), Datetime(2011,6,2,0,0,0,0,0), Datetime(2011,6,3,0,0,0,0,0), Datetime(2011,6,7,0,0,0,0,0), Datetime(2011,6,8,0,0,0,0,0), Datetime(2011,6,9,0,0,0,0,0), Datetime(2011,6,10,0,0,0,0,0), Datetime(2011,6,13,0,0,0,0,0), Datetime(2011,6,14,0,0,0,0,0), Datetime(2011,6,15,0,0,0,0,0), Datetime(2011,6,16,0,0,0,0,0), Datetime(2011,6,17,0,0,0,0,0), Datetime(2011,6,20,0,0,0,0,0), Datetime(2011,6,21,0,0,0,0,0), Datetime(2011,6,22,0,0,0,0,0), Datetime(2011,6,23,0,0,0,0,0), Datetime(2011,6,24,0,0,0,0,0), Datetime(2011,6,27,0,0,0,0,0), Datetime(2011,6,28,0,0,0,0,0), Datetime(2011,6,29,0,0,0,0,0), Datetime(2011,6,30,0,0,0,0,0), Datetime(2011,7,1,0,0,0,0,0), Datetime(2011,7,4,0,0,0,0,0), Datetime(2011,7,5,0,0,0,0,0), Datetime(2011,7,6,0,0,0,0,0), Datetime(2011,7,7,0,0,0,0,0), Datetime(2011,7,8,0,0,0,0,0), Datetime(2011,7,11,0,0,0,0,0), Datetime(2011,7,12,0,0,0,0,0), Datetime(2011,7,13,0,0,0,0,0), Datetime(2011,7,14,0,0,0,0,0), Datetime(2011,7,15,0,0,0,0,0), Datetime(2011,7,18,0,0,0,0,0), Datetime(2011,7,19,0,0,0,0,0), Datetime(2011,7,20,0,0,0,0,0), Datetime(2011,7,21,0,0,0,0,0), Datetime(2011,7,22,0,0,0,0,0), Datetime(2011,7,25,0,0,0,0,0), Datetime(2011,7,26,0,0,0,0,0), Datetime(2011,7,27,0,0,0,0,0), Datetime(2011,7,28,0,0,0,0,0), Datetime(2011,7,29,0,0,0,0,0), Datetime(2011,8,1,0,0,0,0,0), Datetime(2011,8,2,0,0,0,0,0), Datetime(2011,8,3,0,0,0,0,0), Datetime(2011,8,4,0,0,0,0,0), Datetime(2011,8,5,0,0,0,0,0), Datetime(2011,8,8,0,0,0,0,0), Datetime(2011,8,9,0,0,0,0,0), Datetime(2011,8,10,0,0,0,0,0), Datetime(2011,8,11,0,0,0,0,0), Datetime(2011,8,12,0,0,0,0,0), Datetime(2011,8,15,0,0,0,0,0), Datetime(2011,8,16,0,0,0,0,0), Datetime(2011,8,17,0,0,0,0,0), Datetime(2011,8,18,0,0,0,0,0), Datetime(2011,8,19,0,0,0,0,0), Datetime(2011,8,22,0,0,0,0,0), Datetime(2011,8,23,0,0,0,0,0), Datetime(2011,8,24,0,0,0,0,0), Datetime(2011,8,25,0,0,0,0,0), Datetime(2011,8,26,0,0,0,0,0), Datetime(2011,8,29,0,0,0,0,0), Datetime(2011,8,30,0,0,0,0,0), Datetime(2011,8,31,0,0,0,0,0), Datetime(2011,9,1,0,0,0,0,0), Datetime(2011,9,2,0,0,0,0,0), Datetime(2011,9,5,0,0,0,0,0), Datetime(2011,9,6,0,0,0,0,0), Datetime(2011,9,7,0,0,0,0,0), Datetime(2011,9,8,0,0,0,0,0), Datetime(2011,9,9,0,0,0,0,0), Datetime(2011,9,13,0,0,0,0,0), Datetime(2011,9,14,0,0,0,0,0), Datetime(2011,9,15,0,0,0,0,0), Datetime(2011,9,16,0,0,0,0,0), Datetime(2011,9,19,0,0,0,0,0), Datetime(2011,9,20,0,0,0,0,0), Datetime(2011,9,21,0,0,0,0,0), Datetime(2011,9,22,0,0,0,0,0), Datetime(2011,9,23,0,0,0,0,0), Datetime(2011,9,26,0,0,0,0,0), Datetime(2011,9,27,0,0,0,0,0), Datetime(2011,9,28,0,0,0,0,0), Datetime(2011,9,29,0,0,0,0,0), Datetime(2011,9,30,0,0,0,0,0), Datetime(2011,10,10,0,0,0,0,0), Datetime(2011,10,11,0,0,0,0,0), Datetime(2011,10,12,0,0,0,0,0), Datetime(2011,10,13,0,0,0,0,0), Datetime(2011,10,14,0,0,0,0,0), Datetime(2011,10,17,0,0,0,0,0), Datetime(2011,10,18,0,0,0,0,0), Datetime(2011,10,19,0,0,0,0,0), Datetime(2011,10,20,0,0,0,0,0), Datetime(2011,10,21,0,0,0,0,0), Datetime(2011,10,24,0,0,0,0,0), Datetime(2011,10,25,0,0,0,0,0), Datetime(2011,10,26,0,0,0,0,0), Datetime(2011,10,27,0,0,0,0,0), Datetime(2011,10,28,0,0,0,0,0), Datetime(2011,10,31,0,0,0,0,0), Datetime(2011,11,1,0,0,0,0,0), Datetime(2011,11,2,0,0,0,0,0), Datetime(2011,11,3,0,0,0,0,0), Datetime(2011,11,4,0,0,0,0,0), Datetime(2011,11,7,0,0,0,0,0), Datetime(2011,11,8,0,0,0,0,0), Datetime(2011,11,9,0,0,0,0,0), Datetime(2011,11,10,0,0,0,0,0), Datetime(2011,11,11,0,0,0,0,0), Datetime(2011,11,14,0,0,0,0,0), Datetime(2011,11,15,0,0,0,0,0), Datetime(2011,11,16,0,0,0,0,0), Datetime(2011,11,17,0,0,0,0,0), Datetime(2011,11,18,0,0,0,0,0), Datetime(2011,11,21,0,0,0,0,0), Datetime(2011,11,22,0,0,0,0,0), Datetime(2011,11,23,0,0,0,0,0), Datetime(2011,11,24,0,0,0,0,0), Datetime(2011,11,25,0,0,0,0,0), Datetime(2011,11,28,0,0,0,0,0), Datetime(2011,11,29,0,0,0,0,0), Datetime(2011,11,30,0,0,0,0,0), Datetime(2011,12,1,0,0,0,0,0), Datetime(2011,12,2,0,0,0,0,0), Datetime(2011,12,5,0,0,0,0,0), Datetime(2011,12,6,0,0,0,0,0), Datetime(2011,12,7,0,0,0,0,0), Datetime(2011,12,8,0,0,0,0,0), Datetime(2011,12,9,0,0,0,0,0), Datetime(2011,12,12,0,0,0,0,0), Datetime(2011,12,13,0,0,0,0,0), Datetime(2011,12,14,0,0,0,0,0), Datetime(2011,12,15,0,0,0,0,0), Datetime(2011,12,16,0,0,0,0,0), Datetime(2011,12,19,0,0,0,0,0), Datetime(2011,12,20,0,0,0,0,0), Datetime(2011,12,21,0,0,0,0,0), Datetime(2011,12,22,0,0,0,0,0), Datetime(2011,12,23,0,0,0,0,0), Datetime(2011,12,26,0,0,0,0,0), Datetime(2011,12,27,0,0,0,0,0), Datetime(2011,12,28,0,0,0,0,0), Datetime(2011,12,29,0,0,0,0,0), Datetime(2011,12,30,0,0,0,0,0), Datetime(2012,1,4,0,0,0,0,0), Datetime(2012,1,5,0,0,0,0,0), Datetime(2012,1,6,0,0,0,0,0), Datetime(2012,1,9,0,0,0,0,0), Datetime(2012,1,10,0,0,0,0,0), Datetime(2012,1,11,0,0,0,0,0), Datetime(2012,1,12,0,0,0,0,0), Datetime(2012,1,13,0,0,0,0,0), Datetime(2012,1,16,0,0,0,0,0), Datetime(2012,1,17,0,0,0,0,0), Datetime(2012,1,18,0,0,0,0,0), Datetime(2012,1,19,0,0,0,0,0), Datetime(2012,1,20,0,0,0,0,0), Datetime(2012,1,30,0,0,0,0,0), Datetime(2012,1,31,0,0,0,0,0), Datetime(2012,2,1,0,0,0,0,0), Datetime(2012,2,2,0,0,0,0,0), Datetime(2012,2,3,0,0,0,0,0), Datetime(2012,2,6,0,0,0,0,0), Datetime(2012,2,7,0,0,0,0,0), Datetime(2012,2,8,0,0,0,0,0), Datetime(2012,2,9,0,0,0,0,0), Datetime(2012,2,10,0,0,0,0,0), Datetime(2012,2,13,0,0,0,0,0), Datetime(2012,2,14,0,0,0,0,0), Datetime(2012,2,15,0,0,0,0,0), Datetime(2012,2,16,0,0,0,0,0), Datetime(2012,2,17,0,0,0,0,0), Datetime(2012,2,20,0,0,0,0,0), Datetime(2012,2,21,0,0,0,0,0), Datetime(2012,2,22,0,0,0,0,0), Datetime(2012,2,23,0,0,0,0,0), Datetime(2012,2,24,0,0,0,0,0), Datetime(2012,2,27,0,0,0,0,0), Datetime(2012,2,28,0,0,0,0,0), Datetime(2012,2,29,0,0,0,0,0), Datetime(2012,3,1,0,0,0,0,0), Datetime(2012,3,2,0,0,0,0,0), Datetime(2012,3,5,0,0,0,0,0), Datetime(2012,3,6,0,0,0,0,0), Datetime(2012,3,7,0,0,0,0,0), Datetime(2012,3,8,0,0,0,0,0), Datetime(2012,3,9,0,0,0,0,0), Datetime(2012,3,12,0,0,0,0,0), Datetime(2012,3,13,0,0,0,0,0), Datetime(2012,3,14,0,0,0,0,0), Datetime(2012,3,15,0,0,0,0,0), Datetime(2012,3,16,0,0,0,0,0), Datetime(2012,3,19,0,0,0,0,0), Datetime(2012,3,20,0,0,0,0,0), Datetime(2012,3,21,0,0,0,0,0), Datetime(2012,3,22,0,0,0,0,0), Datetime(2012,3,23,0,0,0,0,0), Datetime(2012,3,26,0,0,0,0,0), Datetime(2012,3,27,0,0,0,0,0), Datetime(2012,3,28,0,0,0,0,0), Datetime(2012,3,29,0,0,0,0,0), Datetime(2012,3,30,0,0,0,0,0), Datetime(2012,4,5,0,0,0,0,0), Datetime(2012,4,6,0,0,0,0,0), Datetime(2012,4,9,0,0,0,0,0), Datetime(2012,4,10,0,0,0,0,0), Datetime(2012,4,11,0,0,0,0,0), Datetime(2012,4,12,0,0,0,0,0), Datetime(2012,4,13,0,0,0,0,0), Datetime(2012,4,16,0,0,0,0,0), Datetime(2012,4,17,0,0,0,0,0), Datetime(2012,4,18,0,0,0,0,0), Datetime(2012,4,19,0,0,0,0,0), Datetime(2012,4,20,0,0,0,0,0), Datetime(2012,4,23,0,0,0,0,0), Datetime(2012,4,24,0,0,0,0,0), Datetime(2012,4,25,0,0,0,0,0), Datetime(2012,4,26,0,0,0,0,0), Datetime(2012,4,27,0,0,0,0,0), Datetime(2012,5,2,0,0,0,0,0), Datetime(2012,5,3,0,0,0,0,0), Datetime(2012,5,4,0,0,0,0,0), Datetime(2012,5,7,0,0,0,0,0), Datetime(2012,5,8,0,0,0,0,0), Datetime(2012,5,9,0,0,0,0,0), Datetime(2012,5,10,0,0,0,0,0), Datetime(2012,5,11,0,0,0,0,0), Datetime(2012,5,14,0,0,0,0,0), Datetime(2012,5,15,0,0,0,0,0), Datetime(2012,5,16,0,0,0,0,0), Datetime(2012,5,17,0,0,0,0,0), Datetime(2012,5,18,0,0,0,0,0), Datetime(2012,5,21,0,0,0,0,0), Datetime(2012,5,22,0,0,0,0,0), Datetime(2012,5,23,0,0,0,0,0), Datetime(2012,5,24,0,0,0,0,0), Datetime(2012,5,25,0,0,0,0,0), Datetime(2012,5,28,0,0,0,0,0), Datetime(2012,5,29,0,0,0,0,0), Datetime(2012,5,30,0,0,0,0,0), Datetime(2012,5,31,0,0,0,0,0), Datetime(2012,6,1,0,0,0,0,0), Datetime(2012,6,4,0,0,0,0,0), Datetime(2012,6,5,0,0,0,0,0), Datetime(2012,6,6,0,0,0,0,0), Datetime(2012,6,7,0,0,0,0,0), Datetime(2012,6,8,0,0,0,0,0), Datetime(2012,6,11,0,0,0,0,0), Datetime(2012,6,12,0,0,0,0,0), Datetime(2012,6,13,0,0,0,0,0), Datetime(2012,6,14,0,0,0,0,0), Datetime(2012,6,15,0,0,0,0,0), Datetime(2012,6,18,0,0,0,0,0), Datetime(2012,6,19,0,0,0,0,0), Datetime(2012,6,20,0,0,0,0,0), Datetime(2012,6,21,0,0,0,0,0), Datetime(2012,6,25,0,0,0,0,0), Datetime(2012,6,26,0,0,0,0,0), Datetime(2012,6,27,0,0,0,0,0), Datetime(2012,6,28,0,0,0,0,0), Datetime(2012,6,29,0,0,0,0,0), Datetime(2012,7,2,0,0,0,0,0), Datetime(2012,7,3,0,0,0,0,0), Datetime(2012,7,4,0,0,0,0,0), Datetime(2012,7,5,0,0,0,0,0), Datetime(2012,7,6,0,0,0,0,0), Datetime(2012,7,9,0,0,0,0,0), Datetime(2012,7,10,0,0,0,0,0), Datetime(2012,7,11,0,0,0,0,0), Datetime(2012,7,12,0,0,0,0,0), Datetime(2012,7,13,0,0,0,0,0), Datetime(2012,7,16,0,0,0,0,0), Datetime(2012,7,17,0,0,0,0,0), Datetime(2012,7,18,0,0,0,0,0), Datetime(2012,7,19,0,0,0,0,0), Datetime(2012,7,20,0,0,0,0,0), Datetime(2012,7,23,0,0,0,0,0), Datetime(2012,7,24,0,0,0,0,0), Datetime(2012,7,25,0,0,0,0,0), Datetime(2012,7,26,0,0,0,0,0), Datetime(2012,7,27,0,0,0,0,0), Datetime(2012,7,30,0,0,0,0,0), Datetime(2012,7,31,0,0,0,0,0), Datetime(2012,8,1,0,0,0,0,0), Datetime(2012,8,2,0,0,0,0,0), Datetime(2012,8,3,0,0,0,0,0), Datetime(2012,8,6,0,0,0,0,0), Datetime(2012,8,7,0,0,0,0,0), Datetime(2012,8,8,0,0,0,0,0), Datetime(2012,8,9,0,0,0,0,0), Datetime(2012,8,10,0,0,0,0,0), Datetime(2012,8,13,0,0,0,0,0), Datetime(2012,8,14,0,0,0,0,0), Datetime(2012,8,15,0,0,0,0,0), Datetime(2012,8,16,0,0,0,0,0), Datetime(2012,8,17,0,0,0,0,0), Datetime(2012,8,20,0,0,0,0,0), Datetime(2012,8,21,0,0,0,0,0), Datetime(2012,8,22,0,0,0,0,0), Datetime(2012,8,23,0,0,0,0,0), Datetime(2012,8,24,0,0,0,0,0), Datetime(2012,8,27,0,0,0,0,0), Datetime(2012,8,28,0,0,0,0,0), Datetime(2012,8,29,0,0,0,0,0), Datetime(2012,8,30,0,0,0,0,0), Datetime(2012,8,31,0,0,0,0,0), Datetime(2012,9,3,0,0,0,0,0), Datetime(2012,9,4,0,0,0,0,0), Datetime(2012,9,5,0,0,0,0,0), Datetime(2012,9,6,0,0,0,0,0), Datetime(2012,9,7,0,0,0,0,0), Datetime(2012,9,10,0,0,0,0,0), Datetime(2012,9,11,0,0,0,0,0), Datetime(2012,9,12,0,0,0,0,0), Datetime(2012,9,13,0,0,0,0,0), Datetime(2012,9,14,0,0,0,0,0), Datetime(2012,9,17,0,0,0,0,0), Datetime(2012,9,18,0,0,0,0,0), Datetime(2012,9,19,0,0,0,0,0), Datetime(2012,9,20,0,0,0,0,0), Datetime(2012,9,21,0,0,0,0,0), Datetime(2012,9,24,0,0,0,0,0), Datetime(2012,9,25,0,0,0,0,0), Datetime(2012,9,26,0,0,0,0,0), Datetime(2012,9,27,0,0,0,0,0), Datetime(2012,9,28,0,0,0,0,0), Datetime(2012,10,8,0,0,0,0,0), Datetime(2012,10,9,0,0,0,0,0), Datetime(2012,10,10,0,0,0,0,0), Datetime(2012,10,11,0,0,0,0,0), Datetime(2012,10,12,0,0,0,0,0), Datetime(2012,10,15,0,0,0,0,0), Datetime(2012,10,16,0,0,0,0,0), Datetime(2012,10,17,0,0,0,0,0), Datetime(2012,10,18,0,0,0,0,0), Datetime(2012,10,19,0,0,0,0,0), Datetime(2012,10,22,0,0,0,0,0), Datetime(2012,10,23,0,0,0,0,0), Datetime(2012,10,24,0,0,0,0,0), Datetime(2012,10,25,0,0,0,0,0), Datetime(2012,10,26,0,0,0,0,0), Datetime(2012,10,29,0,0,0,0,0), Datetime(2012,10,30,0,0,0,0,0), Datetime(2012,10,31,0,0,0,0,0), Datetime(2012,11,1,0,0,0,0,0), Datetime(2012,11,2,0,0,0,0,0), Datetime(2012,11,5,0,0,0,0,0), Datetime(2012,11,6,0,0,0,0,0), Datetime(2012,11,7,0,0,0,0,0), Datetime(2012,11,8,0,0,0,0,0), Datetime(2012,11,9,0,0,0,0,0), Datetime(2012,11,12,0,0,0,0,0), Datetime(2012,11,13,0,0,0,0,0), Datetime(2012,11,14,0,0,0,0,0), Datetime(2012,11,15,0,0,0,0,0), Datetime(2012,11,16,0,0,0,0,0), Datetime(2012,11,19,0,0,0,0,0), Datetime(2012,11,20,0,0,0,0,0), Datetime(2012,11,21,0,0,0,0,0), Datetime(2012,11,22,0,0,0,0,0), Datetime(2012,11,23,0,0,0,0,0), Datetime(2012,11,26,0,0,0,0,0), Datetime(2012,11,27,0,0,0,0,0), Datetime(2012,11,28,0,0,0,0,0), Datetime(2012,11,29,0,0,0,0,0), Datetime(2012,11,30,0,0,0,0,0), Datetime(2012,12,3,0,0,0,0,0), Datetime(2012,12,4,0,0,0,0,0), Datetime(2012,12,5,0,0,0,0,0), Datetime(2012,12,6,0,0,0,0,0), Datetime(2012,12,7,0,0,0,0,0), Datetime(2012,12,10,0,0,0,0,0), Datetime(2012,12,11,0,0,0,0,0), Datetime(2012,12,12,0,0,0,0,0), Datetime(2012,12,13,0,0,0,0,0), Datetime(2012,12,14,0,0,0,0,0), Datetime(2012,12,17,0,0,0,0,0), Datetime(2012,12,18,0,0,0,0,0), Datetime(2012,12,19,0,0,0,0,0), Datetime(2012,12,20,0,0,0,0,0), Datetime(2012,12,21,0,0,0,0,0), Datetime(2012,12,24,0,0,0,0,0), Datetime(2012,12,25,0,0,0,0,0), Datetime(2012,12,26,0,0,0,0,0), Datetime(2012,12,27,0,0,0,0,0), Datetime(2012,12,28,0,0,0,0,0), Datetime(2012,12,31,0,0,0,0,0), Datetime(2013,1,4,0,0,0,0,0), Datetime(2013,1,7,0,0,0,0,0), Datetime(2013,1,8,0,0,0,0,0), Datetime(2013,1,9,0,0,0,0,0), Datetime(2013,1,10,0,0,0,0,0), Datetime(2013,1,11,0,0,0,0,0), Datetime(2013,1,14,0,0,0,0,0), Datetime(2013,1,15,0,0,0,0,0), Datetime(2013,1,16,0,0,0,0,0), Datetime(2013,1,17,0,0,0,0,0), Datetime(2013,1,18,0,0,0,0,0), Datetime(2013,1,21,0,0,0,0,0), Datetime(2013,1,22,0,0,0,0,0), Datetime(2013,1,23,0,0,0,0,0), Datetime(2013,1,24,0,0,0,0,0), Datetime(2013,1,25,0,0,0,0,0), Datetime(2013,1,28,0,0,0,0,0), Datetime(2013,1,29,0,0,0,0,0), Datetime(2013,1,30,0,0,0,0,0), Datetime(2013,1,31,0,0,0,0,0), Datetime(2013,2,1,0,0,0,0,0), Datetime(2013,2,4,0,0,0,0,0), Datetime(2013,2,5,0,0,0,0,0), Datetime(2013,2,6,0,0,0,0,0), Datetime(2013,2,7,0,0,0,0,0), Datetime(2013,2,8,0,0,0,0,0), Datetime(2013,2,18,0,0,0,0,0), Datetime(2013,2,19,0,0,0,0,0), Datetime(2013,2,20,0,0,0,0,0), Datetime(2013,2,21,0,0,0,0,0), Datetime(2013,2,22,0,0,0,0,0), Datetime(2013,2,25,0,0,0,0,0), Datetime(2013,2,26,0,0,0,0,0), Datetime(2013,2,27,0,0,0,0,0), Datetime(2013,2,28,0,0,0,0,0), Datetime(2013,3,1,0,0,0,0,0), Datetime(2013,3,4,0,0,0,0,0), Datetime(2013,3,5,0,0,0,0,0), Datetime(2013,3,6,0,0,0,0,0), Datetime(2013,3,7,0,0,0,0,0), Datetime(2013,3,8,0,0,0,0,0), Datetime(2013,3,11,0,0,0,0,0), Datetime(2013,3,12,0,0,0,0,0), Datetime(2013,3,13,0,0,0,0,0), Datetime(2013,3,14,0,0,0,0,0), Datetime(2013,3,15,0,0,0,0,0), Datetime(2013,3,18,0,0,0,0,0), Datetime(2013,3,19,0,0,0,0,0), Datetime(2013,3,20,0,0,0,0,0), Datetime(2013,3,21,0,0,0,0,0), Datetime(2013,3,22,0,0,0,0,0), Datetime(2013,3,25,0,0,0,0,0), Datetime(2013,3,26,0,0,0,0,0), Datetime(2013,3,27,0,0,0,0,0), Datetime(2013,3,28,0,0,0,0,0), Datetime(2013,3,29,0,0,0,0,0), Datetime(2013,4,1,0,0,0,0,0), Datetime(2013,4,2,0,0,0,0,0), Datetime(2013,4,3,0,0,0,0,0), Datetime(2013,4,8,0,0,0,0,0), Datetime(2013,4,9,0,0,0,0,0), Datetime(2013,4,10,0,0,0,0,0), Datetime(2013,4,11,0,0,0,0,0), Datetime(2013,4,12,0,0,0,0,0), Datetime(2013,4,15,0,0,0,0,0), Datetime(2013,4,16,0,0,0,0,0), Datetime(2013,4,17,0,0,0,0,0), Datetime(2013,4,18,0,0,0,0,0), Datetime(2013,4,19,0,0,0,0,0), Datetime(2013,4,22,0,0,0,0,0), Datetime(2013,4,23,0,0,0,0,0), Datetime(2013,4,24,0,0,0,0,0), Datetime(2013,4,25,0,0,0,0,0), Datetime(2013,4,26,0,0,0,0,0), Datetime(2013,5,2,0,0,0,0,0), Datetime(2013,5,3,0,0,0,0,0), Datetime(2013,5,6,0,0,0,0,0), Datetime(2013,5,7,0,0,0,0,0), Datetime(2013,5,8,0,0,0,0,0), Datetime(2013,5,9,0,0,0,0,0), Datetime(2013,5,10,0,0,0,0,0), Datetime(2013,5,13,0,0,0,0,0), Datetime(2013,5,14,0,0,0,0,0), Datetime(2013,5,15,0,0,0,0,0), Datetime(2013,5,16,0,0,0,0,0), Datetime(2013,5,17,0,0,0,0,0), Datetime(2013,5,20,0,0,0,0,0), Datetime(2013,5,21,0,0,0,0,0), Datetime(2013,5,22,0,0,0,0,0), Datetime(2013,5,23,0,0,0,0,0), Datetime(2013,5,24,0,0,0,0,0), Datetime(2013,5,27,0,0,0,0,0), Datetime(2013,5,28,0,0,0,0,0), Datetime(2013,5,29,0,0,0,0,0), Datetime(2013,5,30,0,0,0,0,0), Datetime(2013,5,31,0,0,0,0,0), Datetime(2013,6,3,0,0,0,0,0), Datetime(2013,6,4,0,0,0,0,0), Datetime(2013,6,5,0,0,0,0,0), Datetime(2013,6,6,0,0,0,0,0), Datetime(2013,6,7,0,0,0,0,0), Datetime(2013,6,13,0,0,0,0,0), Datetime(2013,6,14,0,0,0,0,0), Datetime(2013,6,17,0,0,0,0,0), Datetime(2013,6,18,0,0,0,0,0), Datetime(2013,6,19,0,0,0,0,0), Datetime(2013,6,20,0,0,0,0,0), Datetime(2013,6,21,0,0,0,0,0), Datetime(2013,6,24,0,0,0,0,0), Datetime(2013,6,25,0,0,0,0,0), Datetime(2013,6,26,0,0,0,0,0), Datetime(2013,6,27,0,0,0,0,0), Datetime(2013,6,28,0,0,0,0,0), Datetime(2013,7,1,0,0,0,0,0), Datetime(2013,7,2,0,0,0,0,0), Datetime(2013,7,3,0,0,0,0,0), Datetime(2013,7,4,0,0,0,0,0), Datetime(2013,7,5,0,0,0,0,0), Datetime(2013,7,8,0,0,0,0,0), Datetime(2013,7,9,0,0,0,0,0), Datetime(2013,7,10,0,0,0,0,0), Datetime(2013,7,11,0,0,0,0,0), Datetime(2013,7,12,0,0,0,0,0), Datetime(2013,7,15,0,0,0,0,0), Datetime(2013,7,16,0,0,0,0,0), Datetime(2013,7,17,0,0,0,0,0), Datetime(2013,7,18,0,0,0,0,0), Datetime(2013,7,19,0,0,0,0,0), Datetime(2013,7,22,0,0,0,0,0), Datetime(2013,7,23,0,0,0,0,0), Datetime(2013,7,24,0,0,0,0,0), Datetime(2013,7,25,0,0,0,0,0), Datetime(2013,7,26,0,0,0,0,0), Datetime(2013,7,29,0,0,0,0,0), Datetime(2013,7,30,0,0,0,0,0), Datetime(2013,7,31,0,0,0,0,0), Datetime(2013,8,1,0,0,0,0,0), Datetime(2013,8,2,0,0,0,0,0), Datetime(2013,8,5,0,0,0,0,0), Datetime(2013,8,6,0,0,0,0,0), Datetime(2013,8,7,0,0,0,0,0), Datetime(2013,8,8,0,0,0,0,0), Datetime(2013,8,9,0,0,0,0,0), Datetime(2013,8,12,0,0,0,0,0), Datetime(2013,8,13,0,0,0,0,0), Datetime(2013,8,14,0,0,0,0,0), Datetime(2013,8,15,0,0,0,0,0), Datetime(2013,8,16,0,0,0,0,0), Datetime(2013,8,19,0,0,0,0,0), Datetime(2013,8,20,0,0,0,0,0), Datetime(2013,8,21,0,0,0,0,0), Datetime(2013,8,22,0,0,0,0,0), Datetime(2013,8,23,0,0,0,0,0), Datetime(2013,8,26,0,0,0,0,0), Datetime(2013,8,27,0,0,0,0,0), Datetime(2013,8,28,0,0,0,0,0), Datetime(2013,8,29,0,0,0,0,0), Datetime(2013,8,30,0,0,0,0,0), Datetime(2013,9,2,0,0,0,0,0), Datetime(2013,9,3,0,0,0,0,0), Datetime(2013,9,4,0,0,0,0,0), Datetime(2013,9,5,0,0,0,0,0), Datetime(2013,9,6,0,0,0,0,0), Datetime(2013,9,9,0,0,0,0,0), Datetime(2013,9,10,0,0,0,0,0), Datetime(2013,9,11,0,0,0,0,0), Datetime(2013,9,12,0,0,0,0,0), Datetime(2013,9,13,0,0,0,0,0), Datetime(2013,9,16,0,0,0,0,0), Datetime(2013,9,17,0,0,0,0,0), Datetime(2013,9,18,0,0,0,0,0), Datetime(2013,9,23,0,0,0,0,0), Datetime(2013,9,24,0,0,0,0,0), Datetime(2013,9,25,0,0,0,0,0), Datetime(2013,9,26,0,0,0,0,0), Datetime(2013,9,27,0,0,0,0,0), Datetime(2013,9,30,0,0,0,0,0), Datetime(2013,10,8,0,0,0,0,0), Datetime(2013,10,9,0,0,0,0,0), Datetime(2013,10,10,0,0,0,0,0), Datetime(2013,10,11,0,0,0,0,0), Datetime(2013,10,14,0,0,0,0,0), Datetime(2013,10,15,0,0,0,0,0), Datetime(2013,10,16,0,0,0,0,0), Datetime(2013,10,17,0,0,0,0,0), Datetime(2013,10,18,0,0,0,0,0), Datetime(2013,10,21,0,0,0,0,0), Datetime(2013,10,22,0,0,0,0,0), Datetime(2013,10,23,0,0,0,0,0), Datetime(2013,10,24,0,0,0,0,0), Datetime(2013,10,25,0,0,0,0,0), Datetime(2013,10,28,0,0,0,0,0), Datetime(2013,10,29,0,0,0,0,0), Datetime(2013,10,30,0,0,0,0,0), Datetime(2013,10,31,0,0,0,0,0), Datetime(2013,11,1,0,0,0,0,0), Datetime(2013,11,4,0,0,0,0,0), Datetime(2013,11,5,0,0,0,0,0), Datetime(2013,11,6,0,0,0,0,0), Datetime(2013,11,7,0,0,0,0,0), Datetime(2013,11,8,0,0,0,0,0), Datetime(2013,11,11,0,0,0,0,0), Datetime(2013,11,12,0,0,0,0,0), Datetime(2013,11,13,0,0,0,0,0), Datetime(2013,11,14,0,0,0,0,0), Datetime(2013,11,15,0,0,0,0,0), Datetime(2013,11,18,0,0,0,0,0), Datetime(2013,11,19,0,0,0,0,0), Datetime(2013,11,20,0,0,0,0,0), Datetime(2013,11,21,0,0,0,0,0), Datetime(2013,11,22,0,0,0,0,0), Datetime(2013,11,25,0,0,0,0,0), Datetime(2013,11,26,0,0,0,0,0), Datetime(2013,11,27,0,0,0,0,0), Datetime(2013,11,28,0,0,0,0,0), Datetime(2013,11,29,0,0,0,0,0), Datetime(2013,12,2,0,0,0,0,0), Datetime(2013,12,3,0,0,0,0,0), Datetime(2013,12,4,0,0,0,0,0), Datetime(2013,12,5,0,0,0,0,0), Datetime(2013,12,6,0,0,0,0,0), Datetime(2013,12,9,0,0,0,0,0), Datetime(2013,12,10,0,0,0,0,0), Datetime(2013,12,11,0,0,0,0,0), Datetime(2013,12,12,0,0,0,0,0), Datetime(2013,12,13,0,0,0,0,0), Datetime(2013,12,16,0,0,0,0,0), Datetime(2013,12,17,0,0,0,0,0), Datetime(2013,12,18,0,0,0,0,0), Datetime(2013,12,19,0,0,0,0,0), Datetime(2013,12,20,0,0,0,0,0), Datetime(2013,12,23,0,0,0,0,0), Datetime(2013,12,24,0,0,0,0,0), Datetime(2013,12,25,0,0,0,0,0), Datetime(2013,12,26,0,0,0,0,0), Datetime(2013,12,27,0,0,0,0,0), Datetime(2013,12,30,0,0,0,0,0), Datetime(2013,12,31,0,0,0,0,0), Datetime(2014,1,2,0,0,0,0,0), Datetime(2014,1,3,0,0,0,0,0), Datetime(2014,1,6,0,0,0,0,0), Datetime(2014,1,7,0,0,0,0,0), Datetime(2014,1,8,0,0,0,0,0), Datetime(2014,1,9,0,0,0,0,0), Datetime(2014,1,10,0,0,0,0,0), Datetime(2014,1,13,0,0,0,0,0), Datetime(2014,1,14,0,0,0,0,0), Datetime(2014,1,15,0,0,0,0,0), Datetime(2014,1,16,0,0,0,0,0), Datetime(2014,1,17,0,0,0,0,0), Datetime(2014,1,20,0,0,0,0,0), Datetime(2014,1,21,0,0,0,0,0), Datetime(2014,1,22,0,0,0,0,0), Datetime(2014,1,23,0,0,0,0,0), Datetime(2014,1,24,0,0,0,0,0), Datetime(2014,1,27,0,0,0,0,0), Datetime(2014,1,28,0,0,0,0,0), Datetime(2014,1,29,0,0,0,0,0), Datetime(2014,1,30,0,0,0,0,0), Datetime(2014,2,7,0,0,0,0,0), Datetime(2014,2,10,0,0,0,0,0), Datetime(2014,2,11,0,0,0,0,0), Datetime(2014,2,12,0,0,0,0,0), Datetime(2014,2,13,0,0,0,0,0), Datetime(2014,2,14,0,0,0,0,0), Datetime(2014,2,17,0,0,0,0,0), Datetime(2014,2,18,0,0,0,0,0), Datetime(2014,2,19,0,0,0,0,0), Datetime(2014,2,20,0,0,0,0,0), Datetime(2014,2,21,0,0,0,0,0), Datetime(2014,2,24,0,0,0,0,0), Datetime(2014,2,25,0,0,0,0,0), Datetime(2014,2,26,0,0,0,0,0), Datetime(2014,2,27,0,0,0,0,0), Datetime(2014,2,28,0,0,0,0,0), Datetime(2014,3,3,0,0,0,0,0), Datetime(2014,3,4,0,0,0,0,0), Datetime(2014,3,5,0,0,0,0,0), Datetime(2014,3,6,0,0,0,0,0), Datetime(2014,3,7,0,0,0,0,0), Datetime(2014,3,10,0,0,0,0,0), Datetime(2014,3,11,0,0,0,0,0), Datetime(2014,3,12,0,0,0,0,0), Datetime(2014,3,13,0,0,0,0,0), Datetime(2014,3,14,0,0,0,0,0), Datetime(2014,3,17,0,0,0,0,0), Datetime(2014,3,18,0,0,0,0,0), Datetime(2014,3,19,0,0,0,0,0), Datetime(2014,3,20,0,0,0,0,0), Datetime(2014,3,21,0,0,0,0,0), Datetime(2014,3,24,0,0,0,0,0), Datetime(2014,3,25,0,0,0,0,0), Datetime(2014,3,26,0,0,0,0,0), Datetime(2014,3,27,0,0,0,0,0), Datetime(2014,3,28,0,0,0,0,0), Datetime(2014,3,31,0,0,0,0,0), Datetime(2014,4,1,0,0,0,0,0), Datetime(2014,4,2,0,0,0,0,0), Datetime(2014,4,3,0,0,0,0,0), Datetime(2014,4,4,0,0,0,0,0), Datetime(2014,4,8,0,0,0,0,0), Datetime(2014,4,9,0,0,0,0,0), Datetime(2014,4,10,0,0,0,0,0), Datetime(2014,4,11,0,0,0,0,0), Datetime(2014,4,14,0,0,0,0,0), Datetime(2014,4,15,0,0,0,0,0), Datetime(2014,4,16,0,0,0,0,0), Datetime(2014,4,17,0,0,0,0,0), Datetime(2014,4,18,0,0,0,0,0), Datetime(2014,4,21,0,0,0,0,0), Datetime(2014,4,22,0,0,0,0,0), Datetime(2014,4,23,0,0,0,0,0), Datetime(2014,4,24,0,0,0,0,0), Datetime(2014,4,25,0,0,0,0,0), Datetime(2014,4,28,0,0,0,0,0), Datetime(2014,4,29,0,0,0,0,0), Datetime(2014,4,30,0,0,0,0,0), Datetime(2014,5,5,0,0,0,0,0), Datetime(2014,5,6,0,0,0,0,0), Datetime(2014,5,7,0,0,0,0,0), Datetime(2014,5,8,0,0,0,0,0), Datetime(2014,5,9,0,0,0,0,0), Datetime(2014,5,12,0,0,0,0,0), Datetime(2014,5,13,0,0,0,0,0), Datetime(2014,5,14,0,0,0,0,0), Datetime(2014,5,15,0,0,0,0,0), Datetime(2014,5,16,0,0,0,0,0), Datetime(2014,5,19,0,0,0,0,0), Datetime(2014,5,20,0,0,0,0,0), Datetime(2014,5,21,0,0,0,0,0), Datetime(2014,5,22,0,0,0,0,0), Datetime(2014,5,23,0,0,0,0,0), Datetime(2014,5,26,0,0,0,0,0), Datetime(2014,5,27,0,0,0,0,0), Datetime(2014,5,28,0,0,0,0,0), Datetime(2014,5,29,0,0,0,0,0), Datetime(2014,5,30,0,0,0,0,0), Datetime(2014,6,3,0,0,0,0,0), Datetime(2014,6,4,0,0,0,0,0), Datetime(2014,6,5,0,0,0,0,0), Datetime(2014,6,6,0,0,0,0,0), Datetime(2014,6,9,0,0,0,0,0), Datetime(2014,6,10,0,0,0,0,0), Datetime(2014,6,11,0,0,0,0,0), Datetime(2014,6,12,0,0,0,0,0), Datetime(2014,6,13,0,0,0,0,0), Datetime(2014,6,16,0,0,0,0,0), Datetime(2014,6,17,0,0,0,0,0), Datetime(2014,6,18,0,0,0,0,0), Datetime(2014,6,19,0,0,0,0,0), Datetime(2014,6,20,0,0,0,0,0), Datetime(2014,6,23,0,0,0,0,0), Datetime(2014,6,24,0,0,0,0,0), Datetime(2014,6,25,0,0,0,0,0), Datetime(2014,6,26,0,0,0,0,0), Datetime(2014,6,27,0,0,0,0,0), Datetime(2014,6,30,0,0,0,0,0), Datetime(2014,7,1,0,0,0,0,0), Datetime(2014,7,2,0,0,0,0,0), Datetime(2014,7,3,0,0,0,0,0), Datetime(2014,7,4,0,0,0,0,0), Datetime(2014,7,7,0,0,0,0,0), Datetime(2014,7,8,0,0,0,0,0), Datetime(2014,7,9,0,0,0,0,0), Datetime(2014,7,10,0,0,0,0,0), Datetime(2014,7,11,0,0,0,0,0), Datetime(2014,7,14,0,0,0,0,0), Datetime(2014,7,15,0,0,0,0,0), Datetime(2014,7,16,0,0,0,0,0), Datetime(2014,7,17,0,0,0,0,0), Datetime(2014,7,18,0,0,0,0,0), Datetime(2014,7,21,0,0,0,0,0), Datetime(2014,7,22,0,0,0,0,0), Datetime(2014,7,23,0,0,0,0,0), Datetime(2014,7,24,0,0,0,0,0), Datetime(2014,7,25,0,0,0,0,0), Datetime(2014,7,28,0,0,0,0,0), Datetime(2014,7,29,0,0,0,0,0), Datetime(2014,7,30,0,0,0,0,0), Datetime(2014,7,31,0,0,0,0,0), Datetime(2014,8,1,0,0,0,0,0), Datetime(2014,8,4,0,0,0,0,0), Datetime(2014,8,5,0,0,0,0,0), Datetime(2014,8,6,0,0,0,0,0), Datetime(2014,8,7,0,0,0,0,0), Datetime(2014,8,8,0,0,0,0,0), Datetime(2014,8,11,0,0,0,0,0), Datetime(2014,8,12,0,0,0,0,0), Datetime(2014,8,13,0,0,0,0,0), Datetime(2014,8,14,0,0,0,0,0), Datetime(2014,8,15,0,0,0,0,0), Datetime(2014,8,18,0,0,0,0,0), Datetime(2014,8,19,0,0,0,0,0), Datetime(2014,8,20,0,0,0,0,0), Datetime(2014,8,21,0,0,0,0,0), Datetime(2014,8,22,0,0,0,0,0), Datetime(2014,8,25,0,0,0,0,0), Datetime(2014,8,26,0,0,0,0,0), Datetime(2014,8,27,0,0,0,0,0), Datetime(2014,8,28,0,0,0,0,0), Datetime(2014,8,29,0,0,0,0,0), Datetime(2014,9,1,0,0,0,0,0), Datetime(2014,9,2,0,0,0,0,0), Datetime(2014,9,3,0,0,0,0,0), Datetime(2014,9,4,0,0,0,0,0), Datetime(2014,9,5,0,0,0,0,0), Datetime(2014,9,9,0,0,0,0,0), Datetime(2014,9,10,0,0,0,0,0), Datetime(2014,9,11,0,0,0,0,0), Datetime(2014,9,12,0,0,0,0,0), Datetime(2014,9,15,0,0,0,0,0), Datetime(2014,9,16,0,0,0,0,0), Datetime(2014,9,17,0,0,0,0,0), Datetime(2014,9,18,0,0,0,0,0), Datetime(2014,9,19,0,0,0,0,0), Datetime(2014,9,22,0,0,0,0,0), Datetime(2014,9,23,0,0,0,0,0), Datetime(2014,9,24,0,0,0,0,0), Datetime(2014,9,25,0,0,0,0,0), Datetime(2014,9,26,0,0,0,0,0), Datetime(2014,9,29,0,0,0,0,0), Datetime(2014,9,30,0,0,0,0,0), Datetime(2014,10,8,0,0,0,0,0), Datetime(2014,10,9,0,0,0,0,0), Datetime(2014,10,10,0,0,0,0,0), Datetime(2014,10,13,0,0,0,0,0), Datetime(2014,10,14,0,0,0,0,0), Datetime(2014,10,15,0,0,0,0,0), Datetime(2014,10,16,0,0,0,0,0), Datetime(2014,10,17,0,0,0,0,0), Datetime(2014,10,20,0,0,0,0,0), Datetime(2014,10,21,0,0,0,0,0), Datetime(2014,10,22,0,0,0,0,0), Datetime(2014,10,23,0,0,0,0,0), Datetime(2014,10,24,0,0,0,0,0), Datetime(2014,10,27,0,0,0,0,0), Datetime(2014,10,28,0,0,0,0,0), Datetime(2014,10,29,0,0,0,0,0), Datetime(2014,10,30,0,0,0,0,0), Datetime(2014,10,31,0,0,0,0,0), Datetime(2014,11,3,0,0,0,0,0), Datetime(2014,11,4,0,0,0,0,0), Datetime(2014,11,5,0,0,0,0,0), Datetime(2014,11,6,0,0,0,0,0), Datetime(2014,11,7,0,0,0,0,0), Datetime(2014,11,10,0,0,0,0,0), Datetime(2014,11,11,0,0,0,0,0), Datetime(2014,11,12,0,0,0,0,0), Datetime(2014,11,13,0,0,0,0,0), Datetime(2014,11,14,0,0,0,0,0), Datetime(2014,11,17,0,0,0,0,0), Datetime(2014,11,18,0,0,0,0,0), Datetime(2014,11,19,0,0,0,0,0), Datetime(2014,11,20,0,0,0,0,0), Datetime(2014,11,21,0,0,0,0,0), Datetime(2014,11,24,0,0,0,0,0), Datetime(2014,11,25,0,0,0,0,0), Datetime(2014,11,26,0,0,0,0,0), Datetime(2014,11,27,0,0,0,0,0), Datetime(2014,11,28,0,0,0,0,0), Datetime(2014,12,1,0,0,0,0,0), Datetime(2014,12,2,0,0,0,0,0), Datetime(2014,12,3,0,0,0,0,0), Datetime(2014,12,4,0,0,0,0,0), Datetime(2014,12,5,0,0,0,0,0), Datetime(2014,12,8,0,0,0,0,0), Datetime(2014,12,9,0,0,0,0,0), Datetime(2014,12,10,0,0,0,0,0), Datetime(2014,12,11,0,0,0,0,0), Datetime(2014,12,12,0,0,0,0,0), Datetime(2014,12,15,0,0,0,0,0), Datetime(2014,12,16,0,0,0,0,0), Datetime(2014,12,17,0,0,0,0,0), Datetime(2014,12,18,0,0,0,0,0), Datetime(2014,12,19,0,0,0,0,0), Datetime(2014,12,22,0,0,0,0,0), Datetime(2014,12,23,0,0,0,0,0), Datetime(2014,12,24,0,0,0,0,0), Datetime(2014,12,25,0,0,0,0,0), Datetime(2014,12,26,0,0,0,0,0), Datetime(2014,12,29,0,0,0,0,0), Datetime(2014,12,30,0,0,0,0,0), Datetime(2014,12,31,0,0,0,0,0), Datetime(2015,1,5,0,0,0,0,0), Datetime(2015,1,6,0,0,0,0,0), Datetime(2015,1,7,0,0,0,0,0), Datetime(2015,1,8,0,0,0,0,0), Datetime(2015,1,9,0,0,0,0,0), Datetime(2015,1,12,0,0,0,0,0), Datetime(2015,1,13,0,0,0,0,0), Datetime(2015,1,14,0,0,0,0,0), Datetime(2015,1,15,0,0,0,0,0), Datetime(2015,1,16,0,0,0,0,0), Datetime(2015,1,19,0,0,0,0,0), Datetime(2015,1,20,0,0,0,0,0), Datetime(2015,1,21,0,0,0,0,0), Datetime(2015,1,22,0,0,0,0,0), Datetime(2015,1,23,0,0,0,0,0), Datetime(2015,1,26,0,0,0,0,0), Datetime(2015,1,27,0,0,0,0,0), Datetime(2015,1,28,0,0,0,0,0), Datetime(2015,1,29,0,0,0,0,0), Datetime(2015,1,30,0,0,0,0,0), Datetime(2015,2,2,0,0,0,0,0), Datetime(2015,2,3,0,0,0,0,0), Datetime(2015,2,4,0,0,0,0,0), Datetime(2015,2,5,0,0,0,0,0), Datetime(2015,2,6,0,0,0,0,0), Datetime(2015,2,9,0,0,0,0,0), Datetime(2015,2,10,0,0,0,0,0), Datetime(2015,2,11,0,0,0,0,0), Datetime(2015,2,12,0,0,0,0,0), Datetime(2015,2,13,0,0,0,0,0), Datetime(2015,2,16,0,0,0,0,0), Datetime(2015,2,17,0,0,0,0,0), Datetime(2015,2,25,0,0,0,0,0), Datetime(2015,2,26,0,0,0,0,0), Datetime(2015,2,27,0,0,0,0,0), Datetime(2015,3,2,0,0,0,0,0), Datetime(2015,3,3,0,0,0,0,0), Datetime(2015,3,4,0,0,0,0,0), Datetime(2015,3,5,0,0,0,0,0), Datetime(2015,3,6,0,0,0,0,0), Datetime(2015,3,9,0,0,0,0,0), Datetime(2015,3,10,0,0,0,0,0), Datetime(2015,3,11,0,0,0,0,0), Datetime(2015,3,12,0,0,0,0,0), Datetime(2015,3,13,0,0,0,0,0), Datetime(2015,3,16,0,0,0,0,0), Datetime(2015,3,17,0,0,0,0,0), Datetime(2015,3,18,0,0,0,0,0), Datetime(2015,3,19,0,0,0,0,0), Datetime(2015,3,20,0,0,0,0,0), Datetime(2015,3,23,0,0,0,0,0), Datetime(2015,3,24,0,0,0,0,0), Datetime(2015,3,25,0,0,0,0,0), Datetime(2015,3,26,0,0,0,0,0), Datetime(2015,3,27,0,0,0,0,0), Datetime(2015,3,30,0,0,0,0,0), Datetime(2015,3,31,0,0,0,0,0), Datetime(2015,4,1,0,0,0,0,0), Datetime(2015,4,2,0,0,0,0,0), Datetime(2015,4,3,0,0,0,0,0), Datetime(2015,4,7,0,0,0,0,0), Datetime(2015,4,8,0,0,0,0,0), Datetime(2015,4,9,0,0,0,0,0), Datetime(2015,4,10,0,0,0,0,0), Datetime(2015,4,13,0,0,0,0,0), Datetime(2015,4,14,0,0,0,0,0), Datetime(2015,4,15,0,0,0,0,0), Datetime(2015,4,16,0,0,0,0,0), Datetime(2015,4,17,0,0,0,0,0), Datetime(2015,4,20,0,0,0,0,0), Datetime(2015,4,21,0,0,0,0,0), Datetime(2015,4,22,0,0,0,0,0), Datetime(2015,4,23,0,0,0,0,0), Datetime(2015,4,24,0,0,0,0,0), Datetime(2015,4,27,0,0,0,0,0), Datetime(2015,4,28,0,0,0,0,0), Datetime(2015,4,29,0,0,0,0,0), Datetime(2015,4,30,0,0,0,0,0), Datetime(2015,5,4,0,0,0,0,0), Datetime(2015,5,5,0,0,0,0,0), Datetime(2015,5,6,0,0,0,0,0), Datetime(2015,5,7,0,0,0,0,0), Datetime(2015,5,8,0,0,0,0,0), Datetime(2015,5,11,0,0,0,0,0), Datetime(2015,5,12,0,0,0,0,0), Datetime(2015,5,13,0,0,0,0,0), Datetime(2015,5,14,0,0,0,0,0), Datetime(2015,5,15,0,0,0,0,0), Datetime(2015,5,18,0,0,0,0,0), Datetime(2015,5,19,0,0,0,0,0), Datetime(2015,5,20,0,0,0,0,0), Datetime(2015,5,21,0,0,0,0,0), Datetime(2015,5,22,0,0,0,0,0), Datetime(2015,5,25,0,0,0,0,0), Datetime(2015,5,26,0,0,0,0,0), Datetime(2015,5,27,0,0,0,0,0), Datetime(2015,5,28,0,0,0,0,0), Datetime(2015,5,29,0,0,0,0,0), Datetime(2015,6,1,0,0,0,0,0), Datetime(2015,6,2,0,0,0,0,0), Datetime(2015,6,3,0,0,0,0,0), Datetime(2015,6,4,0,0,0,0,0), Datetime(2015,6,5,0,0,0,0,0), Datetime(2015,6,8,0,0,0,0,0), Datetime(2015,6,9,0,0,0,0,0), Datetime(2015,6,10,0,0,0,0,0), Datetime(2015,6,11,0,0,0,0,0), Datetime(2015,6,12,0,0,0,0,0), Datetime(2015,6,15,0,0,0,0,0), Datetime(2015,6,17,0,0,0,0,0), Datetime(2015,6,18,0,0,0,0,0), Datetime(2015,6,19,0,0,0,0,0), Datetime(2015,6,23,0,0,0,0,0), Datetime(2015,6,24,0,0,0,0,0), Datetime(2015,6,25,0,0,0,0,0), Datetime(2015,6,26,0,0,0,0,0), Datetime(2015,6,29,0,0,0,0,0), Datetime(2015,6,30,0,0,0,0,0), Datetime(2015,7,1,0,0,0,0,0), Datetime(2015,7,2,0,0,0,0,0), Datetime(2015,7,3,0,0,0,0,0), Datetime(2015,7,6,0,0,0,0,0), Datetime(2015,7,7,0,0,0,0,0), Datetime(2015,7,8,0,0,0,0,0), Datetime(2015,7,9,0,0,0,0,0), Datetime(2015,7,10,0,0,0,0,0), Datetime(2015,7,13,0,0,0,0,0), Datetime(2015,7,14,0,0,0,0,0), Datetime(2015,7,15,0,0,0,0,0), Datetime(2015,7,16,0,0,0,0,0), Datetime(2015,7,17,0,0,0,0,0), Datetime(2015,7,20,0,0,0,0,0), Datetime(2015,7,21,0,0,0,0,0), Datetime(2015,7,22,0,0,0,0,0), Datetime(2015,7,23,0,0,0,0,0), Datetime(2015,7,24,0,0,0,0,0), Datetime(2015,7,27,0,0,0,0,0), Datetime(2015,7,28,0,0,0,0,0), Datetime(2015,7,29,0,0,0,0,0), Datetime(2015,7,30,0,0,0,0,0), Datetime(2015,7,31,0,0,0,0,0), Datetime(2015,8,3,0,0,0,0,0), Datetime(2015,8,4,0,0,0,0,0), Datetime(2015,8,5,0,0,0,0,0), Datetime(2015,8,6,0,0,0,0,0), Datetime(2015,8,7,0,0,0,0,0), Datetime(2015,8,10,0,0,0,0,0), Datetime(2015,8,11,0,0,0,0,0), Datetime(2015,8,12,0,0,0,0,0), Datetime(2015,8,13,0,0,0,0,0), Datetime(2015,8,14,0,0,0,0,0), Datetime(2015,8,17,0,0,0,0,0), Datetime(2015,8,18,0,0,0,0,0), Datetime(2015,8,19,0,0,0,0,0), Datetime(2015,8,20,0,0,0,0,0), Datetime(2015,8,21,0,0,0,0,0), Datetime(2015,8,24,0,0,0,0,0), Datetime(2015,8,25,0,0,0,0,0), Datetime(2015,8,26,0,0,0,0,0), Datetime(2015,8,27,0,0,0,0,0), Datetime(2015,8,28,0,0,0,0,0), Datetime(2015,8,31,0,0,0,0,0), Datetime(2015,9,1,0,0,0,0,0), Datetime(2015,9,2,0,0,0,0,0), Datetime(2015,9,7,0,0,0,0,0), Datetime(2015,9,8,0,0,0,0,0), Datetime(2015,9,9,0,0,0,0,0), Datetime(2015,9,10,0,0,0,0,0), Datetime(2015,9,11,0,0,0,0,0), Datetime(2015,9,14,0,0,0,0,0), Datetime(2015,9,15,0,0,0,0,0), Datetime(2015,9,16,0,0,0,0,0), Datetime(2015,9,17,0,0,0,0,0), Datetime(2015,9,18,0,0,0,0,0), Datetime(2015,9,21,0,0,0,0,0), Datetime(2015,9,22,0,0,0,0,0), Datetime(2015,9,23,0,0,0,0,0), Datetime(2015,9,24,0,0,0,0,0), Datetime(2015,9,25,0,0,0,0,0), Datetime(2015,9,28,0,0,0,0,0), Datetime(2015,9,29,0,0,0,0,0), Datetime(2015,9,30,0,0,0,0,0), Datetime(2015,10,8,0,0,0,0,0), Datetime(2015,10,9,0,0,0,0,0), Datetime(2015,10,12,0,0,0,0,0), Datetime(2015,10,13,0,0,0,0,0), Datetime(2015,10,14,0,0,0,0,0), Datetime(2015,10,15,0,0,0,0,0), Datetime(2015,10,16,0,0,0,0,0), Datetime(2015,10,19,0,0,0,0,0), Datetime(2015,10,20,0,0,0,0,0), Datetime(2015,10,21,0,0,0,0,0), Datetime(2015,10,22,0,0,0,0,0), Datetime(2015,10,23,0,0,0,0,0), Datetime(2015,10,26,0,0,0,0,0), Datetime(2015,10,27,0,0,0,0,0), Datetime(2015,10,28,0,0,0,0,0), Datetime(2015,10,29,0,0,0,0,0), Datetime(2015,10,30,0,0,0,0,0), Datetime(2015,11,2,0,0,0,0,0), Datetime(2015,11,3,0,0,0,0,0), Datetime(2015,11,4,0,0,0,0,0), Datetime(2015,11,5,0,0,0,0,0), Datetime(2015,11,6,0,0,0,0,0), Datetime(2015,11,9,0,0,0,0,0), Datetime(2015,11,10,0,0,0,0,0), Datetime(2015,11,11,0,0,0,0,0), Datetime(2015,11,12,0,0,0,0,0), Datetime(2015,11,13,0,0,0,0,0), Datetime(2015,11,16,0,0,0,0,0), Datetime(2015,11,17,0,0,0,0,0), Datetime(2015,11,18,0,0,0,0,0), Datetime(2015,11,19,0,0,0,0,0), Datetime(2015,11,20,0,0,0,0,0), Datetime(2015,11,23,0,0,0,0,0), Datetime(2015,11,24,0,0,0,0,0), Datetime(2015,11,25,0,0,0,0,0), Datetime(2015,11,26,0,0,0,0,0), Datetime(2015,11,27,0,0,0,0,0), Datetime(2015,11,30,0,0,0,0,0), Datetime(2015,12,1,0,0,0,0,0), Datetime(2015,12,2,0,0,0,0,0), Datetime(2015,12,3,0,0,0,0,0), Datetime(2015,12,4,0,0,0,0,0), Datetime(2015,12,7,0,0,0,0,0), Datetime(2015,12,8,0,0,0,0,0), Datetime(2015,12,9,0,0,0,0,0), Datetime(2015,12,10,0,0,0,0,0), Datetime(2015,12,11,0,0,0,0,0), Datetime(2015,12,14,0,0,0,0,0), Datetime(2015,12,15,0,0,0,0,0), Datetime(2015,12,16,0,0,0,0,0), Datetime(2015,12,17,0,0,0,0,0), Datetime(2015,12,18,0,0,0,0,0), Datetime(2015,12,21,0,0,0,0,0), Datetime(2015,12,22,0,0,0,0,0), Datetime(2015,12,23,0,0,0,0,0), Datetime(2015,12,24,0,0,0,0,0), Datetime(2015,12,25,0,0,0,0,0), Datetime(2015,12,28,0,0,0,0,0), Datetime(2015,12,29,0,0,0,0,0), Datetime(2015,12,30,0,0,0,0,0), Datetime(2015,12,31,0,0,0,0,0), Datetime(2016,1,4,0,0,0,0,0), Datetime(2016,1,5,0,0,0,0,0), Datetime(2016,1,6,0,0,0,0,0), Datetime(2016,1,7,0,0,0,0,0), Datetime(2016,1,8,0,0,0,0,0), Datetime(2016,1,11,0,0,0,0,0), Datetime(2016,1,12,0,0,0,0,0), Datetime(2016,1,13,0,0,0,0,0), Datetime(2016,1,14,0,0,0,0,0), Datetime(2016,1,15,0,0,0,0,0), Datetime(2016,1,18,0,0,0,0,0), Datetime(2016,1,19,0,0,0,0,0), Datetime(2016,1,20,0,0,0,0,0), Datetime(2016,1,21,0,0,0,0,0), Datetime(2016,1,22,0,0,0,0,0), Datetime(2016,1,25,0,0,0,0,0), Datetime(2016,1,26,0,0,0,0,0), Datetime(2016,1,27,0,0,0,0,0), Datetime(2016,1,28,0,0,0,0,0), Datetime(2016,1,29,0,0,0,0,0), Datetime(2016,2,1,0,0,0,0,0), Datetime(2016,2,2,0,0,0,0,0), Datetime(2016,2,3,0,0,0,0,0), Datetime(2016,2,4,0,0,0,0,0), Datetime(2016,2,5,0,0,0,0,0), Datetime(2016,2,15,0,0,0,0,0), Datetime(2016,2,16,0,0,0,0,0), Datetime(2016,2,17,0,0,0,0,0), Datetime(2016,2,18,0,0,0,0,0), Datetime(2016,2,19,0,0,0,0,0), Datetime(2016,2,22,0,0,0,0,0), Datetime(2016,2,23,0,0,0,0,0), Datetime(2016,2,24,0,0,0,0,0), Datetime(2016,2,25,0,0,0,0,0), Datetime(2016,2,26,0,0,0,0,0), Datetime(2016,2,29,0,0,0,0,0), Datetime(2016,3,1,0,0,0,0,0), Datetime(2016,3,2,0,0,0,0,0), Datetime(2016,3,3,0,0,0,0,0), Datetime(2016,3,4,0,0,0,0,0), Datetime(2016,3,7,0,0,0,0,0), Datetime(2016,3,8,0,0,0,0,0), Datetime(2016,3,9,0,0,0,0,0), Datetime(2016,3,10,0,0,0,0,0), Datetime(2016,3,11,0,0,0,0,0), Datetime(2016,3,14,0,0,0,0,0), Datetime(2016,3,15,0,0,0,0,0), Datetime(2016,3,16,0,0,0,0,0), Datetime(2016,3,17,0,0,0,0,0), Datetime(2016,3,18,0,0,0,0,0), Datetime(2016,3,21,0,0,0,0,0), Datetime(2016,3,22,0,0,0,0,0), Datetime(2016,3,23,0,0,0,0,0), Datetime(2016,3,24,0,0,0,0,0), Datetime(2016,3,25,0,0,0,0,0), Datetime(2016,3,28,0,0,0,0,0), Datetime(2016,3,29,0,0,0,0,0), Datetime(2016,3,30,0,0,0,0,0), Datetime(2016,3,31,0,0,0,0,0), Datetime(2016,4,1,0,0,0,0,0), Datetime(2016,4,5,0,0,0,0,0), Datetime(2016,4,6,0,0,0,0,0), Datetime(2016,4,7,0,0,0,0,0), Datetime(2016,4,8,0,0,0,0,0), Datetime(2016,4,11,0,0,0,0,0), Datetime(2016,4,12,0,0,0,0,0), Datetime(2016,4,13,0,0,0,0,0), Datetime(2016,4,14,0,0,0,0,0), Datetime(2016,4,15,0,0,0,0,0), Datetime(2016,4,18,0,0,0,0,0), Datetime(2016,4,19,0,0,0,0,0), Datetime(2016,4,20,0,0,0,0,0), Datetime(2016,4,21,0,0,0,0,0), Datetime(2016,4,22,0,0,0,0,0), Datetime(2016,4,25,0,0,0,0,0), Datetime(2016,4,26,0,0,0,0,0), Datetime(2016,4,27,0,0,0,0,0), Datetime(2016,4,28,0,0,0,0,0), Datetime(2016,4,29,0,0,0,0,0), Datetime(2016,5,3,0,0,0,0,0), Datetime(2016,5,4,0,0,0,0,0), Datetime(2016,5,5,0,0,0,0,0), Datetime(2016,5,6,0,0,0,0,0), Datetime(2016,5,9,0,0,0,0,0), Datetime(2016,5,10,0,0,0,0,0), Datetime(2016,5,11,0,0,0,0,0), Datetime(2016,5,12,0,0,0,0,0), Datetime(2016,5,13,0,0,0,0,0), Datetime(2016,5,16,0,0,0,0,0), Datetime(2016,5,17,0,0,0,0,0), Datetime(2016,5,18,0,0,0,0,0), Datetime(2016,5,19,0,0,0,0,0), Datetime(2016,5,20,0,0,0,0,0), Datetime(2016,5,23,0,0,0,0,0), Datetime(2016,5,24,0,0,0,0,0), Datetime(2016,5,25,0,0,0,0,0), Datetime(2016,5,26,0,0,0,0,0), Datetime(2016,5,27,0,0,0,0,0), Datetime(2016,5,30,0,0,0,0,0), Datetime(2016,5,31,0,0,0,0,0), Datetime(2016,6,1,0,0,0,0,0), Datetime(2016,6,2,0,0,0,0,0), Datetime(2016,6,3,0,0,0,0,0), Datetime(2016,6,6,0,0,0,0,0), Datetime(2016,6,7,0,0,0,0,0), Datetime(2016,6,8,0,0,0,0,0), Datetime(2016,6,13,0,0,0,0,0), Datetime(2016,6,14,0,0,0,0,0), Datetime(2016,6,15,0,0,0,0,0), Datetime(2016,6,16,0,0,0,0,0), Datetime(2016,6,17,0,0,0,0,0), Datetime(2016,6,20,0,0,0,0,0), Datetime(2016,6,21,0,0,0,0,0), Datetime(2016,6,22,0,0,0,0,0), Datetime(2016,6,23,0,0,0,0,0), Datetime(2016,6,24,0,0,0,0,0), Datetime(2016,6,27,0,0,0,0,0), Datetime(2016,6,28,0,0,0,0,0), Datetime(2016,6,29,0,0,0,0,0), Datetime(2016,6,30,0,0,0,0,0), Datetime(2016,7,1,0,0,0,0,0), Datetime(2016,7,4,0,0,0,0,0), Datetime(2016,7,5,0,0,0,0,0), Datetime(2016,7,6,0,0,0,0,0), Datetime(2016,7,7,0,0,0,0,0), Datetime(2016,7,8,0,0,0,0,0), Datetime(2016,7,11,0,0,0,0,0), Datetime(2016,7,12,0,0,0,0,0), Datetime(2016,7,13,0,0,0,0,0), Datetime(2016,7,14,0,0,0,0,0), Datetime(2016,7,15,0,0,0,0,0), Datetime(2016,7,18,0,0,0,0,0), Datetime(2016,7,19,0,0,0,0,0), Datetime(2016,7,20,0,0,0,0,0), Datetime(2016,7,21,0,0,0,0,0), Datetime(2016,7,22,0,0,0,0,0), Datetime(2016,7,25,0,0,0,0,0), Datetime(2016,7,26,0,0,0,0,0), Datetime(2016,7,27,0,0,0,0,0), Datetime(2016,7,28,0,0,0,0,0), Datetime(2016,7,29,0,0,0,0,0), Datetime(2016,8,1,0,0,0,0,0), Datetime(2016,8,2,0,0,0,0,0), Datetime(2016,8,3,0,0,0,0,0), Datetime(2016,8,4,0,0,0,0,0), Datetime(2016,8,5,0,0,0,0,0), Datetime(2016,8,8,0,0,0,0,0), Datetime(2016,8,9,0,0,0,0,0), Datetime(2016,8,10,0,0,0,0,0), Datetime(2016,8,11,0,0,0,0,0), Datetime(2016,8,12,0,0,0,0,0), Datetime(2016,8,15,0,0,0,0,0), Datetime(2016,8,16,0,0,0,0,0), Datetime(2016,8,17,0,0,0,0,0), Datetime(2016,8,18,0,0,0,0,0), Datetime(2016,8,19,0,0,0,0,0), Datetime(2016,8,22,0,0,0,0,0), Datetime(2016,8,23,0,0,0,0,0), Datetime(2016,8,24,0,0,0,0,0), Datetime(2016,8,25,0,0,0,0,0), Datetime(2016,8,26,0,0,0,0,0), Datetime(2016,8,29,0,0,0,0,0), Datetime(2016,8,30,0,0,0,0,0), Datetime(2016,8,31,0,0,0,0,0), Datetime(2016,9,1,0,0,0,0,0), Datetime(2016,9,2,0,0,0,0,0), Datetime(2016,9,5,0,0,0,0,0), Datetime(2016,9,6,0,0,0,0,0), Datetime(2016,9,7,0,0,0,0,0), Datetime(2016,9,8,0,0,0,0,0), Datetime(2016,9,9,0,0,0,0,0), Datetime(2016,9,12,0,0,0,0,0), Datetime(2016,9,13,0,0,0,0,0), Datetime(2016,9,14,0,0,0,0,0), Datetime(2016,9,19,0,0,0,0,0), Datetime(2016,9,20,0,0,0,0,0), Datetime(2016,9,21,0,0,0,0,0), Datetime(2016,9,22,0,0,0,0,0), Datetime(2016,9,23,0,0,0,0,0), Datetime(2016,9,26,0,0,0,0,0), Datetime(2016,9,27,0,0,0,0,0), Datetime(2016,9,28,0,0,0,0,0), Datetime(2016,9,29,0,0,0,0,0), Datetime(2016,9,30,0,0,0,0,0), Datetime(2016,10,10,0,0,0,0,0), Datetime(2016,10,11,0,0,0,0,0), Datetime(2016,10,12,0,0,0,0,0), Datetime(2016,10,13,0,0,0,0,0), Datetime(2016,10,14,0,0,0,0,0), Datetime(2016,10,17,0,0,0,0,0), Datetime(2016,10,18,0,0,0,0,0), Datetime(2016,10,19,0,0,0,0,0), Datetime(2016,10,20,0,0,0,0,0), Datetime(2016,10,21,0,0,0,0,0), Datetime(2016,10,24,0,0,0,0,0), Datetime(2016,10,25,0,0,0,0,0), Datetime(2016,10,26,0,0,0,0,0), Datetime(2016,10,27,0,0,0,0,0), Datetime(2016,10,28,0,0,0,0,0), Datetime(2016,10,31,0,0,0,0,0), Datetime(2016,11,1,0,0,0,0,0), Datetime(2016,11,2,0,0,0,0,0), Datetime(2016,11,3,0,0,0,0,0), Datetime(2016,11,4,0,0,0,0,0), Datetime(2016,11,7,0,0,0,0,0), Datetime(2016,11,8,0,0,0,0,0), Datetime(2016,11,9,0,0,0,0,0), Datetime(2016,11,10,0,0,0,0,0), Datetime(2016,11,11,0,0,0,0,0), Datetime(2016,11,14,0,0,0,0,0), Datetime(2016,11,15,0,0,0,0,0), Datetime(2016,11,16,0,0,0,0,0), Datetime(2016,11,17,0,0,0,0,0), Datetime(2016,11,18,0,0,0,0,0), Datetime(2016,11,21,0,0,0,0,0), Datetime(2016,11,22,0,0,0,0,0), Datetime(2016,11,23,0,0,0,0,0), Datetime(2016,11,24,0,0,0,0,0), Datetime(2016,11,25,0,0,0,0,0), Datetime(2016,11,28,0,0,0,0,0), Datetime(2016,11,29,0,0,0,0,0), Datetime(2016,11,30,0,0,0,0,0), Datetime(2016,12,1,0,0,0,0,0), Datetime(2016,12,2,0,0,0,0,0), Datetime(2016,12,5,0,0,0,0,0), Datetime(2016,12,6,0,0,0,0,0), Datetime(2016,12,7,0,0,0,0,0), Datetime(2016,12,8,0,0,0,0,0), Datetime(2016,12,9,0,0,0,0,0), Datetime(2016,12,12,0,0,0,0,0), Datetime(2016,12,13,0,0,0,0,0), Datetime(2016,12,14,0,0,0,0,0), Datetime(2016,12,15,0,0,0,0,0), Datetime(2016,12,16,0,0,0,0,0), Datetime(2016,12,19,0,0,0,0,0), Datetime(2016,12,20,0,0,0,0,0), Datetime(2016,12,21,0,0,0,0,0), Datetime(2016,12,22,0,0,0,0,0), Datetime(2016,12,23,0,0,0,0,0), Datetime(2016,12,26,0,0,0,0,0), Datetime(2016,12,27,0,0,0,0,0), Datetime(2016,12,28,0,0,0,0,0), Datetime(2016,12,29,0,0,0,0,0), Datetime(2016,12,30,0,0,0,0,0), Datetime(2017,1,3,0,0,0,0,0), Datetime(2017,1,4,0,0,0,0,0), Datetime(2017,1,5,0,0,0,0,0), Datetime(2017,1,6,0,0,0,0,0), Datetime(2017,1,9,0,0,0,0,0), Datetime(2017,1,10,0,0,0,0,0), Datetime(2017,1,11,0,0,0,0,0), Datetime(2017,1,12,0,0,0,0,0), Datetime(2017,1,13,0,0,0,0,0), Datetime(2017,1,16,0,0,0,0,0), Datetime(2017,1,17,0,0,0,0,0), Datetime(2017,1,18,0,0,0,0,0), Datetime(2017,1,19,0,0,0,0,0), Datetime(2017,1,20,0,0,0,0,0), Datetime(2017,1,23,0,0,0,0,0), Datetime(2017,1,24,0,0,0,0,0), Datetime(2017,1,25,0,0,0,0,0), Datetime(2017,1,26,0,0,0,0,0), Datetime(2017,2,3,0,0,0,0,0), Datetime(2017,2,6,0,0,0,0,0), Datetime(2017,2,7,0,0,0,0,0), Datetime(2017,2,8,0,0,0,0,0), Datetime(2017,2,9,0,0,0,0,0), Datetime(2017,2,10,0,0,0,0,0), Datetime(2017,2,13,0,0,0,0,0), Datetime(2017,2,14,0,0,0,0,0), Datetime(2017,2,15,0,0,0,0,0), Datetime(2017,2,16,0,0,0,0,0), Datetime(2017,2,17,0,0,0,0,0), Datetime(2017,2,20,0,0,0,0,0), Datetime(2017,2,21,0,0,0,0,0), Datetime(2017,2,22,0,0,0,0,0), Datetime(2017,2,23,0,0,0,0,0), Datetime(2017,2,24,0,0,0,0,0), Datetime(2017,2,27,0,0,0,0,0), Datetime(2017,2,28,0,0,0,0,0), Datetime(2017,3,1,0,0,0,0,0), Datetime(2017,3,2,0,0,0,0,0), Datetime(2017,3,3,0,0,0,0,0), Datetime(2017,3,6,0,0,0,0,0), Datetime(2017,3,7,0,0,0,0,0), Datetime(2017,3,8,0,0,0,0,0), Datetime(2017,3,9,0,0,0,0,0), Datetime(2017,3,10,0,0,0,0,0), Datetime(2017,3,13,0,0,0,0,0), Datetime(2017,3,14,0,0,0,0,0), Datetime(2017,3,15,0,0,0,0,0), Datetime(2017,3,16,0,0,0,0,0), Datetime(2017,3,17,0,0,0,0,0), Datetime(2017,3,20,0,0,0,0,0), Datetime(2017,3,21,0,0,0,0,0), Datetime(2017,3,22,0,0,0,0,0), Datetime(2017,3,23,0,0,0,0,0), Datetime(2017,3,24,0,0,0,0,0), Datetime(2017,3,27,0,0,0,0,0), Datetime(2017,3,28,0,0,0,0,0), Datetime(2017,3,29,0,0,0,0,0), Datetime(2017,3,30,0,0,0,0,0), Datetime(2017,3,31,0,0,0,0,0), Datetime(2017,4,5,0,0,0,0,0), Datetime(2017,4,6,0,0,0,0,0), Datetime(2017,4,7,0,0,0,0,0), Datetime(2017,4,10,0,0,0,0,0), Datetime(2017,4,11,0,0,0,0,0), Datetime(2017,4,12,0,0,0,0,0), Datetime(2017,4,13,0,0,0,0,0), Datetime(2017,4,14,0,0,0,0,0), Datetime(2017,4,17,0,0,0,0,0), Datetime(2017,4,18,0,0,0,0,0), Datetime(2017,4,19,0,0,0,0,0), Datetime(2017,4,20,0,0,0,0,0), Datetime(2017,4,21,0,0,0,0,0), Datetime(2017,4,24,0,0,0,0,0), Datetime(2017,4,25,0,0,0,0,0), Datetime(2017,4,26,0,0,0,0,0), Datetime(2017,4,27,0,0,0,0,0), Datetime(2017,4,28,0,0,0,0,0)]" + "
" ] }, - "execution_count": 6, "metadata": {}, - "output_type": "execute_result" + "output_type": "display_data" } ], "source": [ - "calendar = sm.get_trading_calendar(query, 'SZ')\n", - "calendar" + "my_sys.performance()" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "帐户初始金额: 200000.00\n", + "累计投入本金: 200000.00\n", + "累计投入资产: 0.00\n", + "累计借入现金: 0.00\n", + "累计借入资产: 0.00\n", + "累计红利: 24265.60\n", + "现金余额: 1437223.90\n", + "未平仓头寸净值: 0.00\n", + "当前总资产: 1437223.90\n", + "已平仓交易总成本: 0.00\n", + "已平仓净利润总额: 1237223.90\n", + "单笔交易最大占用现金比例%: 99.99\n", + "交易平均占用现金比例%: 99.81\n", + "已平仓帐户收益率%: 618.61\n", + "帐户年复合收益率%: 8.86\n", + "帐户平均年收益率%: 26.62\n", + "赢利交易赢利总额: 2273323.90\n", + "亏损交易亏损总额: -1036100.00\n", + "已平仓交易总数: 74.00\n", + "赢利交易数: 31.00\n", + "亏损交易数: 43.00\n", + "赢利交易比例%: 41.89\n", + "赢利期望值: 16719.24\n", + "赢利交易平均赢利: 73333.03\n", + "亏损交易平均亏损: -24095.35\n", + "平均赢利/平均亏损比例: 3.04\n", + "净赢利/亏损比例: 2.19\n", + "最大单笔赢利: 441078.40\n", + "最大单笔盈利百分比%: 77.50\n", + "最大单笔亏损: -144279.00\n", + "最大单笔亏损百分比%: -15.28\n", + "赢利交易平均持仓时间: 44.55\n", + "赢利交易最大持仓时间: 118.00\n", + "亏损交易平均持仓时间: 15.93\n", + "亏损交易最大持仓时间: 79.00\n", + "空仓总时间: 6418.00\n", + "空仓时间/总时间%: 75.00\n", + "平均空仓时间: 86.00\n", + "最长空仓时间: 2580.00\n", + "最大连续赢利笔数: 2.00\n", + "最大连续亏损笔数: 1.00\n", + "最大连续赢利金额: 780802.40\n", + "最大连续亏损金额: -195900.00\n", + "R乘数期望值: 0.04\n", + "交易机会频率/年: 3.18\n", + "年度期望R乘数: 0.13\n", + "赢利交易平均R乘数: 0.15\n", + "亏损交易平均R乘数: -0.05\n", + "最大单笔赢利R乘数: 0.78\n", + "最大单笔亏损R乘数: -0.15\n", + "最大连续赢利R乘数: 0.88\n", + "最大连续亏损R乘数: -0.69\n", + "\n" + ] + } + ], + "source": [ + "per = Performance()\n", + "print(per.report(my_sys.tm, Datetime(datetime.today())))" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "calendar = sm.get_trading_calendar(query, 'SZ')\n", + "# calendar" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, "outputs": [], "source": [ "x1 = my_tm.get_funds_curve(calendar, Query.DAY)" @@ -131,12 +214,12 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { - "image/png": "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OyE7HA6NG9ZbH3xWa87ynQ3WinSJy2ne/9FfN6lf9xL5A0EdPtaqF+ado26lxL9sSbkivWHDBl144YWqqKjQwIEDNWfOHB06dEiStHz5ck2ePFk9evTQCSecoPnz56u5uTnk+UuWLNGpp56qsrIynXXWWVq/fn3I9pUrV2rChAkqKyvT6aefrldffTVk+6ZNmzRt2jSVl5dr5MiReuqpp0K279y5U5deeqm6deumQYMG6b777kv1SwQAAACApCUzRlqSPM62+JXVMdLtFelyj0sf/HiW/v3CkyVJW/c3aHXNYa3bfkSfHjimrz3ybvbeFKkH6Xnz5um8887T6tWrtWjRIr355pu64YYbJEmffPKJ/uVf/kVvv/22fvGLX+j//u//9JOf/MR87qpVq3Tttdfqlltu0Zo1azRs2DDNnj1bR48elSRt27ZNs2fP1syZM7Vu3TpNnz5dl19+ubZv3y5Jqq+v16xZszRixAitWbNGN910k2688UatXr1akuT3+3XJJZfI5/Pprbfe0oIFC3THHXfo2WefzfRzAgAAAICozFm7EwRpd/uyVNkM0t72CnSpx6lupW59Y9pIPX7jRP3qunH61XXjzP027z2qYDZL4V1cyrX9p59+WkOHDpUkjR07VnV1dbr++uvV2NioefPmmft98Ytf1FtvvaVly5bpnnvukSTde++9mj17tr7zne9IkhYtWqSBAwfqueee05w5c/TQQw9p9OjR5v4PPvigXnjhBT3xxBNasGCBnnzySfn9fj322GMqKSnR2LFj9dJLL+mRRx7RlClT9PLLL2vz5s16/fXXNWDAAI0fP17Lly/Xww8/rKuvvjrTzwoAAAAAQgSDQbOrdryu3ZLkdmW/It3SXpEuaX/tUrdLM75wvLl9ZfUBPfveLknS3vpmndCrPHtv3oWlHKSNEG0oKytTIBB9gLvf71ffvn3N+5WVlbr77rvN+71799b48eO1evVqzZkzR5WVlbrooos6Gud2a9q0aWbFubKyUjNmzFBJSYm5z4wZM7RkyRJz+/jx4zVgwICQ7fPnz1cwGJQj7Bu7paVFLS0ds9nV19ebt71eb+IPA7ZlHD+OY+HiGBYHjmNx4DgWB45jceA42o91HWefzyevN3anX2d77dofyM4xXLnloNbvqJUkeVyOqK956RnHm0G6pdUrr5dx0rGkckwy+hSD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ptmzZ2vmzJlat26dpk+frssvv1zbt2+XJNXX12vWrFkaMWKE1qxZo5tuukk33nijVq9eLUny+/265JJL5PP59NZbb2nBggW644479Oyzz2b6OQFAlxA+RvoX15xp3rYGacY7AwCArizlycaefvppDR3aNvPq2LFjVVdXp+uvv16NjY26++67dcstt+j666+XJD322GM69dRT9eabb+r888/X4cOH1bt3b5100klRX/uhhx7S6NGjdc8990iSHnzwQb3wwgt64okntGDBAj355JPy+/167LHHVFJSorFjx+qll17SI488oilTpujll1/W5s2b9frrr2vAgAEaP368li9frocfflhXX311up8RAHQZ1lm7B/cu15Djys371iANAADQlaVcUjBCtKGsrEyBQEC1tbX64IMPdPHFF5vbvvCFL+iEE04wK8aHDx9Wv379Yr52ZWWlLrroIvO+2+3WtGnTzOdXVlZqxowZKikpMfeZMWNGyPbx48drwIABIdvXrFmjYLyBfwAASZKRoz0uh5b/23Q5LBOMef38HgUAAJAyXP4qGAxq0aJFmjx5svbt2ydJGjFiRMg+w4YN065duyRJhw4d0pYtW1ReXq7Bgwdr5syZuuuuu9S/f39JbWOooz2/qqrK3D579uyYrx/r+S0tLTp48KD5PoaWlha1tLSY9+vr683bXq83tQ8DtmIcP45j4eIY5kdLa9vn3bvcI5cC8no7qtAtXp95O9njwnEsDhzH4sBxLA4cx8LHMbSvVI5J2kHa6/Vq7ty5qqys1MqVK9XQ0CBJqqioCNmvoqLCDKtf+cpXNGXKFDmdTlVVVelHP/qRPvzwQ73zzjtyuVxqaGiI+/xktodXvI39rYHZcPfdd2vBggVRv75ly5Yl9TnA3jiOhY9jmFs7GyTJrdbWFi1dujRk2559ThkdmcK3JcJxLA4cx+LAcSwOHMfCxzG0n8bGxqT3TStI79q1S9dcc41qamq0YsUKTZw4UWvXrpUktba2huzb3NxshtkhQ4ZoyJAhkqRx48Zp1KhRmjZtmt5//32dddZZKi0tjfv8dLdLkQFfku68807ddttt5v36+nqz6/qsWbPk8XhS+FRgJ16vV8uWLeM4FjCOYX5s2FUnbVyjivJyzZ49LWTbU5+vleprJSmid1AsHMfiwHEsDhzH4sBxLHwcQ/uy9lBOJOUg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iNwRE2kJokNVSB3T3dXVAGCoy2vjHnR1NQAAe/80CivScjDTDW4wEHmqDS8OQ5mmBuGBfm5xs46IiIiIGsZEmhyWEBmElc8muboaRB7NTyFHuMK+BcuIiIiIyLU4oZWIiIiIiIhIAibSRERERERERBIwkSYiIiIiIiKSgIk0ERERERERkQRMpImIiIiIiIgkYCJNREREREREJAETaSIiIiIiIiIJmEgTERERERERScBEmoiIiIiIiEgCJtJEREREREREEjCRJiIiIiIiIpKAiTQRERERERGRBEykiYiIiIiIiCRgIk1EREREREQkARNpIiIiIiIiIgmYSBMRERERERFJwESaiIiIiIiISAIm0kREREREREQSMJEmIiIiIiIikoCJNBEREREREZEETKSJiIiIiIiIJGAiTURERERERCQBE2kiIiIiIiIiCZhIExEREREREUnARJqIiIiIiIhIAibSRERERERERBIwkSYiIiIiIiKSgIk0ERERERERkQRMpImIiIiIiIgkULi6Au5ECAEAqKioQGlpKXx9fV1cI2ourVbLOHo4xtA7MI7egXH0Doyjd2AcPR9j6L5KS0sB1OWFjZEJe45qI/Lz89GpUydXV4OIiIiIiIhcRK1W44EHHmj0GCbSZvR6PS5evIiePXtCrVYjJCTE1VWiZiotLUWnTp0YRw/GGHoHxtE7MI7egXH0Doyj52MM3ZcQAvfv30dMTAzk8sZnQXNotxm5XI7Y2FgAQEhICL/YXoBx9HyMoXdgHL0D4+gdGEfvwDh6PsbQPYWGhtp1HBcbIyIiIiIiIpKAiTQRERERERGRBEykrSiVSixcuBBKpdLVVSEHMI6ejzH0Doyjd2AcvQPj6B0YR8/HGHoHLjZGREREREREJAF7pImIiIiIiIgkYCJNREREREREJAETaSIiIiIiIiIJmEgTERERERERScBEmoiIiIiIiEgCJtK1hBBYtGgRYmJiEBgYiKlTp+Lu3buurhaZ+fLLLyGTySz+paammspXrVqF+Ph4+Pv7Y8yYMcjNzbV4/+bNm5GYmAiVSoXBgwcjMzOztT9Cm5ednY2kpCQcOnTI4nVHY3fgwAEMHDgQKpUKvXr1wq5du1r8s7RVtmK4d+/eem1z8uTJFu9jDN3DqVOnMH78eAQEBCA6OhozZ85EYWGhqZxt0TM0Fke2R8/xz3/+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\n", + "image/png": "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", "text/plain": [ "
" ] @@ -151,12 +234,12 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9IAAAMCCAYAAACFrf7xAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAACzg0lEQVR4nOzdeXxU9b3/8ffMJJkkJIQ1AmEL4ApGCJSlIgiKC3Jr9brVW6wo1hYpvfX2uvxaby94K+2tba9ar7cqVWstrUptVXBhU1xYFJVFxYABTNiTkIRsk9l+fyRzMpOZycxkksz2ej4ePJyZs8w3cxJn3vP5Lia32+0WAAAAAAAIiznWDQAAAAAAIJEQpAEAAAAAiABBGgAAAACACBCkAQAAAACIAEEaAAAAAIAIEKQBAAAAAIgAQRoAAAAAgAgQpAEAAAAAiABBGgAAAACACBCkAQAAAACIQMRBeufOnbrkkkuUnZ2tQYMGacGCBaqsrPTZp7S0VBdffLH+9Kc/+TxuMpkC/rv11lslSU6nU2az2WdbTk6Ozzl2796tGTNmKCsrS6NGjdKzzz7rs72srEzz5s1Tr169NGTIED344IOR/ogAAAAAAAQVcZBevHixLrzwQm3ZskUrVqzQ22+/rZtuukmStG/fPi1cuFDjx4/X+vXr/Y7du3evz79t27bJarXqhhtukCSdPHlSbrdbb731lrHPzp07jeNra2s1Z84cFRYWauvWrbrlllt08803a8uWLZJagvgVV1whh8Ohd955R0uXLtXdd9+t559/vlMvDgAAAAAA7aVFesBzzz2nYcOGSZKKiopUU1Oj+fPnq6GhQevWrdOJEye0adMmTZgwwe/YMWPG+Ny/7777NHHiRM2ZM0eSVFVVJUkqLi5Wbm6u3/FPP/20nE6nnnjiCWVkZKioqEivvvqqHnvsMU2dOlWrV6/Wnj17tG7dOuXn56u4uFjr16/Xo48+quuuuy7kz+ZyuXT48GHl5ubKZDJF+tIAAAAAABKU2+3WqVOnNGTIEJnNHdecIw7SnhDtkZmZKZfLJUm6/fbb9b3vfS+s85w8eVIPP/ywXnjhBeOxqqoqZWRkBAzRkrRx40bNnj1bGRkZxmOzZ8/WqlWrjO3FxcXKz8/32b5kyRK53W6/cGyz2WSz2Yz7hw4d0jnnnBNW+wEAAAAAyaesrExDhw7tcJ+Ig7Q3t9utFStWaMqUKcrOzo7o2N///vcqKCjQJZdcYjxWWVmp5uZmZWVlKT8/X+eff74eeOABjRw5UlLL2Ou5c+f6nGf48OEqLy83thcWFvptt9lsqqio0MCBA322LV++XEuXLvVr25NPPhnxzwMAAAAASFwNDQ1auHBh0MKut04HabvdrkWLFmnjxo3atGlTRMe6XC79/ve/15133unz+JQpU4xx03v27NHSpUs1a9Ys7dy5U7m5uaqrq/MLuNnZ2UZVua6uTgMGDPDbLsmn8uxx7733+rShtrZWw4YNU3Z2tq688kqlp6dH9HMhftjtdq1du1Zz5szhOiYormFy4DomB65jcuA6JgeuY+LjGsav2tpaLVy4MKxhvp0K0uXl5br++utVWlqqDRs2aNKkSREd//rrr+vw4cOaP3++z+MDBgwwgnBRUZGmTp2qkSNH6vXXX9e1114rq9Wq5uZmn2OampqMsBxsu6SAFWar1Sqr1Rqwjenp6fxiJwGuY+LjGiYHrmNy4DomB65jcuA6Jj6uYfyJ5HpEPGt3SUmJpkyZotzcXO3YsUNTp06N9BR64YUXdNFFF6lPnz4d7jd8+HD1799fBw8elCQVFBSorKzMZ5+ysjKNGjWqw+15eXnq169fxO0EAAAAAKC9iIP0jTfeqGnTpmnNmjU+k3qFy+l06pVXXtE3v/nNkPuWlpaqoqLCmO17+vTp2rBhg5xOp7HP+vXrddFFFxnbt23bppqamoDbAQAAAACIVkRBuqSkRNu3b9cNN9yg0tJS7du3z/jnHV47smvXLlVWVuqCCy7w2/bUU0/piSee0I4dO/Tqq6/qG9/4hoqKijRv3jxJ0sKFC1VdXa3Fixdr9+7duv/++7Vr1y4tWbJEknTttdeqf//+WrBggXbs2KHHH39cq1at0l133RXJjwkAAAAAQFARjZE+evSopJbA2t4jjzyixYsXhzzH9u3blZ2drTPOOMNvW15enn784x/r8OHDGjx4sObOnauf//znSktraWZBQYFeeeUVLV68WH/4wx80duxYrVmzRiNGjJDUMg76tdde02233abJkyersLBQK1eu1JQpUyL5MQEAAAAACCqiID1jxgy53e6w9g2236233qpbb7014Larr75aV199dYfnnTlzpnbt2hV0+7hx47R58+aw2ggAAAAAQKQiHiMNAAAAAEAq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a+owGMwWr+daExEREVHw8BsbEZEfJud0Dsh1VKooJtFEREREYYJzpImIiIiIiIgUYCJNREREREREpAATaSIiIiIiIiIFmEgTERERERERKcBEmoiIiIiIiEgBJtJERERERERECjCRJiIiIiIiIlKAiTQRERERERGRAkykiYiIiIiIiBRgIk1ERERERESkABNpIiIiIiIiIgWYSBMREREREREpwESaiIiIiIiISAEm0kREREREREQKMJEmIiIiIiIiUoCJNBEREREREZECTKSJiIiIiIiIFGAiTURERERERKQAE2kiIiIiIiIiBZhIExERERERESnARJqIiIiIiIhIASbSRERERERERAowkSYiIiIiIiJSgIk0ERERERERkQJMpImIiIiIiIgUYCJNREREREREpAATaSIiIiIiIiIFmEgTERERERERKcBEmoiIiIiIiEgBdbArEEqEEACAuro6VFVVQaPRBLlG5CuTycQ4hjnGMDIwjpGBcYwMjGNkYBzDH2MYuqqqqgA05YWeRAlvzrpClJaWokuXLsGuBhEREREREQVJSUkJOnfu7PEcJtIOrFYrDh8+jD59+qCkpASJiYnBrhL5qKqqCl26dGEcwxhjGBkYx8jAOEYGxjEyMI7hjzEMXUIIVFdXIyMjAyqV51nQHNrtQKVSITMzEwCQmJjID3YEYBzDH2MYGRjHyMA4RgbGMTIwjuGPMQxNSUlJXp3HxcaIiIiIiIiIFGAiTURERERERKQAE2knWq0Wc+fOhVarDXZVyA+MY/hjDCMD4xgZGMfIwDhGBsYx/DGGkYGLjREREREREREpwB5pIiIiIiIiIgWYSBMREREREREpwESaiIiIiIiISAEm0kREREREREQKMJEmIiIiIiIiUoCJdCMhBObNm4eMjAzExcVh0qRJKCsrC3a1yMEHH3yAqKgo2U9+fr5UvmjRImRnZyM2NhZjx45FcXGx7PUrVqxA7969odPpMHToUOzZs6et38IVr7CwEDk5Odi6davsuL+xKygowODBg6HT6dC3b1+sXbu21d/LlcpdDDds2ODSNidOnCh7HWMYGvbv34+bbroJer0e6enpmDp1Ki5duiSVsy2GB09xZHsMH5999hn69+8PvV6PrKwszJ8/H46b6bA9hgdPcWR7jHCChBBCLFy4UKSkpIiVK1eK7777TvTu3VuMHz8+2NUiB6+99prIzc0VR48elX7KysqEEEIsW7ZMaLVa8fHHH4vdu3eLkSNHir59+wqLxSKEEOL7778XarVa/PnPfxb79u0TkydPFmlpaaKqqiqYb+mKsWfPHjFlyhQRGxsrAIgtW7ZIZf7Grri4WMTFxYmnnnpK7N+/X0ybNk3ExsaK48ePB+OtRixPMVy+fLnIzMyUtc0zZ85I5Yxh6Bg1apR4+eWXRWFhofjqq69Edna2yMvLE0KwLYYTT3FkewwfL7zwgli6dKkoLCwU7733noiOjhbvvvuuEILtMZx4iiPbY2RjIi2EsFgsIjU1Vbz55pvSsdWrVwsAori4OIg1I0ezZ88WkydPdls2aNAgMXPmTOn5Tz/9JACIDRs2CCGEmDRpkrjtttuk8vLycqHVasUHH3zQupUmIYQQc+bMEb/61a/E+vXrXZIwf2P3xBNPiAEDBkjlJpNJZGZmiueff76V39WVxVMM//73v4ucnJxmX8sYho5Tp07Jnn/66adCpVKJ2tpatsUw4imObI/hKy8vT0yaNEkIwf8bw5ljHNkeIxuHdgMoKirCxYsXMX78eOnYmDFjoFKpsH379iDWjBxdvnwZqampLscrKiqwd+9eWfx69eqFTp06SfHbuHGjrLxdu3bIyclhfNvIiy++iE8++QTdunWTHQ9E7DZu3IhbbrlFKler1Rg9ejRjG2DNxRBovm3aMYaho0uXLrLnOp0OVquVbTHMNBdHgO0xnFksFrRv357tMczZ4wiwPUY6JtKANOckOztbOhYbG4sOHTqgtLQ0WNUiJ5cuXcLixYuRkJCA/v3749VXX4XJZMLx48cByOMHAFlZWSgtLUV5eTkqKiqaLafWFxUV5fZ4IGJXXFzM2LaB5mII2Nrmt99+i7i4OPTu3RtPP/00ampqAIAxDGFCCCxevBi5ubk4f/48ALbFcOQYR71ez/YYhmpra7F48WLs2LED+fn5/L8xTDnHEeD/j5FOHewKhIKamhqoVCpotVrZcb1eD6PRGKRakbN58+Zhzpw5MBqNWL9+PebOnYsLFy7g9ttvB2CLlyN7/Ox/sNyVX7x4sW0qT255io23saupqWn29dQ2Hn/8cdx7770QQmDbtm14/vnncezYMXz++eeMYYgymUyYPn06Nm7ciIKCArbFMOUcR4DtMdzodDoYjUYkJiZi0aJFGDBgALZs2QKA7TGcuIsjwPYY6ZhIA9BqtbBarTCbzVCrm/5JDAaDy4eXgqdPnz7S49zcXFgsFixcuBBTpkwBADQ0NMjOt8fPfoOkuXIKnpZi403stFotYxtk3bt3lx4PHjwYSUlJePDBB1FWVsYYhqDS0lLcfffdKC4uxoYNGzBkyBDs3LkTANtiOHEXR4DtMdzs27cPlZWV2L17N2bOnIkDBw7gjjvuAMD2GE7cxfGVV15he4xwHNoNIDMzEwBkwySMRiPKysrczgek0JCTk4Pa2lqkp6cDAEpKSmTlJSUl6NatG1JTU6HVapstp+Cxtz1/YpeZmcnYhpicnBwAwMmTJxnDEHPkyBHk5uYiISEBhYWFGD58OAC2xXDTXBzdYXsMbb169UJubi5mzJiB1157Da+++irbYxhyF8f6+nqX89geIwsTadg+1LGxsVi3bp10bPPmzYiKisLo0aODWDPyZOfOnUhOTkaXLl3QtWtXWfyOHDmC0tJSjBs3DiqVCiNGjJCV2+8ajhs3LhhVp0aZmZl+x27kyJGycovFgk2bNjG2QbRz506oVCpkZ2czhiHmvvvuw4gRI7Bq1SqkpaVJx9kWw0tzcXSH7TF8qNVqCCGQlJTE9hjG7HG0WCwuZWyPESaIK4aHlFmzZon09HSxZs0asXXrVtGrVy+Rn58f7GqRg/z8fLF69Wqxb98+8frrrwudTicWLlwohBDirbfeEnFxcWLZsmVi165dYuTIkWLixInSa1euXCmio6PFe++9J/bt2ycmTZokBgwYIMxmc7DezhXp+PHjLlsn+Ru7H374QajVajFv3jxRVFQkpk2bJjIyMrhHeCtxF8NnnnlGfPHFF6KwsFD84x//EMnJyWLatGlSOWMYGg4fPiwAiOXLl8v2ND169KioqKhgWwwTLcWR7TE8VFZWigceeECsXbtWFBUViSVLloj09HRx//33CyH4f2O4aCmObI+RjYl0I4PBIKZNmyYSExNFcnKy+M1vfiMMBkOwq0UOpk6dKlJSUoRerxcDBw4UH3/8sVRmtVrFc889J1JTU0V8fLy4//77RXl5uez177zzjsjIyBCxsbFiwoQJoqSkpI3fAblLwgIRu+XLl4vu3bsLrVYrRo0aJQ4cONAWb+eK5C6Gc+bMEWlpaUKn04k+ffqIN9980+UmFWMYfJs3bxYA3P68/fbbbIthoqU4sj2GB6PRKO655x7RsWNHodPpRM+ePcXLL78sffdkewwPLcWR7TGyRQkhRFv3ghMRERERERGFK86RJiIiIiIiIlKAiTQRERERERGRAkykiYiIiIiIiBRgIk1ERERERESkABNpIiIiIiIiIgWYSBMREREREREpwESaiIiIiIiISAEm0kREREREREQKMJEmIiIiIiIiUoCJNBEREREREZECTKSJiIiIiIiIFGAiTURERERERKTA/wF14cjYBAMV/gAAAABJRU5ErkJggg==\n", + "image/png": "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NzdXTmfbJ1o333yzXn/9dZ188slBP1dqqyRLbVO/s7OzA4ZoSVq5cqVmzpyp7Oxs47GZM2caFemVK1eqtLRUgwYN8jq+fv16udr3igMAAADSkWeDahsVaUnS5KP6JXoISFERfQzlcrm0cOFCTZkyRfn5+coIcUPzp59+Wnl5eZo6daokqbq6WlarVXl5eRo1apSuvvpq7dq1yzi/vLxcJSUlXtcYOXKkKisruzxusVh0+PBhv9e3WCyqr6/3+gMAAAD0RE6PupLNkd5Fpl45bRNz77xwXIJHglQVdpC22Wy66aabtHLlSj322GMhP/8vf/mLfvGLX+jBBx9Uv35tnwRNmTJFGzZs0Pr16/Xggw9q06ZNmjFjhhoaGiRJjY2Nys/P97pOfn6+LBZLl8clGed4mj9/voqKiow/vhVzAAAAoKdweszQTPc10nnZmW3/zcpM8EiQqsIK0pWVlTr77LP11ltvacWKFTrllFOCfm5ra6tuuukm3XrrrXriiSd06623GscGDBigU089VRMmTNAVV1yhpUuXavfu3UZjspycHFmtVr/rucNyZ8cl+QVsSbr77rtVV1dn/KmoqAj66wAAAABSid2jJG11dATphlabfvn6Vn2yqyYRw0oIZ/v3ItPE3toIT8jNxsrKyjRjxgydeOKJ2rRpk9d65O60tLTo/PPP1/79+7V27VqVlpZ2ef7IkSPVv39/7d69W5JUXFzsF3YrKio0evRo4/j27dv9jhcVFRlVb085OTnKyckJevwAAABAqnJ6BOm6lo7i0x/eLdPf1u7W39bu1q7fXZSIocVFk8WuyiMtOnZIoVGdJ0cjXCFXpK+++mqddtppWrJkSUghWpLuvfde7du3T+vXr+82REtta54PHz6sMWPGSJKmTZumFStWGM3JJGn58uU655xzjOMbNmxQXV1dwOMAAABAunJ4BOlrF24wbn+5Pz36BF37zAad/9Bqrf2m2vhemEjSCFNIFemysjJt3LhRd911l8rLy72ODRw4UEVFRV0+/8UXX9T3vvc91dTUGFtdSVKvXr00ZMgQPfvss7Lb7Zo8ebIqKip01113acKECZozZ44k6YYbbtAf/vAHzZs3T7feeqtee+01bdmyRYsWLZIkXX755brnnns0d+5c/epXv9L69ev16quvavXq1aF8mQAAAECP4/BYI91k7ShM2Rzp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srIyTZkyRYWFhdq0aZOmTm379Lq4uFiS/Kq6FRUVGj16tNdjL7/8ss455xz16dOny9caOXKk+vfvr927dxuv0dX1OzteVFSkfv36hfaFAgBiyu4TpO+56DgtuPwkfWticYJGFLpWm8O43TsvrM+mE8J3jTQhGgCA0IQcpK+++mqddtppWrJkiVdTr+LiYh111FF67733jMfKyspUWVmpc845x3jM4XDozTff1Le+9a1uX6u8vFyHDx82un1PmzZNK1askMPR8cZl+fLlxvWnTZumDRs2qK6uLuBxAEDycDi9w9wNZ47WdycNV25W6mzNNG5Ib+N2hlJnSrqNZmMAAEQkpCBdVlamjRs36qqrrlJ5ebl27Nhh/Kmrq9Ptt9+uhx9+WC+//LI++eQT/eAHP9CcOXN04oknGtfYsmWLqqurdeaZZ/pd/9lnn9XTTz+tTZs26a233tIll1yiCRMmaM6cOZKkG264QbW1tZo3b562bt2qBx54QFu2bNFtt90mSbr88svVv39/zZ07V5s2bdJTTz2lV199VXfccUck3yMAQAz4VqRTUaYpQ9dMHSVJcqRQ226rx9TuBZeflMCRAACQmkKah3bgwAFJbYHV16OPPqp58+apqqpKt9xyi1pbW3XppZfqscce8zpv48aNys/P1zHHHON3jaKiIv3sZz/Tvn37NHToUM2ePVv/8z//I7O5bZjFxcV68803NW/ePD3zzDMaP368lixZolGj2t7E5Ofna+nSpbrxxhs1efJklZSUaNGiRZoyZUooXyYAIA56yl7G7oZjvmu+k5nV0TbW88cP1ncnDU/waAAASD0ZrkAbLKep+vp6FRUVqa6uTr179+7+CQCAsG2urNUlj31o3N/1u4sSOJrw3ffmF3r2w1265eyjdccF4xI9nKA888FO3f/Wl7rkpGF65D8mJno4AAAkhVDyYET7SAMAEK6eMLVbkjLbt+tKpandX+yrlyRlZfI2AACAcPAbFACQEKk0FborqTi1+9VPKyVJ7399KMEjAQAgNRGkAQBx12pzqLrJmuhhREVGe0U6hXK0oaf8HQAAEG+ps+klAKBH2F3dpNkPr1GT1dH9ySnAPTu6pzRPAwAA3aMiDQCIqy/21feYEC11rJF2ptAaaQAAEBmCNAAgrmyOtj2MTz+6v84+dmCCRxO5DII0AABphyANAIgr9xRo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lbn99ts1cuRIvffee4qJifHcT39sW2qLoz/0x7YjKChIxhhFRUXRH9swdxxdLleNNvpjO9OCFcNblTlz5pjY2Fjz/vvvm23btpk+ffqY1NTUlr4seElNTTVr1641u3fvNs8884xxOBxm4cKFxhhjnn32WRMeHm5WrlxpPvvsMzNq1CgzZcoUz2PXrFljAgMDzUsvvWR2795tpk6dagYPHmzKyspa6uVccPbv319j26Smxu3zzz83QUFBJi0tzWRlZZmZM2eauLg49gc/h/zF8dFHHzWrV682mZmZ5u9//7vp1KmTmTlzpqedOLYO3333nZFkVq1a5bOn6b59+0x+fj79sY2oL470x7ahoKDA3HnnneaDDz4wWVlZ5h//+IeJjY01d9xxhzGG/49tRX1xpD+2byTSlUpKSszMmTNNZGSk6dSpk/ntb39rSkpKWvqy4GXGjBmmc+fOJiwszAwZMsQsXbrU01ZeXm7++Mc/mujoaNOhQwdzxx13mJMnT/o8/vnnnzdxcXEmNDTUTJ482eTk5JznV3Bh85eANUfcVq1aZZKSkozdbjejR482e/bsOR8v54LlL45z5841MTExxuFwmH79+pnFixfX+JCKOLa8zZs3G0l+b8899xz9sY2oL470x7bB6XSaW2+91XTr1s04HA7Tu3dv89RTT3nee9If24b64kh/bN8CjDHmfI+CAwAAAADQVrFGGgAAAAAAC0ikAQAAAACwgEQaAAAAAAALSKQBAAAAALCARBoAAAAAAAtIpAEAAAAAsIBEGgAAAAAAC0ikAQAAAACwgEQaAAAAAAALSKQBAAAAALCARBoAAAAAAAtIpAEAAAAAsOD/A1gyFKzawF3cAAAAAElFTkSuQmCC", "text/plain": [ "
" ] @@ -175,12 +258,12 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9IAAAL9CAYAAADHHl6JAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAB8gElEQVR4nO3deXxU9b3/8XcmIZMFCJsRiAQS2wIi1EYg0BtRQvFRAbVaUeq9eo2CtJJ6K21VrrYWtPKzWntRWqoWWqWVCqVWRbBViAgtBEEMoGK0cUlUEDArkG3m/P4IczJrZg2z5PV8PPJgZr5nzpyZMxPyns93STIMwxAAAAAAAAiIJdoHAAAAAABAPCFIAwAAAAAQBII0AAAAAABBIEgDAAAAABAEgjQAAAAAAEEgSAMAAAAAEASCNAAAAAAAQUiJ9gHEErvdrk8//VR9+vRRUlJStA8HAAAAAHCaGIahxsZGDR06VBZL1zVngrSTTz/9VMOGDYv2YQAAAAAAoqS6ulpnnXVWl9sQpJ306dNHUscL17dv3ygfDQAAAADgdGloaNCwYcPMXNgVgrQTR3fuvn37EqQBAAAAoAcKZJgvk40BAAAAABAEgjQAAAAAAEEgSAMAAAAAEASCNAAAAAAAQSBIAwAAAAAQBII0AAAAAABBIEgDAAAAABAEgjQAAAAAAEEgSAMAAAAAEASCNAAAAAAAQSBIAwAAAAAQBII0AAAAAABBIEgDAAAAABAEgjQAAAAAAEEgSAMAAAAAEASCNAAAAAAAQSBIAwAAAAAQBII0AAAAAABBIEgDAAAAABAEgjQAAAAAAEEgSAMAAAAAEASCNAAAAAAAQSBIAwAAAAAQBII0AAAAAABBIEgDAAAAABAEgjQAAAAAAEEgSAMAAAAAEASCNAAAAAAAQSBIAwC6ZBhGtA8BAAAgphCkAQA+fX/NXk375VY1t9mifSgAAAAxgyANAPDphYpPVXX0uLa9dzTahwIAABAzCNIAAL+Son0AAAAAMYQgDQDwK4kkDQAAYCJIAwAAAAAQBII0AMAvKtIAAACdgg7S+/bt08UXX6yMjAwNHjxYJSUlOnbsmNm+YsUK5eXlKT09XcXFxaqqqnK5//r16zV69GilpaVpwoQJ2rNnj0u7YRhavny5Ro4cKavVqtzcXB08eNBsP3DggKZMmaL09HTl5+dr9erVLvevrq7WrFmzlJmZqaFDh+qhhx4K9ikCAAAAAOBT0EG6tLRUF110kXbu3KmVK1dq69atuv766yVJ69at02233aYlS5Zo+/btamtr02WXXSa73S5J2rFjh+bMmaP58+ervLxcubm5mjFjhhobG839/+QnP9G9996ru+++W2+88YaWL1+u3r17S5IaGho0ffp05eXlqby8XDfeeKNuuOEG7dy5U5Jks9k0c+ZMtbe3a9u2bVq8eLHuuOMOrV27NuwXCgAAAAAASUoyDMMI5g7V1dUaNmyYef3pp5/Wddddp8bGRhUVFemCCy7QsmXLJEkHDx7U6NGjtWXLFk2dOlVXXnmlbDabnnvuOUlSXV2dBg8erBUrVqikpEQHDx7Uueeeq82bN+vCCy/0eOxHHnlE9913n2pqapSamipJmjRpkkaOHKknn3xSzz//vK666irV1NQoOztbkjRnzhx99tln2rp1q9/n1tDQoKysLNXX16tv377BvCwAkJBG3PmiJOn3N0zQ1FHZUT4aAACA7hNMHgy6Iu0coiUpLS1NdrtddXV12rt3ry655BKzbdSoURoyZIhZMS4rK3Np79evnwoKCsz2p556Sl/72te8hmjH/YuLi80QLUnFxcUu+y8oKDBDtKO9vLxc3r4vaGlpUUNDg8sPAAAAAABdCWuyMcMwtHLlShUWFurw4cOSpLy8PJdtcnNzVVNTo9raWtXV1flsl6SdO3dq3Lhx+uEPf6js7Gx95Stf0S9/+UszBFdVVXV5f1/tLS0tOnr0qMfxL126VFlZWeaP+5cEAIBTmGwMAADAFHKQbmtr080336yysjItX75cTU1NkqSMjAyX7TIyMtTS0uK3XZI+++wzbdiwQampqdq4caO+973v6Y477tCTTz4pSWpqaury/r7aJZnbOFu0aJHq6+vNn+rq6pBeCwAAAABAz5ESyp1qamp0zTXXqKqqSlu2bNH48eO1a9cuSVJra6vLts3NzcrIyJDVau2yXZLa29s1ZswYLV26VJI0fvx4/etf/9JTTz2lG264QVartcv7+2qXPAO8Y3vHcQEAXAU5hQYAAECPEXRFurKyUoWFherTp48qKio0adIkSVJOTo4keVR1q6urlZ+fr0GDBslqtfpsl6Ts7Gx96Utfcmn/yle+YnYbz8nJ6fL+vtqzsrI0YMCAYJ8qAPRodqccTc9uAACATkEH6WuvvVaTJ0/Wxo0bXSb1ysnJ0YgRI/Tyyy+bt1VWVqqmpkbTpk2TxWLR5MmTXdrr6+u1e/duTZs2TZL09a9/3Zw4zOGtt97SV77yFUlSUVGRtmzZIpvNZrZv3rzZvH9RUZF27dql+vp6r+0AgMDZnSrSSUlEaQAAAIeggnRlZaX27NmjOXPmqKqqSu+//775U19fr4ULF2rZsmVat26ddu/erZtuukmzZs3S2LFjJUkLFy7UM888o8cee0wVFRUqKSnRyJEjNWPGDEnSLbfcon//+9+69dZb9cYbb+jBBx/UCy+8oB/+8IeSpLlz56qurk6lpaU6cOCA7r33Xu3fv1+33nqrJGn27NkaOHCgSkpKVFFRoccff1zr16/X7bffHsnXDAB6BHp2AwAAeBfUGOlDhw5J6gis7h599FGVlpbqyJEjuuWWW9Tc3KzLL79cy5cvN7e59NJLtWzZMi1ZskS1tbUqLi7Whg0blJycLKljxu+NGzfqBz/4gR577DGNGDFCTz/9tIqKiiR1VL1feOEFlZaWatWqVRozZow2btyo4cOHS+oYB71p0ybNmzdPEydOVF5entasWaPCwsLQXh0A6MHsJGkAAACvkgxmkzEFswA3ACS65jabRv3kJUnSkzdO1IVfOSPKRwQAABCcA5/U686/7tOcCbn6r0nDu9w2mDwY1jrSAIDE5TJGOorHAQAAEKqfPf+WDnzSoJ89/1ZE90uQBgB4Zae/EgAAiHPvHmqUJLVH+A8bgjQAwCtG/gAAgHiXmtI9kZcgDQDwymUdafp2AwCAOESQBgCcXhSkAQBAnHMO0vYIdu8mSAMAvHKdbIySNAAAiD9WpyBdf7ItYvslSAMAvGIdaQAAEO+cK9IP/ePdiO2XIA0A8MpwuUyoBgAA8afd1vk3zJ/KP47YfgnSAACvnCvSFKcBAEA8srmNi47UOGmCNADAK+fwTI4GAACRcrqW2DzZatN7nze53tZmi8i+CdIAAK9cgjQlaQAAEAG7PvhCE37+il7c91m3P9bTuzy7ct/+l30R2TdBGgDglUvX7igeBwAASBw3/eF1HW1q1YKn3+j2x6qpPeFx24v7IxPgCdIAAK9cJhujIg0AACKgzW4/bY/lazx0JP6uIUgDALxy/s+HHA0AACIhSUmn7bE++sKzIi1JJ1rDHydNkAYAeOUcntftronYLJcAAKDnSjp9OVqvvnvE6+11J9vC3jdBGgDglfPa0S+9dUg7q45F8WgAAEAiOI052qfa461h7yMlAscBAEhA7gXoT+ubo3MgAAAgYVgiVJL+y54aHW5o1oKpXwpo+5X/PV43PblbkrT4hbfUPyPVY5vWk00et/lCkAYAeGV3Gxjd2n76JgcBAAAJKkIl6R+tq5AkTRudrVGD+3rdJjM1Wcdbbbp75mhNG32mvjH6TL3yzmG9/mGt1+3tLd7HVHtDkAYAeOU+wVibjSANAADCE26OXvzCWzrR0jlZWN0J3+OdW04VAS796lBJ0v1XnqvpB7PV7mPelxPHG3Xz/wV2HARpAIBX7ktDUJEGAADhSgqja3f1Fyf0+39+GNC27Ta7GZitKR1Tg2X3SdM1E3J93qehoUE3B3gsTDYGAPDK/bvaVirSAAAgTOEMkf6k7qTHbb6W6DzS1GJetqYkh/6gPhCkAQBeuY+RbqEiDQAAwhTOZGM2L12yDY+v/qWXDnymyUu3mNdTUyIfewnSAACv7G652eZ+AwAAQJCcw3BLu62LLT35Gtvs7u6/vWVezkrvpWRL5BfdIkgDALxy/4Y3wP+7AAAAfKo/2Tk5WFcThXnj9Ut9r3+fdN6Y1qt7Ii9BGgDglfuYI19jkAAAAEJxojW4inSbzfOPkTYv3/Q3t3UG7rE5/YI+rkAQpAEAXnkGaZI0AAAIT2Zq58Rf//68Kaj7ehsj3eZlDpemlnbz8j2XnhPUYwSKIA0A8Mp9sjH36wAAAMGaOirbvHz33w4EdV9vY6Tb/czhMmxARlCPESiCNADAK88gHaUDAQAACcP5z4tDDc26ftUufXG8NaD7tntZirPVS3fv04EgDQDwyv2/JSrSAAAgXO7ds1+rPKLHXvt3QPf1WpH2Eq5PB4I0AMAr9zHR5GgAABAum5c/KJqa271s6eW+3sZIuwXpVqcx0zsWFQd5dIEjSAMAvHL/gpeKNAAACJfdSxgOdJ3nQLp2H3eaaGxQb2uQRxc4gjQAwKufPf+Wy3WCNAAACJe3ivSzez8J6L6BdO1+/cMvJEmpyRb1Su6+uEuQBgB49fZnDS7XydEAACBc3iYvPSPAynEgXbsPNzRLklq7eew0QRoAEBBm7QYAAOHy1rX7WICzdrd4WTO6za1rd+2JNknSdyYOC+HoAkeQBgAExH3yMQAAgGB5qyrXn2xTY3Ob3/seqm/2uM29Il13KkhnpaeGeISBIUgDAALCGGkAABAub2OkJWn3h7V+73voVLfty88bqqRT85N5BOmTHdXtfhm9wjhK/1K6de8AgIRB124AABAu567dva0pmjF2sNburtH/PrtfZ/RxHSs9IDNVv7hqnLL7pEnq7No95ctn6My+aXr8tSq1u3Xtrj9Vke5PkAYAxAIq0gAAIFyOvye+MzFXcy/I078/b9La3TX6rL5Zn3npuv3K25/r2sJcSVLbqSCdmmJRr+SOkvTqnR/p+YpPze2PNXVUpLu7azdBGgAQEHI0AAAIl6OAPG1Uts4+o7fyB2Xqr7d83awkO6z65wfa9t5R1Z/svN3RjbtXskVfObOPJOlEq00nWm0u902xJGn0kD7d+CwI0gCAADHZGAAACJeja3eypaOinJSUpILc/h7bbXvvqLa9d1QNTpOQOZa0Sk1J0jfPzdFXz+qnppZ2j/tm97Wa3cG7C0EaABAQxkgDAIBwOWbttpwK0r70Te+Iqs4V6db2zoq0JI0YlNkdhxgQZu0GAASEMdIAACBcjr8nkpO6DtKZqR1B+oRTxdm5a3e0Rf8IAABxgRwNAADC5QjSFj9JND01WZJcxj+3nRpgTZAGAMQNKtIAACBcZtduPxXpjFNB+h9vHzbnaXFUpFMJ0gCAeEGQBgAA4XLMuZLsZ4x0eq9k8/KGfZ9JcurandL1fU8HgjQAICBMNgYAAMIVaEX6ZFtnl+73DjdK6pxsLBYq0szaDQAICAVpAAAQqr/t/UTr9lTri+OtkvxXpMfmZJmXDze0SGKMNAAgDrGONAAACNUPnnlT/3z/mLnus79Zu798Zh9998KzJUlVR5skOa8jHf0YG/0jAADEBcZIAwCASPGToyVJhXkDJEnNbXbZ7IbZLZyKNAAgbjBGGgAARIq/rt2SZO3VEVeb22zmRGOS1CuZycYAAHGCijQAAIiUQIJ02qmZu096BOnox9joHwEAIC6QowEAQKT4m7VbktJSOoJ0c5vdnGhMIkgDAOIIFWkAABApgVWkO+Jqi1NFOtmSFNB9uxtBGgDgwbn7lIONQdIAACBCApl5u7e1Y7XmxpZ23fXs/o77xUA1WiJIAwC8+OPOjzxuoyINAAAixRpAkD6jj1Vn9U+XJL3yzueSpAGZqd16XIFKifYBAABiz/ufN3nc1k5FGgAAREggQTopKUlPXD9er757RIY6/g6Z8uUzuvvQAkKQBgB4cB57NPv8s7RuTw1duwEAQEgML73arKcmEvNn9JC+Gj2kb6QPKWx07QYAeHCeSXNgb6skucyWCQAAECj3Xm1JSbGxFnQ4CNIAAA/OK1KknvqPzmb3nIAMAADAH/dJTA2jo9t2PCNIAwA8OFekHWs1MkYaAACEorU98b6MJ0gDADw4j5G2nlrDsZ2u3QAAIAStXpbVjHcEaQCAB+eKdFqvjslAmGwMAACEoqb2pMv1G74+IjoHEkEEaQCAB6eCtNJOzarZzhhpAAAQgit/8y+X6xmpgc3YHcsI0gAAD84VaUfXbirSAAAgEjKt8b8KM0EaAODBuSLtWOeR5a8AAEAkUJEGACQk5yUp0lMZIw0AACInM5WKNAAgATnP2p2W4lj+ijHSAAAgfBlWKtIAgATk3LU749S3xq3tdhkGVWkAABC4lnabx21UpAEACcm5a3fmqW+N7QbjpAEAQOBsdkNfX7rF43bGSAMAEpJz5bl3Wue3xs1evlUGAADwpvZEq44db/W4nVm7AQAJyXlesYzUFDkK1M2tBGkAABCY5jbvfzcQpAEACcnuVJFOsSQp7dQSWM1tTDgGAAACc7zFR5BOgK7d8f9VAAAg4no7fVOcbElSWi+LTrbZdO+Lb6tPGv91AAAA/2q9dOuWpIwEqEjH/zMAAERc/4xU83KvZIuy+6Sp9kSbXn77cBSPCgAAJIL0XlSkAQAJyNGx+8KvnCFJWn7t1/Tqu0dkiFm7AQBA4O7feNC8vOTyMRqbk6Vk53U24xRBGgDgk2OSsS+f2UdfPrNPdA8GAADEHecg/Y3RZ2pov/QoHk3kMNkYAMCD8/JXAAAAofrmmMHm5b7pvaJ4JJFFkAYAuKg/0aaTp5ariP+OVwAAIJrmXpBnXk6E2bodgg7S+/bt08UXX6yMjAwNHjxYJSUlOnbsmNm+YsUK5eXlKT09XcXFxaqqqnK5//r16zV69GilpaVpwoQJ2rNnj9lms9lksViUlJRk/vTu3dvl/gcOHNCUKVOUnp6u/Px8rV692qW9urpas2bNUmZmpoYOHaqHHnoo2KcIAD1WY3ObvrrkH/rpc29JkpKSiNIAACB05w3rp/OH99eVBTkJ9XdF0EG6tLRUF110kXbu3KmVK1dq69atuv766yVJ69at02233aYlS5Zo+/btamtr02WXXSa7vWPd0R07dmjOnDmaP3++ysvLlZubqxkzZqixsVGSVFtbK8Mw9Oqrr+q9997Te++9p3379pmP3dDQoOnTpysvL0/l5eW68cYbdcMNN2jnzp2SOoL4zJkz1d7erm3btmnx4sW64447tHbt2rBfKADoCQ4eanS5njj/3QEAgGhISbZo/fe+roevPi/ahxJRSUaQA+Gqq6s1bNgw8/rTTz+t6667To2NjSoqKtIFF1ygZcuWSZIOHjyo0aNHa8uWLZo6daquvPJK2Ww2Pffcc5Kkuro6DR48WCtWrFBJSYkqKys1cuRINTQ0qE8fz0ltHnnkEd13332qqalRamrH0iyTJk3SyJEj9eSTT+r555/XVVddpZqaGmVnZ0uS5syZo88++0xbt271+9waGhqUlZWl+vp69e3bN5iXBQASwp6PavXtFf8yr39jdLZ+998TonhEAAAAp0cweTDoirRziJaktLQ02e121dXVae/evbrkkkvMtlGjRmnIkCFmxbisrMylvV+/fiooKDDbv/jiC6WmpnoN0Y77FxcXmyFakoqLi132X1BQYIZoR3t5ebnXiXNaWlrU0NDg8gMAPZnnahTUpAEAANyFNdmYYRhauXKlCgsLdfjwYUlSXl6eyza5ubmqqalRbW2t6urqfLZL0rFjx9Ta2qr09HQNHz5c1157rT788ENz26qqqi7v76u9paVFR48e9Tj+pUuXKisry/xx/5IAAHoai9vYpQQaygQAABAxIQfptrY23XzzzSorK9Py5cvV1NQkScrIyHDZLiMjQy0tLX7bJamwsFC7du1SeXm5HnzwQVVUVGjq1KnmGOqmpqYu7++rXZK5jbNFixapvr7e/Kmurg7ptQCAROERpKN0HAAAALEsJZQ71dTU6JprrlFVVZW2bNmi8ePHa9euXZKk1tZWl22bm5uVkZEhq9XaZbskDRo0SIMGDZIkjRs3TpMmTdKIESP00ksvafbs2bJarV3e31e75BngHds7jgsAQAUaAAAgEEFXpCsrK1VYWKg+ffqooqJCkyZNkiTl5ORIkkdVt7q6Wvn5+Ro0aJCsVqvPdm9yc3M1cOBAffTRR+ZjdHV/X+1ZWVkaMGBAsE8VAHocunYDAAD4F3SQvvbaazV58mRt3LjRZVKvnJwcjRgxQi+//LJ5W2VlpWpqajRt2jRZLBZNnjzZpb2+vl67d+/WtGnTvD5WVVWVjh49qi996UuSpKKiIm3ZskU2m83cZvPmzeb9i4qKtGvXLtXX13ttBwB0zeL2v0ISnbsBAAA8BNW1u7KyUnv27NGdd96pqqoql7YzzjhDCxcu1KJFi3TeeecpLy9Pt912m2bNmqWxY8dKkhYuXKgrrrhCU6ZM0aRJk7R48WKNHDlSM2bMkCT9/ve/V3t7uyZOnKjq6mrdeeedGjdunGbNmiVJmjt3rn75y1+qtLRUCxYs0LPPPqv9+/drzZo1kqTZs2fr7rvvVklJie655x6Vl5dr/fr1eu2118J+oQCgJ0imIg0AAOBXUEH60KFDkjoCq7tHH31UpaWlOnLkiG655RY1Nzfr8ssv1/Lly81tLr30Ui1btkxLlixRbW2tiouLtWHDBiUnJ0uSsrKy9KMf/UiffvqphgwZohkzZujnP/+5UlI6DjMnJ0cvvPCCSktLtWrVKo0ZM0YbN27U8OHDJXWMg960aZPmzZuniRMnKi8vT2vWrFFhYWForw4A9DBJBGkAAAC/kgxvCyz3UMEswA0AiajqSJOKf7nVvD5z7BD9+j8LonhEAAAAp0cweTCsdaQBAAmOijQAAIAHgjQAwCdyNAAAgCeCNADA5D7Wx33MNAAAAAjSAAAnzJoBAADgH0EaAOAT9WgAAABPBGkAgBPXkjQ9uwEAADwRpAEAPpGjAQAAPBGkAQAm9zHSTDYGAADgiSANADAx1xgAAIB/BGkAgE/P7v0k2ocAAAAQcwjSAAATy18BAAD4R5AGAAAAACAIBGkAgMlglDQAAIBfBGkAgImu3QAAAP4RpAEAAAAACAJBGgBgoiINAADgH0EaAAAAAIAgEKQBACYmGwMAAPCPIA0AMNG1GwAAwD+CNAAAAAAAQSBIAwAAAAAQBII0AAAAAABBIEgDAEyMkQYAAPCPIA0AMDFrNwAAgH8EaQAAAAAAgkCQBgCY6NoNAADgH0EaAAAAAIAgEKQBACYK0gAAAP4RpAEAJoO+3QAAAH4RpAEAAAAACAJBGgBgcq9H5w7IiMpxAAAAxDKCNADA5N6z+zf/WRCdAwEAAIhhBGkAgE9Z6b2ifQgAAAAxhyANAHDiWpK2WJKidBwAAACxiyANAPCJHA0AAOCJIA0AMLmPkbamJEfnQAAAAGIYQRoAYHKftTvTSpAGAABwR5AGAPhERRoAAMATQRoAYHLv2g0AAABPBGkAAAAAAIJAkAYAmAynkvSG7xdF8UgAAABiF0EaAGByxOizz8jUuTlZUT0WAACAWEWQBgB4SEpiAWkAAABfCNIAABOTjQEAAPhHkAYAeKAeDQAA4BtBGgBgMkRJGgAAwB+CNACg06kczRBpAAAA3wjSAAAPSXTuBgAA8IkgDQAw0bEbAADAP4I0AMADXbsBAAB8I0gDAEwsfwUAAOAfQRoAYGLWbgAAAP8I0gAAD0n07QYAAPCJIA0AMNG1GwAAwD+CNADAA/VoAAAA3wjSAAATBWkAAAD/CNIAAJNxqm83Q6QBAAB8I0gDADwQpAEAAHwjSAMATHTtBgAA8I8gDQDwkMR0YwAAAD4RpAEAnShJAwAA+EWQBgCYDDHZGAAAgD8EaQCAB3I0AACAbwRpAIDJoGs3AACAXwRpAIAn+nYDAAD4RJAGAJioSAMAAPhHkAYAmBw5mno0AACAbwRpAIAHenYDAAD4RpAGAJgM+nYDAAD4RZAGAHigIA0AAOAbQRoAYKIeDQAA4B9BGgBgcvTsTmKQNAAAgE8EaQCAB2I0AACAbwRpAIATOncDAAD4Q5AGAHigZzcAAIBvBGkAgInVrwAAAPwjSAMATI4cncQoaQAAAJ8I0gAAT+RoAAAAnwjSAAATXbsBAAD8CzpI79u3TxdffLEyMjI0ePBglZSU6NixY2b7ihUrlJeXp/T0dBUXF6uqqsrl/uvXr9fo0aOVlpamCRMmaM+ePV4fp66uTgMHDtQ3vvENl9sPHDigKVOmKD09Xfn5+Vq9erVLe3V1tWbNmqXMzEwNHTpUDz30ULBPEQB6PArSAAAAvgUdpEtLS3XRRRdp586dWrlypbZu3arrr79ekrRu3TrddtttWrJkibZv3662tjZddtllstvtkqQdO3Zozpw5mj9/vsrLy5Wbm6sZM2aosbHR43GWLl2qL774wuW2hoYGTZ8+XXl5eSovL9eNN96oG264QTt37pQk2Ww2zZw5U+3t7dq2bZsWL16sO+64Q2vXrg36hQGAnshg+SsAAAC/kgwjuI581dXVGjZsmHn96aef1nXXXafGxkYVFRXpggsu0LJlyyRJBw8e1OjRo7VlyxZNnTpVV155pWw2m5577jlJHVXnwYMHa8WKFSopKTH3+fbbb+vCCy9UQUGBbDabXnnlFUnSI488ovvuu081NTVKTU2VJE2aNEkjR47Uk08+qeeff15XXXWVampqlJ2dLUmaM2eOPvvsM23dutXvc2toaFBWVpbq6+vVt2/fYF4WAEgIL1R8qu+v2atJ+QP055snR/twAAAATptg8mDQFWnnEC1JaWlpstvtqqur0969e3XJJZeYbaNGjdKQIUPMinFZWZlLe79+/VRQUGC2S5LdbtfcuXN11113aciQIS6PVVZWpuLiYjNES1JxcbHL/gsKCswQ7WgvLy+Xt+8LWlpa1NDQ4PIDAGDWbgAAgK6ENdmYYRhauXKlCgsLdfjwYUlSXl6eyza5ubmqqalRbW2t6urqfLY73H///WpublZpaanH41VVVXV5f1/tLS0tOnr0qMf+li5dqqysLPPH/UsCAOhp6NgNAADgX8hBuq2tTTfffLPKysq0fPlyNTU1SZIyMjJctsvIyFBLS4vfdkn65z//qV/84hf64x//qJSUFI/HbGpq6vL+vtolmds4W7Rokerr682f6urqgJ8/ACSyJArSAAAAPnmm1QDU1NTommuuUVVVlbZs2aLx48dr165dkqTW1laXbZubm5WRkSGr1dple01Njb797W/rV7/6lc455xyvj2u1Wn3ev6t2yTPAO7Z3HBcAQF6HwQAAAMBV0BXpyspKFRYWqk+fPqqoqNCkSZMkSTk5OZLkUdWtrq5Wfn6+Bg0aJKvV6rN95cqVOnz4sBYsWKC0tDSlpaVp9erVKisrU1paml577TXl5OT4vL/jGLy1Z2VlacCAAcE+VQDosahIAwAA+BZ0kL722ms1efJkbdy40WVSr5ycHI0YMUIvv/yyeVtlZaVqamo0bdo0WSwWTZ482aW9vr5eu3fv1rRp07RgwQK98847evPNN82fyy67TIWFhXrzzTc1fvx4FRUVacuWLbLZbOY+Nm/erGnTpkmSioqKtGvXLtXX13ttBwAEhsnGAAAAfAuqa3dlZaX27NmjO++8U1VVVS5tZ5xxhhYuXKhFixbpvPPOU15enm677TbNmjVLY8eOlSQtXLhQV1xxhaZMmaJJkyZp8eLFGjlypGbMmKHk5GQNGjTIZZ9ZWVlqbGzUqFGjJElz587VL3/5S5WWlmrBggV69tlntX//fq1Zs0aSNHv2bN19990qKSnRPffco/Lycq1fv16vvfZayC8QAPQk9OwGAADwL6ggfejQIUkdgdXdo48+qtLSUh05ckS33HKLmpubdfnll2v58uXmNpdeeqmWLVumJUuWqLa2VsXFxdqwYYOSk5MDevycnBy98MILKi0t1apVqzRmzBht3LhRw4cPl9QxDnrTpk2aN2+eJk6cqLy8PK1Zs0aFhYXBPE0A6PHo2g0AAOBbksHMMqZgFuAGgET07N4a3fZMhS748iCtvokvIQEAQM8RTB4Max1pAEBi4atVAAAA/wjSAAAPSfTtBgAA8IkgDQAwUZEGAADwjyANAPBAPRoAAMA3gjQAwERBGgAAwD+CNADA5FjIgSHSAAAAvhGkAQAeyNEAAAC+EaQBACa6dgMAAPhHkAYAeGD5KwAAAN8I0gCATpSkAQAA/CJIAwBMxqkkTT0aAADAN4I0AMADPbsBAAB8I0gDAEwGXbsBAAD8IkgDALygJA0AAOALQRoAYKIgDQAA4B9BGgBgcnTtZow0AACAbwRpAIAHcjQAAIBvBGkAgMmgczcAAIBfBGkAgAe6dgMAAPhGkAYAmFj+CgAAwD+CNADA5MjRSYySBgAA8IkgDQDwQNduAAAA3wjSAIBO9O0GAADwiyANADCZXbupSAMAAPhEkAYAAAAAIAgEaQCAydGzm8nGAAAAfCNIAwA8kaMBAAB8IkgDAEwGk40BAAD4RZAGAJg615EGAACALwRpAICHJKbtBgAA8IkgDQAw0bMbAADAP4I0AMAD9WgAAADfCNIAABMFaQAAAP8I0gAAk2PWboZIAwAA+EaQBgB4IEcDAAD4RpAGAAAAACAIBGkAgAeWvwIAAPCNIA0AMLH8FQAAgH8EaQCAyTg1bzf1aAAAAN8I0gAATyRpAAAAnwjSAAATXbsBAAD8I0gDADwkUZIGAADwiSANADBRkAYAAPCPIA0AMDm6drP6FQAAgG8EaQCAB3I0AACAbwRpAIDJoHM3AACAXwRpAIAHunYDAAD4RpAGAJhY/goAAMA/gjQAwAPLXwEAAPhGkAYAeKBrNwAAgG8EaQCAyaBvNwAAgF8EaQCAByrSAAAAvhGkAQAmCtIAAAD+EaQBAKbOHE1JGgAAwBeCNADAA127AQAAfCNIAwBMdO0GAADwjyANAPBAQRoAAMA3gjQAwGSIkjQAAIA/BGkAgMnRtZsx0gAAAL4RpAEAHpLo3A0AAOATQRoAYKJjNwAAgH8EaQCAB7p2AwAA+EaQBgB0Yv0rAAAAvwjSAACTI0ZTkAYAAPCNIA0A8JBE324AAACfCNIAABM9uwEAAPwjSAMAAAAAEASCNADAZLAAFgAAgF8EaQCAydG1myHSAAAAvhGkAQAekpi3GwAAwCeCNADARMduAAAA/wjSAAAPdO0GAADwjSANADCx/BUAAIB/BGkAgMkxazcFaQAAAN8I0gAAD3TtBgAA8I0gDQDoRNduAAAAvwjSAAAPSZSkAQAAfCJIAwBMFKQBAAD8CzpI79u3TxdffLEyMjI0ePBglZSU6NixY2b7ihUrlJeXp/T0dBUXF6uqqsrl/uvXr9fo0aOVlpamCRMmaM+ePWbbhx9+qFmzZmnQoEHq16+fLr/8cn300Ucu9z9w4ICmTJmi9PR05efna/Xq1S7t1dXVmjVrljIzMzV06FA99NBDwT5FAOixDIPJxgAAAPwJOkiXlpbqoosu0s6dO7Vy5Upt3bpV119/vSRp3bp1uu2227RkyRJt375dbW1tuuyyy2S32yVJO3bs0Jw5czR//nyVl5crNzdXM2bMUGNjoyTp448/1oQJE/TSSy/pr3/9q6qqqnT11Vebj93Q0KDp06crLy9P5eXluvHGG3XDDTdo586dkiSbzaaZM2eqvb1d27Zt0+LFi3XHHXdo7dq1Yb9QANCjkKQBAAB8SjKM4FYNra6u1rBhw8zrTz/9tK677jo1NjaqqKhIF1xwgZYtWyZJOnjwoEaPHq0tW7Zo6tSpuvLKK2Wz2fTcc89Jkurq6jR48GCtWLFCJSUlHo+1bt06XX311aqrq1NWVpYeeeQR3XfffaqpqVFqaqokadKkSRo5cqSefPJJPf/887rqqqtUU1Oj7OxsSdKcOXP02WefaevWrX6fW0NDg7KyslRfX6++ffsG87IAQEK4b8Pb+t32DzT/wnwtumR0tA8HAADgtAkmDwZdkXYO0ZKUlpYmu92uuro67d27V5dcconZNmrUKA0ZMsSsGJeVlbm09+vXTwUFBWa7O5vNprS0NGVmZpr3Ly4uNkO0JBUXF7vsv6CgwAzRjvby8nJ5+76gpaVFDQ0NLj8AACmJkjQAAIBPYU02ZhiGVq5cqcLCQh0+fFiSlJeX57JNbm6uampqVFtbq7q6Op/tzmw2m9544w3de++9+vGPf6yUlBRJUlVVVZf399Xe0tKio0ePehz/0qVLlZWVZf64f0kAAD0Nk40BAAD4F3KQbmtr080336yysjItX75cTU1NkqSMjAyX7TIyMtTS0uK33WHevHlKTU3V+eefr/PPP1933nmn2dbU1NTl/X21S3J5DIdFixapvr7e/Kmurg7qNQCAROPovMPqVwAAAL6FFKRramp00UUXacOGDdqyZYvGjx8vq9UqSWptbXXZtrm5WRkZGX7bHZYsWaK9e/fqb3/7mz7++GOdf/75Zpdrq9Xa5f19tUueAd6xfd++fV1+AADMNQYAANCVoIN0ZWWlCgsL1adPH1VUVGjSpEmSpJycHEnyqOpWV1crPz9fgwYNktVq9dnuMGTIEI0bN06XX365Nm3apA8//FB//vOfzcfo6v6+2rOysjRgwIBgnyoA9DgGnbsBAAD8CjpIX3vttZo8ebI2btzoMqlXTk6ORowYoZdfftm8rbKyUjU1NZo2bZosFosmT57s0l5fX6/du3dr2rRpXh8rKSlJFotFNptNklRUVKQtW7aY1yVp8+bN5v2Lioq0a9cu1dfXe20HAASGrt0AAAC+BRWkKysrtWfPHs2ZM0dVVVV6//33zZ/6+notXLhQy5Yt07p167R7927ddNNNmjVrlsaOHStJWrhwoZ555hk99thjqqioUElJiUaOHKkZM2ZIkn7yk5/oqaeeUkVFhV577TVdeeWVSk9P15VXXilJmjt3rurq6lRaWqoDBw7o3nvv1f79+3XrrbdKkmbPnq2BAweqpKREFRUVevzxx7V+/XrdfvvtkXzNACBhBbcgIgAAQM+UEszGhw4dktQRWN09+uijKi0t1ZEjR3TLLbeoublZl19+uZYvX25uc+mll2rZsmVasmSJamtrVVxcrA0bNig5OVmSNGLECN1///366KOP1L9/f02ZMkU7duzQmWeeKamj6v3CCy+otLRUq1at0pgxY7Rx40YNHz5cUsc46E2bNmnevHmaOHGi8vLytGbNGhUWFob26gBAD8XyVwAAAL4lGd4WWO6hglmAGwAS0c+ef0t/+NeH+n7xl/TDi0dG+3AAAABOm2DyYFjrSAMAEgvfrQIAAPhHkAYAmBwxOonZxgAAAHwiSAMATPZTFWliNAAAgG8EaQCAydGzm4I0AACAbwRpAIDJfipIW0jSAAAAPhGkAQBO6NoNAADgD0EaAGBydO22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", "text/plain": [ "
" ] @@ -199,12 +282,12 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { - "image/png": "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74crZXU2zzuWW24aLg0AA0M1+Gr+HZDLbSeqEweHYWNqHs609BIbabzb/zr2sbFFVldFBKqx7P+MhMZbidH9guy+HhERERExkSYisqBUyPH2zFGdnqfTCyQt34urFfUAgJenxUKl7Dzx/c0dA7ExNQ91jTq8vDUDAFCjbcaRnNJ25zoikQaA2bf3d8h1iIiIiMiAiTQRUTco5DJ8nfwLjHtzNwBYDNO2pU+gD7wUMjTpBD4/esXmuRqVYxJpIiIiInIsJtJERN0U5qfCiodGwcdbYXVotjWBPl7Y9GwCDmeXwDgyXKfXY3VKNgDgL/fH4u0d56CUyzDxll49VXUiIiIisgMTaSIiO8wa10/ya+L6ByOuf7DFsaQhvXDuejXmTBiA+0b2gRACfYM1jqomERERETkQE2kiIhcwbmAIxg0MAQBEBfk4uTZEREREZIvtTVKJiIiIiIiIyAITaSIiIiIiIiIJmEgTERERERERScBEmoiIiIiIiEgCJtJEREREREREEjCRJiIiIiIiIpKAiTQRERERERGRBEykiYiIiIiIiCRgIk1EREREREQkARNpIiIiIiIiIgmYSBMRERERERFJwESaiIiIiIiISAIm0kREREREREQSMJEmIiIiIiIikoCJNBEREREREZEETKSJiIiIiIiIJGAiTURERERERCQBE2kiIiIiIiIiCZhIExEREREREUnARJqIiIiIiIhIAibSRERERERERBIwkSYiIiIiIiKSgIk0ERERERERkQRMpImIiIiIiIgkYCJNREREREREJAETaSIiIiIiIiIJmEgTERERERERScBEmoiIiIiIiEgCJtJEREREREREEiidXQFXIoQAANTV1aGqqgpeXl5OrhF1V1NTE+Po5hhDz8A4egbG0TMwjp6BcXR/jKHrqqqqAtCaF9oiE1056yaRn5+Pfv36ObsaRERERERE5CR5eXno27evzXOYSJvR6/U4f/48hg8fjry8PAQEBDi7StRNVVVV6NevH+PoxhhDz8A4egbG0TMwjp6BcXR/jKHrEkKguroakZGRkMttz4Lm0G4zcrkcUVFRAICAgAB+sD0A4+j+GEPPwDh6BsbRMzCOnoFxdH+MoWsKDAzs0nlcbIyIiIiIiIhIAibSRERERERERBIwkW5DpVLhtddeg0qlcnZVyA6Mo/tjDD0D4+gZGEfPwDh6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fxZKpVIsXrxYZGRkiHnz5onIyEjuEd5DrMXw5ZdfFlu3bhXp6enio48+EsHBwWLevHmmcsbQNZw/f14AEFu2bLHY0/TChQuioqKCbdFNdBZHtkf3UFlZKR5//HGxa9cukZGRIT799FMREREhHnvsMSEE/za6i87iyPbo2ZhIt9BqtWLevHkiICBABAcHixdeeEFotVpnV4vMzJ07V4SEhAiNRiNuu+02sX79elOZXq8Xr7zyiggLCxN+fn7iscceE+Xl5Ravf//990VkZKTw8fER999/v8jLy7vB74CsJWGOiN2WLVvEoEGDhEqlEhMnThSZmZk34u3clKzFcNGiRaJ3795CrVaL4cOHi1WrVrW7ScUYOt/+/fsFAKtf7733Htuim+gsjmyP7qGhoUE8/PDDIjw8XKjVajF06FCxdOlS0/892R7dQ2dxZHv0bDIhhLjRveBERERERERE7opzpImIiIiIiIgkYCJNREREREREJAETaSIiIiIiIiIJmEgTERERERERScBEmoiIiIiIiEgCJtJEREREREREEjCRJiIiIiIiIpKAiTQRERERERGRBEykiYiIiIiIiCRgIk1EREREREQkARNpIiIiIiIiIgmYSBMRERERERFJ8L9uux0ltzzZsQAAAABJRU5ErkJggg==\n", + "image/png": "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n7cGB+Ct93xJC1/drodXfqwjHT4dSWF6t6G6f7lGD+yT8u3tIqUg3dDQoKuuukp1dXVasWKFzjjjDK1f31my7+gIftLb29vNMHvMMcfomGOOkSRNmDBBxx57rKZNm6Z33nlHZ555pkpLS2PePtXjUnjAN65fWpr71ukAAAAAeicjlya65jdXXElM7T7YElyErCh26V8uOFFXnD5CHl/8NdZWSzft1kMrtkvqDORFToft15MnIukgXVtbq3POOUennnqqNm3aZK5Hrq6ultS5/dSxx3ZvMF5fXx+10dfEiZ0d1z755BOdeeaZqq6uVn19fdB16uvrNWbMGPMx4h3/6KOPwo5XVVVpwAD7LVAHAAAA0Lv5jH2kbR6kS7q2kXq3vjFoe6tQw/uXaW9zd5AeO7yfGXxrjk6+knzHBSfqm1NGqdjljPm4vU3S38k111yjs846S08//bSczu5eZdXV1Ro9erSWLVums88+W1Jn6G5oaNC5554b8b6MKvZxxx0nSZo6daqWLVumn/70p5I6t7N69dVXdeedd5rHH330Ufl8PrlcLknS8uXLzfufOnWqnnrqKTU1NamqqirsOAAAAABkktHU2JV0G+fsGjWgQtv3tejepe8ndP0ip0Mb//18M4CnY3C/srTvw26SCtK1tbXasGGD7rrrLtXV1QUdGzRokO644w7dfffdOu2001RTU6Pbb79dM2fO1KmnnipJevDBBzV8+HCNGzdO77//vu68805ddNFFOu200yRJt912myZNmqS5c+fqK1/5in7zm9/I7/dr1qxZkqQbbrhBP//5zzVnzhzdeuut+stf/qItW7Zo8eLFkqQrr7xSP/rRjzR79mz95Cc/0bp167RkyRKtXr06zacJAAAAAMIZa47tPrX76kkj9cFnzXJ7o0/NPmhpDjauukqVZcXZGFqvlFSQ3rNnj6TOwBpq/vz5mjNnjvbv369bbrlF7e3tuuyyy7RgwQLzOhUVFbrzzjt14MABjRw5Utddd51+9KMfmccnTJigxYsX66677tJ9992nSZMm6eWXX1ZlZaWkzqr3c889pzlz5uixxx7T2LFj9cILL2jUqFHm/b/44ou68cYbNWnSJNXU1Gjx4sWaPHly8s8MAAAAAMRhLDm2e7Ox808ZovNPGRLzOv/19236zasfS8qPzto9yRGItMFygWpublZVVZWa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dJCeM2eOzj77bK1du1aPPvqoVq1apeuuu06S9Mwzz+j222/X3LlztWbNGnk8Hl166aXy+zvXVN13331yOp16/vnn9fe//12SdOGFF6q5ubNT644dO3TxxRfrvPPO01tvvaXp06fr8ssv186dOyVJzc3NOv/881VTU6N169bp+uuv16xZs7R27VpJks/n0yWXXCKv16vXXntN99xzj37wgx/o6aefTvuJAgBkVj5kT6PZWMPnbdrT1J7j0fSs00cdZVakS4uC3z5QkQYAFJqk10g/9dRTGjFihCRp/Pjxampq0rXXXqvW1lbNmzdPN998s6699lpJ0qJFi3TyySdr1apVOuecc/T973/fvK0k/f73v1d1dbXefPNNfelLX9L8+fN13HHH6f7775ckPfTQQ3r22Wf1+OOP65577tETTzwhn8+nRYsWqaSkROPHj9fSpUu1cOFCTZkyRc8//7y2bdumV155RYMHD9bEiRO1fPlyPfzww/ra176WiecLAJAh+TAd2hoff/iXLXps1pk5G0tPO6qi2KxIlxa5go7RawwAUGiSrkhbg7AklZWVye/3q7GxURs3btRFF11kHjvppJM0bNgws2Ic6bZSZyVZklauXKkLL7zQPF5UVKRp06aZt1+5cqVmzJihkpIS8zozZswIOj5x4kQNHjw46Pi6devyYgohAOQTf+QG0L2KtRK7u7EthyPpeR5fQJ6urt0VJcFB2kFFGgBQYNJqNhYIBPToo49q8uTJ2rt3rySppqYm6DojR45UQ0NDxNsvWrRI5eXlmjJliiSprq4u5u1TPe52u3XgwIGwx3e73Wpubg76AgBkR15UpC35sdiV3/07PT6/PN7OIF0WFqRzMSIAAHIn5Vd9j8ejm266SStXrtSCBQvU0tIiSaqoqAi6XkVFhdxud9jtf/e73+nf/u3f9MADD2jAgAGSpJaWlpi3T/W4pIhjmDdvnqqqqsyv0Io5AKDn5EGODgnS+ZMmIwVjj89vTu0uLw6d2p0/3zsAAIlIKUg3NDTo7LPP1tKlS7VixQqdccYZKi0tlSR1dHQEXbe9vT0o3La3t+umm27Srbfeqt/85je69dZbzWOlpaUxb5/qcSk84EvS3XffraamJvOrvr4+qecBAJC6vKhIW1ZJ51NFOtL34vEF1NE1tTs8SGdlWAAA2EbSr/q1tbWaPHmyKisr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", "text/plain": [ "
" ] @@ -223,12 +306,12 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 14, "metadata": {}, "outputs": [ { "data": { - "image/png": "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\n", + "image/png": "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edUbKAAz/Rj5IqVGUZWVlbrqqqt0yy23aNy4caqpqdH111+vK6+8UgMHDtSsWbN0xRVX6LzzztO1116rH/7wh5o8ebKmT5+u5557Ti+//LKef/75vvpaAAAAgISCMW9EbYWkyMZJ/TX9uH/W1PbWCCrRVGxLgt7MTD9Gvkl5g6g77rhDXV1duvDCC2Wz2XTxxRdr7ty5crvdamho0NatWyVJ1113nQzD0F133aUtW7Zo//331xNPPKGzzjor618EAAAA0Bufz1C7y98bprLMPw23yxRq+61S229raqO/HvNlib5e89B+8MyysJC7rHGvpP7b1xfoTcqh1uFw6N5779W9994bdd3GjT3d4ywWi77zne/oO9/5TkYDBAAAALLhisc+Cp2uLPeHWnM1M90tblIVXNfb12tqYzWK8oaF2viVafN62ac+bIx5zKGj69IdIpBVKYdaAAAAoBC92rAjdDpYqTXzJAh52dRfa2q9MSqxniRD7bqmDkn+4P29Mw6Iur7MZtH/HkGvHOQHQi0AAABKTrBSaxZrum5f6K81tbG+HnP35x2trrj3sbPN3/34VxccrvOmjMnyCIHsItQCAACg5FTEqtT2U6gNralN0IwpE8Gpw11ur7771LKw6/6xOHwq8artrZo8InorTZfHX8V12KO/T0C+IdQCAACg5MSeftxfobZvK7UOe8+unZEhNlJTm0uTR0Rf3h0IteX2lHYABXKCUAsAAICSUxVj+nH/benj/39fdT+ur3Ho6pP31YbmTh06pi6sHjznpVVhx7Z0ucPOb2vp0h/fXKeNu/xragm1KASEWgAAAJScWGtq3f2+prbvtsT5/v9Mjnl5ZKjd29kTalu63Dp2zuth1zsItSgA/JQCAACg5JTZrGHb1kixuwX3hZ5Kbb88XELmSu2nO9qirqdSi0LATykAAABKjtUSXYV099P04+CmPn21pjYVezq6Q6c7u71R15fbiAvIf/yUAgAAoORYLBYdPmZg2GXeJKcfr2tq123/Wamdrc60HjuYnftqTW0q9poqtV3dnqjrmX6MQsBPKQAAAEqO1SI9fvl0Da91hC7rilGpjOXCBxbq4XfW6+onFqf12P2xpjaeGfsNkSQNri6XJO3t7KnUdrmjv/5qBy14kP/4KQUAAEDJsVosslktGl9frR2tLknRnYDjaW73H//Bhj0pP67P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", "text/plain": [ "
" ] @@ -251,6 +334,20 @@ "x_plot(x3, 'MM_FixedCapital', ax)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, @@ -275,7 +372,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/hikyuu/examples/notebook/010-Portfolio.ipynb b/hikyuu/examples/notebook/010-Portfolio.ipynb index e8ffedf9..1d814443 100644 --- a/hikyuu/examples/notebook/010-Portfolio.ipynb +++ b/hikyuu/examples/notebook/010-Portfolio.ipynb @@ -6,23 +6,29 @@ "id": "2dbefd71", "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-04-02 16:52:36,011 [INFO] hikyuu version: 1.3.5_202404021336_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:91) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2022-03-06 16:25:02.753 [HKU-I] - Using SQLITE3 BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2022-03-06 16:25:02.753 [HKU-I] - Loading market information... (StockManager.cpp:497)\n", - "2022-03-06 16:25:02.753 [HKU-I] - Loading stock type information... (StockManager.cpp:510)\n", - "2022-03-06 16:25:02.754 [HKU-I] - Loading stock information... (StockManager.cpp:424)\n", - "2022-03-06 16:25:02.798 [HKU-I] - Loading stock weight... (StockManager.cpp:527)\n", - "2022-03-06 16:25:03.131 [HKU-I] - Loading KData... (StockManager.cpp:139)\n", - "2022-03-06 16:25:03.133 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:162)\n", - "2022-03-06 16:25:03.134 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:165)\n", - "2022-03-06 16:25:03.134 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:168)\n", - "2022-03-06 16:25:03.145 [HKU-I] - 0.01s Loaded Data. (StockManager.cpp:150)\n", - "Wall time: 951 ms\n" + "2024-04-02 16:52:36.400 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-04-02 16:52:36.428 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", + "2024-04-02 16:52:36.440 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", + "2024-04-02 16:52:36.446 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", + "2024-04-02 16:52:36.588 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", + "2024-04-02 16:52:37.734 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", + "2024-04-02 16:52:38.442 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", + "2024-04-02 16:52:38.442 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", + "2024-04-02 16:52:38.443 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", + "2024-04-02 16:52:38.451 [HKU-I] - 0.72s Loaded Data. (StockManager.cpp:150)\n", + "CPU times: total: 297 ms\n", + "Wall time: 2.95 s\n" ] } ], @@ -34,32 +40,34 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 2, "id": "6d8078dc", "metadata": {}, "outputs": [], "source": [ "# 创建一个系统策略\n", - "my_mm = MM_FixedCount(100)\n", - "my_sg = my_sg = SG_Flex(EMA(C, n=5), slow_n=10)\n", + "# my_mm = MM_FixedCount(100)\n", + "my_mm = MM_Nothing()\n", + "my_sg = my_sg = SG_Flex(EMA(CLOSE(), n=5), slow_n=10)\n", "my_sys = SYS_Simple(sg=my_sg, mm=my_mm)" ] }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 3, "id": "79aa46ca", "metadata": {}, "outputs": [], "source": [ "# 创建一个选择算法,用于在每日选定交易系统\n", "# 此处是固定选择器,即每日选出的都是指定的交易系统\n", - "my_se = SE_Fixed([s for s in blocka if s.valid], my_sys)" + "# my_se = SE_Fixed([s for s in blocka if s.valid], my_sys)\n", + "my_se = SE_Fixed([s for s in sm.get_block(\"指数板块\", \"沪深300\")], my_sys)" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 4, "id": "cd7c2dc5", "metadata": {}, "outputs": [], @@ -71,7 +79,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 5, "id": "c9d3a04d", "metadata": {}, "outputs": [], @@ -85,7 +93,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 6, "id": "d7d51202", "metadata": {}, "outputs": [ @@ -93,32 +101,39 @@ "name": "stdout", "output_type": "stream", "text": [ - "Wall time: 3.25 s\n" + "spend time: 14.395 ms | Portfolio_readyForRun (hikyuu_cpp\\hikyuu\\trade_sys\\portfolio\\Portfolio.cpp:110)\n", + "2024-04-02 16:53:00.351 [HKU-E] - 2022-04-12 00:00:00 cash(238.0800) must be <= current cash(238.0800)! (TradeManager.cpp:460)\n", + "2024-04-02 16:53:00.806 [HKU-E] - 2023-06-20 00:00:00 cash(548.0800) must be <= current cash(548.0800)! (TradeManager.cpp:460)\n", + "2024-04-02 16:53:00.822 [HKU-E] - 2023-07-06 00:00:00 cash(536.8200) must be <= current cash(536.8200)! (TradeManager.cpp:460)\n", + "2024-04-02 16:53:00.838 [HKU-E] - 2023-07-20 00:00:00 cash(751.9400) must be <= current cash(751.9400)! (TradeManager.cpp:460)\n", + "2024-04-02 16:53:00.942 [HKU-E] - 2023-11-03 00:00:00 cash(1684.1600) must be <= current cash(1684.1600)! (TradeManager.cpp:460)\n", + "2024-04-02 16:53:00.959 [HKU-E] - 2023-11-17 00:00:00 cash(1684.1600) must be <= current cash(1684.1600)! (TradeManager.cpp:460)\n", + "2024-04-02 16:53:00.987 [HKU-E] - 2023-12-01 00:00:00 cash(1684.1600) must be <= current cash(1684.1600)! (TradeManager.cpp:460)\n", + "CPU times: total: 312 ms\n", + "Wall time: 804 ms\n" ] } ], "source": [ "# 运行投资组合\n", "q = Query(-500)\n", - "%time my_pf.run(Query(-500))" + "%time my_pf.run(Query(-500), 10)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 7, "id": "40d5c33f", "metadata": {}, "outputs": [ { "data": { - "image/png": "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", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA88AAAMNCAYAAACmq0Z8AAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd5hc9X0v/veZvjM7s1WrsuqNIiSajGgGATYmYONyDeaSgE0SfO+NsUni2I5/SRzjFNuJQxyMHVywiR2MQ67hgikGI0RHSDQJgVBB0mpXK2n7bJk+c35/nPP9zjnTZ3f6vF/Psw/StD2LdnfO53yaoqqqCiIiIiIiIiLKylLtAyAiIiIiIiKqdQyeiYiIiIiIiPJg8ExERERERESUB4NnIiIiIiIiojwYPBMRERERERHlweCZiIiIiIiIKA8Gz0RERERERER5MHgmIiIiIiIiyoPBMxEREREREVEeDJ6JiIiIiIiI8miq4Hnnzp0466yz8MILLxT1vOHhYXzmM59BV1cXWlpa8LGPfaw8B0hEREREREQ1yVbtA6iE119/Hd/61rfwyCOPIBgMFvXcqakpXHTRRVi6dCkefPBB+Hw+7N+/v0xHSkRERERERLWoKYLnBx54AE6nE4888gguu+yyop77rW99C4lEAr/5zW/gcDgAAGeccUYZjpKIiIiIiIhqVVOUbf/d3/0dfvGLX2DlypVp96mqim9961tYvHgxfD4fLr/8clNm+Z577sEXvvAFGTgTERERERFR82mK4FlRlKz3/cu//At+8IMf4M4778QzzzwDr9eLD3/4w4hGozhy5AgGBwfh8/mwefNmdHV14f3vfz9effXVCh49ERERERERVZuiqqpa7YOolMOHD2PFihV4/vnnceGFFyISiaC7uxv3338/rrjiCgDA6Ogouru78cILL8Bms+Hcc8/Fhg0b8Nd//ddYvHgx/vEf/xEvvfQS9u/fj87Ozip/RURERERERFQJTZF5zmb//v2YmprCxz72MbhcLrhcLvT29gLQAu1YLAYA+OIXv4hrrrkG5513Hn7+859jcnISjzzySDUPnYiIiIiIiCqoKQaGZRONRgEAv/71r7Fq1SrTfQsXLsTQ0BAAYPXq1fL2jo4O9PT04MSJE5U7UCIiIiIiIqqqpg6eV69eDZvNhmPHjuGqq65Ku9/r9aKnpwfbtm3D+eefDwAYGRnB0NAQ1q5dW+nDJSIiIiIioipp6uC5tbUVN998M77yla/AZrPhzDPPxN69e/Gb3/wGv/jFL2CxWPDnf/7n+Lu/+zssWrQIK1euxF//9V9j7dq1uPLKK6t9+ERERERERFQhTR08A8B3vvMdAMBf/MVfIBgMYtWqVfjTP/1Tef+Xv/xlBAIB3HrrrZicnMQll1yCRx55BHa7vUpHTERERERERJXWVNO2iYiIiIiIiGajqadtExERERERERWiYcu2E4kEBgcH4fV6oShKtQ+HiIiIiIiIKkRVVUxNTWHRokWwWEqTM27Y4HlwcBBLliyp9mEQERERERFRlfT392Px4sUlea2GDZ69Xi8A7X+Wz+er8tEQERERERFRpUxOTmLJkiUyLiyFhg2eRam2z+dj8ExERERERNSEStnCy4FhRERERERERHkweCYiIiIiIiLKg8EzERERERERUR4MnomIiIiIiIjyYPBMRERERERElAeDZyIiIiIiIqI8GDwTERERERER5cHgmYiIiIiIiCgPBs9EREREREREeTB4JiIiIiIiIsqDwTMRERERERFRHgyeiYiIiIiIiPJg8ExERERERESUB4NnIiIiIiIiojwYPBMRERERERHlweCZiIiIiIiIKA8Gz0RERERERER5MHgmIiIiIiIiyoPBMxEREREREVEeDJ6JiIiIiIiI8mDwTERERERERJRH0cHzrl27cPnll8PtdmPBggW46aabMDo6mvXxU1NTuPXWW3HzzTen3bd06VIoimL6GBkZkff39/fjwx/+MDweDxYtWoTvfOc7xR4uERERERER0ZwVHTzfcsst2Lx5M7Zt24a7774bzz77LG688ca0x01PT+NrX/saVq5cibvuuguqqqY9ZmxsDPfeey/2798vPzo7OwEA8XgcV111FWKxGJ5//nncdttt+MpXvoL7779/Fl8mERERERER0ezZin3CvffeiyVLlgAANmzYAL/fjxtuuAGBQABut1s+rq+vD48//jh++MMf4o477kh7nUgkgpmZGaxfvx6rV69Ou//RRx/Fu+++i6eeego9PT0466yzsGXLFnz/+9/HtddeW+xhExEREREREc1a0ZlnETgLLpcLiUQi7XGnnnoqduzYgU984hMZX2dsbAwA0N3dnfH+rVu34qyzzkJPT4+87dJLL8Urr7ySMYsd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mLYNAFcsa83jkRARERFRvjB4JiJK4fiAV/v7xxZPzeOREBEREVG+MHgmIkphTlMFAMBuMaHObcvz0RARERFRPnBVFRFRCjesnQ23zYyrl7fl+1CIiIiIKE8YPBMRpVDlsOLmC+bm+zCIiIiIKI9Ytk1ERERERESUAoNnIiIiIiIiohQYPBMRERERERGlwOCZiIiIiIiIKAUGz0REREREREQpMHgmIiIiIiIiSoHBMxEREREREVEKDJ6JiIiIiIiIUmDwTERERERERJQCg2ciIiIiIiKiFBg8ExEREREREaXA4JmIiIiIiIgoBQbPRERERERERCkweCYiIiIiIiJKgcEzERERERERUQoMnomIiIiIiIhSYPBMRERERERElAKDZyIiIiIiIqIUGDwTERERERERpcDgmYiIiIiIiCgFBs9EREREREREKTB4JiIiIiIiIkqBwTMRERERERFRCgyeiYiIiIiIiFJg8ExERERERESUAoNnIiIiIiIiohQYPBMRERERERGlwOCZiIiIiIiIKAUGz0REREREREQpWPJ9ABNFlmUAgMfjyfOREBERERER0WQScaCIC3OhZIPnoaEhAEBbW1uej4SIiIiIiIjyYWhoCNXV1Tn5XJKcy1C8gEQiEZw4cQKyLGP69Ok4duwYqqqq8n1YROPi8XjQ1tbG5zMVPT6XqVTwuUylgs9lKiXi+bxnzx6cdtppMJly061csplnk8mEadOmaen6qqoqvhBQyeDzmUoFn8tUKvhcplLB5zKVktbW1pwFzgAHhhERERERERGlxOCZiIiIiIiIKIWSD57tdju+/e1vw2635/tQiMaNz2cqFXwuU6ngc5lKBZ/LVEom6vlcsgPDiIiIiIiIiHKl5DPPREREREREROPF4JmIiIiIiIgoBQbPRERERERERCkweCYiIiIiIiJKgcEzERERERERUQolHTzLsozbb78dU6dOhdvtxuWXX47u7u58HxbRmHbs2IFly5Zh48aNUbffe++9aG9vh9PpxAUXXICDBw9G3f/444/j9NNPh8PhwNlnn4233357Mg+bSLNz505cdNFFcLlcaG5uxvXXX4/e3l7tfj6XqZj87ne/w+LFi+FyuTB9+nR897vfhXFJCZ/PVGwefPBBSJKE3/zmN9ptfB5TMfnlL38JSZKi/tx8883a/RP+fJZL2F133SXX1dXJTz/9tPyXv/xFPv300+WPfvSj+T4sojhvv/22fNVVV8lOp1MGIG/YsEG779FHH5Xtdrv84IMPylu3bpXPO+88ecGCBXI4HJZlWZbfeOMN2WKxyD/+8Y/l7du3y1dccYXc1NQkezyefH07VMbOP/98+Xvf+568Y8cO+ZlnnpHb29vlSy65RJZlPpep+HznO9+RH3nkEXnHjh3yfffdJ5vNZvm//uu/ZFnm85mKj9frlWfMmCEDkB966CFZlvk8puJz9913yytXrpT379+v/enu7pZleXKezyUbPIfDYbmhoUH+0Y9+pN325z//WQYgHzx4MI9HRhTvn//5n+XPfe5z8ssvvxwXPC9dulS+9dZbtX/v3btXBiCvX79elmVZvvzyy+VPfOIT2v39/f2y3W6Xf/nLX07eN0CkOnr0aNS/H374YdlkMskjIyN8LlPRu+SSS+TLL79clmW+NlPx+da3viVfeeWVUcEzn8dUbG677Tb5iiuuSHjfZDyfS7Zse9euXejp6cFHP/pR7bY1a9bAZDJh8+bNeTwyonh33HEHHnroIcyaNSvq9oGBAWzbti3qeTx//ny0tLRoz+NXXnkl6v6amhosW7aMz3PKi7a2tqh/OxwORCIRPpepJITDYdTX1/P5TEVn586duPfee/HjH/9Yu43PYypGfX19aGhoiLt9sp7PJRs8i/r29vZ27Tan04nGxkZ0dHTk67CIEpIkKeHthw4dAhD9PAaA6dOno6OjA/39/RgYGBjzfqJ8kmUZDzzwAFauXInOzk4AfC5TcRoZGcEDDzyAN998EzfffDNfm6mo+Hw+fPazn8W//Mu/YNq0adrtfB5TMert7cUDDzyAyspKLF68GD/4wQ8QDAYn7flsGf+3UJiGh4dhMplgt9ujbne5XPD7/Xk6KqLMDA8PA1Cet0bieZzs/p6ensk5SKIEgsEgbrzxRrzyyit4/fXX+VymouVwOOD3+1FVVYV7770XZ555JjZs2ACAz2cqDn/3d3+HhoYG/O3f/m3U7XxdpmJ0++2345//+Z/h9/vx8ssv49vf/ja6urpw2WWXAZj453PJBs92ux2RSAShUAgWi/5t+ny+uB8aUaESF38CgUDU7eJ5nOp+onzo6OjApz/9aRw8eBDr16/H8uXLsWXLFgB8LlPx2b59OwYHB7F161bceuut2L17Nz75yU8C4POZCt/999+PJ554Atu3b4fJFF1wynMMKkZnnHGG9veVK1ciHA7jrrvuwlVXXQVg4p/PJVu23draCgBRaXi/34/u7u64vlKiQiWex8eOHYu6/dixY5g1axYaGhpgt9vHvJ9osu3btw8rV65EZWUlduzYgXPOOQcAn8tUvObPn4+VK1fipptuwt13340f/OAHfD5T0fje976H3t5etLe3w+FwwOFwAAC++MUv4rOf/SwAPo+puC1btgwjIyNobm4GMPHP55INnpctWwan04kXX3xRu+21116DJElYvXp1Ho+MKH2tra2YOXNm1PN437596OjowLp162AymbBq1aqo+0WGZN26dfk4ZCpz1157LVatWoVnn30WTU1N2u18LlMpsFgskGUZ1dXVfD5TUXjppZewe/dubN++XfsDKEH1c889x+cxFb0tW7agtrYWbW1tk/N8zmpGeJH4+te/Ljc3N8vPPfecvHHjRnn+/PnyzTffnO/DIhrToUOH4lZV/eQnP5Hdbrf86KOPym+99ZZ83nnnyR/72Me0+59++mnZbDbL9913n7x9+3b58ssvl88880w5FArl41ugMvb+++/LAOTHHnssav/i/v375YGBAT6XqagMDg7Kn//85+Xnn39e3rVrl/yb3/xGbm5ulj/72c/KsszXZipeMKyq4vOYis3NN98s//nPf5a3b98u//CHP5QdDod81113ybI8Oc/nkg6efT6ffMMNN8hVVVVybW2tfMstt8g+ny/fh0U0pkTBcyQSkf/1X/9VbmhokCsqKuTPfvazcn9/f9TH/fSnP5WnTp0qO51O+dJLL5WPHTs2yUdOJMuvvfaaDCDhn3vuuYfPZSoqfr9fvuaaa+QpU6bIDodDPu200+Tvfe972nkEn89UrIzBM5/HVGyuv/56ua6uTna5XPKSJUvkBx98ULtvMp7PkizL8rjz5UREREREREQlrGR7nomIiIiIiIhyhcEzERERERERUQoMnomIiIiIiIhSYPBMRERERERElAKDZyIiIiIiIqIUGDwTERERERERpcDgmYiIiIiIiCgFBs9EREREREREKTB4JiIiIiIiIkqBwTMRERERERFRCgyeiYiIiIiIiFJg8ExERERERESUwv8HWnhOtnG4ZxUAAAAASUVORK5CYII=", "text/plain": [ - "
" + "
" ] }, - "metadata": { - "needs_background": "light" - }, + "metadata": {}, "output_type": "display_data" } ], @@ -129,9 +144,167 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "db82df69", "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABMMAAAP2CAYAAADq+VqrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzddVhUWR/A8e/QjZQoKIqiYqLYK3YX6tqxirXqmti9xq5ivOraucbata7d3d2FCdhICUjOef+Y5S7jDIixxno+z3Ofmbnn3HPPvTMwc3/3hEoIIZAkSZIkSZIkSZIkSZKkb4DB566AJEmSJEmSJEmSJEmSJH0qMhgmSZIkSZIkSZIkSZIkfTNkMEySJEmSJEmSJEmSJEn6ZshgmCRJkiRJkiRJkiRJkvTNkMEwSZIkSZIkSZIkSZIk6Zshg2GSJEmSJEmSJEmSJEnSN0MGwyRJkiRJkiRJkiRJkqRvhgyGSZIkSZKUYbGxsemmv379+hPVRJIkSZIkSZLejwyGSZIkSdI3JFu2bBw/flx53axZM4YMGZKhbXft2oW7uzu7du3Sm/706VPy5cvH3LlzM1yfbt26MWHChAznlyRJkiRJkqQPZfS5KyBJkiRJH2LWrFlMnTqVoKAgZs+eTadOnTK8rZ+fH0uXLkUIAcDZs2epVq0a48ePp1u3bh+lftWqVePw4cOYmJhorY+NjcXU1BRDQ0NlnRCC2NhY7t+/T86cOXn69CkhISGEhYURGhpKSEgIQUFB9OnTB1NTU2JiYtLcr6OjI46Ojjrrnz59SkJCAgCvXr1iy5YtrFixIkPHUr16dfz8/KhTpw4zZ87UOkexsbE0atSITJky0bx5c63twsLCCAsL01ummZkZY8aMoXLlytjb2+ukGxgYkCtXrgzVT5IkSZIkSZIyQgbDJEmSpHe2ZMkS2rdvr7y2sLAgR44c1KtXj4EDB+oNwnyI/fv3ExUVRcOGDbXWb9myhR49etCqVSsCAgLInTv3B+3H2NgYW1tbzMzMPqicNw0dOpRRo0ZprcuZMyeTJ0+mSZMmyrqQkBCyZ8+uvJ4+fTqTJ0/G1taW0NBQfHx8cHV15eXLlwwbNozjx49jYWGhs7+oqChln+fPn6dXr14cPXpUJ9/KlSuxs7PD19c3Q8dhYGDAhAkTyJ07N2XKlFHWx8fH07BhQ0JDQzly5Ah2dnZa202ZMoVff/013bJLly6td72lpSXR0dEZqp8kSZIkSZIkZYQMhkmSJEnvbcKECRQpUoSwsDAOHTrElClT+PPPP7l48SKWlpYfbT9Vq1alXbt2OsGw3bt3AzB37lysra0/eD9eXl48fPjwg8t505QpU1i4cKHWuqdPn9KlSxf69OmjrEtOTtbKM3bsWMaNG8eDBw9wd3fnzz//1Ao0Dhw4UCfIBprWaCmioqI4efKk3nrNmTOHJ0+eYGxsrDe9dOnSrF+/XitA9zZZs2bVer1jxw5++eUXmjdvTv78+TEy+uenx4ULF6hduzZ//vknZcuWRa1WY2DwzwgON2/exNPTM8P7liRJkiRJkqSMkMEwSZIk6b2VKlWKSpUqAdCqVSvs7e0JCAhg48aN/PDDDx9U9puBEX1evXoF8FECYf+mvn37vlfLsNRdKD+2bdu2cenSJaZNm0bNmjWV9YsXL+bPP/9k8+bNmJubkzVrVgIDA3W2j4yMxNbWVnkdGhqKra2tTmDNxcWFhIQEWrdujYWFBatWrcLd3Z3t27fj7+/P6tWrKV26NH369GH//v2cOnWKpKQkhg4dysKFCzl58iReXl7/2nmQJEmSJEmSvj1yAH1JkiTpoylXrhygCeqk+Ouvv6hYsSI2NjaYmZlRpEgRZs2apYzTBXDw4EFUKhULFy6kU6dOWFpaKgEklUoFwNKlS1GpVOTMmZMHDx6gUqlYunQpACqVCpVKxYMHDwDN+FXDhw8nX758mJqaYmdnR7169Th79my69U+px5IlS7TWHz58mNq1a2NnZ4epqSn58uVj7NixJCYmZui8BAQEkClTJq0lKCiIdu3aaa0rWLCg1nbVqlVDpVLh7u4OgJOTEyqVSm9rsHchhFDKEELg6empLGq1mpw5c+Lp6UmOHDkwNDTEw8NDWZKSkvjxxx/58ccfyZUrl7I+ICCAevXqcerUKa31FhYWmJiYcOTIESwtLfnxxx85dOgQdevW5enTpzRr1gwbGxvmzp3LlClTMDc3p0GDBhw5coQzZ87IQJgkSZIkSZL00clgmCRJkvTRXLp0CYB8+fIBMHnyZBo2bIiRkRHTpk1j4cKF5MmThx49etC7d2+d7WfNmkV4eDhLly6lUaNGtGnThh07dgCawNCOHTtYunQpzs7O7NixQ+kOuGPHDnbs2IGzszOvX7+mUqVKTJw4kXr16rF8+XLGjBnD9evX8fHx4cSJE+90TGvWrKFy5co8f/6cgIAAli5dSvny5fn5559p3LhxhsoYPHgwERERLFiwABsbG5o3b87z58+JiYkhIiKCiIgIXr58ybVr17S2W7ZsGYGBgQwaNAiA06dPExgYSK9evd7pGN70559/cv36dWrXrs3Vq1e10u7cuUOOHDl0tomKimLIkCF4eXkRERHB3LlztVruzZs3T+nOWqxYMfbs2aO1va2tLdu3b2fNmjWUL1+ec+fOERQURJUqVUhKSmLdunV4e3sDsGLFCk6fPk2hQoU+6DglSZIkSZIkSS8hSZIkSe9o8eLFAhDbt28Xr169Enfv3hW//fabMDc3F6VKlRKJiYkiJCREmJiYiFq1agm1Wq21fcuWLYVKpRK3bt0SQghx4MABAQgvLy+RnJyssz9AtGvXTmd9u3btxJtfZRMnThSAWLVqldb658+fCxsbG1GuXLk0t0+px+LFi4UQQsTGxgoHBwdRuHBhkZCQoFXekCFDBCD27NmT7rkKDAwUa9euFTVq1BDly5cXFy5cEEIIER8fL8LDw8X69euFj4+PqF69uoiPjxcXLlwQ8fHxWmV4e3sLQDx9+lRZV7VqVfHzzz/r3WfqtAMHDghDQ0MlzdDQUPz5559i0aJFYt68eSJXrlxa2zo7O4uFCxcqrxMTE8XMmTOFk5OTsLW1FVOmTBFJSUlpHu+lS5eEj4+PAETdunVFcHCwVvqaNWvE6NGjhRBC9OrVS1hYWIhdu3aJsWPHiurVq4uEhATRokULsXXr1jT3IUmSJEmSJEkfQrYMkyRJkt5bnTp1sLa2Jnfu3AwcOJA2bdqwe/dujIyM2LVrFwkJCfTu3Vvp6piiY8eOCCE4cOCA1vrGjRu/dZywt9myZQtZsmShRYsWWuudnJzw9fXlxIkTxMbGZqisEydO8PLlS3766SedsbA6duwIwL59+9Itw9bWltatW7Nv3z6Cg4Np3rw57u7uSvfN7t27U6tWLVasWIGJiQlFixbFxMRE2f7q1atcuHABgIoVK2qds9GjRytdRFMvb6tTpkyZ6NChA3Xr1uX+/fvKAPunTp3i2bNnyjhwoBmXbfTo0TRs2JCDBw/StGlTnjx5QkhIiN7F3t6elStXMmfOHAIDA5WJFG7evEmNGjUICAigYMGCdOvWjVWrVrFv3z6qVq1KnTp12LdvH/Pnz6dBgwZ069aNH374gaioqLe8S5IkSZIkSZL0bmQwTJIkSXpv06dP5+jRo5w/f56wsDDmz5+vDKr+5MkTAL1d7lxcXAB4+fKl1npXV9cPrtOTJ09wc3PTm+bi4oJarSYiIiLDZcG7HcObnJycCA0NJTExkQsXLtChQwdev35N4cKFmTdvHg8fPiR37txUq1aNR48e6Ww/Y8YMqlevDkDr1q1p2rSpMuOlv78/wcHBOouPj4/euhw+fBi1Wq28dnV1pWrVqkydOhWABQsW8N1335E7d24lj52dHXfu3GHgwIEUK1aM7Nmzv3Vxc3MjZ86c3LhxAzs7O/7880/8/Pzo3r0758+fRwjBvn37sLS0pEqVKhgZGdGgQQNy5crFkCFDKF++PDdv3iRHjhzUr1+fpKSkdM+xJEmSJEmSJL0LOZukJEmS9N4KFy6sDJr/JgcHB0AzmH7+/Pm10h4/fgyAs7Oz1voPbRWWst/g4GC9aY8fP8bY2Bh7e/sMlwXaEwKkLgt0j+FNycnJXL58meXLl7Nu3Tpq167N+vXr8fHxITk5maioKLy8vEhMTKRMmTLs3LlTGUj/9u3b/P7776xfv57du3fTrVs3bty4waRJk/j111+xs7MjW7ZsOvucPHkydnZ2AMTFxSGEoGHDhmzdulVr4gKAYcOGUblyZcqXL8+SJUtYvny5Tnk2NjY8f/4cgNevX2NmZka9evWoVq0affr0YevWrQwePFgZfyylTinvZ6NGjWjUqJFSXpMmTZRZNNevX4+fn5/yniUmJiqt8H755ReEEDotCyVJkiRJkiTpQ8iWYZIkSdK/onr16hgYGDB9+nSdtIULF2JiYkKtWrUyVJaJiQkxMTEZylurVi0eP37M+vXrtda/ePGCzZs3U6NGDczMzDJU1nfffYe1tTWzZ8/WaZ20cOFCAHx9fdMtw9fXl/LlyzNv3jwsLCw4efIkjRs3xtLSEnNzc/LmzUvt2rVRqVRYWFhQsWJFpVvk6NGjqVatmtaMigsXLmTq1KmULl2ahw8f8r///U9Ju3PnDrNmzaJ06dLkzZsXgN27d6NWq3n8+DFnzpzB0NBQq36VKlWiTZs29OzZkzJlyuh0L/0YKlWqpLc7p0qlomnTpsTExCivTUxMtNINDAyUbpySJEmSJEmS9DHIlmGSJEnSvyJ37twMHjyYcePGUb16dVq3bo2xsTEbN25k48aNTJ48OcPdIgsVKsSePXtYunQppqam6QZs+vbty5o1a2jdujWnTp2idOnSPHnyhKlTp2JkZMSUKVMyfAw2NjZMnjyZLl26UKZMGbp06YKNjQ179uzh999/p0ePHpQsWTLdMiZOnEjfvn0xNzcnNjaW/PnzY2Vlhb+/P7GxsaxevZrY2Fji4+OxsbHBz8+PHTt2UKxYMXLnzs2wYcO0yrOwsAA0rcaaNWtGmzZtlLTQ0FD69etHTEwMAwcOBMDPzw8jIyMCAgL0tryLiopSWrmFhIRw9uxZSpQokeFzlBErVqzg9evXetN27tzJoEGDlJlI9cmePftHrY8kSZIkSZL0bZPBMEmSJOlf8+uvv5I3b15mzpxJt27dMDAwoFixYmzcuFGr29zbzJ49m86dOytBqfSCYTY2Nhw/fpxRo0axdu1apk2bhr29PTVq1GDMmDG4u7u/0zH8+OOPuLq6MmHCBPz9/UlOTiZ//vzMmzePzp07v3V7Z2dn/vrrL3777Tfy5cvH4cOHAbS6/v3+++8MGDCA77//nvbt21O1alUARowYgbGxMQ8ePNAqMyQkhNq1a1OlShV+++03ZX2ZMmWYM2cOHTt2JFu2bLRq1YoiRYowceJEvXU7deoUbdq0ITo6mj179jBq1CjKlCnDTz/9xNChQ8mSJcs7nau0pBf0zJIlCyqVCg8Pj4+yL0mSJEmSJEl6G5V4c/AQSZIkSZI+2KtXr/jpp59Yt24d+fPnp0+fPuTIkQNbW1uSkpJo3bo11apVY/bs2QCcPn2aVatWsXz5cqytrVm4cCFVqlQB4MGDB7i7u/PixQsSExOpUKEC7u7ubNmyBVNTU5199+rVi/nz53P06FGdVl5GRkasXLmSAwcOMH/+fCpVqsTy5cvJmjUrSUlJ/Prrr4wfPx6VSkWLFi1wdXXl119/fe/zMGjQIAICAtJMTxkzLDo6+r33IUmSJEmSJEnvQrYMkyRJkqR/gbW1Nfnz5+fQoUOULl0a0HT3vHbtGiqVinz58tGjRw8lf6lSpShVqhQTJkxgw4YNFChQQG+5lpaWNGrUiNGjR+sNhAFMmTKFfPnyUaRIEb3pjx49Yvv27cyfP58OHToordSMjIz4+eefadu2Lb/88gsuLi74+/vj5+f33uchZSB/SZIkSZIkSfpSyJZhkiRJkvQJqdVq4OPMnPmh9fjcdZAkSZIkSZKkz0EGwyRJkiRJkiRJkiRJkqRvhrwlLEmSJEmSJEmSJEmSJH0zZDBMkiRJkiRJkiRJkiRJ+mbIYJgkSZIkSZIkSZIkSZL0zfhqZ5NUq9U8fvwYa2trZRYsSZIkSZIkSZIk6cMJIXj16hUuLi5ywhVJkv5zvtpg2OPHj8mePfvnroYkSZIkSZIkSdJ/VnBwMNmyZfvc1fiiCCFkgwxJ+sp9tcEwa2trQPPP2cbG5jPXRpIkSZIkSZIk6b8jKiqK7NmzK9dd/wXr169n69atLFmyBIAnT57g6+vLmTNntPLFxMRgaWmpt4zY2FiKFy/OggUL8PHxSXNfN27c4M6dO9SvX/+d6vjixQsOHTpEkyZNtNYnJiaydOlSWrZsmWbdUps3bx5du3Zl5cqVtGzZMt28vXv3pkuXLhQoUOCd6ipJXzXxlYqMjBSAiIyM/NxVkSRJkiRJkiRJ+k/5L15vrVu3TrRr1055HRwcLHLkyKGVJyAgQNSqVSvdcsaPHy+KFy8u1Gp1mnlOnDgh7O3txcuXL4UQQuzatUssXrxY77J582Zlu61bt4rChQvrlLd48WKRO3dukZCQ8NbjPHz4sDA1NRXFihUTDg4OIjAw8K3H8+OPPyqvJ0yYIBwcHISDg4NQqVTCzs5OODg4CGdn57fuWwgh5syZI/LkySNMTU1Fvnz5xNy5c7XSw8PDRevWrYW1tbVwcHAQAwYMEMnJyUp6XFyc6Nmzp7C3txfW1taiQ4cOIiYmRklXq9Vi1KhRImvWrMLCwkI0bNhQPH/+XGsfs2fPFjlz5hRmZmaicuXK4u7du1rp69evF56ensLU1FSUKFFCnD17Viv90KFDwtvbW5iamooCBQqInTt3pnm8H3o80ufx1bYMkyRJkiRJkiRJkqSPZeXKlUyePJlDhw4p6w4fPkyNGjV08iYnJ2Nubq61LiAggD59+gBQpkwZ/P39iYyMxN7enpMnT3L58mUuX75McnIyxYoVAzQ9nQClBdmdO3coWLCgVrlqtZoJEyYwbtw4jI2NmTp1Kq1btyZz5sw69dq5cyeNGzemS5cuTJs2jU6dOlG+fHm2bduGt7e3ki8mJgZHR0dy586NEAK1Wk2hQoUICQlh48aNhIaG8vr1a+zs7Hj69CkmJiYZOodXrlxh9uzZBAQE4OHhwY4dO+jWrRv29vY0bdoUgFatWvH8+XN2797No0ePaNeuHY6OjgwcOBAAf39/du7cyfr160lKSqJt27aYmpoye/ZsACZNmsT06dNZsmQJDg4OdOrUiXbt2rF9+3YA1q1bh7+/PwsWLKBAgQL06dMHX19fLl++jIGBASdOnKBFixZMmjSJypUrM2bMGOrUqcOdO3ewtrbm/v371KlTh+7du7NkyRLmzJlDo0aNuH79Ojlz5tQ55g89Hukz+dzRuPf1X7xTIUmSJEmSJEmS9CX4L11vXbhwQZiYmAh7e3thYWEhMmXKJIYNG6bVMmz+/PnC3t5enDx5UmvbAwcOiHz58r11H+3atROTJk0SQgiRN29eYWpqKkxNTYWxsbFWq6sePXqI3r17K6+XLl0qKlSoIF6+fCl69+4tSpQoIUqUKCF69+4tevfuLRITE8WCBQtEuXLllJZo/fr1E+3bt9faf2Jiohg6dKgwMjISv/76q7I+OTlZ9OjRQ1hYWIipU6cqLZaio6NFgQIFxIoVK8SSJUuU/E2aNBG7du0ST548Efv37xd58+YVT548EU+ePBFRUVFvPQ8vX74U0dHRWuuqV68uWrRoIYQQ4tKlSwIQ58+fV9IHDRok3NzclO2NjIzExo0blfQ5c+YIMzMzERsbK5KTk4Wjo6OYMmWKkr5jxw4BiHv37gkhhChWrJjo1auXkn7jxg0BiP379wshhGjUqJHw9fVV0sPDw4Wpqan4/fffhRBC+Pv7Cy8vL61z6+rqKkaOHKlzvB96PNLnI6cFkSRJkiRJkiRJkv7TypYty7x582jatCljx47VSlu6dCljx47l4MGDlC5d+oP3devWLeLi4oiIiCBbtmzUqVNHSYuLi9NqUZaQkICpqSlGRkZky5aN58+fU7x4cTJnzsz06dMJCwvj559/ZtasWahUKuLi4ujcuTNr167l3LlzAOzbt4/ixYvzxx9/sHPnToYOHaqUb2BgwIwZM1i0aBEjR46kZMmSbN68GUtLS86fP8+QIUMYPnw4hQoVolChQhQpUgQLCws8PT0ZMmQIdnZ2NGzYkHLlyjFixAgASpUqhb+/v95jt7e31xnTzMzMjOTkZAAOHDiAs7Oz0jIOoEqVKgQFBfHkyROOHDmCWq3Wao1XpUoV4uLiuHjxIleuXCE0NJTatWsr6RUrVsTAwICTJ08SERHBhQsXtNI9PT3JmjUrJ0+eVOqQOj1Tpkx4e3trpdeqVUtJNzIyokKFCkp6ah96PNLn85/vJqlWq5U/PEmSJEmS3p2hoSEGBvL+mSRJkvTfVLduXapXr46Li8tHLfeXX36hSJEilClThocPH5IjRw5iYmLIkSOHkic+Ph5TU1NsbGzo378/48ePZ/jw4QghmDFjBm3btuXFixdUr16dyMhITExMcHBwwNnZmREjRjBx4kSlS9+dO3eoVq1amvXp1q0biYmJ9OjRg4oVKzJs2DAaNGjAtGnTiI2NpVSpUkowsEyZMuzcuZPNmzdTqVIl1q5dy9WrVwHIly8fuXLlytA5ePDgAXv37mXKlCkA3Lt3D3d3d608bm5uAISEhHDv3j0yZ86sFVBLnW5kpAlhpC7D3NwcJycnQkJCuH//vk56ShkhISGEh4cTERGRZnp6dbx06ZLO8X3o8Uifz382GCaEIDIyktjY2M9dFUmSJEn66llYWGBrayunkpckSZL+E6ZNm8b8+fN5+fIlhQoV0klfuHAh9erVQwjxXuUfOHCAgIAA7t69S48ePciaNSvTp08nPDwcGxsbJV98fDxmZmaAJjhiaGhItmzZOHjwIB4eHgwePJj+/fuTM2dOsmbNqgRVXrx4Qa5cubC2tiYoKAhnZ2et/YeEhODh4UFcXJxO3eLj43n+/DnXrl2jVq1aeHl5kSVLFqpUqUKNGjU4evSoknfkyJGsXbtWa/s//vgjQ+fg9u3b1KtXD29vbzp06ABAdHQ0FhYWWvlSXsfHx+tNNzMzQ6VSER8fT1xcHAYGBpiamuqUkbJ96jLfJT00NDTdOsbHx+sc44cej/T5/GeDYSmBMBsbG0xMTOSPd0mSJEl6D0IIEhISiIqKAjRdCSRJkiTpa9enTx+6du2Kj48PDx48wNfXl86dOysD2aeIiYnh9u3bSsAqLYmJiUyYMAHQDCLfpEkTkpOT+emnn7hz5w5Lly4F4NGjR7i6uirbxcXFKYGdU6dOUbZsWQDOnz9PsWLFqFSpkt79OTk58fDhQ+zt7d/52E1NTcmePTv79+/n5s2bLFu2jODgYJ4+fcqGDRu0BuYXQiitsd7Fn3/+Sfv27alQoQIrVqxQBuA3NTUlISFBK29KwM7CwkJvemJiIkIILCwsSEpKQq1Wk5SUpFWvuLg4ZXtA7z4ykp5eHd8Man2M45E+n/9kMEytViuBMCsrq89dHUmSJEn6qqX8gI2KisLGxkZ2mZQkSZL+c2rUqMHKlSt1gmGRkZGUL19ea4ZJfWbNmoWbmxtqtZpq1aoxZMgQBgwYgBACKysrwsPDMTEx4datW1qzRaYeQ+zChQuYmJigVqvZtWsX3bt35+DBg1StWlVn5srXr1+zbds2ZWwrMzMzvS2N3mwUUrx4cc6ePUtgYCDDhg3j4sWL/PrrrzRt2pTt27czfPhwevbsqeSPjo7WGQPsbWbOnEm/fv0YP348/v7+WnVwdXVVZn1MkTKjpru7O66urjx79ozExESMjY0BCAoKAiBXrlzExMQAmpZvKTM7xsfHKy3lUgKNwcHB5M6dW2sfzZo1w9HREVNTU2WfqdOLFy+u1FFfur6uoR96PNLn85/8NZsyRlhGp3+VJEmSJCl9Kd+pchxOSZIk6WsWGhrKsWPHdK4V27Rpw86dO3UGNb99+7ZWS660dO/enfr162NgYMDSpUvp06cPoGkhVa5cOf73v/+xe/dunJycyJMnj7Ld69evlUDXgAEDiIqKomrVqly4cIHq1asDULJkSaKjo7WW8uXLa+3//v37dOrUiejoaIQQBAcHY2pqihACIQSBgYG0bt2a48ePA5qW3oUKFeL169eMGDECT09PevXqxaBBg5T6CSF4/vw5jo6OGT6/ly5dwt/fnzVr1tC3b1+dYJyPjw8PHz4kMDBQWbdv3z68vb2xs7OjXLlyJCYmcvDgQa10JycnChcujLe3N+bm5uzZs0dJP3ToECqVigoVKuDq6krOnDm10m/fvk1ISAhVq1bFwMCAsmXLaqVHRkZy9uxZqlatqtQxdXpycrISlHzThx6P9Pn8J1uGpZBdIyVJkiTp45DfqZIkSdLXysTEBCcnJ0AT4ImNjaVx48ZaeSwtLenbty+tWrXixIkT2NraApruihUqVHin/dWqVYukpCRAMwnN9OnTSU5OpkGDBnTs2BFDQ0Mlb0xMDNbW1gDY2tqyefNmcufOjbGxMQ8fPszwPrNkyUJ0dDS1a9dm27ZtWmkhISHUrl2b/v37K0FAJycnvv/+e44fP87OnTsBGDVqFK9evcLOzo4SJUpw4sQJDA0NCQkJIXv27MoNsXbt2lGyZEl69OihU481a9aQPXt2ChUqxJ07d7TScufOTYUKFShevDgdOnRg6tSpPHjwgOnTpyvdSHPmzMn3339Pr169WLBgAbGxsfz8888MGjQIQ0NDzM3N6datGyNHjsTNzQ0rKyt69+5Nly5dlC6jffv2ZciQIRQtWhR3d3f8/f2pV6+eEnzq27cvjRo1okKFCpQpU4bRo0eTL18+ZdbPXr16UapUKcaMGcP333/P7NmzUavV+Pn5AZqWb2fOnGHp0qUffDzSZyS+UpGRkQIQkZGROmkJCQni0aNHIiEh4TPU7N+jVqs/dxUkSZKkb9R/9btVkiRJ0i+9662v1bp160S7du2U18HBwSJHjhxi+/btomDBgiIxMVF89913okyZMuLp06ciIiJCmJqaivPnz7/zvhITEwUgEhMTlX27urqKsLAwrXwtWrQQ48aNE0Jorve6dOkiqlatKn7++WdhZ2cnli1bJkqXLq1TfsWKFcWOHTu01iUkJIjhw4eL0NBQERwcLExNTYUQQhw7dkzMmzdPp4wrV66ImjVrKq9//vlnsWDBAiGEELGxsaJ06dKiYcOGwtHRUWzatEnJV7JkSdG/f3+9x92+fXsB6F1evXolhBAiKChI1KhRQ5iamgo3NzcxZ84crTLCw8NF8+bNhYWFhXB2dhajR4/WuhaOi4sT3bp1EzY2NsLOzk707NlTxMXFKelqtVqMGDFCODo6CisrK9G6dWsRHh6utY+ZM2cKFxcXYW5uLurWrSuCg4O10tetWydy584tTE1NRfny5cXVq1eVtP79+4uSJUsqrz/0eKTPQyXEe06P8ZlFRUVha2tLZGSk1mwcoBmQ7sWLFzg5OSn9cr8G8fHxDBw4kP79+5M9e3attJCQEGrWrMmePXt0pvx99eoVCxcupGfPnu81uKEkSZIkvc3X+t0qSZIkvZ/0rre+VuvXr2fr1q0sWbIEgD179lCzZk2cnZ2ZPHkyrVu3JiwsjBo1atC/f39u377N5s2bOXv27DvvKykpCWNjYxITE7l06RJ16tRhxYoVVKtWjYsXL2Jubs6rV6+oW7cuCxYsoGbNmrRq1YqbN29y5MgR7O3tOXr0KElJSVSvXl1pqZYiMjKSLVu2UKtWLTw8PLh7926G61axYkUOHjzI1atXKVOmDNmyZQM0XUgDAgIoV64c7dq1w9ramt27d7N582ZatWpFnz59+PXXX+XYodJ/gvwUf0EMDAwQQuDl5aXTtDVbtmwUK1aMOXPm6GwXExPDhg0bKFeuHA8ePKBatWqoVKp0lxTDhg3D3d0dCwsLChUqxMaNG7XKjo+Pp1evXjg4OGBjY0PHjh2JjY1V0oUQjB49GhcXFywtLWnUqBEvXrzQKiMyMhI3Nzelf/rHdPjwYYoXL46ZmRkFCxZk165dAISHh5M9e3ZOnTr11jI2bNhA/vz5MTMzo2TJkpw7dy7NvKdOnaJcuXKYmZmRI0cOJk6cqJX+tvMpSZIkSZIkSdKXwdzcnNq1a3P16lVat24NgL29PSdPnqRFixYcPHiQSZMmfdA+4uPj6dy5MwsXLqRatWoADB48GE9PT8qVK0fFihWpW7cuJiYmuLi4cPjwYaW7n4+PD6AZ9D40NFRrKVeunLKPO3fuKGODZWRJPX6Vj48PN2/e5ObNm/To0YPAwEAKFy6Mk5MTmzZtwtDQkEaNGrF+/XoWLFhAeHj4B50PSfpSyJZhX6AFCxbw/Plz2rRpQ758+ZT1arVaJwqfMm1rYmIiPXr0IDQ0lMjISNq1a0fTpk2VfClTzz579oycOXOS8rZ36NCBFi1akDlzZhYuXMi8efM4f/680p/6p59+YufOnSxatIikpCTatm1Lo0aNmD17NgATJ05kwoQJLFmyBAcHBzp16kTOnDm1ZtT46aefUKlUzJo166Oep/v371O4cGG6d+9OmzZtmDNnDkuWLOH69evkzJmTdevWMWrUKK5cuZLm3YsTJ05QoUIFJk2aROXKlRkzZgxHjx7lzp07St/9FBEREeTMmZMmTZrQs2dPLl++zI8//sicOXOU/uNvO5+SJElfq6/9u1WSJEl6N//FlmHvKi4uDjMzsw8uRwihM/ZmyvXY28bkFEKgVqv/lfGlkpKSiIiI0Bkgf9++fXoHi3/8+LFOLyVJ+lrJYNgX5M1/kg8ePKBEiRKEhobq5I2Ojsba2po33774+HguXrxI9uzZOXLkCFu2bGH58uV06NABNzc3hg4dyvHjx6lUqZJOmWq1mkyZMvHLL7/Qq1cvwsLCcHZ2Zu3atTRq1AiAuXPn4u/vT1hYGKampjg7OzN06FD8/f0B2LlzJ7Vr1+bevXu4u7sTGhpKzpw5uXLlCu7u7h/xbGkGPty/f78y40tSUhI5c+akY8eOjB49GrVaTb58+Zg6dSr16tXTW8b3339PcnIyf/31F6AJeGXJkoU5c+bQvn17rbxnzpyhVKlSREVFKYGy77//nqxZs+oN9L15PiVJkr5mX+t3qyRJkvR+ZDBMkqT/MtlN8gvSsmVL+vfvr7T2yqihQ4cq3fFMTU0pXbo0f/31F/7+/vTv3x+AMWPGsGHDBnr37p3mbChCCJKTk3FwcADgyJEjqNVqatSooeSpUqUKcXFxXLx4kStXrhAaGkrt2rWV9IoVK2JgYMDJkycBTRfEUqVKKYGwgwcPolKpOHToEBUrVsTCwoKSJUty9erVdzpmgAMHDlCrVi3ltZGRERUqVFD2bWBgwA8//MCKFSvSLSN1/TNlyoS3t7dSRmpeXl54enoye/ZsEhMTOXfuHMeOHaNJkyZ6y37zfEqSJEmSJEmSJEmS9Pl9M8EwIQSxCUmfZclo47uBAweybt06vvvuO54/f57hY6tRowZ9+vShS5cuyiD806dP5/Dhw3h5eTFz5kwyZ87MkSNHuH79Oq1atdIp4/nz5/Tu3Rs3NzelFdi9e/fInDkzlpaWSj43NzdAM6D/vXv3ALRafJmbm+Pk5ERISAigCahVrFhRZ38DBgxg4MCB7Ny5k8jISDp16qSkPXz4EDMzM71L6sBcSuuz1Nzc3JR9gyY4d+TIEb3nLTw8nIiIiLeWkcLExISVK1cyevRoTE1NKVGiBD/++COVK1fWyavvfEqSJEmSJEmSJEmS9Pl9M1MPvk5MpsDIXZ9l39fH1MTC5O2nOqVF0pAhQ7CzsyM6OjpD5VeqVImLFy8yfPhwoqKiqFevHsOGDcPW1pakpCR69uxJmzZtyJQpE7t27dIaVP7w4cNUq1aNxMRE8uTJw+rVq7GwsAA0XTFTnqcwMzNDpVIRHx9PXFwcBgYGmJqaauWxsLAgPj4e0AzmqK+L4qBBg6hbty6gCYylBPJMTU1xcXFRuj6+KXV99NUv9b4B8uTJw6NHj5SyU0s5v/rK0Nc19enTp9SrV4/WrVvTqVMnLl++TN++fSlYsCAtWrQA0j+fkiRJkiRJkiR9PcLCwpgxYwYjRozQOwZxmzZtyJYtGwEBATppERERZMuWLcPXdPpcvnyZIkWKvNe2oaGhZMuW7Z17HQ0ZMoSAgACOHz9O2bJl32vfkvQ1+GZahn0tsmbNyv/+9z+MjY2Jiori5cuXGBkZ6SyZMmXS2s7e3p7Zs2eTkJBAlSpVcHBwwMjISBnw0dHRESMjI6ysrKhatSqHDh0CoESJEly6dInDhw/z/fffU65cObZs2QJoulwmJCRo7ScxMREhBBYWFpiamqJWq0lKStLKExcXpwSAwsPDldlQUvP29laeu7u7I4RQAlDGxsZ4enrqXVJapqVVv9T7Tjlu0HyRvSklOPa2MlJMnToVOzs75s+fT+nSpencuTODBg1SuqJC+udTkiRJkiRJkqTPY+7cudjb21O0aFFlyZIlC1myZNFaZ2Vlxc6dO5XtVq5cSdeuXT9KHQoVKoSFhQVWVlZYWVlhaGiod3gWgKCgIOrWrcu0adM+yr4zYs2aNUycOBEvLy/atm2rt4FARsydO5e8efNiZmaGp6cn8+bN00qPiIigTZs22NjY4OjoyMCBA1Gr1Up6fHw8vXr1wsHBARsbGzp27EhsbKySLoRg9OjRuLi4YGlpSaNGjXjx4oXWPubMmYO7uzvm5uZUqVJF6dWUYsOGDeTPnx8zMzNKlizJuXPntNIPHz5M8eLFMTMzo2DBguzalXbDmg89Hunz+GZahpkbG3J9TM3Ptu+MevXqFW5ubhw7dowXL15QqFAhrly5opNPCMGtW7eU1xcuXKBLly6cOHFCCU4JIWjevDnr1q2jadOmrFq1SqccCwsL8ufPD0D58uV58uQJ48ePp379+ri6uvLs2TMSExOVwZKDgoIAyJUrFzExMYCmy2TOnDkBzR/6ixcvyJUrF6DpWpi6pVaK1IMvGxkZKfUFTTfJ3Llz6z0/FStWZN++fQC4uroSHByslR4cHKzsG/6ZbfPNVmGgCZSZmprqLaN48eI6+S9fvoyXl5fWJAfe3t48evSIsLAw7O3t0z2fkiRJkiRJkiR9Pr6+vixZskR5PWrUKK1HQGuiMXt7e7Zu3UqJEiWoUqWK0hvkfanVak6dOqXMNF+iRIk087q5ubF//37Kli1LmTJlKFOmTJp5k5OTdYbmSU5OBtBpuACaGSzfnJ1y8eLFdO7cmUmTJtG9e3caNmyIj48PO3bseKeJ0K5cucLs2bMJCAjAw8ODHTt20K1bN+zt7WnatCkArVq14vnz5+zevZtHjx7Rrl07JYgE4O/vz86dO1m/fj1JSUm0bdsWU1NTZs+eDcCkSZOYPn06S5YswcHBgU6dOtGuXTu2b98OwLp16/D392fBggUUKFCAPn364Ovry+XLlzEwMODEiRO0aNGCSZMmUblyZcaMGUOdOnW4c+cO1tbW3L9/nzp16tC9e3eWLFnCnDlzaNSoEdevX1eue1P70OORPhPxlYqMjBSAiIyM1ElLSEgQjx49EgkJCZ+hZh9m48aNwtbWVsTHx4sJEyaI2rVrZ2i7MWPGiO+++05rXZ8+fcRvv/0mADFmzBgxcODAt5bToUMHUapUKSGEEPfv3xeA2L17t5I+b9484eTkJJKSkkRsbKwwNzcX8+fPV9J37doljI2NxcuXL4UQQlSuXFksXLhQST9w4IAARHBwcJrrEhISxI0bN/QuDx8+VLbz8/PTOuakpCSRLVs2MX36dGVdYGCgMDIyEsnJyXqPt1KlSqJVq1bK64iICGFmZiY2b96sk7djx46iaNGiWusCAgKEpaWlSEpK0lt+6vMpSZL0Nfuav1slSZKkd5fe9dbXaM6cOcLR0VGULl1aWVxdXYWrq6vWOmtra7Fjxw6tbc+fP6/3937r1q3FoEGDdNa3bdtWVK1aVRgaGoqaNWuK6dOnC7VaLezs7MTTp0+VfMWLFxcnTpxIt94zZ84UXl5eaV7PCCGEoaGhADK85MiRQ9k2OjpadOzYUZiamorFixcr61+/fi0aN24s7O3txYoVK9KtY2ovX74U0dHRWuuqV68uWrRoIYQQ4tKlSwIQ58+fV9IHDRok3NzclO2NjIzExo0blfQ5c+YIMzMzERsbK5KTk4Wjo6OYMmWKkr5jxw4BiHv37gkhhChWrJjo1auXkn7jxg0BiP379wshhGjUqJHw9fVV0sPDw4Wpqan4/fffhRBC+Pv7Cy8vLyU9MTFRuLq6ipEjR+oc74cej/T5yG6SX5gtW7ZQp04dTExM2LFjB+XKlcvQdlu3blVmRUxISKBTp068ePGCn376CYCePXty8+ZNunXrRmJiIgD79u1jxIgRHD16lIsXLzJhwgSWLl1K586dAciZMyfff/89vXr14ujRo+zevZuff/6ZQYMGYWhoiLm5Od26dWPkyJHs2rWLY8eO0bt3b7p06aJ0jfT29ub06dPvdA4y2k2yV69enD59mjFjxnD16lV69uyJWq3Gz89PyXP69GmKFi2qt48/QN++fVmzZg3z5s3j0qVLtG/fnnz58lGnTh1A02d+yJAhAHTr1o2rV6/i7+/PhQsXWLZsGePGjaNbt24YGhq+9XxKkiRJkiRJkvT51K1bl5MnTypLp06d6NSpk9a61MO5REVFKRNupXU9oU+dOnVo0KABRkZGNGnSBG9vb/bt24ezszPOzs5KPjMzMx4+fJhuWcHBwVy6dElvL58USUmaSdtSlsTERIoWLYpKpaJp06ZaaUIIHjx4AGhaUBUqVIiTJ09y4sQJresoMzMz1q9fz4gRI/Dz86Nq1aocPnwYgFKlSuHv76+3Lvb29loTsKWUldJS7cCBAzg7O1OsWDElvUqVKgQFBfHkyROOHDmCWq3WmjitSpUqxMXFcfHiRa5cuUJoaKhy7Qua3kMGBgacPHmSiIgILly4oJXu6elJ1qxZlS6pBw4c0ErPlCmTMn53SnqtWrWUdCMjIypUqKC3S+uHHo/0+chg2BckLi6ODRs20LBhQy5dusSRI0do2bLlW7d7/vw5Z86coU6dOly/fh0fHx9evXrFwoULtfItX76cBw8eULp0aa5evUr27Nk5duwY9erVo0KFCmzcuJGVK1dqzey4aNEivLy8qFmzJm3btqVbt2707dtXSR83bhyNGjWiWbNm1K9fn+rVqzN58mQlvUaNGuzevVurz/THUqxYMVatWsWyZcsoUaIEV69eZffu3VhbWyt5du7cSbVq1dIso379+vz222+MGTOGsmXLkpCQwNatW5Vmw3fv3uXu3bsAFC9enG3btnH06FHKli3LsGHD6NOnD+PHjwfI0PmUJEmSJEmSJOnz2Lx5M0WLFqVIkSIYGhoyY8YMNm3apDVm2NmzZ5X8ZcuWxdPTEzs7O549e0b37t21xnFeuXIlEydO1Fo3ZswYmjdvzg8//ICRkRGdOnVSuvKNHj1aqz7t27enY8eOqFSqNMcZXrduHc2bN2fMmDFKQOltJk2aRPHixTExMcHAwIBt27bp5Nm2bRt+fn506tQJc3NzvL29UalUOou/vz+DBw/Gzs6OXr16kZSURL58+bSGpknPgwcP2Lt3L1WqVAHg3r17Ot0uUxo8hISEcO/ePTJnzqwVUHszHdAqw9zcHCcnJ0JCQrh//75OekoZISEhhIeHKwFOfenp1TElPbUPPR7p8/lmxgz7Gpw4cYKEhARq165N8+bNad++fYb+yWzbto2sWbPi7e3NpEmTqFSpEhMmTEClUmn1Ebe2tmbLli0MHjwYW1tbsmfPzv79+9MtO1OmTKxevTrN9JS+zmn1d65evTrGxsZs2bKFBg0aUKlSJZ3+7PrWZVSTJk1o0qSJ3rRnz56xYcMGrl+/nm4Z3bt3p3v37nrT1q5dq/W6Ro0aWlH91PLmzfvW8ylJkiRJkiRJ0qfXtGlT8ufPz7lz5zh9+jR9+/ZVxrA6cuQI06ZNY+jQobi4uCitt65duwagjBk8a9YsZs2apZSZ3mySKcLCwnjw4AGDBg2iWbNmWmkdO3akY8eOaW577NgxwsLC+P333ylVqhRr1qyhVatW6R7noUOHWLRoEXv37mX58uVMnDiRSpUqUbhwYa1eNnXr1uXevXs4OzszbNgwZf3Ro0fp1KkTN2/e1Ck7Pj4eIyMj/vjjj3TrkOL27dvUq1cPb29vOnToAEB0dLTOZGUpr+Pj4/Wmm5mZoVKpiI+PJy4uDgMDA50xoS0sLJTtU5f5LukpEwakVUd9Y2F/6PFIn49sGfYFqVy5Mvfu3cPa2po5c+YwZcqUDG1nbm6udMUbMGAAEydO1BrkPTUjIyMmT55M9uzZP1q906NSqZg0aRJDhw795H/sQ4cOpXPnzuTIkeOT7leSJEmSJEmSpC+Lg4MDo0aNYt68eTx58oRFixZhb2+Pvb09y5cvp0uXLpQqVQoPDw+tnibv4969eyxYsIDY2Fjc3d15/fo1Q4YMwdjYWJlJ0srKCktLyzSv2wCGDRtGmzZtsLCwYMSIEYwcOZKEhIQ089+6dYuWLVuybNkyrKysAE0rpJEjR+Lr60tERIRW/tRdNjNC36Rkafnzzz8pVaoUnp6e7NixAxMTE6WMN48hZdIzCwsLvemJiYkIIZR0tVqtMzFAXFyckg7o3UdG0tOr45tBrY9xPNLnI4NhX5isWbMCkCNHjgz/E27RooXWDCipGRkZIYQgU6ZMH6mG765Bgwb07t1ba/bLf1tkZCTu7u7p3qWRJEmS3lFMKEwrDKcXfO6aSJIkSdI7O3DgAGvWrKFYsWIYGxszbtw4goODWb58OYGBgVSuXJlnz5590D7UajVFixbl0KFDmJmZ8fjxYxo1asT58+dxd3cnMjKS6OhooqOjGTNmDA0aNNBbztatWzl//rzSaqtp06ZYWloyYcIEvfnv3r1L1apVGT58ON99951Wmp+fH2XKlKFy5cq8ePFCWf/06VOdbpHly5fn1q1bOuvT6o2jz8yZM2nRogUjR47kr7/+0rqudXV1JTg4WCt/ymt3d3dcXV159uyZMs41QFBQEAC5cuXC1dUV0O5iGB8fz4sXL7TS9e0jV65cODo6YmpqmmZ6enXU12vrQ49H+nxkMEz6JH788UeKFCnyyfZna2vL8OHDMTMz+2T7lCRJ+s+LegRJ8XBu6eeuiSRJkiS9k82bN5MjRw6KFSvGnj17yJQpEzt37sTX15f8+fMzc+ZMbty4QdmyZQkMDHzv/RgYGPDkyROWL1+OgYGBMlZUrly58PT0ZOXKlQA8fPiQcePG6Q1uBQcH07lzZ4YPH06WLFmUcufPn8+4ceN0Jig7d+4cPj4+tG/fXplA7U2zZ88mb968lChRgqNHjwKQJUsWrl69qowFJoTgyJEj5MuXTxls//jx4/z000+sX78+Q8d/6dIl/P39WbNmDX379tVp+ebj48PDhw+1zvG+ffvw9vbGzs6OcuXKkZiYyMGDB7XSnZycKFy4MN7e3pibm7Nnzx4l/dChQ6hUKipUqICrqys5c+bUSr99+zYhISFUrVoVAwMDypYtq5UeGRnJ2bNnqVq1qlLH1OnJyckcPHhQSf+YxyN9Rp9hBsuPIr2pfuX075IkSZL0cSUkJIhHx9eKhHE5hfjZVojYsM9dJUmSJOlflN711tfo8ePHYsWKFWLfvn3i5MmT4urVq2LJkiWiTJky4vXr10q+gIAA8eOPP2ptC4gnT56Iy5cvi1GjRinrW7duLQYNGqR3f+Hh4cLS0lJr3Z07d4Srq6vYsmWL8PDwEPPnz9fZ7tWrV6Jo0aKidu3aIikpSSe9X79+ImvWrOLu3btCCCFevnwp7OzsxMiRI8WOHTuEpaWlsgBar589eyZ69eolSpQoIRITE4UQQrx+/VpUrFhRNGnSRCQmJoojR46IfPnyCSGEuHz5snBxcRFbtmzRqkPbtm3FjBkz9B73kCFDhLu7uwgMDNRZ1Gq1UKvVonjx4sLHx0ecOXNGrFu3TlhaWor169crZXz//ffC09NTHDlyROzatUtkyZJFTJ48WUnv27evyJIli9i5c6c4evSo8PT0FD169FDSp0+fLiwtLcXatWvFmTNnhI+Pj6hXr56SvnnzZmFoaCjmzp0rLl68KBo1aiS8vLyU833+/HlhZGQkRo8eLa5cuSK6desmXFxcRFRUlBBCiBkzZoi2bdsKIcRHOR7p85DBMEmSJEn6lrx6JsTTq0Ko1e+0WUJCgnh04Pe/g2E2QtzY9i9VUJIkSfoS/NeCYUIIceTIEVG9enXx66+/CiGE2LFjhyhXrpwQQojevXuLNm3aiP3794v4+Hhlm8TERAGI3r17C0tLS/Hbb78pae8aDBNCiEGDBglA1KpVSyfYdfv2bVGoUCFRqFChNM97YmKiqFWrlsiSJYvYu3evEEKI+/fv6+R78eKFMDU11VtG6uCfEEJERESI4cOHi5iYGK1g2KZNm8TGjRt1ti9ZsqTo37+/3rLbt28vAL3Lq1evhBBCBAUFiRo1aghTU1Ph5uYm5syZo1VGeHi4aN68ubCwsBDOzs5i9OjRQp3qd0tcXJzo1q2bsLGxEXZ2dqJnz54iLi5OSVer1WLEiBHC0dFRWFlZidatW4vw8HCtfcycOVO4uLgIc3NzUbduXREcHKyVvm7dOpE7d25hamoqypcvL65evaqk9e/fX5QsWVJ5/aHHI30eKiHecxq/zywqKgpbW1siIyOxsbHRSktMTOTFixc4OTlhbGz8mWooSZIkSV+gU/Ph9Uvw9gObrBneLDExkRcH5uJ0bgrG8WFQpjvUGvfv1VOSJEn6rNK73vranDhxgsGDB3P79m0GDBhAo0aNcHR05H//+x+nT59m+/btvHjxgsWLFzN37lzs7OxYtGgRRYsW5cCBA1SpUgUvLy+WLVumNfRLWrNJPn78mPv371OvXj3Cw8N59eoVu3fvZtq0aTx//pw+ffqwePFiwsPD6dKlC5UrV8bT05M8efJQoEAB1q1bh52dXZrHEx0dTcuWLTl8+DA3btzAxcVFJ09oaCjZsmVTBnPXx8jIiOTk5Ayfx3bt2rFkyZIM55ekL5nR566AJEmSJEn/stfhYGwJSXGaQBhAXMQ7BcNIitcsKR4e/ahVlCRJ+s+JiwRTG0hntkDp00hOTqZVq1b4+flhZGSElZUVcXFxZMqUiT/++AMAJycnBg4cSL9+/Vi9ejVOTk4AFChQgGHDhjFy5EhlRsS3ad68OcePH6djx47cuXMHLy8vihcvTtu2bfHz88PY2JiuXbuyceNGVq9eza+//sr+/fvZvHkzRYsWxcgo/ct0KysrNm/ezO3bt/UGwjLqzRkZJelbIluGSZIkSdJ/lRDw8Bg8OAKZckBWL7ixWZPmUQ2ylcxwUYkRT3hxfAVOVxdgHPsUVAYw6AGY2f47dZckSfqahZyFRTXApw9UHfm5a/Ne/kstw/RJTk7G0NDwk+wrIiKCTJkyfRF1kSRJQ84mKUmSJEn/VYG7NYEwgIiH8OTSP2kJMe9WVlyk5tExH9jnAqGGoFMfp56SJEn/NU+vgEjW/r8rfVE+ZfApvUAYfNq6SJKkIYNh0meXMo2vJEmS9BElJcDj85rnKa23Ih7+kx4f/W7lxUVpHjNl17QwA3h558PqKEmS9F+V0q08OeHz1kOSJEnSSwbDviKvXr3iwYMHnDlzhq1bt3L+/Hmt9I0bN7J27VqtdQkJCTRq1Ijg4OBPWdW3ypYtG3fuaC6ili9fTuXKld+6zebNm9M8js2bNxMUFPTWMi5cuMDVq1ffrbJ6hISEoFKpOHjw4AeX9Tk8fvwYBwcH1qxZ86/uZ/r06WTLlo2YmHdsgfIZVKtWDT8/P0Dzd5M9e3YmT578eSvF1/9ZS0ulSpXo1KnTe23bpEkTGjRo8EH7X758Oar/+hgusX+PDWZsAe4VddMz2jLswTG4uBJinmte22aDTG6a56mDa5IkSdI/klOCYYmftx6SJEmSXjIY9oXp1KkT9erVo1KlSpQoUYI8efKQOXNmTExMcHFxoXLlynTv3p05c+Zw6tQ/3VOEEIwYMQJTU1Ot8kxMTMiWLRvDhw/Xu78WLVrg5uZG0aJFlcXKygoPDw+tdSqVSpmJJDk5mZcvX3L79m3279/Ptm3bPuiY169fT7ly5d6a7+7du5QsWZI9e/ZorT98+DBt2rRRgmvp2b9/P+3bt0etVqebLzIyEjc3N44fP/7WMjPq9u3bH62s1EaNGoVKpUKlUmFqakr+/PmZNm1ausfo4uLC7du3adas2Qfvf8mSJWkO8tm9e3euXr2KpaXlW8uJi4vTG+xcv349RYoUITExYz8mAwICqFevXobypsXExITLly/Tt2/fDyrnc4uIiKBNmzbY2Njg6OjIwIED3/rZ/1ySkpK4f/9+hvIuW7aM1atX/8s1+nccPnyY4sWLY2ZmRsGCBdm1axcA4eHhZM+eXev/enqeP39OixYt+OWXX9LOFBtK8PMI6g1ZgmWuUrg0CWDymiPA30HABO2WYR06dEClUhESEvLPSiEg+JQm6BV6S7MuU/ZUwbC334SQJEn6JiX93SJMtgz7qoWFhTF69Og0fz+1adOGwYMH602LiIjAysrqg/Z/+fLlDOXT9zv56tWrPHr06J33efv2bf7888933k6SvjYyGPaFadKkCZ07d2bkyJHMmDGDwoULY25uzuPHj3n16hX379/n0KFDbNu2jW7duinbbdq0CWtraxo0aMDo0aMxMzNTlvnz57Nq1SqtdSmzpgCMGTOGixcvKkuJEiVYuHCh1roUarUae3t7PD098fT0ZOTIkaxbt46dO3dibGyMo6Oj1mJlZaW0thk5ciTr16/XOt6oqCj27t1LkyZN3npu/P39mTNnDoGBgcq6Cxcu0LhxY/744w+qVKmirD9x4gRWVlY6y9ChQ7lx4wY2NjY6aSn1BBgyZAj169fnu+++y+hbl65169aRL1++j1KWPm5ubgQGBnLmzBm6du3K4MGDmTZtWrrbODg4/OstYwwNDd86RkKK4sWLs2jRIq110dHRdO/enYULF2Z4MowBAwYQFBT0wa3e7OzsMDD4uv9FtmrVips3b7J7927mzZvH7Nmzv4jWbvqk/O/KCAsLC8zNzf/lGn189+/fp06dOlSrVo0zZ85QsWJFGjVqxIMHD7Czs2PKlCl06NAh3YDl06dP8ff3J0+ePGzYsCHd/SVHPaPukGUkCUOOHDnC6F5tGTR/J2tPP9ZkSNUy7OrVqyxbtky3kPhX/7RuSGGTTTMYP8hgmCRJUlqSZTfJL83cuXOxt7fXuuGfJUsWsmTJotMwYOfOncp2K1eupGvXrh+lDoUKFcLCwkK5/jA0NOTkyZN68wYFBVG3bt23/qYHmDJlCrVr19a6oeXn58fRo+8+8/OzZ89o3749L168eOdtUwQHB1OvXj0sLS1xcXFJ9/enEILRo0fj4uKCpaUljRo10tn3nDlzcHd3x9zcnCpVqnDv3r33rpskpfi6r/T+g2rVqkWDBg1wdnbG398fMzMzzp07h6OjIwBnz57Fy8uL3377TdkmOTmZkSNHMnHiRI4fP07p0qWJi4sjNDSUbNmysWrVKhISEnj9+jVxcXHExcXxww8/KNuPHTuWMmXKKMv58+f56aeftNalMDAwICIighcvXmBhYcHOnTtZsmQJAOXKlSM0NFRrSf2PLygoiNDQUK3jXbJkCfHx8Xh7eyutm1IWKysrYmJitAJWP/zwAwMHDlRelypVirCwMFq3bq2sq1KlCmXLluXo0aO8fPmS6OhooqOjuXfvHjlz5uTAgQOEhYUp66Ojozl8+LByHKGhoSxbtoz+/ft/tPc1Pj7+7Zk+gLGxMR4eHhQpUoTevXvz448/snDhQr15v9Tx2fSdoyVLllCwYEFKlSqV4XIMDQ3p378/kyZN+pjV+9d97Pfl8uXL7NixgwULFlCmTBkaN25Mjx49mDVr1mepz9v8238jX4IZM2bg4eHBhAkTKFy4MNOnT8fe3p7FixcD0LhxYxISEti+fXuaZZw/f55Lly7x119/4erqmu7+tu3cw82gFyybOR5vb286D5lI07pVmLXlrCZDYiyo1Qgh6NatG40aNdItJEbPD2HbN1qGfaH/UyRJkj6rJNlN8kvk6+urdcO/a9eudO3aVadhQAp7e3u2bt3KmjVrPkqrdLVazalTp5RrkGLFiqWZ183Njf379/PLL7+kGTBL0b9/f0qVKkWNGjVISkriyJEjXLt2jX79+pE1a1YMDAzIli0b2bJlw8LCgoEDB/Lw4UOtxhIpS/Xq1YmJiSFbtmw6aSnXpOlJTk6mbt26Sj1Gjx7NoEGDdIbzSTFp0iSmT5/OvHnz2LNnD7du3aJdu3ZK+rp16/D392fMmDEcPXqUxMREfH19v9jeDtLXQwbDvjD379+nX79+NGjQgC5duigtmTZv3szIkSOpWrUq3bp1o1evXso2M2fOpGDBglSoUAF/f38lUt6rVy88PDyUC5xOnTqxYMECnX2OGDGCkydPKou3tzezZ8/WWpfax2pNlJSUpNzFEEIoy3fffcdff/1FdHQ0lpaWWkGrsLAw9u7dq7x+/fo1Gzdu5NWrV8q6/fv3A7B48WIqVKjAs2fPCAoKon79+kydOpXcuXNTvnx5Vq9eTXx8PL169aJdu3ZERmpmStuwYQOlSpXC3d1dqeu5c+coV64cZmZm5MmTh927d2sdS1xcHAMGDCBXrlxYWFhQqlQpzp07B2juyqQEH1UqFTlz5gTgwYMHtGzZkqxZs2Jra0vDhg0/6A5Mavnz51fGUEvpxrh9+3ayZs1K06ZN9Y5DdePGDerXr6+0mmvfvr2StnXrVgoXLoyFhQVFixbVuluWnl9++UU5XtBcyFepUgVra2scHByYNGkSDx48QKVScffuXUaPHo1KpVICkytWrNBqsZdRzZs359q1a9y6dUtvemxsLF26dMHe3h5bW1v69u2rE/wxMjJS6qFWqxk5ciRubm6YmZlRuHBhrfdqyZIlFCpUCFNTU1xcXNi8eTMAhw4donLlymTKlIksWbLQr18/ZT8HDx5EpVKxadMmcufOrQT83vZZy6gDBw7g7Oys9SOrSpUqBAUF8eTJE538fn5+VKtWjV9++QVLS0slaPb777/j4eGBpaUl3333HadPn1a20fd+ppyPN7vOptedVqVSsW/fPpYuXYpKpWLUqFHpHlvq8d1Stp83bx5+fn5YW1vj7u7OunXrtLZZuXIlefPmxczMDB8fH71dMtM61p07d6JSqTh27JiSt0qVKhlq0ZragQMHqFWrlvLayMiIChUqKP9jDQwM+OGHH1ixYkWaZdSuXZv9+/dTqVKlt+/v+Fm887iQ2S2vZoWxOVXqNePUmfMIoQIEJMYwd+5cYmNjtVobK1KCYaqU904FNlk1ATGA+CiIi3hrXSRJkr45ybKb5Jdo27ZtWjf8Fy5cyMKFC3UaBqSWJ08eDh48SNOmTTO8n3bt2tGkSRPi4uKoVasWM2bMQAjB06dPyZw5c4bLyZMnD6NHj6Zr167pBn8MDQ0ZPXo0Z86cISgoiA4dOvD7778TEhLCzJkzKVCgACEhIVy7dg0rKyuaN29Ojhw5lIYSjx49on379kRGRhIXF0diYiLx8fFcuXKFkSNHEhsbqzS2eJtt27Zx8+ZNli1bprkZ17kzTZs21XtDVq1WM2nSJIYPH670ypkyZQo7duxQfquNHz+eLl268MMPP1C8eHEWLFjAtWvXOHToUIbPoyTp8+0Ew4TQdAn5HMs73DVfu3Yt06dP5/Xr18yYMYN+/frRqVMnGjRowKlTp7hw4QL+/v5aAamNGzfy119/YW5uTnx8PF26dGH16tXs3btXa5BoPz8/+vbtq9XNEDTdF4sWLYqHhwcqlYr79+/Tp08frebCKYKCgrCyssLS0pKYmBiyZMmClZUVL1++fOe3ZOXKlYSHh+tclD5+/Fhvi4eDBw/i5eVF3759SU5OBuDevXv4+/tTokQJnaDBb7/9Rs2aNdm7dy/jxo3j1atX9O7dm5w5c9K/f39atGjBpk2beP36NadPn8bWVjPb2pEjR6hY8Z/BpsPDw6levTqZM2fm4MGDTJkyhXHjxmnt69mzZ9y7d485c+Zw9OhRLC0tadOmDQATJ05UWsgFBgYq/7hPnDiBm5sba9euZdOmTZw5c4aRI0cqZf7xxx9679aYmZmlP04Qmr7+qYN5QgimT5/Ohg0bGDJkiE7+Bw8eUK5cOdRqNVu2bGH//v0UL14cgNOnT9O0aVO6du3KiRMnaNy4Mb6+vhkaoy01tVpN7dq1yZ49O8ePH2ft2rW4ubmRLVs2AgMDcXNzo2fPngQGBtK4cWNev37NuXPntN6LjDI1NaVMmTIcOXJEb3r37t3ZsmULCxYsYN++fTx69CjNvACzZ89mxowZzJ07l1OnTmkFDX777Te6dOlC69atOXnyJPPmzVO68G3fvp3vv/+effv2MXHiRKZOnapzV2zChAnMnz+f6dOnZ+izBlCjRo00PxsPH2oGNL93757WZwA0dxgB7TGhUrl58ybXr19Xgjbr16+nX79+/Pzzzxw7doxixYpRu3ZtwsPD03w/30dgYCDfffcd33//PYGBgVrB/owaP348ZcqU4eDBgxQpUgQ/Pz/l/9LBgwf54YcfaNasGcePH6d169ZMnDhRa/v0jrVWrVo0atSIoUOHAprg8NmzZ5UWumndWTUzM6NGjRrKPtJ6T1K/HxUrVkz3s5juzYjo5xAbpnmenMi94Ce4Z7UDSyet/cXHxxMaq/lBfevaRQYPHsycOXP0dwuO+ftHr6s3WGUFh1xgaAwmFv+UK7tKSpIk6ZItw75IdevW1brh36lTJzp16qTTMCBFVFQUERERuLu7v9PwGXXq1KFBgwYYGRnRpEkTvL292bdvH87Ozjg7Oyv5Uv92S0twcDCXLl1i1apV6eYLDQ2lSZMmDBs2jFGjRtGyZUvlWkkIwbhx44iPj2fSpEnK7/wUtra2XLhwQec6oU+fPly6dEnn2EuVKoW/v7/eehw4cABvb2+toF+VKlU4deqUzs3nK1euEBoaSu3atZV1FStWxMDAgJMnTxIREcGFCxe00j09PcmaNetbW8tJ0tvov03/X5QYC+NcPs++hz4Gk7cPIA7Qt29f+vfvj6GhIQ8ePGDo0KE8fPiQTZs20aBBA1q2bEmNGjW0Wu3s3LkTtVpNkSJFmD17NmfPnqV169a4uLhQo0YNYmNjlSU+Pp6OHTty6NAhVCoVAQEBBAYGsm/fPp4+fcqcOXOoWLEisbGxrF+/nlWrVrFmzRpCQkIwNTXFzc2N6Oho/vrrLxo2bMjTp091+tVnVFJSEtOmTaNnz568fPkSBwcHwsLCePToEZ6enkq+wMBABg8ezJ49exg1ahS9e/fG0NAQgLx583Ljxg2mTp3K999/T8WKFZk6dSp582paQtStW5dBgwYxYsQISpcuzdatW5k0aRIrV67k1KlTXLt2jcTERMLCwpQA3J07d7QGYJ8zZw4mJibKuGugacGROk+OHDm0xu/p168f9evXJyIigsyZMytfeh4eHkqeli1b0rJlS63Xe/fuVV77+vpSsmRJvecurSbKcXFxbN++nfnz52vdfVGr1QwdOlQZA+3NYMiECRNwdHRk06ZNythcKS2VRo4cSe/evenevTsAXl5erFmzhjVr1jBs2DC99dAnIiKC58+fU7NmTQoXLqyV5uHhgbGxMfb29so5unnzJgYGBuTIkSPD+0gtT5483L17V2d9SEgIy5YtY8OGDTRs2BDQBB7fDFKkdvv2bbJmzUrNmjUxNDTEy8sL0Mw6+fPPPzNs2DDlx0PqllgTJkxQnhcvXpzp06dz4sQJmjdvrqzv1q0bVatWBWDcuHFv/awBLFy4kNjYWL11dXHR/J+Ljo7GwsJCKy3ldVpdEqOiopg/f74y2Gu9evUYP3680rJx+vTprFixgu3bt1O7du0038935eHhgbm5OdbW1lp/I++ifv36yngekydPJm/evJw7d44aNWowbtw46tevrwSRvb29uXfvnlY37uHDh6d5rK1bt2bq1Knkz5+fbdu2MWjQIH755Rflf4aLi4vW2IqppX4P0npPUr8fefLk4dGjR8THx+tMiKJQJ0Pwac1jivhXcH4pGJlD2e4Q+5Lo1/E42tloAldv1CceU+ISYmjp15U+ffpQqlQp/TOWprQMs80GOSpA6tarmdw06RFBkNVLf10lSZK+VbJl2Bdp8+bNFC1aFLVazbVr18iUKRPZs2dn06ZNSp7UN3zLli3Ly5cvefbsGU+ePGHs2LHMmzdPSU9prZX6N8XIkSMZOXIkERERDBkyhE6dOnHz5k1q1aqlczOuffv2dOzYkRYtWrB582bq16+vU+d169bRvHlzxowZQ4sWLZTroNS2bdtG586dqVevHtOmTePp06eMGDGCOXPm8Ouvv9K6dWtatmzJ9u3bGThwoM7vDCMjIxYvXszYsWNRq9UYGBiwdetWrl27pvc3Tr58+ciVK5fec5zWzb/4+HhCQ0NxcnLSygto5Tc3N8fJyYmQkBCl0cTbbiZK0vv4dlqGfSWMjY25f/8+PXv2pHr16lSsWJErV67QoEEDQDM4+IABA5QWCqD5hzF27Fh8fX357rvvKF68OFu3bqV///6YmZmxZcsWChQowJQpU5SB+FNmFrG0tKRjx47s2LGDhw8fMmjQIExNTfHw8CAwMJDhw4fj6OiozCiZYtGiRahUKnx9fZUuV8eOHdMZQD+9cbc6dOiAn58fFSpUULqV7d27F29vb2X2wdjYWMqUKYO1tTW3bt2ib9++Ol8ApqamDB48mOvXr2NsbExAQAB3796lZs2aVKhQgVq1ajFixAi6dOnCs2fPaNy4MQsWLKBx48Y0aNCA58+fkzdvXiV4FB4ejr29vVL+5cuX+e6775TgBECRIkW06iCEYMWKFTRt2pSCBQsqzagjIiLSPP6YmBgmTpxIrVq1yJUrF9OmTdPKb2trq0xU8ObyZjDs3r17mJmZYWFhQefOnQkICNDpXpjeuFvHjx+nTp06egepP3fuHFOmTNFq7XLjxg0ePHiQZnn62Nvb06lTJ/z8/OjQoQNXrlxJN/+b78O7cnR0JCwsTGf91atXUavVWhMumJiYaAVg39SxY0eeP39O4cKFWbBggTKz6rVr14iMjMTX11fvdg8ePGDAgAFUqFCBrFmzcu7cOZ3PROr3JSOfNdD8AEjrs5HyHpqampKQoP0DPKXebwZkUhQoUEAJhMXExHD79m169+6tvO+WlpZERUXx4MGDd34//22p7+Km/GBK6cp6+fJlrfcbtM/r244VNAHvoUOH0rp1a8zMzJTgMGj+b6f1fqRuLZfWe5L6/Uj529b32VXc2Qv3D2pu8qQIuwfqJEh4Ba/DISYUUxMjEoT2PS/lM2BjR6dJf2JrZaHVIlWLEBD7d6vfVK3LFCmD6Ienf0db+kwSX8vx3CTpc0qSA+h/aZo2bcqff/5J27ZtKVCgAIsWLSIoKIhjx44REBBAlixZmD59OhcvXlRmur927RpPnz5Vypg1axZJSUnK0qpVKwYOHKi17s3v1bCwMB48eMCgQYN0ZnLv2LEj0dHRCCH0BsKOHTtGWFgYv//+O8bGxnoniIqMjGTAgAHMnDmT+fPnc/PmTSpXrszr16+5ePEiXbp0wcrKii1bttC3b18mTpxI1apVEUKQlJSESqXC0tKSkiVLsnnzZmXIlBYtWihjUKeMzdynTx9AcyO5Z8+ees/zu9yQjY6OxsDAQOcGYMrNwujoaK3t30yXpA/x7bQMM7bQtND6XPvOoIEDBzJp0iRUKhVFihRh5syZTJw4kZiYGBITEzEwMMDa2pq5c+fy6NEjFi1axOHDh5k6dSpdu3alSZMmlCpVioEDB/Ls2TOyZctGnjx5MDMzw8DAAEdHR86fP68EtpycnLh//z579+5l6dKlWFlZ0a9fPxo3boypqSn16tVj1apVzJo1S7nIvnfvnvJPrlq1ajRr1oxBgwZRrlw5nZYFc+fO1WnCevHiRV69egVouvy0adOGyZMn4+fnx8KFC2nVqpWS18LCglu3brF+/Xpy5cqFpaUlsbGxGBgYYGZmRlRUFBYWFhgZGREeHs7Dhw/JnDkzkZGRhIaGcurUKYoWLUpCQgJdu3Yle/bsqFQqKlSowIwZM7h79y5bt27l559/VupkYmKi9c81OjpaZ1rkN//5jh49mvnz5xMQEMDYsWMJDg7W6h6lT926dUlISGDYsGEULFiQJUuWKGNUASxbtowOHTro3TbljlOK7Nmzs2vXLqX13psBQ0NDQ60Ay5uEEGmO55SYmMiQIUO0WrEBSrfSd7FgwQJat27NlClT8PLyYvLkyfTt21dv3jffh3cVFxent2VNypfqm4G/9Pbl5eVFYGAgc+bM4eeff2bSpEkcOXJEaeqt79yFhoZSokQJpUVSvnz5dM4hoPXZyshnDaBq1appjpNw9+5dcuTIgaurq85A7MHBwYDu3TV9dUlKSkIIwYwZM6hQoYJWvpSATVrvp4GBgU4z+H97kNPU72fK+5FSh+joaExMTLTypz6vGTlW0MwwGhsbq/w/TfHw4UNy586tt14VK1Zk3759ALi6uirvQYrg4GCtO6spwao0W4U9uQSP/x7LRKghSZOfsFSzKkU/g9hQXB1tCHwRpbM/W1tbXiUYsGLvRYyNjZSbDynvkYeHBz/88AMLpk8EdaJmvDDzTJCUrFWW1iD60pclcC+s/QGKtoK6//vctZGkb1Oy7Cb5pXFwcGDUqFE8fvyYLFmysGjRIrp06QJoAmVdunShVKlS6f5mzqh79+6xYcMGYmNjcXd3Z8mSJQwZMoRevXppfccLIYiNjU1z4qJhw4bRpk0bLCwsGDFiBMOGDaNJkyZav2tsbW25du0a+/fv15rFPWU8tDedOXOGXLlyaTV0uHfvnlb3zQ/xLjdkTU1NUavVJCUlaf2eTrlZmHKu3nYzUZLex7fTMkyl0nRV/BzLOww4365dO1auXMnBgwdZtmwZW7Zs4ccff6RatWq8fPmSoKAg7t+/z/nz5wkODiYiIoJLly5RqFAhIiMjKVWqFHXq1AE0g/Fnz55dz6n4pz6jRo3CycmJOnXqEBISgoGBAYsWLcLHx4d8+fJx4cIFVq1aRb169YiJiQE0AbuUbpq9evWiQIECeHp68tdff5GUlKS1Lz8/P2bOnAloumBNmzaNKlWqaP1Da9asGTExMbRt25YLFy7Qtm1brTJSLka7dOlCaGgoP/30E5MnTyY0NJQqVaqwdetWQkNDlQt8ExMTnJycOHv2rDLe2ciRI2nbti0qlYoePXpgYGBAr169aNasGQYGBowdO5ZBgwYBmgDh8+fPlf17eHhw6tQprWN7c0yfjRs30q5dO9q2bYunp6dO97yUC+eUMsLCwjh06BA///wzdevWJWfOnNy8eVNrmwYNGnD16lW9y08//aSVN6Vliru7u96m029TuHBh9u/fr/eLOOV43mzxkjVr1nfeD0ClSpXYvHkzw4YN49dff1XWGxgYaJ1jJycnIiIiSEx8vx+Rz58/19udNKUb3vHjx5V1kZGRb23ZZGdnx9ChQ7l+/TpPnz5l7dq15MuXDxMTEyXYkdrhw4d5+fIlc+fOpUKFCjg6OnL79u1095GRzxpoJodI67OR0k3Sx8eHhw8fao0RuG/fPry9vbGzs0u3HqD5YeXs7ExQUFC6LRP1vZ+ZM2dGrVbz7NkzJd+bYxW+6c33/2Py8PDQGvwetM9rRo41KCiIIUOGsHbtWm7duqX149LFxSXN9yNlpkjQvCd79uxRXicnJ3Pw4EGlmyxoPrdGRkZaP2YBUKvh3iG4tUPzWvX333lclCYoFv7gn7zRzyH6GT6FcnL68m1lchDQfAaqVq2KS7bs3Fjah8t/zVFmz0o5pl27djFmzJh/ukhaOoBKz88FGQz7Mr28Cxs6aFoOXt2g+exIkvTpJclukl+iAwcOsGbNGooVK4axsTHjxo0jODiY5cuXExgYSOXKlbV+v7wPtVpN0aJFOXToEGZmZjx+/JhGjRpx/vx53N3diYyMVCb+GjNmjNID6E1bt27l/PnzyrAkTZs2xdLSUmsYjhQqlYrExEQ8PT2JiIhQhicZOHCg8joiIgIzMzOSk5OV64WUm4Pvc/2QlrRu/tna2ur0+kgZciJ1l8f4+HhevHhBrly5lPS33UyUpPfx7QTDvhIps0L27NmTV69ekTNnTuWuPWhaGQwfPlyZatfR0RF/f3/OnTvHkiVL6N+/PzY2NoDmYjxlbKPUNm3apLTg8vX1ZdGiRezbt48pU6YwYcIEPD09qVKlCteuXePx48dERERgYmLC5s2befnyJVeuXNFq4TJv3jw8PDzYtWuX1gxn33//PVu2bFFam1y7do08efJw9epVHBwclHxGRkb88ssvLF++nE6dOuleBL6H+fPnY21trTTpTVlq167NjRs3sLKy0kkvVKgQoOlulXrGvM6dOxMUFETHjh05e/Ysq1ev5n//077T7uzszPbt2zlz5gybNm1i+vTpWukpQclly5Zx/vx5rK2tsbCwYNmyZVy+fJlp06bpBFTepZvkhxoyZAhXrlzhhx9+4MSJExw/flwZ+6Bv376sWrWKUaNGceHCBXbv3k2TJk20Jk0QQnDw4EGtJSV4muLx48f079+fY8eOcfHiRc6fP0+ePHm0ztGePXu4dOkSwcHBuLm54eDgwNmzZ5U87dq1U4KrM2fOVKZdPnHiBD4+PloX/adPn1YGB12+fLnSXa1o0aKULl2aLl26sHPnTk6cOEGzZs3SvQs4depUVq9ezdWrV9m9ezevX78mT548WFpa0rdvX4YPH87MmTO5dOkS69evZ/v27crdtVmzZnHhwgW6dOmS5jhfKTLyWYOMdZOsUKECxYsXp0OHDpw9e5b169czffp0pYv1o0ePUKlUylgN+vTt25cpU6Ywffp0Ll26xJYtW5Qm/Om9nyVLlsTS0pJx48YRFxfHyZMnWbZsWbrHnjIQ/7lz5/SO9fYhunbtyqpVq5g0aRIXLlxg/PjxWuPzve1YAX788UeqVq1Kw4YNGTlyJIMGDVKC5hntJtmrVy9Onz7NmDFjuHr1Kj179kStVmt1aT59+jRFixbVHaT3/kEIOg4IcPEGl7/HpouLhKgnSguxIQt2MeTXKRARTNNKhXBwcKB9+/ZcunSJ+fPns2HDBgYOHIixVSY83TLjmd1ep665c+fWBLuVYJieLpLwTzdJGQz7PBJfw+rWsHvEP+uEgLXtNJ8L0HSZDU0/CC9J0r8kOVU3Sdll+YuwefNmcuTIQbFixdizZw+ZMmVi586d+Pr6kj9/fmbOnMmNGzcoW7bsW2/ipcfAwIAnT56wfPlyDAwMlGu5XLly4enpycqVKwFNy/Jx48bpDW4FBwfTuXNnhg8fTpYsWZRy58+fz7hx47SuVdISGRmpdeNZn5SeMamvNz+Uj48Pp0+f1nsz7k3e3t6Ym5tr3SxMGdu6QoUKuLq6kjNnTq3027dvExISorc8SXoXMhj2hQkMDKR8+fLUrl1bGew8tRUrVrBu3Trq1q1LVJSm+8uxY8f45ZdfqFWrFnZ2dgwcOJATJ05w5swZnYG3AY4ePcq2bdsATfev2NhYfvrpJ+Lj4/H09MTGxgYTExOMjIyoVKkSgwcP5tdff6Vly5Y4ODiwadMmnW5hZ8+epVu3bgQEBCjr+vXrR4cOHZQWGXv37mXz5s1Ky5UUJ0+epG/fvtSrV48ZM2YwevRopSnt+/rxxx+VOy6plx07dpA/f369adeuXQM0M/Xt3r1b6TJUsGBBli9fzpEjR/Dx8WH27NnMnj1ba39Tp07F2NiYChUqMGXKFIYPH66V7uPjQ9u2benZsyfdu3fH2NiYJUuWcOzYMcqWLcvly5eVwb8/h0KFCrFjxw5u375NpUqV+P777wkPDwc0LfemTp3K0qVLKVOmDF27dsXd3V0JuoLmDljlypW1ljdnCTU3N+f06dPUrFmTKlWqYGxszOrVq5X0MWPG8OLFC8qWLcuFCxcAqFatGjt27FDy3LhxQ5lx5+HDh9y4cQPQjA1148YNJdh09+5dgoOD8fHxATQBhpQWkwCrVq0iZ86cNGzYkBYtWtC8eXOtge/fZGFhwYABAyhRogQjRoxg2rRp1KpVC4CxY8fi7+9PQEAApUuXZvTo0VhaWlKuXDnlb6dGjRoUKlRIa1wrfTLyWcsolUrFn3/+iYWFBT4+PvTr14/JkyfTuHFjAE6dOpXu4Keg+Rvu168fAQEBlClThv79+yv/l9J7Px0cHPjjjz/Yvn079vb2jB49Os1xJVIMGDBAOW/vMyFHen788Uf69+/PhAkTqFChAjdu3GD06NEZPtYlS5Zw4MABJUDcrVs3HB0d0+zim5ZixYqxatUqli1bRokSJZTgqrW1tZJn586dVKtWTXtDdTI8uax5nrcW5K0Jmf5u9RsXBeF//62Z2nL30Uvu3r0H6kQsbOzZsWMnT548oVSpUkyZMoVVq1ZRunRpMPm7S2yCdtBaS5RmPEis0pgCPnXLMHmh9+nd2Ao3t8Lx6XD/75aO4Q/g2RUwNIEsf09sEXTis1VRkr5pSalahKn/nZbP0rspWbIk48ePZ9++ffz++++MHTuWNm3aoFKpuHDhAkFBQURERNClSxetAfFTu3Llis5vCH3SCi5NnTqVIUOGsHXrVqpVq0ZAQAD58uXTyhMdHY2vry/FihWjX79+WmmlS5eme/fuNGzYMN0bmqBpiODo6JjudVVgYCAODg7KTOgZlfoG9ZuaNm2a5s040L6JbW5uTrdu3Rg5ciS7du3i2LFj9O7dmy5duiityPr27ctvv/3GunXrOHv2LB07dqRevXofPIGTJCG+UpGRkQIQkZGROmkJCQni0aNHIiEh4TPU7P3t3btXODo6ihkzZgghhHj+/LlISEgQvXr1Ep06dVLyhYWFCR8fH1GwYEERHBwsfH19Rd26dcWMGTPEjRs3xKlTp0SWLFnEb7/9pmzTqlUr8csvv4ikpCRRs2ZNMXv2bLF06VKRO3du4eXlJVatWiWePn0qIiIiRPXq1cWECROEEELcuHFD9OnTR2TKlEk0aNBAREREKGVaWlqKV69eiUuXLgknJyexdu1anWP67bffhJOTk3jw4IHWeldXV3Hu3DkxYMAAYW5uLqZPny6EEOL06dPC3d1duLi4iPHjx4vly5cLS0tLYWpqKoyNjYWlpaUwNjYWpqamwtLSUhgaGgpzc3NhaWkpVCqVsLCwELa2tmme4wMHDoiCBQum+z6o1WqRJ08esWnTpnTzSf++I0eOCGdnZxEfH/9O2/Xu3Vvrb6Z06dLir7/++tjV+6oNGjRI+Pv7f+5qSKk8ffpUWFhY6Py/FKF3hDgwToijvwmhTtasi4/WrDswXogTszXPg05pXh8Yp1mub0l7ZxHBmjwnZmtePzgqxNFpQrx6rnmtThbi8BRNnsjHQgg9360JsUKMshPiZxsh7uz/iGdCypA/GmvO/c82QsyrKERyshDXN2tez/ERYt8vmucbfvzcNZWkb9P8yv/8jcZHf+7avJf0rre+VkeOHBHVq1cXv/76qxBCiB07dohy5coJITS/H9u0aSP279+v9dszMTFRAKJ3797C0tJS6xqrdevWYtCgQXr3FR4eLiwtLXXWDxo0SACiVq1aIikpSSvt9u3bolChQqJQoUJpnvfExERRq1YtkSVLFrF3715l/d27d8XixYuVcooUKSIKFy4s7O3txQ8//CC2bt0qJk6cKEJDQ5VtBg8eLBo2bJjeKdOrZMmSon///mmmX7lyRZQpU0aYmJiIfPnyiQ0bNihpf/31l7C3txePH2t+X8TFxYlu3boJGxsbYWdnJ3r27Cni4uKU/Gq1WowYMUI4OjoKKysr0bp1axEeHv7OdZakN8lg2Bdk6tSpYv369crrokWLCkA4ODiI/fu1LzRev34thg0bJqKjtb9c//rrL2FhYSGmTJmitX7nzp0iU6ZMwtjYWOTMmVMEBweLxYsXi+3btwshhDh69KgABCA8PDzE/fv3tbaPiIgQs2bNEmq1WlmXEgzbsWOHWLJkSbrH9fLlS611rq6uok+fPqJcuXLi6tWrWmlxcXFiypQpwtfXV+cL4kNlJBgmhBCbNm0SBQoU0PpHLH0eDRs2FGPHjs1w/lu3bglnZ2fx8OFDIYTm/4GNjY3O38q3rkqVKmLPnj2fuxpSKh06dBC9e/fWTbi+RROUur1Le/3Juf8Evo5N1wSnUq97elW3rBRxUf8E0x5fEuJAgOZ1SlAr8rHm9eEpSgBO73fr1r6aC73/FRDi9d83S+4fFeLMIiFSfV9IH9mrZ/8EIsc6ax6vrBdi/6+a53/+JETgXs3zqYU/d20l6ds0+7t/gmGxYZ+7Nu/lvxQMO378uKhQoYLIkiWL+N///ifu3bsnoqKixM8//yxq164thNA0RJgwYYJwd3cX3t7e4sKFC0IIIfbv3y8A4eXlJS5duqRVblrBsEePHomjR4+KTJkyCSGEiIqKEuvXrxc+Pj4ib968Yvbs2aJkyZLCw8NDTJo0SZw9e1ZER0eLrFmziqpVq4qwsPQ/M69evRL16tUTNjY24tGjR+LMmTNizZo1YsSIEaJSpUrC2tpajBw5UqjVavH06VPxv//9TxQuXFg4OTmJXr16iatXr4q7d+8KGxsbsXv37o9whiXp66MS4uvs2xAVFYWtrS2RkZFa3bVAM/vdixcvcHJy0pkx7mujVqt1x45JR1JSEsHBwWnOFve2fQkhPuoAiulJ+eip3mGCgU9p/vz5lClThiJFinzuqnzTnj9/zqJFixgwYECaM16mtnnzZszMzN46m6ckfUkiIyOZMWMG/fv31x6/LjlJ0w0uOR6K/QC22f5Ju7kNnl4GQzMo1lrTnfHan/Di78k4vuulmcQlLTe2wLOr2uusskCJ9hB0Cu7tB4c8ULgJkMZ3a3w0zPXRdNUs0VEzc+HkvBDzHJoshkLff4SzI+k4ORd2DgLX4uBRHQ4FgHtFTffXW9ugVgAUbQ0TcmgmWOh7A2xc3l6uJEkfz4wS8PLvcaf6B6bd5fwLlt711tfm6NGjXLt2DT8/P4yMjLCysiIuLo5MmTLxxx9/aA0tk5yczOrVq6lUqRKurq48e/aMGTNmMHLkSJ3Zqdu0aUO2bNm0hooBKF++PMePH6djx44MHDgQLy8vihcvzg8//ICfnx/GxsYIIdi4cSOrV69m79697N+/n+TkZIoWLZqh37xCCG7fvk2+fPno0aMHV65coVChQlSqVImaNWvqfc9OnDjB/Pnzadq0Kfny5WPWrFlMmTLlPc+qJH3dZDBMkiRJ+qI8fPiQ3Llz60179OjRR5v6+6sQGghX14OpLZTppj07ccwLzVhR2UuDrWa2JR4eh/uHwMoZSnRIv+zE13B6ASTGoBlCVA2ooFxvTaAs7C7krgrZS2myp/XdemcvLG8M5nbgtx3mlNWsz1IEuhx+pxmVpQxaWA1CzkDtiZC7CswsoRknzNweop9Cu63gXh7mltcETBsvUoKakiR9ItMK/zPBiP817ZsZX4n/UjBMn9SzKv7bIiIi0p0k7FPWRZIkjbeHnCVJkiTpE3JxceHq1at60z72TKpfvFdPNY92OXSDSpZOui2vsvx98ZWtxNvLNjaHfLU1rcncymhalMW+1AzCHvn3FOcpM0amx70SmFhrZi48Nfef9U8vw9394CFne/roXvw9Q6R7RXDw0LxPEQ81gTCALJrZkclVUfM+XPhDBsMk6VNLPYB+ckLa+aTP5lMGn9ILhMGnrYskSRoyGCZJkiR9UYyNjfH09Pzc1fgyxIZqHi2dMpbf1Bq8WmS8fMc8UL4fqAw0LcViX8KDY5pumUZmGevWY2gEOX3g9g64uOLvethCfCTsG6MJzJnZZrxOUvoS4zTnFsDaWRMkzVMdzizUrLPNrmmlB1DqRzg5B+4dhJCzGQuSSpL0cSTHp3qe+PnqIUmSJOmV8cGoJEmSJEn6tGJeah4tHP69fRgYagIqdjk1r2NfaB6zFMl4F8dcFTWP6iTNY52JmtZiTy7C77XgwHhNUCY56WPW/NsU8/f7Y2gCZpk0zz2q/5PuXOif55ncoMjfwdHDkz9J9SRJ+ptsGSZJkvRF+08Hw77S4dAkSZIkCdRqeB2meW75CbqHZnID/g5+OReG3JW1ktP9Ts1V6Z/nBkaQ3xf8tmoG5H9+XTPA+87BcH7Jx671tyf6uebRMvM/wUr38prgGGi6yqbm469p+Xd7B7y4pQlI3tgCryM+WZUl6Zuk1TJMBsO+ZfKaVJK+TP/JbpJGRkaoVCrCw8OxsbHB0NDwi52xUJIkSZL0ig0DDDWzRRqYQ+K/3c3GCPLUgYRYcC0GSclAMkIIkpOTiYqKQqVS6Z/hyslTM2h/9DNw8QYTC3ApCp33acYRe3ZNM37Ymd81s07K7+T3F/N3MMwqVddZE0vIWwtubNYExlJz9NCk3doOF1eCoTEcngRle0DNXz9dvSXpW6JW/9NSFmQ3ya9YWFgYM2bMYMSIERgY6LYjSWs2yRQ3btygQYMGXL9+XZnF8sGDBzg6OnL06FH69OnD2bNn09z/5cuXP/nM9kOGDCEgIIDjx49TtmzZT7pvSfqU/pPBMJVKhZOTExEREYSHh3/u6kiSJEmSRkIM3D8M9rnBKW/6eSNCIEaAuRWEhn6a+qkcwRQIfamTZGJigoODg/6bSyqVZlbDS6s044elsM0GNX7RDK7/v/zw/JpmFsS/Z6j8bM4vg3NLwMRKM56Zhb1mfC3ngp+3XhkR/UzzaPXGrKoNZmpagbl6627j1VITDLu0GpJea9a9uPXv1lOSvmWpW4WBbBn2hZg7dy5Dhw7Fzc1NWff0qWbikSxZsijr7ty5w/r166lVqxYAK1eu5NGjR8yfP/+d97l69WoKFy6s/0bSWwQFBVG3bl369etHnz593nn7nDlz8vDhQ71p4eHhegf1X7NmDRMnTsTLy4u2bdty4sSJ95q8KDg4mG7dunHgwAFsbW3p27cv/fv315tXCMGYMWOYN28ekZGR1KhRg/nz5+Pk9M9Nnzlz5jBx4kSePn1K2bJlWbhwIbly5XrneklSav/JYBhoWoc5ODigVqtRq9WfuzqSJEmSBKf+hJOTNd3Wmi1LPyh0dz1cmgUFGoDTD5+ujnoYGBhgYGCQfivraqM0Qb7SXXTTzO2gUGO4uBwOTdAEZ9wrardu+lQS42DX8H8GoU8R+QjarP/09XlX0X+PGfbmpApmtvoDYQB5a2rGF0uZbRLg1ZN/pXqSJAFJMhj2pfL19WXJkiXK61GjRmk9AlSqVEl5bm9vz9atWylRogRVqlShRYuMT1KTmJjIwoULWbRoEa1bt2bXrl3ExMSQL18+VCoVSUlJxMTE4OjoSK5cuTh9+rTW9m5ubuzfv5+yZctSpkwZypQpo3c/Dx8+JHfu3AAkJydjaGhIxYqasTwvXLhA0aJFtfKn9V2+ePFiOnfuzKRJk+jevTsNGzbEx8eHHTt24O7unuHjTk5Opm7duri4uHDkyBHOnTtH165dcXNzo1mzZjr5J02axPTp01myZAkODg506tSJdu3asX37dgDWrVuHv78/CxYsoECBAvTp0wdfX18uX76st7WeJGXUfzYYBpo/dENDQzlVrSRJkvRleHwa4v8eB2xTZ+h6VNMqSZ/nVzR57bODsfGnq+P7ss4CFQeknV6igyYYdmevZnGvCO02f7r6pQjcpQmEWbtA9THw7Cocm/b1tJRKq2VYeoxMoXCTf2acBIh6/HHrJUnSP94Mfslukl+Mbdu2aQWVQkJCANi5c6ey7vr161rb5MmTh4MHD75zd8WVK1diYmJCzZo1lVZmVlZW3Lp1K8PdJPPkycPo0aPp2rUr58+f1xv8yZEjB0lJSRw9epSuXbty9epVQNMyLMXMmTP58ccfMTEx0dk+JiaG3r17s3z5chYuXIifnx8Af/75J23atKFEiRLMmDGDVq1aZei4t23bxs2bN9m7dy+ZM2fG29ubffv2MWvWLJ1gmFqtZtKkSQwfPpz69esDMGXKFGrXrs39+/dxd3dn/PjxdOnShR9+0NwYXLBgAfnz5+fQoUNUrlxZZ/+SlFEylCpJkiRJn8rjC5pHEyuIegQ7BqWdN/Tv4IyT579fr0/B1RvK9QG37zSv7x+GV0/T3eRfcXmt5rFIMyjSVDN2FkBkMCS+/vT1eVfKmGGZ3227Ym0A1T8zUL4O0229IknSxyFbhn2x6taty8mTJ5WlU6dOdOrUSWudt/c/rWyjoqKIiIjA3d39nVohJSYmMnr0aJydnRFCEBcXR1xcHADx8fHExcWRkJCglZZWb6bg4GAuXbrEqlWr3vu4e/bsSWxsrM76devWUahQIU6ePMmJEyeUQBiAmZkZ69evZ8SIEfj5+VG1alUOHz4MQKlSpfD399e7rwMHDuDt7U3mzP98T1WpUoVTp07pTCZw5coVQkNDqV27trKuYsWKGBgYcPLkSSIiIrhw4YJWuqenJ1mzZuXkyZPvdS4kKcV/umWYJEmSJH0xYsMg4u+xO5r/Acsbw5W1ULAReNbRzqtWw4vbmueO+T5tPf8tKhVUH615vrCaZuywG1ugVOdPV4fYMLi9S/O8SHPNo6UjmNpqWouF3QfnAp+uPu8j+j2DYS7FoP12zSyUc77TjGn06gnY5fzoVZSkb55sGfbF2rx5M0WLFkWtVnPt2jUyZcpE9uzZ2bRpk5Lnzp07yvOyZcvy8uVLnj17xpMnTxg7dizz5s1T0lMCWJMnT1bWjRw5EiMjI549e0bmzJk5ceIEDRo0ADQBJi8vL606ZcuWDdCMadakSROdOq9bt47mzZszZswYWrRo8d69nt7sHrlt2zb8/PwYOnQomzZt0goCvmnEiBFcv36dXr16cfbsWfLly5fmmF337t3T6Vbp5uZGfHw8oaGhWmOB3bt3D0Arv7m5OU5OToSEhHD//n2d9JTyUlr1SdL7ksEwSZIkSfoUUlqF2efWDDb/XS9N97ytfSBXRc2MgCkeHtMMdG5s8d8MVhRooAmGXf/r0wbDrv8F6kRwLvxP0Eul0sy4+OgcvLzz9QTDLN8xGAaQ4+9WeTZZIfyBpmXef/HzJUmfm2wZ9kVq2rQp+fPn59y5c5w+fZq+ffvStGlTAI4cOcK0adMYOnQoLi4uODtruqJfu3YN+CeQNGvWLGbNmqWUmdZskq6urowePZr169dTrlw5Qt9zIpxjx44RFhbG77//TqlSpVizZo1Od8Xk5GRMTU2V50ZGRvTu3VtJT0jQfP5S8qSoW7cu9+7dw9nZmWHDhinrjx49SqdOnbh586ZOfeLj4zEyMuKPP/5Is87R0dE6g+5bWFgo27+Z18DAQKduFhYWxMfHEx0drbX9m+mS9CHeq5vkpUuX8Pb25ujRo2nmOXz4MMWLF8fMzIyCBQuya9curXQ3NzdUKpXW8r7/JCRJkiTpi5cSDHMppnmsNAQy5dCMAXVji3be4zM0j14twfA/eN8qv6/m8eGxf4I7n8LNbZrHQt9rr3fw0Dy+DPx0dXlfSsuwdxgz7E3WWTWPctwwSfp3yNkkv0gODg6MGjWKefPm8eTJExYtWoS9vT329vYsX76cLl26UKpUKTw8PLC2tv6gfa1fv54SJUoory9duoSjo2Oay/Pn+r8Lhw0bRps2bbCwsGDEiBGMHDlSCW6lMDQ0JCkpiYMHD1KwYEGSkpL43//+p6RHRERgamqKmZmZTvkpQb+MejNolVaeN+uY0kX0zaCWqakparWapKQknfwWFhbK/vSV92ZZkvSu3ikYdv78eZo1a0bZsmW5cOFCmvnu379PnTp1qFatGmfOnKFixYo0atSIBw8eKHnCwsJYsWIFgYGBymJvn8YgwpIkSZL0tXszGGZsBsX+niXyUqpxQJ7f1AzyjgrKdv+kVfxk7HJozoNQw7VNn2afCTGaccoA8r3RLVUJht39NHV5XwmxkPBK8/xDZuJMCYbJGSUl6d+h0zJMdpP8Uhw4cIA1a9ZQrFgxjI2NGTduHMHBwSxfvpzAwEAqV67Ms2fPPng/ZcuW1XqdmJhIlixZCA0N1VmSkpL0jhe2detWzp8/r7Taatq0KZaWlkyYMOGd6nL79m1y5MjBq1evdNKePn2q00ClfPny3Lp1S2e9vi6c+ri6uhIcHKy1Ljg4GFtbW53rfVdXVwCtLo/x8fG8ePGCXLlyKen6ykurm6YkZdQ7BcM2btyIqakpW7duTTffjBkz8PDwYMKECRQuXJjp06djb2/P4sWLAU1kNyYmhsKFC+Ph4aEscmpUSZIk6T/r8UXNY0owDDSDuAPcOwSRISAEHJ6oWZe/Hjjk/qRV/KS8WmoeT86C5KT0834M9w5qWmtkygFOb4zDlnKeX97R2eyLkjJ4vpEZmNq8fzkyGCZJ/65/o5uknPDig23evJkcOXJQrFgx9uzZQ6ZMmdi5cye+vr7kz5+fmTNncuPGDcqWLUtg4OdtKRwcHEznzp0ZPnw4WbJkAcDAwID58+czbtw4Tp8+ne729+/fZ86cObi7u7N7926EEHTurBmWIDAwEBsbzXdIlixZuHr1Kr169SIpKQkhBEeOHCFfvnwIIRBCcPz4cX766SfWr1+fobr7+Phw+vRpIiMjlXX79u2jatWqOnm9vb0xNzdnz549yrpDhw6hUqmoUKECrq6u5MyZUyv99u3bhISE6C1Pkt7FO0Wfxo4dyx9//PHWKOyBAweU6WMBjIyMqFChgjLjQ1iYZlr5N/sSS5IkSdJ/UtQTiAoBVJA11cC5djkghw8g4OxiODoVrm7Q5Cunf5am/4xibcDcXjN21fVN6eeNevLhLStu7dA85qutGScsNYc8mscvPRgW/ULzaJlZ9xjehU1KN0kZDJOkf4XOAPofGAy7uAp+yQxXN35YOd+4kiVLMn78ePbt28fvv//O2LFjadOmDSqVigsXLhAUFERERARdunTRGhA/tStXrjB69Oj32v+tW7fIli2bzhIVFaWVLzo6Gl9fX4oVK0a/fv200kqXLk337t1p2LChMvh8isjISB4/fkyhQoWoX78+tWvXJjk5mSVLlmjNEunh4UHjxo15+lQzo3Pu3Lm5dOkSLVq00OmueOXKFZo0aaI1myNAu3btmDlzpt7jbNq0KQ4ODrRv355Lly4xf/58NmzYwMCBAwE4ceIEPj4+REZGYm5uTrdu3Rg5ciS7du3i2LFj9O7dmy5duiityPr27ctvv/3GunXrOHv2LB07dqRevXoULlw4g2dekvR7p2DYmzNQpCWtGSRSmj++fPkS0MwK4erqiq+vL5cvX063zPj4eKKiorQWSZIkSfriJCfCX93h1Px/1t0/pHl0KQamVtr5vVpoHo9Mhn1//8CuOQ6yFf/36/o5mVhCmW6a53tHwerWsPFHODRRExC8d1ATIFxUE6Z4woaOGSs38TUcngRPLmlev7gN55b+M4tk3pq629j/fZMv9qVmxskvVfTfXXfedSbJN8mWYZL07/rY3SQvLNc8Xl6Tfp4Ts9JOl8iaNStubm4EBASwb98+ChYsiLOzM4aGhpiZmdGnTx9++OEHSpUqxYwZM5TtUgJEAQEBlC1bFjs7u/faf758+QgJCdFZUlppgabVVtmyZUlKSmL16tV6Z44MCAjAy8uLcuXKsW/fPoKCgihbtiz169cnR44cdOnShUOHDhEbG0uzZs3o1asXLi4uWmVs27aN169fA5oZLv/66y88PT11xua6d+8eM2fOpF69elrrb9y4wcOHD/Uep4WFBTt27ODJkyeUKlWKKVOmsGrVKkqXLg3AixcvuHHjBrGxsQCMGzeORo0a0axZM+rXr0/16tW1gpE9evSgb9++/PTTT1SuXJkcOXKkO4C/JGXUvzIqb3R0dLozPri5uXHy5EksLCx48OABAQEBVKxYkStXrihTy75p/Pjx7x2FlyRJkqQMefUM1rYFr+ZQosP7lXH/8N8XLss1M/XlraEJ7IBm1sg3FWqsGSPs3iGIj4LvekLZn97zAL4ypTrDsd8gMlizpOf6X5rzmKsSqNVwa7tmrK/Mntr5jv0GB8fD0d+gQn84MO6fwaxNrCBHOd2yTa3A2gVePdaMG2bxBY1hGnJWcwEccgYigjTrZDBMkr5sH3MA/fhoCD6leR50QvP/782hZRJiYHMvEMngUU23K7jEiRMnGDx4MLdv32bAgAE0atSIV69ecfLkSSUYNWzYMBYvXkzHjh2xs7Nj0aJFFC1alCNHjgBw8OBBjh8/TpEiRd6rDjdv3tTbMyqlkcfr16+pWLEiBQoUYN26dVpBstSMjIxYt24dLVu25Pvvv+fKlSuUK1eOuXPn4uWlaX1+7do1JbDXv39/Vq9ezfPnz1Gr1Vy6dAmVSkW2bNkwMjIiOTlZKfuXX35RnutrCNOuXTuWLFny1m6ahQoV4sSJE3rTfH19lcYxoBlEf/bs2cyePVtvfpVKxZgxYxgzZky6+5Skd/WvBMPSmkEiJUBmbW2tRIYLFy5MpUqVcHNzY9WqVQwYMEBvmUOGDKFv377K66ioKLJnz/5vVF+SJEn6Vl1cDsEn4fl1KNJc03rpXT1N1dL5r5+g6zFNoAs0gZw3mVhA8+WaC5zY0A8PdHxNzO2g1RrN+bHKrAkGvryr6a4YGwaZ3CBbSU0Q6NJK2D0c6k6FA79oAmO2btD70j8XhnFRcPLvH9MJr2Dvz5rnWQqDWSbNGG1GacyE5ZD772BYIGQv+W8fecYkJ8KyBpAQrb3ewuHDyk3dTVKID+tyKUmSrqSP2E0y6ASo/25ZFhep+X7KUkg7z8s7mkAYwIMjMhimR3JyMq1atcLPzw8jIyOsrKyIi4sjU6ZMSisjJycnBg4cSL9+/Vi9ejVOTpqJSgoUKMCwYcMYOXIkJiYm710HT09Prl69qrM+U6ZMAJibm7N582aKFi2KkVH6l+lWVlZs3ryZ27dv4+bmptWSat26dXTo0IHRo0cr189Vq1bl119/xcbGBpVKxaBBgzA2NtbpFilJ35J/JRiW1gwSaY01Zm1tjYeHR5pNLUETYMvIVK6SJEmS9N5u79Y8xkdpuup5t333Mp6kBMNUEPMCVjTWBFkMTSF76bS3MzD4tgJhKXL6aJb0xLyEm9vg6RVYVO2f9ZFBmlk6U7qUnlmguVh0yAPmmTStqQo3hYZzwfAtP3mcC2ouIh9fhKKtPuSI9As+Axf+gNxVNLNZGmXggioiSBMIMzKHSoM03UlBEyT8ECktw5JeQ1yEJigpSdLHo9My7AO6Sd49oP066IRuMOzF7X+ePzgKJTu9//7+o3x8fPDx+ee75vXr1yQnJ+vthmhoaEjr1q2V187OzlotplJbvnx5mvusVKmSMmZ2iRIl9AbCACIiIpTnJUqUSPc4UlOpVOTLpxv49PX15cKFC3h4eCjrnJycuHLlSobLlqRvwb8yfaOPj4/WjA/JyckcPHgwzRkfIiIiuH37ttYfrCRJkiR9UrFhEJKq2f/Z39+vnKd//9is8Ytm1r+U125lwNj8w+r4rbJ0gFrjNAPuW7toAko5y2vSbmzWPEYEw/G/B/OtMAD8tkHn/dBo/tsDYaBpgQbw6Gz6+YTQdF2MCNY8z6jdw+D8UljXDuaW0wTt3ibs78GRHXKDjz90PQrl+oB3u4zvVx9jc01LOYBXTz+sLEmSdL1LyzDxf/bOMjyKqw3D98aVhCSECAR3C8GluFuBEtyKFCnFWhyKtbRA248Wt0Ipxa14sQSH4BKCW4BAjDjx/X6clWx2I0Dwc1/XXrNzzpmZMxvZmWfe93mV4LdUeCQaEs3UafZ5VQLYg+P6Y0LTiWEv87/pE8aQEPYxYG5uLu+rJZJskCNiWGpqKs2aNWPLFlHhZOjQofj5+TFt2jSuXr3KN998Q2pqqqaKxa5du/jtt984f/48Bw8epHXr1tja2tKr12te3EkkEolE8qrcOQTKVBF1Y2wmIo4en8/+9qmpwttFXZGwfEdo+qO235BfmCT7VOwOY+7BtwHQYytU/lK0X98phMzV7eFFuLhhLPuFSId0r6TvrZMR+VRP44MuQ1K84TEpybB9CCxrCHPKwrzKEHw9630nxsLjc+K9hb24cfWdqe1/cBLOLNe/gVWLYQ6qokQu5aDxVLDNm71zyoxcKjPlqMevvy+JRKLLy3iGnf8Ldn8HO4fDknpw6Ee48I8QxqKfQbC/GFdXVOLjwUn9/xWhN7TvY0Mg5LpIOZeimEQikWRIjohhSUlJBAQE8OTJEwAqVqzI2rVrWbVqlSYkdN++fdja2gIiL3rp0qXUqlWLXr16UaBAAfz8/F65ModEIpFIJK/NLVVEc5l2UFJVNSlgR/a2PTEPfsgDx/4HKMHGRaQ8Vu4rvMfMc4n9SnKOoo2FaBl2GxbXEQJTLnfhQZadSLD02BcA6zzCmyfoEpxfBffTRGCkJIniChdWAwowMhHH3jEs6xvOR2cgNRly5YMOqojD04vgmb/4HfurFewaCXcO6m6nEcMM20y8FmpPoQcncn7fEsmnTnarSYbdgb3jxXtjc3h2FY7MEn6TV7eI/x0gRP5iTcDIFGKeiu3SEnpLLE1U0cd/tYa5XrBzhBTE3gOU8mcgkbyXvJIYVrBgQZRKpSbv2tzcnAcPHjBkyBDNmA4dOnD79m3i4+M5cuQIZcqU0fTVqlWLgIAAXrx4waNHj1i9erVeuVeJRCKRSN4aKclwWyWGFWuiNbpX34hkRnICHPtNiB1HVQa2LuXEUqGAdothzP03I2h8yljk0v6cIgOFD1b3zWBnuCp1ligU2lTJPaNg+zewsoWoRpmcIMSqG7tE6mun1TD0IphaiYIL/lv09/ciAuZVhVVt4Z6oREaBmlC0oRBblSmwsJYQ2FJVBsb+W3X38SbFsJcVfCUSSfZJHwmWUWTY3rGQFCvSvkf4Q8PJIqIV4Ml5CA4Q7/OWFenNBVXVcC+sSrPvZG1EctkvxDI2RCzPrYBDhr2uJNkjPDycqVOnkpqaarC/e/fujB07NsPtAwICKFGihMao3sbGhtDQUACOHTuWpUfY5cuXM+1Xk5SkL7hevXqVx49fPvr35s2bbN26NeuBEskHzhvxDJNIJBKJ5IPi9gGICxOeVPmrQf6qov3xOXGjkRnX/hXbpsU1Tdl1hQKMPk5fkndOjSHiZ1axOww+Bc6lXm9/6pvQoEvatsMz4WcPESmmMALvlVCqFdjnF/5dAPsnQ9IL3X35LRGpS3d94PRi0aa+kW32s8r/RylSc9XHvb5LN4LkTYphxZqIyLrQmxByA55dEwLemyA+CnYMl1Fokk8HdWSYOlLLkBiWmqr9m2g6A2zywGcjoXIf0RZ8DUJUYphzSbGsOkAsz60UafkAEQ/E/k0soNpXgEL8z6ijSqs8+otWVPvEWbRoEQ4ODnh6empeLi4uuLi46LTZ2Niwd+9ezXZr1qxh4MCBr3TMdevWUa5cuSyrQxri4cOHtGzZkjlz5mQ59rfffqN58+Y8evRI09a7d2+OHTv20sd99uwZX375JSEhIS+9rZrAwEBatWqFtbU1bm5uOtUu06NUKpk6dSpubm5YW1vTrl07vWMvXLiQQoUKYWlpSYMGDbh79+4rz00iUSPFMIlEIpFIzv8llp5dwdgUnEqAuR0kxWn9WtITsANOLRLpbgC5C2n71JFhkjdL4brCR+zz+aJ65OuijgwDEWnWZq5IeU1WeYg1nQElmmvH1PwG7PKLyLQTc7Xt8VFwcr52PTFaLAuoxDD7/DDoOHx3G77yhT77RIrmi+dw74gYk5IMz1VVtt+EGJY2sm5DT1hYA/5XBnZ9J8y8H57KuWOd/VNEqGwZkLW4LJF8DKjFL3Mb1bqBNMmIB6JarLEZOJfWtqvfP7um9STMoxL6izcT3zXxkXBprWhTm+c7FgPXCjD8ing40GCC9n/Okws5d24fOG3atOHixYua18CBAxk4cKBOW9poLQcHB3bu3Mn69etZt27dSx0rKSmJZcuW0b9/f7p164aTkxOxsbGUKFECJycnWrVqxaVLl3BycqJq1ap623t4eHDo0CF++OEHTVXKjPjuu++oWrUqTZo0ITk5maNHj+Lv78+3336Lq6srRkZG5MuXj3z58mFlZcXo0aN58OABFhYWeq/GjRsTGxtLvnz59PqcnJyyPO+UlBRatmypmcfUqVMZM2YMGzZsMDh+9uzZ/PHHHyxevJj9+/dz48YNHS/xjRs3MmLECKZNm8axY8dISkqiTZs2GUbrSSTZRYphkg8anxvBtJ57jHMPwt/1VCQSyYdK1BO4qXoC7NVTLI2MIJ8qWifQT3+buHDY0Av2jhHRY0Ym0GOLqHRobAb59C9qJR8A7l4i+guEWbVXTxgZAAOOwpd7oPog3fFmVsLQHoRf3L6JMLcSLG8C8RFCxLJU+aFaO4NjuupeNnnAraLwOCvVWrRdWC1EsahHwr/M2Fz8Xr0J1McMUd1wJ8bAmaXCzPvPprqeaa/D7QNiGflQW/1TIvmYUUeGmanFMAORYc9UD1rylNT1OcxTElBAXKh+ZJiRkfb/0OnFwg9MLYblKS6W9vlFARHQCmsh2Sj08Ymwa9cuqlevrnktW7aMZcuW6bSdP69bPKdYsWL4+vri7e39Usdas2YNZmZmNG3alH/++YfQ0FCsra25ceMGoaGh7Ny5kwoVKhAaGoqfn4FrDdWxp06dysCBAzMVf4yNjZk6dSpnzpzh4cOH9OnThz///JNHjx4xb948SpcuzaNHj/D398fGxoZOnTpRoEAB4uPjiY+P5/Hjx3z55ZdERkYSHx9PUlISCQkJXLlyhe+//564uDji4+M1KZ5ZfcbXr19n1apVeHl50b9/f7y9vZk/f77e2NTUVGbPns3EiRNp3bo1NWvW5LfffmPPnj3cu3cPgJ9++okBAwbQo0cPKlWqxNKlS/H39+fw4cPZ/ElIJIaRYpjkg2ad30OuPI5kyJoLRL7IwJxUIpFIMuPCPyJVzaOm1lQctIKWId+wkOvC80ktnHh2E8JH/0Mw4AjYub/5eUtyHnNbaDBRiGAVe4g2IyOR9lqgpuFtyrQHjxoiivDEXOHdo76BrTMaao8Q74s2EimzGVH6c7H03wKzCsN/E8R67oLZr4j5spRoIcRbFNDyV+i2GSp9Ca6eon/XyIyNv7NLQrRulNmJudLQW/LxoxbDMosMe3ZVLNNHEptZaSvIKlPB1BrsPLT9nl1VxUNuCeP8EJUY5lRc/xjq7zT1GAktW7bk1KlTmle/fv3o16+fTpuXl5dmfFRUFBERERQqVAijl/hfnJSUxNSpU8mbNy9KpVIjOgEkJCQQHx9PYmKiTl9GYldgYCCXLl1i7dq1mR4zNDSUDh06MGHCBKZMmUKXLl24e/cuI0aMQKlUMmPGDBISEpg9ezaVKlXS2dbOzo4LFy4wbtw4nfbhw4dz6dIlvXOvWrUqI0aMMDgPHx8fvLy8cHZ21rQ1aNCA06dP6xUTuHLlCqGhoTRvro26rlu3LkZGRpw6dYqIiAguXLig01+yZElcXV2zjJaTSLLiFcotSSRvl4dhcUzbeY2BdQtTuaCDTt/dkFgAgiLjmbrDn986er6DGUokkg+aW/+JZcVuuu35VSlzhiLD1E/iizSA9ktFtUiAXK7iJflw+ezblxuvUEDzmbCiJVg7Qd0xwhAbhagkqlBA3jJaX7CMKFhH+AHd+g+e34frO0X7myy8YO0EvXaKG+4CNURbsUYi8nFeZSH6nloAtYa9+jHuHRURbrauYr9PzsOh6cLvzcoh6+0lkg+RFHVkmK1q3VBkmEoMy1tGv8+5tNYzME8JXUHc3FakP971EVHNd31Eu0t5/f3kUUWUZTcyLPwe2LoIs/6PlO3bt+Pp6Ulqair+/v7Y29uTP39+tm3bphlz+/ZtzfsaNWoQFhbGs2fPCAoKYvr06SxevFjTrxaw0npiff/995iYmPDs2TOcnZ05efIkn38uHnhYWFhQoUIFnTnlyycKvyxatIgOHTrozXnjxo106tSJadOm0blzZ4yN9X1Id+3aRf/+/WnVqhVz5szh6dOnTJo0iYULF/Ljjz/SrVs3unTpwu7duxk9ejQJCQmYm5trtjcxMWHFihVMnz6d1NRUjIyM2LlzJ/7+/ly8eFHveCVKlKBwYcPfT3fv3qVQoUI6bR4eHiQkJBAaGkqePHl0xgI64y0tLcmTJw+PHj3SRIcZ2l9afzSJ5FWQYpjkveffi485EPAMG3NjHTEsJVXJg7A4zfqW848Z1rAYBRyt38U0JRLJh4q6RL2r7sUp7irPkOf3YM8YEQUWdkeIHWmfxMsbeolrBfjuhjDLNhQ5ULRR1vswMoIWs0A5E9Z0hFv7RPubrkLqUU2/zcoBGk+HfweD78+iQl1mVTrjI+HSOriyCcysRcXMKv3EDbU6RbJkK5G6dXIeHP0VrmyEQSfEjb1E8rGhFxmWSZpkRmKYWhA3VBikWBMhgh3/XaRTWtiJv7v0qCPDnt8XRT6yErk29xPz6rRaCOMfGd7e3pQqVYpz587h5+fHyJEjNamPR48eZc6cOYwfPx43Nzfy5s0LgL+/+DkpVJG98+fP10n36969O/ny5ePnn3/WOZa7uztTp05l06ZN1KpVK1vphYY4fvw44eHh/Pnnn1StWpX169fTtWtXnTGRkZGMGjWKefPm0b59e86fP0+7du3w9vbm4sWLGrFtx44dbNmyhVmzZjFr1iyOHj1KSkoKpqamWFlZac4xV65cOvtXbw/Qr18/5syZw99//53hnGNiYvS8xaysrAARFZd+rJGRkY4wpx6fkJBATEyMzvbp+yWS10GKYZL3nmfRIqRYnQa58vg9lECDks4kpqRiZmKEm50F98PiCIqMl2KYRPIpkZIsok7SX+ArlbBzhLgh+XxextUcXzyHFyrPwfSig6U9FK6vqga4SNtuZKxNeTGUliL5NDHLoe8ehQKazxJG+snx2nSpt41nV+Ff9vCE+FsysxYeYvmrQvmOUKqNmOuTi7C2C0Q/0W5710ekF3v/pRXDijaCYo2FL9vuURDxEB6chOJN3snpSSRvFLX4ZZZBmmRCjIjCAlVl2XSkFcDU0V1pKdYE/hsnhDCAMu20PmFpsc4jfAtfPBcpla4GosfUxIYKD0yUkLd0xuM+YBwdHZkyZQpPnjzBxcWF5cuXM2CAqNDp7e3NgAEDqFq1KhYWFq99rE2bNpGQkMCmTZsAuHTpEg0bGhAsVVy7dk0nrVDNhAkT6N69O1ZWVkyaNIkJEybQoUMHzMzMNGPs7Ozw9/fn0KFD2Nvba9rVfmjpOXPmDIULF9aIXyAitNQC4Otibm5OYqKuAKxOEU0vapmbm5OamkpycrJOxc34+HisrKw0Ipmh/aXfl0TyskjPMMl7T3CUUP0jXyQRl5jM1J3XmLrjGsduiwuAQo7W5LYWXwgRcdI3TCL5ZEhJhlWfw+xiEP1Ut+/6LlG97tIauLgm432EqdJQbF0NixndNkHXjeDZXevp9OAEhNwQ76UYJnkTOBSCNvOgUF0o3fbdzEGh8hFTGIsoNf+tEBssolU29IR13UTE5IrmQgjLXVCIeE1+FNtc+xc2fSkq5plYQMHaQkgu+4WoiAcQKP1eJB8pWUWGBQcASrDJK9KV05M2WsxQZJhjEd0KxhW6GJ6HQpEmVfJG5nO+fUDMyaUc5HpDRTveA3x8fFi/fj0VK1bE1NSUGTNmEBgYyOrVq7l16xb169fn2bNnr32cGjVq6KwnJSXh4uJCaGio3is5OdmgX9jOnTs5f/48EyYID0lvb2+sra2ZOXOm3liFQkFSUhIlS5YkIiKCiIgIgoODGT16tGY9IiICCwsLUlJSNKmW6ugqQ6mXr4q7uzuBgYE6bYGBgdjZ2eHg4KA3FtBJeUxISCAkJITChQtr+g3tL6M0TYkku0gxTPLeExIj/klHxSfzPC5J47u7zk/8Uyycxxo7S1MxRproSySfDif+gAfHIDEa7h/Ttqemgs8M7brPj5AYa3gfak+WjFLRjE1E5Erb+fDFcmFkHB8hquKBruG+RJKTlPeGXttFxcl3Rd7SUPMb8d6xGHReA7WGg5Ep3NglIiaT4qBIQ1E4otoAqDkEqvYX2/hvFcsmP2hFAYD8qtRMQ358EsnHgCYyLAPPsMz8wgAciojURyMTfYN9ECJXMVVUZe6C2r8pQ2hM9LPwDbup8s8s1jTzcR8w27dvp0CBAlSsWJH9+/djb2/P3r17adOmDaVKlWLevHkEBARQo0YNbt269U7nGhgYSP/+/Zk4cSIuLi4AGBkZsWTJEmbMmJFh9cm0REZG8uOPP2Y6Jjo6GgBr65zLrKlduzZ+fn5ERkZq2g4ePGgwMs7LywtLS0v279+vaTt8+DAKhYI6derg7u5OwYIFdfpv3rzJo0ePMo20k0iyg0yTlLz3pI0Mi4jTXkxceSz+wRbOY83j5y80YyQSySdAyA3hZaQm6BKUU5nOXtsGwf7C1N7CXghXJ+dD3dH6+wlX+YVlx5fJ2FT4K905JNYtc4OV4+uchUTy/tNoivD7yltGVLkr2VL8rZ2YK/y+Cn4m+o3TXFLWGwdXN0NsiKhOWaWf7j7VN+6Pzor0MWPTt3Y6EslbIatqkurvHkMpkCD+nrpvFQ97bF0Mj6naX0RX1hqeeaXa7JjopyTDnYPifbGPN3W5SpUq/PTTT7i4uGBtbY2NjQ1nz55l0aJFXLhwQZMeOXPmTH755Rcdo3w1V65cYcuWLUyePPmlj3/jxg0d/y01UVFROusxMTG0adOGihUr8u23ukVdqlWrxtdff03btm05duxYptFR/v7+ODk5ER8fn2Hq561bt3B0dMTS8uWKJvTq1YsqVaowZMgQvT5vb28mTpzIl19+yeTJkzl9+jSbN2/myJEjAJw8eZJRo0axa9cu7OzsGDRoEN9//z0eHh7Y2NgwbNgwBgwYoIkiGzlyJOPGjcPT05NChQoxYsQIWrVqRblyBoRiieQlkGKY5L1GqVQSEq2KDHuRRKSBNMjCTjbExCcDEPHCgEGpRCL5uIiPhPU9RLUu81yQEAVPr2j7L6hMXasPBqdisLmvMBmu1Bts0vlxqM3zHYtk79gFamrFMKcSmd+ASCQfAwqFtrKqGpdy0H5JxttY2osU40A/qPyl/t+JU3ER9RIfKf523b1yfNoSyTtFU00ygzTJF8/FMrMHKvmyqEDrVExEZGaFJjIskzTJwNPi79HSAfJVznqfHyiurq54eHgwbdo06tWrx/jx4wkMDMTY2BgLCwuGDx9OWFgYffr0YcSIEZrtkpPFfcbPP//MsmXLmDFjRkaHyJQSJUpw9epVvfa0Pl+3bt2iffv2AKxbt85g+uLPP/+Mv78/tWrVYvXq1ZoIqeLFizNw4EDNfoYNG4adnR3u7u60bNmSTp068e233+pUc9y9ezefffbZS59LQECAQY8zEL5ge/bsoX///lStWpVChQqxdu1aqlUTD0JCQkIICAggLi4OOzs7ZsyYwYsXL+jYsSPGxsZ0796d2bNna/Y3ZMgQQkJCGDx4MPHx8Xz++efMmzfvpecskaRHimGS95qoF8kkpogc+oTkVI2ZfloK57HmQZhIgZKRYRLJB8S/X0PkY+iyNvtl3FNTYFMfCL0Btm7Q6jdY2xmeXham+QqFVuAqXA88qouosCfnwfcnaPU/3f1llSaZngK1tO+dimVvG4nkU8TNU7wMYWQkosNu7RM34VIMk3xsJKvEr4w8w+JV6WOW9m9+LnnLqaoh3xLfj4Ye/tzcI5ZFG2VccOYD5+TJk4wdO5abN28yatQo2rVrR3R0NKdOndJUT5wwYQIrVqygb9++5M6dm+XLl+Pp6cnRo0cB8PX15cSJE5Qvn0khgky4fv26XpVF0EaGvXjxgrp161K6dGk2btyoV9VRjYmJCRs3bqRLly60b9+egIAAnjx5wt27d7l79y7169fn3LlzjBgxgilTphAcHMw///zDuHHjePr0KY8ePeKrr77C0tKSBQsWaEz+X4as0jTLli3LyZMnDfa1adOGsLAwzbq5uTkLFixgwYIFBscrFAqmTZvGtGnTXnqeEklmSM8wyXtNcDrx62HYC70xhfPYYGclDfQlkg+KhBhRqe6uD5zRr3SUIbf2CZNfE0shohWuL8y648IgOkikekSqTFhzFxDiWJMfxPq5v/SfjGvSJLMZGeZeCYxVFbukX5hE8urkryqWD1Um+nvGwJzycHi2Nmrm+B8wu6goWiGRfEhoIsPUnmHprk9fRIilhf2bn4tNHijSQLw3VFAmJRkubxDvS7d58/N5R6SkpNC1a1fu37/PsGHDKF26NLly5eL3339n8ODBAOTJk4fRo0dz69YtRo4cqYmgKl26NBMmTMDPz++VhTCAkiVLGjTQV4telpaWbN++nb1795I7d+5M92VjY8P27dvx8/PDzc2NlStXMn/+fMLCwhg8eDCPHj1i6tSpKBQK8ubNy8iRI7l8+TL//vsvUVFRPHjwAKVSSd++fWncuPErn5NE8iEjI8Mk7zXBqhRJNQ/CdU2wnWzMsLM01Rjoy8gwieQDIeKh9v3R38CrF1gYfgKqgzpFsWI3bdRJnhIQfA2CLoNzSVCmCMHKRuWzUrAWlGgBN3YLnzHvFaI9Llx70+1QiGxhYi6enN/YBfmrZ28biUSiT4HaYnl9F+wdJ8z4AXx+gPN/QYc/4dB0EVGzbTAMOiE8y+KjhChubguunmCb952dgkSSIZrIsAwM9OMjxPJtiGEAnt3Eg6RLa8V3YuAZUezCzFq0xzwTKZsfsXl+7dq1qV27tmb9xYsXOlUV02JsbEy3bt0063nz5uWHH34wuN/Vq1dneMx69epx6pQQ/CtXrmwwRRIgIiJC875y5eynqSoUCkqUEA/msps2WKNGDZ1Kl7/99lu2jyeRfGzIyDDJe036yLDA8DgAijmLsPMK+ewBsJfVJCWSD4uIB9r3L8Lh1MLsbXfXVywL19e2qSttPb0Mz1X7tc8vUrHU1BdlyfHfqo0OU6dI2rqKG4Ls0nYBDDiq76MkkUiyj0d1KN0WUpPglCo1ppw32HtAZCCsaK4VEJ7fgwOTIfQ2LG8ifADXdIQ/KoqbeonkfSMlnYF+arKodKxGnSZpYfd25lOihRDeoh7D3+3AdwYcmCr61D6b5TuDidnbmc97giEhTCKRfDpIMUzyXhOSPjIsTIhhn3u6sapPVX5qL26C7ayEGBYhxTCJ5MNALVqpn4qf/VP3RsEQkY8h9KbwPimofbqLiypl4ellrchmX0B3W5eyouIdSjjyi2jT+IVlM0VSjaU9uL56moREIkGkMLddKKK7QHiItV0E3TaL1LLUZJEC3Uh1w+63BOZVgpAAsM4DuQtCUizsGCZS0CIewt3DIu0yq/8lEsmbRh0ZpjbQBzj6C+wcKfwtX7xFzzAAUwso31G37cxSuLwRbu4V6xW76W8nkUgkHzEyTVLyXhMcpSuGqdMm7azMqFNcWwnFXqZJSiQfFuo0yfIdhYdJzFMIumjYSPtFBARdEtEiAG5eujcQ6siwoEuiwiMIv7D01BkF13fC1U1Qb6w25dI5g9L2EonkzWJmBT22iojNMu3A2ATyFIcvlsLGL6HGYKg9XIz1WyKiWvKUFJUqTa1gXmUI9oe5lXSjTT27Q5u5utGhhlAqIemFmIdEkpNoIsNstW0+P4pltYGQoI4Ms397c6o7FpSpULSxSJe8tg229BN9+apC3jJvby4SiUTyHiAjwyTvNcHRCRRRPGab2STqGF3StKvFLzVpPcNSU5VvdY4SieQVUN+4OhXXGvve2GN47K5vYVUb2PWdWC9cT7ffzVNEi0U81Jpxp48MU48r3kzcDPw3Aa5uFu2eXV/jRCQSyWth5QBV+oqlmhLNYfwTaPi9WK89HEb4w/Ar8NVhkQZt7QhNVeJCxAMwMgHHYuJ/wcXVsKGHELyTE0Tk2KmFcGCK1sg8NRW29IcZbvDvEIh+9jbPWvIxo1RqU3zTRoapCbutff+20iRB/M20/BVKNBN/O+q5lf4cOv719ubxgREeHs7UqVNJzSDitHv37owdOzbD7Z88ecKDBw8y7M+M0NBQjh07xvLly1m4UN9OYvjw4UyZMuWV9i2RSKQYJnnPCYlOoLfxf3ga3aGL8SFNu72VrhiWSyWGKZUQnZD8VucokUhegedp0hlLtBDvMxLD1JXkklXVZNOLYRZ24FpBNfaYWOYuaHhfdUaL5c094mbFvZJ4SSSS94v0UV0KhfATM7XQtlXoIqrFNpwMI67BN2fhi2UivfL6TuGNNLMQ/OEFe8fCsf9pRfBD0+HKRkApPJOW1IOE6Ld1dpKPmeQ0WQ2mFuL3MS1ht1R9Vu/Oo8suHww4AgOPQ8dVkMvt3czjLbNo0SIcHBzw9PTUvFxcXHBxcdFps7GxYe/evZrt1qxZw8CBA1/pmCNGjGDatGkAxMTEEBERYfAVFRUFwJgxY6hZsyaOjo7kyZOHzz77jH79+jF58uTX/wBUTJs2DYVCwbFjx/T6Ll26hJeXl8E+gLt379KoUSO9wgEXLlygfv362Nvb4+TkRI8ePQgPD9f0FyxYEIVCofdq2LBhlvPt06cPCoWCR48eadrmzp1LiRIlsLS0pGjRoixatCi7py+RaJBimOS9Jjg6nupGAQC4KsI07faWuhcPFqbGWJiKX2dpoi+RvOcolWm8vTygWBMRzfHsim6VSYCYYIh+AihECmSeUpC/qv4+03qIgeE0SYB8laBImguvql+98mlIJJJ3jEIBNb+Bz0Zqq0qW/QK+3A0Ve4iKskmxEPlQ/I8BuPgPBOyEY6oKanXHQK584v/MlY3v5jwkHxcpacQwY3MwTid4hd4Uy7eZImkIxyLCT/MTo02bNly8eFHzGjhwIAMHDtRpS1vR0cHBgZ07d7J+/XrWrVv3Usc6e/Yshw4dYubMmYCoLpk7d26Dr9KlSwOicmXNmjUZN24cjo6O/PPPP9y/f5+goCDNPtIKdS9LcHAwv/zyi177+fPn6dixIzVq1ODChQt6/bdv36Zfv354enpy8OBBvf5bt27Rpk0bfHx8WLFiBb6+vgwaNEjTf/jwYW7duqV5BQQE4OzsTOfOnTOd79WrV1m1apVe+507d5g9ezanTp2id+/eDBo0iD17MnioKpFkgPQMk7zXpEQ/o5jRYwBcFdqnC+kjw0AIZE+T4omISyK/g163RCJ5lyQnwo3dooR76baQIJ6AYu8h/HryV4eHJ+DGXqiWRqAKUqVHOxWDr08LIc2QD1DBz+DEXO26oTRJNXXHwJ2DYJNXzEUikXxceFQXr9RUUVgj7Lb4H7K4Ltw7oq0oW2MI1B8P5rlg3wQ4u0KkUt/YDeU6gkWud3sekg8TtXk+CCHM2Ewb2QyiKiq83RRJiYZdu3ZRvXp1zbo62iitwHTt2jWdbYoVK4avry/ly2e/eE5ycjJDhgzh119/xdTUlFWrVnH69GlNBcuxY8cSGhrKsmXLdLYbOXKk5v28efPw8vKiQIFMrmlekhEjRtC0aVM2bdqk075lyxbMzc3ZuXOnwWitAwcOEBISwpEjR6hYsaJef8eO2gINFStW5Pr16/z000+atvTnsHz5ciwtLendu3eGc1UqlQwaNIh27drpzXfOnDma9xUqVGDt2rXs37+f5s2bZ7g/iSQ9UgyTvLfEJ6VQNvEKqB6o5SESU5JJwkRTPTItdpamPI2Klyb6Esn7RtILWFgLwu+I9Wv/iqW1s9a4ulhjIYbdO5xODLsolq6eIgpEoTB8DI/qIvJDmSpubC1zZzwfj2rQ5z9RkS5typVEIvm4MDISXoFunmK9UB3xPybmmYgGqz9BtHt2hYPThHC2sBa8CIdbB6DL2oz/50gkGRGj8p+zdBC/g8bprlnVaZJvq5KkRIeWLVuycuVKzbracyut91a9evU076OiokhNTaVQoUIYZVWUIw0//PADrq6u9OzZkx49evD8+XN69uyZ6Tbh4eF8//33Ous//PAD9vb2gK5Qlp7Ro0dz4MABzp8/n+GYPXv2cPjwYfbu3asnLk2fPh2FQsH9+/cNbjtgwICXShVNSUnB0dHRYF9ycjI//vgj48ePx9RU/55OzaJFi4iLi2PQoEF6832Z40kkGSHFMMl7S0h0AjWMtE9mjBRK8iqeE6RwxtZc/1dXbaJ/41k0m88/4qs6hSnlKp/qSiTvnEA/IYSZWoMyRTcqTI06zfHBcYiPhOVNIJc7mFqKdrUnWEZY2AnB7Ml5ERWW1Q2sR/XM+yUSyceHZzchhgE0ma4V460chIn4lQ1CCAPhK3h1M5Tr8G7mKvlweX5fLNXelenTJONUth/vOk3yE2X79u14enqSmpqKv78/9vb25M+fn23btmnG3L6tLXJQo0YNwsLCePbsGUFBQUyfPp3Fixdr+tXG+mlTD7///nsWLVpEfHw8jo6OmJqacvny5SznZmpqSsmSosL19evXiY6OplChQuTNK9LALS0tM9w2X758lC2bcdpraGgoffr0Yf78+djY6Bd2UGRx3ZRVv5qkpCROnDjB3LlzNV5p6dm0aRMRERH06NEjw/3cuHGDsWPHsn//fuLi4jIcFxkZybx584iMjOTLL7/M1hwlEjVSDJO8t4TEJFDdSDdM2ZUw4izdtf+QA3bAoR/gs++wsyoEwO8HbhIVn0x4bCJ/9THgLSSRSN4u6gqPJZqDmTWcV1WtSuvr5VZRmAm/eA6HZ0HIdfFSGw9nJYaBENSenM/YL0wikXzalG4Dl9YKob1MO92+6gOF+JW3jEi7PjUf9owWUasynU3yMqg9MdXfRekjw9TI36u3jre3N6VKleLcuXP4+fkxcuRIvL29ATh69Chz5sxh/PjxuLm5aQQof39/QCsGzZ8/n/nz52v22b17d/Lly8fPP/+sc6xhw4YRFRVFlSpV+Ouvv3B2ds5yfra2tgwZMgSADh2EEF+3bl0aNmyYpRg1dOjQDPuUSiW9e/emSZMmtG/fPsPor9elSZMmHDhwAIDvvvuOXr16GRy3YMECvvzyywzFvfj4eLp06cLw4cOpWrUqvr6+emMePHhA8eLFSUxMxMXFhVWrVuHm9mkUgpDkHNJAX/LeEh38kCJGQaRgRJxDKUCY6Ncyu62tLnfkF3HDvKUfnaP+BCAqXlSTPHknjFhZWVIiefc8PCmWHtWhSl9te9rIMGNTyF9NvD+dpiKQMkUsXcplfZzqg6BUa6iZ8QWhRCL5hDG1hJ7boO18/ehR90ow8hr094FGUyB3IRHBc8fnXcxU8iGTtloy6EeGqZFpkm8dR0dHpkyZwuLFiwkKCmL58uU4ODjg4ODA6tWrGTBgAFWrVqVo0aLY2tq+1rEsLCzo2rUr33zzDU2bNn2pbS9dusT169fJnz8/I0aMoHv37iQlvboNzMSJE7lz546OiPcmWLZsGefOnWPt2rXs27ePhg0bkpysey927do1jh49Sr9+/TLcT79+/bCzs9NJGU2Pm5sbly5d4vjx4wwbNoy2bdvKipKSl0aKYZL3Fps7uwC4Z16KZCchhhUzeswvLybDylZw55DKT0hc0DYM/YfiikDN9okpqRy9FfK2py2RSNKSkgyPzoj3HtVFhFc+VcRmnpK6YwvWEstU1YWTqSqFKXeh7N005HKDTquFJ5hEIpG8LLYuYGwCJmZQtJFoU4v5Ekl2SZ8maZRBIo6MDHsn+Pj4sH79eipWrIipqSkzZswgMDCQ1atXc+vWLerXr8+zZ89e+zgDBgzg0qVL3Llzh7p16+Lq6kp8fHyW26WmpjJs2DAmTZqEkZERK1eu5PHjx7Rt2zbTdMHMmDFjBrdv38bJyQkLCwtKlCgBQMOGDWnSpMkr7dMQHh4eVKxYkU6dOrF161aOHDnCoUOHdMZs3LiRUqVKUapUKYP7ePDgAf/88w/Hjx/H2toaCwsLzRyLFi1K//79AW1Kac2aNRk7dizfffddhmmZEklGyDRJydshMVaYaFs7ZXsTt0e7Abjm0Ag3e9HWwug05iSAEtjURzQW+kxU7gk8RQlFIDeV+TX72H8tmGZlXXPoJCQSyUsT7A+JMcLU3lmUDcd7BdzYA2W/0B1b8DPtezcvkZ50eKb095JIJG+fAjXgzFJtJPq7IjkBHp8DjxrSzP9DIX2apNonMz3SM+yts337dr755hsePnxI6dKlKVGiBHv37mXjxo1ERkYSGxtLVFQUNWrU4L///qNYsWKvfKwSJUrQv39/ihcvTvfu3SlevDgWFlkX7Zk+fTqRkZF4e3szZswYrK2t2bFjB+PGjUOpVL7SXAICAnTWHz9+TKNGjVixYgW1a9d+pX1mhYmJkBlSUlJ02rdu3Urbtm0z3M7NzU1vvn5+fvTq1Yv//vuP4sWLZ3i89MeSSLJCimGSt8P6HuLp6pAzYJcv6/HP7+MafYUUpYJHbk0xdRTeYUWMgrRjXjwXyzLtxIVi4CkKK0S/Sy4LnkbF43MjmJRUJcZG8gJSInknqP3C8lcFI5X/l10+qNpff6ybF5hYihL0Zb+AagPBqTgUqvv25iuRSCQAHjXF8tlViI8Ci3dUkGfbIOFl9vl8qNj93cxBkn2USv00yZhgbb+xGaQkivcyTfKtU6VKFX766SdcXFywtrbGxsaGs2fPsmjRIi5cuKARq2bOnMkvv/yiY5Sv5sqVK2zZsoXJkydneqxx48YBohrlqVOnWL58OUOHDiV37oyrXUdERDBr1ixOnjypU7nS1taWefPmAdCuXTsKFND3Rp03bx5nzpzhr7/+0utTm/KrUZ+nh4cHHh4eeuNfhSFDhlCvXj1KlChBUFAQkyZNomjRotSvX1/n/C5dusRPP/2U6fzTz/fp06cAFClSBFdXVwICAli4cCHt2rXD0dGREydOMGvWLIYNG5Yj5yL5dJBpkpI3z4sIkdKYFAeBp7O3zdUtAJxMLY2lgxum9vl1+xVG2mXJ1uBYFIBCKrGsd62C2FmaEh6byMXAiBw4CYlE8kqk9QvLChMzqPG1EMUqdBHpSuU6gE2eNztHiUQiSU8uV5HmpkwVFXFzmucPIPR25mMenxNCGMC17Tk/B0nOExMsHuigADvVtWtqGq8n6zTfZzJN8q3j6uqKh4cHP//8MwcPHqRMmTLkzZsXY2NjLCwsGD58OD169KBq1arMnTtXs53a9+rnn3+mRo0amQpaIAzrBw0aRLly5XB0dGTUqFEaQSc+Pp74+HhSUlJISUnRrMfHx2Nra8uOHTsoX758hvseNmyYJsUwbaTYgwcP9CKq3iZubm6MGjWKKlWq0L9/fzw9PTly5IhONNy5c+cAqFixot72LzN/R0dH7t27h7e3NzVq1GDhwoXMnj2b6dOn58zJSD4ZZGSY5M0T6IfIayTrCz8QHkMX1wCwI7UmtW3Mwc5dd0yd0XD4ZyjeTNwoO4owZnVkWLVCDhy9FcLx22E8eh5HpQKZf2lJJJI3gFKpjQzzqJG9bRpOEi+JRCJ513jUFP5PD09AsUY5s89HZ2HXSAi6JB7ofT4fynlD2G1wKgHqaBClEg5M0W53/ygkxYOpBVzfLSpp1x2lXxVT8m5Rp0ja5RMPeNJj7QRRj8V7mSb5Vjl58iRjx47l5s2bjBo1inbt2hEdHc2pU6fIlUtEfk6YMIEVK1bQt29fcufOzfLly/H09OTo0aMA+Pr6cuLEiUzFKhCVJ4sVK0bTpk2pW7cuuXPnZsqUKTg4OOiNXblypeb9woULGThwYKb7joqKIiAgAHNzc44ePUqPHj0AmD17drY/i4IFC2aYcplZnxpD/ePHj2f8+PGZbtewYcMM953Z/OvVq6eznbOzMzt27Mj0WBJJdpBimOTNk9Z8NvRm1uPPLIOwW0Riy56UqrS1MRdl0FUkG5lj8tm3okS6+qmbkxDDCimeYm6ioIybHbbmopR11ItXr74ikbwVbuyB/yZAo8lQ+vN3PZucI+IhRAcJ82A3r3c9G4lEInk5CtSES2ty1jfMZ4YQwkBEnW0bBPsmisqVNYZA0x9F343dcO+ISKszs4EX4eJ6ytQKNvaGlATYOgjylALnkhkeTvKWSZ8imRYzG7B21q7LNMm3SkpKCl27dqV3796YmJhgY2NDfHw89vb2/P333wDkyZOH0aNH8+2337Ju3Try5BGRfKVLl2bChAl8//33mJllUB00HSNHjtRZnzhxImPHjs10G1NT0yz3Gx8fT/PmzQHhS9a5c+dszUcikegjxTDJmyetGBZ2K/OxMcHgIy4Ef6czUVjjZGMGljYkYI45CUTYl8HJxAzyltFul7sgSoURtrxgYl0HzEyMyGUpfr2j4pMNHUkieX84vwrC78CGntB5DZRs+a5nlDOoo8JcPcHM6p1ORSKRSF6aQqqiHg9Piij3/FUzHpv0Aq7vEq88JeCzb8E43Y1tQoyI8AL4yhcurgW/xUIIAzi9CKr0A1NL2P6NaKs+GGJD4eJq8Fsq5pKSoPVX3NQH+h8SEWOSd4+mkqQBMcypuEyTfIfUrl1bxyz+xYsXpKSkYGxsrDfW2NiYbt26adbz5s3LDz/8YHC/q1evztbxTUxMNKby2eX+/ft6bc7OzoSHh7/UfiQSiWGkZ5jkzZIULzwv1ITeFqH/AIlxEBGoO/7UQkiIItWlAivjhWm2k405KBQk2YiqkDZFauofx8QcheopXI+iIhLM1kIVGRYvI8Mk7zmpaQTbjb1FRJWaE/NgfnWICtLb7L3nZfzCJBKJ5H0jd0Gtaf3OkcLGwRDxkbC0IWzuC/5bwPcnWNkSop7ojrt3WJin23uIhwTNZ4L3Sui6AQrXF98Fe0aLByNxYeBSDuqPh6INxfY3dokIMTcvGHwSrJxExd4AmS703hBxXyzTRoZ1Wi1+Zl8s0/XAlGmS7xxDQphEIvl0kGKY5M3y5IK48LNyBIUxJMUKv4x/vGFmQZhTFi78ox3/zB+A6DLdSMUIEyMFdpZC1LLxqACARYn66Y8iUJnoE3YLnl7B3kxUkIyWkWGS9534NGXXUxLhyibxPikeDs+EkACRMvOh8bJ+YRKJRPK+0WgaWOaGZ1dg20DxgC9gB/hvg9Bb4kHFpj5ClLJygqpfgbmdKBi0spVuJcGb/4ll8WagUIhXmXZQvKlIkwe4tU9sa2oFX/wJJuZQuJ62cFDectB9MzgUAq+eqv3uFcvnDyA58W18KpKMUKdJ5i6obSvVGr7yAcci2jRJIxMws37r05NIJBKJFimGSd4s6siQArXEhRvAjqHiYi8lQazvHK6t1KQKLw8zEx5hjjZmGBkJUYtWc6DXTijS0PCxVL5hHJgKi2rTyf8rchMlxTDJ+098hFiW7SCW/qKaKrf2QYJKKAvLRvGJ94kXz4WIBzIyTCKRfLhYO0LTGeL9lY2wtAGs7w4be8G8yvBbSbh9QKQtdt8MLWbDAF+w8xDp73+3Ew88lEq4tV/sp1hT/eO4VYRKXwqPsPKdod9ByFNc9Fk5QO2RQkTruU2sg1gHuL0f/LfC7xVgUW0IyYY/qyTnUSohWPW9p35Amx51mqSFnRBDJRKJRPLOkGKY5M3y7KpYulfSVHwk+JpYfrFcPC1LSYQNvcTTTFUVnmfGLgA4Wptr92XlIPw7Mrp4UF94qIQF58jLbDKbSkpMWE6ekUSS87yIEMuK3cXT4qdXRErxlY3aMdkpPvE+oRa4HYuJ6lkSiUTyoeLZFb7cI1IZzWzAtYJIezO1ElHvNnmhw3Jw8xTjHQoL0comr7gOOvYbPDkP0U/ENgVrGz5Oq//BxGBovxjyltbtazgJuq7X/X+arzJYOog0ze1DASWE3oCl9bXCW+Qj4WcmefNEPYG4UPE7kf7np8Y2r1ha6lcVlEgkEsnbRRroS94s6mgWp+LiAkF9P2/jAqXbiqeadw+LC8T7RyA5HhTGPE51AJ7hZGuewY4NkPYpXOW+vPDfTZEXQdSJ2ALkUEl0ieRNoI4Mcygk0mFuHxBVVdUpNSDScT4UHp+DvePEe49q73YuEolEkhMUqCkEruziWEREtK/rAqcWacWp4s0yNrt/2UghI2Mo1gQurxNRxBb2kLcsPDgGazuLqPx7h8GpBPTZK75rQm6IbYykV1KOo64SmqekKIJgiAK1oFJv8V0v+WB5/PgxFStWpGLFivz3339Zb6Di7t27eHl50aJFC9asWZPp2H/++YekpCR69+79mrOVSCQZISPDJG8OpRLC7oj3jkWFIKamfEcwNgFzG21VyOsqTyS7fITECZN9J5vslS8GIH81KFRX+HW0+IXASqMBaBi3N2PTW4nkXZMUL0RgEDcyZdqL96cXilRiWzexHvFQjH2fSU2BI7NheRORHmTrBrWGv+tZSSQSybuhRHPIX11UfXx2VfyPb/pjzh6jeJqUy5pDoMdWKN9JmPHfOyzaQ2+I/8vzqwuRbHFd2DIA5pSDs3/m7HxehkM/atNIX5a4cPD9GcLv5fy8XhW1GOZaIeMxxqbQ+nfhFSd556xcuRKFQmHwNXz4cIPbREdH88UXX+Do6MiRI0eYM2dOto4VFxdHp06diImJYe3atWzdujXT8UWKFOHnn3/WrB8/fhwnJyecnJwwNzfH1tZWs3769OnsnjK+vr4ULlyYR48e6bT/+eefep/BkCFDdMYsXLiQQoUKYWlpSYMGDbh7966mL6PPsW/fvpoxSqWSefPmUaJECczNzfHw8OD69es6x1i3bh0VKlTAwsICFxcXfHx8NH2BgYG0atUKa2tr3Nzc+OWXXzI8z8uXL9OkSROsrKxwcXHhyy+/JCxMmy106NAhvbm2atUq25+j5ONAimGSN0f0U0iMEeHiuQtq0yRBpByocVaFkqsNwnMXJDRG+InlsXmJyDBTC+i1Xfh1GBmRVLwVocpc5FGGac1lJZL3DXVUGAowzwWl24gLaVMrMLMVpsoWdoBSCEzvMxt7waEfxE1YmXYw6LjWy08ikUg+NRQKaDxVu976d8jllrPHKNpQFCmydYWqA8DEDNougoaThQdZ57XC0D/slnjAYmQqigFcXiceshyeDampOTun7PDoHByZBXcO6VoCZJeL/4iqnYdnvt48ggMgIeb19qEmO2KY5J3Rt29fTExMMDIywtjYGBMTEw4fPsznn3+OUqnUeU2ePNngPkJDQ2nYsCERERH4+Piwbds2xo0bx48/Zi5yx8fH8/nnn+Pv78+hQ4fo1asX3bt358CBA3pjq1evTokSJejXrx8AZcuWxc3NjX379hEaGkpoaCgVK1Zk+/btmvVq1bKOwvfx8aFZs2Y0a9aMe/f0ReTw8HCqVavGrVu3NK8pU6Zo+jdu3MiIESOYNm0ax44dIykpiTZt2pCq+v+Rdrtbt27h5+eHubk5nTt31uxj0qRJTJ8+nYkTJ3L+/HnmzZuHjY2Npn/ZsmX069eP/v37c/bsWVatWkXevCK1OCUlhZYtW5KcnMzRo0eZOnUqY8aMYcOGDQbPd8iQIdSrV49Tp06xfPlyDh8+TM+ePXXO193dXWfOS5cuzfJzlHxcyDRJyZtDnSKZu4C4OHOrKKK3HAqDcyntOLWvQnSQarxWDHN8mciwdNha27AxpS6DTHbA6UUiNeHKBrj2LzSaCs4lX3nfEkmOofYLs7ADIyMwt4UBR3TH+C2Fx2dFqqQ6kvJ9I/KxqLCmMILPF0CFztIcWCKRSDyqQ5u5oEyFMm1zfv8WdjD4tEh7tMgl2oyM4LOR2jHdNoqo3XIdoGgjOLUAkhPg/CphUxF4Ggq8xaq/SiXs/167fnkDVOmb8XhDRDwUS7UP7atwZRNs7iv8azutfvX9qFGLYWrvOMl7xfLly1m+fDmNGjVi4MCBdOjQgZUrV7Jt2zYA/Pz8SExMpHZtw55+hw4dokePHhQpUoRdu3aRJ08eXFxc2L59Ox07duTs2bMsXLgQFxcXne2Cg4Pp0KEDFy5cYMuWLdSpU4datWqRlJRE69atWbJkCT169NCMNzMzY/78+fz777/MnTsXgD/++IPIyEiePXuGUqnkxo0bODs78/TpU0xNTXF0dMzy/P/++2+KFClCnz596NSpk16/WhwqWtRw8YeffvqJAQMGaOa6dOlSSpUqxeHDh6lfv77edpMmTaJSpUo0btwYgOvXr/Pzzz9z8OBB6tatC0CZMtpr2vDwcEaOHMn8+fPp1asXIIRANbt27eL69escOHAAZ2dnvLy8OHjwIPPnz6djx4568/3nn3/Inz8/AOXLlycyMpIePXoQFxeHlZUV4eHh5M2bN8PzlXwaSDFM8uZQi2FqLy9TC+i7T3+cc7qb+9wFCAsWpcGdXiYyLB22Fib8k9KQr4x3Ynz/KMwpqxXcIgJFmWuTV9+/RJIjqCPDLO0zHuNUTIhhYe+xb9g9lYDn6gmeXd7pVCQSieS9wqtn1mNeB5s8mfd7VIPum7TrDVVCVGyoiBDz32pYDLt3VDxEDLkJruWh2c854zV28z/ha2ZsDqlJEHhKCFPnV0HNoVAsGz6v0U/FMvSWiGwzeslkl4Ro+G+8eH99l9ifrUvm22RGTLAQFlEI3zbJB8fu3buJiIjQE8OCgoKYOHEif//9N6NGjWLKlCmYmppq+hs3bsyFCxfo2LEjpUqVYuTIkQwdOhQ7OzsOHz5Mjx49MDY25ujRo5QtW5bkZGHd8tdff1GiRAl69erFvn37mD17Ni4uLhw5coROnTqxf/9+TYpg4cKF2b59O2ZmZnh5eVGiRAn69u1LQkICSqWSixcvMnr0aA4cOMD58+cNnt/y5ctRKBT4+voa7A8PD8fJyXDBo4iICC5cuMCMGTM0bSVLlsTV1ZVTp05Rv359nfHPnz/njz/+YONGbdTnqlWrqFixokYIS8+mTZuwsbGhW7duBvt9fHzw8vLC2dlZ09agQQOGDh2KUqlEke4BrFoIU2NhYaGJYsvqfCWfDjJNUvLmSC+GZUTaKDHQiQx7XTHskdKZ4Ulfk2phL4QwhZFIPQv2h8OzXnnfEkmOoYkMs894jDrV8H020VeLYYXqvNt5SCQSiSR7lFV5VF7bJjwfQVT2Dr0tIrf+aiUEqsBT4LcE9owRUV2vg1Ip0iMBqg8UXq8gIrTuHQafH7K3n5hnYpkUB1GPMh9riCO/aPehTH21VM20qKPCnIoJP1zJB0l6QQXg888/5/r16/z444/MmDEDMzMzPa+pQoUKERISwk8//cTChQvZv38/gwcPpkGDBtStW5f79+9TsWJFTE1NdV6TJ0+mdevWXLlyhaJFi3Ls2DE2bdpEYGAg9+/f5+rVq3h5eVGuXDlARI2dOnWKP/74g1WrVumITfny5dOJpMrOuaUlLCyM5cuXY2trS/ny5Zk1axZJSUkAmrTKQoUK6Wzj4eGh5z0GsHjxYtzd3WnSpImm7dSpU5QvX55vv/0WZ2dnihcvzq+//opS9T/l1KlTlCtXjl9//ZV8+fJRsGBBxo0bp5nD3bt3DR4/ISGB0NDQTM9NqVSyfPlyqlWrhpWVleZ8Dxw4gLW1NaVKlWLs2LHExORQyrTkg0FGhkneHNkVwyztIVc+7cVM7oKExoQDryeGmRgbYWVmzI7Emozu9hX576wTN+qxwbChJxz7H3j1EH5mEsm7Ij5SLDOLDHN8z8UwpVJr1FzY8BM/iUQikbxnFK4v/MRinsH2oaKw0eWNkBSrHVOhq6h07DMDziwFlNB4GphZv9oxH5wQFYdNLKDGN6J68l2tQTZPLghTfIdCGe8DtJH+ICLX7D2yP4eEGJEqClCiJdzYBZfWQc1vsr+P9DxWReNIv7D3lsmTJ/Pjjz+SmpqKj48PCoWCX3/9VdOfmJiIubn+fcfOnTvJkycPCoWCUaNGadobNWpEv379dDyxAL788kvMzc0JCQnh8OHD1K5dm7///lvT36pVK9q2bavxBAPhh7VmzRqqVatGo0aN+OabbyhbtiyfffYZN2/eZPHixTrHWLNmDaVLl6ZBgwaatqFDh776hwNMnTqVCRMmkJCQwMGDB5k8eTLBwcH88ssvGpFILSSpsbKyIiEhQactNTWVxYsXM3LkSJ32oKAg/P396dOnD7t37+bo0aOMGjUKR0dHevfuTVBQEBcvXsTd3Z0tW7bg7+/PN998g7m5OVOmTCEmJkYvkks9n/RzSEtSUhKDBw/Gx8eHI0e0NiQDBw6kS5cuKJVKTp48yffff8/t27fZtGlThvuSfHzIyDDJm0N9456VGAZa3zAgxa4g4bGqNEnbV/cMAxEdBhBpZA/1xog0gNKfQ8HPQJkiwvIlkneJOk0y08gwVSXWp5dhUW3Y3B8u/AMpSW96dtkj/C5EPQZjM1E5TSKRSCTvPyZmUEHlHXRxNZxbKYQwUysh6nRcBe0WQt3R0FxlVH9mGSysqa0W/rKc+EMsK3QR6Z2l20C+KsK3y0OVqnltW+b7UCoh+pl2PfTmy80h5DqkJIK1M7SdL767nl2Fp1debj9pua0yQvd4i95rkpdi6tSpJCcn06BBA9avX09ycjJ2dnaa/oiICOzt7fW2c3Z2zjKqKi1qQW3AgAFUr16d5ORknZdSqSQ1NVWvrWvXrpiamuLr60uzZs2oUqUKfn5+PH78mAULFvDixQvNMZRKJSYmORvTUrp0aTw9PalWrRrjx49n/PjxLFq0CKVSqTmnxMREnW3i4+P1BLK9e/fy5MkTHR80gOTkZMqUKcNPP/1E5cqVGTFiBO3atWPVqlWafltbW5YtW0bVqlX58ssvGTRokKbf3Nzc4PFBX6RT8+jRI+rVq8fOnTs5dOgQlStX1vQVKVIELy8vKlWqxJAhQ/j999/ZvHkzISEhL/vRST5gpBgmyXn2jIX1PeD5fbGenWpy6oqS5rkIT7UmVSm8tx2sXk8My2UhcvqjXqQTDSqonuJc2fj6If8SyeugTpPMLDLMobC4aE9NFhfrVzbAv4OFsf77wF1fscxXFcwMX5BIJBKJ5D2k2c/QdSNU6g0Vu0PvXTD+iSjkUvpz7bhqA6DbZhHJ//y+uM5LUt2cK5Xa95nx4KSqurdCG4VlZg39DggD+/IqYc5/a+b7iY8QlTHVhN6AF8+136dZERwgls6lwDI3FFOlct3an73t0xMbCo/OiPfFm73aPiTvnJs3b5InTx5SUlL0+gYOHKiXGnnw4EG6dOmi13716lUApk2bppcWaWpqyu7duxkwYIBeu7m5ObGxsYwYMQJPT088PT0JCAjgwIEDHDx4UCclMiYmBmvrV4zOzCZeXl7ExsYSFhaGu7s7AIGBgTpjAgMDKVy4sE7bxo0badiwoZ6w6OzsrGdWX7x4cZ49e6bpL1y4MEZp/P/S9ru7uxs8vp2dHQ4ODnrzv3nzJtWqVcPW1pZLly5RvXrmD2u9vLwAePDgQabjJB8XUgyT5CzxUXB6IQRsF5FXplai3HdWqM1GcxckLE6o/rmtzDAxfr1fUXVkWFR8sm5HyVbiSWDIdRHafv/Y+xNlI/m0yE5kmIkZDD4JvXZC1w1Qzlu0B2x/07PLHtIvTCKRSD5MjIyheBNo/Tt8Ph8K1s64EnCxRtD/IFjnEd6rqz6HVW3hl2Lwo4uIGkvPHR84OF1Ena1ViV3lvMGxiP7YUq1BYSz8tzKLPEsbFQbw6CwsqCEi1pLisz7nkOtiqfasVV+Dht/NeltD3NoPKMGlPNi5v9o+JG+dqKgoypUrx5gxY4iJieHKlStMmDCBW7duMXToUCZOnKgZu2jRIsaOHcuJEydQKpUolUoaNmzI2rVrUSqVpKSkMHDgQPz8/DS+XVOmTNGMPX36tOZ9y5YtWbp0KUqlkgsXLmjak5OTsba2pnz58hgbG/PPP/9QtmxZ2rZtS4kSJejZU1uI4/Hjx3pVK3MaPz8/cufOjYODA+7u7hQsWJD9+7WC8c2bN3n06BENGzbUtKWkpLBjxw7atm2rt7+aNWty6tQpnTZ/f3+KFy+u6b948aIm2it9f+3atfHz8yMyMlLTf/DgQZ3jp6Vr167UqFGD3bt365juZ3a+RkZGer5kko8bKYZJcpbn93TXnUtlfFGVlpItwasXNPye0Gh1JcnXiwoDsFVFhkXHpxO6LO21TwL/bAorW8LG3sJA9vgfcO6v1z62RJItshMZBmDtBIU+g+JNodEU0RZ4WjyRfpekpsL9o+K99AuTSCSSjxtbF/hiGaAQ30F3fSBWlVa0d5wQsuKjIGAHrOkEf7eFo7/AjmHCIzN/NSG8GcLaCQrXE+8Pz8x4Dmq/MIWqsuWzq6It6jEEXcz6HNSRYXlKiqXan0yd0fCy3NwrljIq7L0nOTmZuLg4fv75Z1xcXLh37x41atTgt99+o1OnTlhYWADg4ODAnj17mD9/vmbbmjVr0rJlS44ePaqzT6VSyZAhQ7h586bG6D4tixYtolmzZjx58kSnPTY2Fm9vb3r37q0jAPXp04fExESuX7/O9evXWbJkiSZ1r3HjxsTFxXHq1CkiIiKwtramVq1aAMybN49evXq98mfzzTffsHfvXi5dusSvv/7KrFmzGDt2rCZSa+TIkfz+++9s3LiRs2fP0rdvX1q1aqVzzleuXCEsLIzPPvtMb/+DBw/mzp07DB06lPPnzzN79mx27NjBt99+C0D37t0xNjamV69enD17luXLl7N48WKNT5u3tzeOjo58+eWXXLp0iSVLlrB582ZGjx4NwMmTJ6lduzaRkZHcvHmTc+fO0blzZ+7evcvt27c1L7WYNmHCBLZt28bly5dZunQp3377LQMGDMDR0fGVP0PJh4c00JfkLOqnai7lRbh9dr0TzKygjfCRCL3wGHg983w1uSxVaZLpI8NAPJm8vlOU9QbxfkldrWeEuxe46H+pSSQ5SnYiw9Jjl0/8jT29LFIlr26GgrUyvsF4kwT7Q1wYmFqDm9fbP75EIpFI3i6F60GXtRB0GXK5CVHp+BxxHfVnc0h+ISo0AhiZiAeeYXfBygE6/pV5On2DiXDnEFxeLx6SFqylP0ZdBdLdS5ueqObhKfDIwrsyfWRY7tcQw5ITxXxBimHvObNmzWLmzJkkJibi7e3N//73Pz777DN27tzJihUrOH/+PNu3ayPuAwMDuXHjBl9//TUArVu3Zt68eZrqhmpiY2OxtbVl+/btGjFNzebNmxk1ahS7d+/Gzc1Np8/a2poTJ07QqlUrateuzY4dO3B1zTybZuvWrQwdOhRPT08GDBjAoEGDmDt3LiDS+wICAl7584mNjaVbt27Ex8dTvHhxlixZouP7NWTIEEJCQhg8eDDx8fF8/vnnzJs3T2cf586dw8rKShPNlZZChQqxe/duhg8fzuLFiylYsCBr1qyhdu3aANjZ2bFv3z4GDx5MrVq1cHFxYebMmXTt2hUQvmB79uyhf//+VK1alUKFCrF27VqqVasGQEhICAEBAcTFxfH06VNACGjpmTt3LkOGDEGhUDBgwACioqIoXLgwkyZNeu0iBJIPD4VS+WEaJkVFRWFnZ0dkZCS5cuV619ORqDn6KxycJnwf2i95pV0sO3qXH3YF0LqCG3O7VHyt6YzfeoU1px8yvFExhjdK9485JRl8fwKLXCIi7OBU3f4y7cB75WsdXyLREBsqqnGlJIqiElW/EjcEfzaHhyfE71qZdtnfn89PcPhn7bqJhfB5MTLO8alnysn58N94KNoIum9+u8eWSCQSyftBXLgo8BIlHmjiUBiKNYXKfSCP/o1xpuwYDudWCD/ZgcfBKF0iy7E5cGCyuNZ8cAIi0/gIFW8OXddlvO/4SPhZVXlyzAMRlR39DH4tDihgYrCwJsguD0/Dn03Aygm+u6U/1w+cj+l+a8OGDcTFxeHt7Y21tTXJycnMmjWLefPm8d9//1GuXDlKlizJwoULqVu3Lh06dKBChQoYGxszadKklzrWvXv3OH36NH369GHDhg3Ur1+fhIQEkpOTqVGjBjNmzKBjx44AxMXF0blzZ27cuMHVq1cxNTXF2dlZ44MVFxdHnTp1+OOPPxgxYgT79+/n7NmzREVF0bRpU0qXLs2aNWvInTt3jn9mEsnHjowMk+Qs4ao0SYfCmY/LhNCYnEuT1BroG4gMMzaBhqovN6USQm7AjT1Qe5gQ9Py3Qb2bL38RJ5EY4tJaOLtcu/7wlDAMfpXIMIASzXXFsOR4cUOQu+BrTjQDUpLg3mEoWEf3RuHuYbEsJFMkJRKJ5JPFykEY4T/zF1H1tq/hZ9TwexHxHHwNHhwXFgFpUUeG2eQVHmeX1kGd7+DIbJG6GfVEmPBX7CEeeKYl5IZY2rpq7QlsnEV0c1IsRDwEp2xUQVcTqtqfa/mPTgj72FCLTyDSJatXr46RkREnTpygYMGCgKgA2aZNG5RKJfnz5+eXX36hcOHCOv5h2cXOzo79+/dTs2ZNTpw4Qa1atVAoFFSrVo3mzZtrxllZWbF161auXbuGqam4bzEzM+P6dRHB6Ovry+LFi6lcuTImJib4+Pjg5uaGm5sbvr6+1KlTh7Nnz9K4cePX+HQkkk8TKYZJXp/g67CuC9Qdm0NimKgQlBNpkmoDfT3PsPQoFNB+sYgWMzaBR+fgxi5Y1kj4ILWYrX9hl5IESXFgYWd4nxJJWiJVT8vzVRXpjTf3wJ5R2fcMS49rBRFhFnYHzG0hIQpCb705MWzPaDj7J1ToAu0WibaUJHGjAtI8XyKRSD51crmJ1+ti5QBl2sL5VSJdMr0YpvYMs3WFeuOgziiwyw8n5sKLcPizGUQ8EMWROq/R9a5N7xcGoj93QZH2//zeS4pht8TSMRuV0yXvDSYmJqxevZrixYvrVC8cMWIEI0aMyJFj5M6dm5o1awLCb0ypVJKamqpzPDXGxsY63ltnz57VvK9Xrx716tXjxIkTeHp6YmWlTTMuVKgQp0+f1kvBlEgk2UM+wpC8PpfXCa+wE3O1Bvo5IIblyQnPMI0YZiAyzBDGKn244ffiIishUlTs2zFcO+beUVjbFWYWgtlF4cqm156n5BMgRvgXUKYdfLEcUAhxSd3+sqKqQiGqSw46AUXqizb1E++cIjlBGCE/PC3mCiLC7fZB8f7JBUiMEVFtLuVz9tgSiUQi+XQpr6o8ee1f4cm17WsIuSna1NUkbfMKuwHHIiJi2b2yaI94IJY3dutGZIO+X5ga9YOkl/UNU4thTlIM+9AoWbKkQWHqTZLd4xmqFFmzZk0dIUyNFMIkkldHimGS1yfoslg+u6L1ilCbkb4CajHMMSfSJDUG+llEhqXHuSQMvwo9/xXmrzf3wMkFsLoD/NVKRI0lRgv/p839YGUr+KU4nFr4ahNNTRUvycdL2ov3Uq20T7rVJsMvmyYJkMsV8pYGJ1Uqb+jN156mhpQkWNZQeKus/kK0WQr/CnaOECLZsf+J9SINZHqIRCKRSHIOj5oi2ishCv5uBxdXi++imBDtQySbdIKBRzXt+4Kq79j/JkBMsLY9+JpYpo0MA21FyfB0VdGzIkyKYRKJRPKhIu9eJK+HUilSvtJibidC3F+RMI1nWE6mSWYzMiwtxiaiYlL1QWL9v3Fwe78Qx6r0g698oXJfQAn3jwoPC9+fIDE2+8d4cFIIaT/lg98raFPp1H0nF+iLZEol+M6Eg9PFexDCRU4QelsYoseF58z+JFo0Hieqi/cSLXX7Xyfd1qmEWKqfUOcEF/5WVVZVCuHXyhEGHIZc+cRT96X1xVN3IxOoNzbnjiuRSCQSiZGRqPqtxtgMIh8KW45olRiW3r6iuMqHqeBn4mFmnpLCTzPwtGhPToRAP/HePV31Y01k2EuIYSlJ2kgymSYpkUgkHxxSDJO8HtFPITZEt82hkK4/w0ugVCq1YphtTohhrxgZlpa6Y8TTSRApbl/7Qctfwa2iWLaZC01+FBdS8ZFweUP29713rBDSkmLFRd767pAUL9LT1ncTAty1bbrbHP0FfGeIZfhduLgWpueBq1te/RzVbOotKgMu+kyIcRJBXDicWfZyQmd61GKY+uK9ZAttn7nd61WBVD+RzqnIsKQXcHiWeF93DLRdCL13g70HdP5HzDfstuivPgjylMiZ40okEolEoqZyH3AoAlX6w4Cj4qHRozPCrxWEgX5a8leBb86LysZGxtq0SXUGw+OzYlsrR3Auo7utOqPhZdIkn9+H1GRhvp8TXmmSd8KQIUP44Ycf3sqxAgICsLCwYNiwYVmOvXDhAlFRUZr16OhoFAoFgYGBmWyVMQcOHDCYfmmI5ORknjx5kuW4lJQUWrZsSWRkpE57q1at2LRJ2shI3n+kGCZ5PdRRYYo0N/Kv4RcW9SKZxBQRCeVonXPVJF8pMkyNuS18dRiGXgTvlcKbQo1CAV49oeYQqPqVaPNboo3YyoywOxB0UXx2XTeCZW54cl4YlQfsgLgwMe7SWu02ATvhUJov7Gf+KrFMKaooZee4GfHsmioSCIh6BP94a83dP3VO/AG7voUjv7za9gkxwlsLRNUqEMJSXpVZquVrFmFwVJn9xoW+flRf9FPYPlQYFNt5wGffgmdXkToM4OYpbjQs7MRx6455veNJJBKJRGII+/ww9Dy0/EV8B/X8VxtdbWYL5jb62zgWARPVw1TXCmIZdEksNdWP6+in9jukEcOyey2lMc8v8soPgSXvP7dv30ahUGT7VbJkSYP7SUxMpFevXlSvXp1ly5Zx+fJlg+PUrFmzhrJly7J3714AUlWZIsbGr/Hw1AARERF6bUOHDsXb21t/cDpOnz7NjRs3sLPLuWJiHh4eep9paGgoAL179zb4maf/TC5cuECjRo2wtrbGzs6OWbNmafqCgoLo2LEjtra2ODg40L9/f6KjozX9kZGR9O7dG3t7e3LlykW/fv2Ii4vT9B88eJBq1apha2uLq6srQ4cOJT4+PsPzuXr1KnXq1MHS0pLChQvz999/6/QHBgbSqlUrrK2tcXNz45dfXvFeQ/JKSDFM8nqoLzBKthAh7KC9oHgFQlR+YbbmJliYvv4/e3WaZNSLJJSvIxRZO2Z9Xp7dwNRKWwo8K/y3imWhOlC8CXRYASjg/F+wf7J23O0DQqB48Rx2qJ4kmVqLZXAaASv4mjb8/1W4qnqCU6SBEDQTo+HOwVff38eE+oL33uFX214dFWZqLcRVNerosFfxC0uLuY1IX4TXiw57chH+8IIrqujGxlO0NxVpyV8Fvr0hntSnPR+JRCKRSN4UbhWh/yEo2Qo+G5n1eFdVYRf1g1v1d3ihuvpj7fKDwkhEjqm/s7NC+oV9EhQpUoQXL17overXr8/cuXN12mJjYw2KXKmpqfTs2ZPk5GT27t3Ld999R9u2bXn06FGGx509ezYzZ87E29ubb775hsREkTmT02JYw4YNWbdunWZ9wYIFLF68GH9/f+zt7bG3t8fExISRI/X/5rZs2UKHDh0YNGgQTk5Omte+ffvo06ePTtt3332XrfmEh4fzzz//cOvWLc3LwUHY78yaNUun/datWzRr1ozOnTtrtr948SKfffYZZcuW5dixY/z3339UqlRJ09+lSxcCAwPZt28fGzZswMfHh2+++UbT37NnT86dO8f27dv5559/2LVrl05/QEAAX331FceOHeN///sff/31F5Mnp7lvS0NUVBSNGzfWVP3s06cPvXv35tSpU4A2si45OZmjR48ydepUxowZw4YNL5FlJHktTN71BCQfOGoxLH91kUJ25xA4l37l3YWpxLCcSJEEsFMZ6CenKolNTMHG/A3+ylvai+pH51bA6cVQsLb+mEfnROqjZW4RGQZQtr1YFqkvUs5OLRCRWQojkR4QdkukXj6/LyJ/nEqAZxc4MEVUtoxK4zN2boVI17SwA1OLzOd7aiHcOwJuXlC0IVzZKNor9hBVAk/8ATf2QtkvXu9z+RhQl3EPuiR+z82ss7mdKo04QfXEyTZdSkfFHkIULZf107cscSomfm9Cb4JH9Vfbx4HJImXXtQI0+UEItRlhavlqx5BIJBKJ5FWxcxfp+tkhb1lAIb7Dw++JFEuAwgbEMBMz4fsVekM8WCzdJuv9ayLDpBj2oVC5cmXOnTtnsG/SpEk663Xr1sXX1xeFQoG/vz+jRo1iz549mJuLexQjIyNMTEywsBDX2wcPHmTUqFEaoUNNYmIi/fr148KFCxw+fBgLCwsmT57MvXv3qF+/Pv/99x+FC+tm1Tx79oy8efPSpUsXypYty6JFizR9hsSw0NBQ8uTJk63PQJEmilGpVPL777/TunVrqlevztatW/ntt984fPgwtWvXJiUlhWnTpnH9+nW9zyclJYU1a9awfft2KleuzMKF2iJirVq1onfv3nTo0CFbc1KTmJhIbGws5cqVo2jRonr9zs7OODs7a9Zv3rzJoUOHuHTpkqbt66+/xtvbmzlz5hg8xpkzZ1i3bh01atQARBTc4sWLAbh27Rrbt2/Hz8+PKlWqAPDrr7/Sq1cvZs+ejYODA0OGDNHsq0KFChw9epT9+/czc+ZMvWOtXLmSlJQUli5dipmZGeXLl2fnzp0sXLiQ6tWrs2vXLq5fv86BAwdwdnbGy8uLgwcPMn/+fDp27PhSn53k1ZCRYZJX48kFODYHHqu+UFzLQ+s/xKtMu1febajGPP/1UyQBrM1NsFUJYE8jMw5hzTHUqZLXd0Gk6mnP1c0wI5+oNrm8kTByvblXiFxGJuIJp5oGk7S+FcWaQo2vxfsDU7TlwVv+qg39f3BMLE1UwsSltfBrcVhST3iPZcSVTcKv7MZu8PlBmKFHPAQzGyjeDEqoTGhv74eU10gx/ViIUolhqcnai+nssK6b+FncV/2c0le+ss8PQ85AraGvP0e1b1fIjVfb/sFJuOsrfic7/p25ECaRSCQSyfuOuY3WRuD0YvEdbu+RccXzIvXFMquoeKUSQm5qI85kZNgHw9mzZ1EqlTqvr7/+munTp+u1+/r6ararUKECcXFxGXp9RUdH069fP9q3b4+ZmfYeJigoiAYNGnD58mUOHTqk8ewyMjJixYoV1KpVi0qVKrF161bNNuHh4ZQpU4axY8cSHx9PuXLlmD9/PklJwv/Y1NRU7/hOTk5ER0dn+tqxYwfOzs46bQC1a9dmxYoVmJiYsGfPHkJDQ2nWrBk2NjbY2toybdo0du3aRf78+WnSpInmmJs2bSIoKIjKlYU3X5kyZciXLx/58uXjyJEjDB48mHz58uHq6sqIESMASEhIoECBAhkKVeHh4ZrzyQ7Tp0/niy++0KSm3rx5kxMnTmQahdapUydWrlxJZGQkQUFBrFu3jk6dOgFCDAPw9PTUjK9Xrx7JycmcP3/e4P5SUlJwdHQ02Ofj40ODBg10ficaNGigEUx9fHzw8vLSEfgaNGjA6dOnXy+jSZJtpBgmeTW2DRZRJOqIGZdy4sa+Uq/XMgIPVUeG5UAlSTUuduKJzVsRw/KWFlWMlClw9k8hMG0fJlIOY56BMhXKdhCfF0CxJrqVN82soNPfULotNJ4qosZy5RP7A/DsDoU+0zd+Ld4E8lXRrocEwNFfDc/xmT/8q3qqUaYdlGoDxqrPu2x7MYd8VUXq3ovn8Og1Ui8/BlJTdFMmHpwQBvOpKZlvl5woPOBSk4UgCvqRYTmJWiB9+IqFDw7/LJae3SB3gZyZk0QikUgk7xL1d6PfErEs0iBjf68iDcTy9qGMfcMSY2FtZ5hfRZsd4agfwSL5uDAxMWHNmjWsXr2af//9V6cvNTWVbt26UbRoUcaPH69p//PPPyldujTOzs4cP34cd3d3ne2MjY1ZuXIl48ePp2PHjrRs2ZKHDx/i4ODAiRMn2L9/P5UrV+bKFWGHovatsrKyMjhHGxsbIiIi+N///oeNjY3ey8LCAoVCodOmpm3btuTLl499+/bx4sULYmJiiImJwcPDg5MnTxITE8O2bds0c1AqlXqRUGFhYZw9e5ZHjx4RFRVFcHAwjx49YvLkyRrhzcTEhJIlS5I/f36D5xAWJvySCxUqhLu7O23atMnQW+3x48esW7eO4cOHa9pOnTqFubk59+/fp0yZMuTNm5eOHTvy7Jn2On7OnDn4+/uTO3du3NzcMDc3Z8KECQAaUevhw4ea8eoiBsHBwTrHT0hIYPv27axfvz5D8e3u3bsUKqQrvnt4eGjSYzPqT0hI0PikSd4sUgyTvDypKVpfIitHkUZnmTtHdq0WwxxzKDIMtGJYUOSLHNtnpqijw04vgdVfCCEsfzVhwv/1GeiwHPoegA5/QuvfDUy4HHT8S0T6WNjB0Asw/AoMPq0db+MMVmmemriUhx5bYeBxaL9MtB37n+EooSO/QPILKNIQvlguxLfvbkC3TdBMJYgYmwihDkT02KdMTLBWjAQhbP1WGpY3ydxk9/k9IX4ChFwXy/SVr3IStQfKkwsvX/jgxh5tVNhn3+b0zCQSiUQieTeofcOUKeKa6rNMfIsK1hb+t5EPtVYWaUmIhpWtRHS/kSk4FRcPKV3Kv5m5S3KUfv36GTRfnz9/PpMmTTLYN2XKFM32hQsXZtq0aXqVJ3fu3ElAQADr16/HSFWYITk5mRUrVjBz5kxq1qyJjY1Nhob7jo6OnDlzBqVSqRGoihcvzokTJ6hRowYLFiwAhBhmYmJiMDJMTXJyMnPmzGHq1Kk5/OnpsmrVKkxMTPRSNqtUqULBggV1XmqhCYQA+N9///HFF4YtWDw8PDh16hRnzpxh0aJFhIWFUbduXYPeaosXL6ZcuXJUrVpV0xYUFISpqSm//vorCxYsYNWqVVy8eFHjKaZUKmnfvj0ODg74+Piwc+dOHj16pEl9rFatGvny5WPkyJGEhYURHBysiWpLe64lSpTAwsICb29vJk2aRNOmTQ2eT0xMjJ54aWVlRUJCQqb9gGaM5M0ixbCX4cFJ+KuN1iMgMU5Ef3xqRD0W0S5GpvDdLSHq5BBhseLzdLTOucgw17cZGQZQooV4SpgYLURDU2tot0hU4ctTXIwxtRAioo1zprsChI+FvYeopGSs8jxTKEQUmhqX8sLI3KUslOsgUh1Tk2DHcFBVnwHE7+vtA+J9vXHaKD7L3FCssa4XVsmWYnl6iTbN71MkWlVa2kj12YfdhhfhokT7o7MZb6f+P5GWNymG2bmL3ztlauYFHEJviYv5LQPExX5iLOweLfpqfC2jwiQSiUTy8aCODANoM1dkMWSEmbXWc9NQquT5v0XEt6UDfLlb2By0na9fmVLyXrJs2TK9VMiFCxeiUCgwNTVl//79ev1TpkzRiZTq27cvmzdvJiYmhpSUFBISEmjQoAGHDh3CzMxMM06pVHLkyBG++ko8IO/Vq5fevpVKJQ0aNMDR0RFPT092796tMYoHMDc3Z+nSpcydOxcQVQ4zigpTU7BgQdauXcv06dP588/s35+NGTOGS5cukS9fPkxMTDSvGzduULt2bUxMTGjatCnHjx/HxMSEyMhIlixZorefM2fOcP/+fZ3Xjz/+mO152NraUq1aNcqVK0fr1q01lTTXrl2rM07tw6X+fNUkJycTExPDypUrqVu3Lk2bNuV///sfvr6+PHz4kL179+Lr68uWLVuoW7cuLVu2ZMWKFSxevJhbt25hZWXF5s2buXr1Kk5OTnh4eGjEtrSpjLt379YIdnPnzqVr164Gz8fc3FxT+EBNfHy85ueYUT9kHAEoyVmkgf7L8N84EXVxZhnUGQXzqwoRoue2dz2zt8vz+2Jp7/FaKZGGiHoh8uHtrTJ+6vGyuNgJP62nUW9JDDM2gf4+wpz+yXkRseNQOOvtXhbnMuIYoE27BCGUtZgt+h6egIv/gFcP0ffwBCREgXUecK+kv8+0lGot/Myu74S1XeErH1E+/FND7RfmUh4iHkBcGCiMxVPmS2tFZUVDhN3Wb7N10W/LSQrXE8e96yvSdW/shpv/QUqiMLy3coKLayAhUoy/skE8KX/xXFTSqjvmzc5PIpFIJJK3SYHaInorTwko/XnW44s0ENdPtw9CtQG6fTfFjTl1voP8VfW3lXxQ7N27lzlz5tC1a1esrKwYOHAg27dvp3Rp3UJgzZs35/Bhw9XEfX19ddL01MyePTvD1LnY2FgsLS0JDw/n2rVrFCig+xBSbcifnuTkZJRKpcH+EydO4OXlBUCTJk2YOnUqAwYMIF++fDo+X4ZISkpiwYIF9O7dWy8Cq2TJkqxcuZLq1atz4MABpkyZwrFjx0hNTdVEwaWlUaNGmJjoygthYWEZRk5lha2tLUWLFuXBgwc67ceOHePZs2e0bdtWp93Z2RkzMzOdNMzixUUgwrNnz7h8+TJ58+bVeLcBeHl5oVQquXLlCsWKFaNq1arcuXOHx48fY2dnR0BAAD/88APly2sjQIsUEfdDlStXpmDBgjRo0IApU6ZojqXG3d2dwMBAnbbAwEBNwQR3d3du3bql129nZ6cjjEreHPJRRnZ5ekUIYQBBl+H+UXFTfNcH4qPe7dzeNs9V/5ByF8zxXUfFC7N2W4scFMNyveXIMACLXFCqFTT83nDVopxAHRlmnUdfZLH3gPoq34J9EyEmRLy/sUcsizfN+kmmkTF8sUyIZgmR2mqTnxpqX7xcbtDiF6jSH7xXiDb/LZCcQRhz2FuODANtqmTADpjrBVsHiDle3yl+fqcXip9l/uoiDVaZKoQwEOeW3SqZEolEIpF8CBibiOit7BaqKdpILO8dERkgauKjtFHXxZvl7Bwlb51t27bRp08fNm3ahL29PR4eHvz+++80bNgQPz9dr1xfX1+DUV0NGzZk4cKFBvsyM3Bv1aoVxsbGmuqB5cqVY9asWaxevRoQkUHpX3FxcVSvXp0hQ4YY7FcLYWrGjx/PiBEjKFu2bJafxfHjx3F0dKRUqVKcPHlSx1Ps5s2bNGjQABsbG1q3bq3ZxpAQBnDgwAEuXryo80qbJvmyREREcPPmTb3Kklu3bqV69eo6ohZAzZo1SUxM5MKFC5o2f39/FAoFRYsWxd3dneDgYB0/LrUnW3pPN3d3d2xsbFiwYAGNGzfO0CRfLf6lpOh7CdeuXZtDhw7p9B08eJCGDRtq+v38/IiMjDTYL3nzyMiw7HL+b+37p1d006OeXYUCNbXr4fdEGlKBmtBiVo75ab03qCPD3oQYpooMy2WRc7+arhrPsLcohr0NijUVFZHKfmHYDLbaILi0Hp5dgT2jRTqrWgwr0SJ7xzC1FKLJ43MQ9STn5v4hEfVYLHO5iQIDZdsL3zxbVyGU3donoujSE6qKDDMyEWnF8OYjwwp9hqaMPIi/0fKdxHETYiD6qXhfbaBIv40KEmNNLHTTbiUSiUQi+RTJWxbsPIRv2J2D2u/3uz7iu9yx6KcZJf+RkJqayvTp05k3bx579uzREYtatmzJH3/8QcOGDZk4cSLfffednidWTnDgwAESEhIwNzfX7N/f31/H0D4tiYmJDB48mICAAK5cuYKRkRETJkzItOKiQqFg1qxZ2ZrPpk2baN5cVJGvUaMGMTExmr60kWFBQUFcvHgx031VqVJF7zOLjo6mXbt2gPj8W7RowVdffUX79u31tt+1axc3btygXr16PH/+nClTpmBra0uvXr10xvn6+hqMNitTpgzNmjWjd+/ezJ07l4SEBEaMGEG/fv3InTs3bdu2Zfz48XTu3Jnp06cTGxvL0KFDqVKliiYdcs2aNRQrVgxTU1PWrFnDpk2bOHHihOYYPXr0oFOnThQsWJDbt28zZswYateuTalSpQCRElulShWGDBlCv379+PXXXxkyZAhff/01W7du5cqVK5q0T29vbyZOnMiXX37J5MmTOX36NJs3b+bIkSOZfs6SnENGhmWHpHi4vF67nhgN/tu060+v6I6/uhmiHokUpIW1RUXBjwmNGPZ63kLhsYkcuxVKaqrWhDw6XohhORoZpvYMe1tpkm8L27ww7CI0nGS439gEPp8rUvr8t8C/X4s0P2NzkU6X7eOoBJy0FRU/JdRpkrau2jYjYyjnLd6rK0WmR50mqX7KDG8+Mswyt7aqaNFGoqBC/fFQuY94Kt5shliaqApU5HIFdy8phEkkEolEAuLholoAC9ipbb/5n1jKqLAPFn9/f2rXrs2aNWs4ceIElStX1hvj7e3Nvn37mDt3LqVLl2b37pwvImVsbIyVlZWOaHTnzh29FLvU1FS2bt2Kp6cnx44d4/Tp01y6dImgoCCKFi3KDz/8QGxsbLaPm5CQgJmZboGy1NRUtmzZohHDMsPV1TXLcVl5hiUlJREQEMCTJ4YfsNvb27N06VJq1apFr169KFCgAH5+fuTOrQ0sSUhIwN/fn4oVKxrcx5o1ayhbtiytWrWic+fONGvWjN9/FwXIbGxs8PHxwcLCgiZNmuDt7U2lSpXYtWsXClVgga+vLw0bNqR27dqcP38eX19fypXT2tHY29szcOBAKleuzLfffsvnn3/Ozp3a/xUBAQGatE53d3d27NjBsWPHqFSpElu3bmX37t2a1FgrKyv27NlDUFAQVatW5bfffmPt2rVUq1Yt089ZknPIyLDscGsfxEdALneRkhZ0UYhdaoLSlXy9f1Qsjc3EuDPLofGbrerxVsmhyLAJW6+w5+pTetYowNQ2ZVAoFJo0yVyWOR8ZFh6bSHxSChamOf+U573FrSLUGgbHfhPeYQDlO75cOpyNSgxTRxt9aqgN9HPphk9TshWc+APu+IhIMSNjUV3y6RWwcoA4VQh2OW9V5SkTYbr7pmm3CB6dERGDxjknKkskEolE8klQqhWcmg8390BKEqAQ9wIgbCYkHyT//vsvJUuW5I8//sgwCgtEdNS1a9eYOHEidnZ2r31cdbXIx48f66XapaamcuTIEa5cuaKJTDp37hzbtm1j9erVREZGMmzYMMaMGaPxClu3bh2nTp3i66+/Zv78+UybNo3+/fvrHff48eMkJCSQK1cuTExMWLRoESVKlNAZk5iYSMeOHWnQoIGmLSoqivj4eBISEnjy5EmOfAZqzM3N9fy/0lKrVi0CAgKy3EdSUlKG/blz52bNmjUZ9hcpUkRHvErPkiVLDBYHUDN37lxNUQNDpE+zrVu3riYV0xBly5bl5MmTGfZL3ixSDMsO6i/AUm0gJUGIYWl5egkurROePC1/g4enRHvlvsKjJ/34D52I1/cMUyqVnLwbBsCqkw9wsjFnaMNimsiwXDkYGWZnaYqFqRHxSak8i4qngOMn5otUdwwE+onIrlrDoELnl9teHRkW/YlHhuVy1W13rwTmdkIof3IR8paB3d/Bhb+1qdG2biJCy8pJVPp8GxWnHIvIFA6JRCKRSF6V/NXE93ZcqKimHXoTYkPAyhE8arzr2UlekfHjx2d7bK5cufjjjz9y5LhffPEFq1atolChQgZFnPz587N06VJy5coFwPfff090dDRjxoyhe/fuBoW76tWra6oZPn/+3OBx//77b5YsWYJSqcTIyIiSJUuyatUqnTEWFhbMmTNHp+3IkSO0bt0aU1NT2rZtS8mSJTM9v0OHDtGxY0fCw8MpU6aMJsJKTXx8PMnJyfj5+XH58uUM9iKRvBukGJYVSqWoKANQrBFEpKkIYV9ACEPB12H3KFGlb303SI4Ha2cRgXN6IQRdEvsx5Ov0oZEQIy4I4LXEsMDwF0TEJaFQiI/mj4O36F69APFJqUDOimEKhQJXO0vuhcbyNDIeDwcrQqITsLEwwcrsE/gTMLWAL3e9+vZqMSw2GFKSRfrlp4Q6Is7WTbfd2EQURwjYDrf+E4UKHqo8BdSm9E5FwdIeRvjLKC2JRCKRSD4EjIyhZAs4vwr2jIGYp6K9/gT5Xf4RMm/evFfa7sCBA9kalytXLnx8fLK93507d+oJSoYwMjJi8ODBGfYvWrSIRYsWZfu4alq1aoVSqcx6IKLCJaBjSC+RfEhIz7CseOYv0qRMLEV5ZtcK2r7SbcDCDlKThBAGWv+wQp+JSBEjU3Fj/LH4hqmjwixzi3N/RS4+igCgvLsdNuYmJKcquROiNWy0yUEDfdBWlFx54j7lp+6j6oyDVJ9xkMgXGYfZSlRY5wGFkag8qBZCPxXioyBR9XuZPjIMRAl2gGP/E0KYuR00miI+LwDHYmJpaiEuriUSiUQikbz/1BouvEJDb0B8JOQtB5V6v+tZST4BsiOESSSSnEGKYVlxe79YFqojbmidSwvvHwD3yuBSXjvWPI04VKgOmJiDs6gsQdCltzPfN43aL8z+9czzLwdGAFAhvz0O1sLM8X6oMIG0MTfB2ChnvwjUJvp7rj4lWuVLFhWfzIOw7BtPfrIYGYtIR9A+Hf1UUEeFmdsZ9llTi2EpiWLZbAbUHgHNfgbzXFCy5duZp0QikUgkkpzDsQj0PyS8V02toOUv8qGWRCKRfGS8khh26dIlvLy8OHbsWIZjjhw5QqVKlbCwsKBMmTL8999/Ov1Xr16lTp06WFpaUrhwYf7+++9Xmcqb55YqBLZYY7E0tYDyncChsEiRUothufLBF0u12xX8TCzdPMXyY/ENe/76fmEAl9SRYfm0YtiDsDgAcuVwVBhoxTCA0q65KOos8u+jXiTn+LE+SjS+YZ+YGPbojFjmcjPcn7uAKLMOUKAWeHYT76sNgLEPoWjDNz9HiUQikUgkOU8uN+jvA9/dBI/q73o2EolEIslhXkoMO3/+PB07dqRGjRpcuHAhw3H37t2jRYsWNGrUiDNnzlC3bl3atWvH/fv3AVGlonHjxhQqVIjTp0/Tp08fevfuzalTp17rZHKUxFjYPhQeqAS/oo20fW0XwNALIlWwYndwKQctZkOxJlB7JNQZLcQy0KZVPrn4Vqf/xnh2VSwdCr3yLpJTUrn6WKSVeua304hh91RRWrksc96PwTWNGDapVWlyW4ljRMXLNMls8SmKYfGRcEBVBbZch4zH1RsnxO82c3V9AWWYu0QikUgkHzYKBZjbvutZSN4jDPlpZddjSyKRvF+8lBi2ZcsWzM3NMy1HCqLkaNGiRZk5cyblypXjjz/+wMHBgRUrVgCwcuVKUlJSWLp0KeXLl2fixIlUqVKFhQsXvvqZ5DTbBsP5vwCFMMzMSPzJWxoGHhNGmwoFNJoMDSZob4RdK4rlkwtweSOE3Xkr038jpKbATVWEX+F6r7ybW8ExvEhKwcbchMJONmkiw4QYZvsGIsOqFnLASAFfeOWjRhFHbFUG/dFSDMsen6IYduhHUTTAsRjU/CbjceU6QO+dsoKjRCKRSCQSyXvOixcvdNZ79+7NlClTdNri4uIMbuvr60u9evWIj4/XtEVERFCmTJlMA0UePHhAVFSUTltycjJXr17VG6tUKklJSdFpu3PnDrdv385w/xnx5MmT9zf7SiJ5D3gpMWz69On8/fffFC5cONNxPj4+NGvWTLNuYmJCnTp1NJFfPj4+NGjQADMzM82YBg0avFJk2N0rxwk4d5jkwHPw+LyIwHp+H5LiDW/wIkKY2Wem4IfdgWv/ivfdN0Pd0S89Lw15y4DCGF6Ew5Z+sKbjq+/rXfHkAtw9LFLG4kKFcX6BWq+8uyuPIgEo654LIyMFjmoxLFSdJpnzkWElXXJxeUpTfvEurzqGENxkmmQ2sVGJYZ+KZ1hqCpxbKd63mCX8/yQSiUQikUgkHyx+fn5UrVqV6OjoDMcEBQVRqlQpg+JW7dq1SUpKYsmSJZq2KVOmUK5cOSpWrJjhPv/44w86d+6sI3LNnj2bzp07ayoyqtmwYQM1atTg2rVrmrYxY8awefPmbJ1jWuLi4hg4cKDOvl6WLVu2UKFCBSwsLChcuDA//PADqampmv6EhASGDh2Ko6MjuXLlom/fvjpiYnR0NP3798fBwQFbW1s6duxIUFCQwWMdPXoUhULBDz/8oGm7e/cuCoVC51W2bFlNf0pKCkZGRjr9NjY2mv6IiAg6d+5M3rx5sbW1pUGDBly5ciXD8z18+DA1atTA0tKS/PnzM3LkSJ3z+fPPP/XmM2TIkJf7UCXvDS8VgpPd6hZ3796lUCHdSCoPDw8uXbqk6W/RooVe/6NHjzLcZ0JCAgkJCZp1tbrusaMLDhYGhC1jMyhUF9wriXTG0p+Lqo/LGkHMM9FWzhvqjxfv03J6MaAUaY+v6/ljaiGqzwRsh9hQCLsN0c/ANu/r7fdtkZwIf30OCZGQX+WXUKzJa5WWDooUQmUhJ2FIro4Mi04QXwZvIjIMhDG/GhkZ9pJ8apFhsSGQkiCqQhaq+65nI5FIJBKJRCJ5TSpXroyLiwu9evVi8+bNeve2KSkpdO/enfz582sEl+bNm3Py5EnNmMTERK5du8b3338PQFJSEkZGRtjb22vGjB07lrFjx2rWf/rpJ9q2bcu1a9coV64cd+7c4ddff+Xw4cOYmOje93Tq1Inw8HCaNGnC1atXCQ8PZ/fu3Zw6dYq5c+fy+PFj3NzcUCgUREZG0rx5czZs2ICFhQWGSElJoWLFigbv4x8/foyjo2OGn1dUVBTffvst06ZNw9PTk1OnTvHNN99gbm7OqFGjABgxYgR79+5l06ZNJCcn07NnT8zNzVmwYAEA33zzDSdPnmTTpk1YWFgwfPhwunTpgq+vr86xlEolo0frB6CEh4djZGTE9evXNedgbq59SP38+XOUSiW+vr64u7sDYGSkjfcJDQ3FxcWFbdu2oVQqGT9+PC1btuT27ds6gTlqvvrqK4YOHcqSJUu4du0agwcPJiEhgfnz52vmU61aNVavXq3ZJu3PXvJh8UZUh5iYGKysrHTarKysNGJWVv2G+Omnn5g6dape+1MciVOCSy5zTIyMIDUZXjyH5HhRCVJdDfLwTLByFEIYiDF+S+DqFuixFVxVRvgvIuCC6pe7+uCXP3lDtPpNvOZVFSWagy6CbdOc2feb5tkVIYQBBKoi90q0yHh8NngeJyrv5bYS/4DUYpiaN+EZlp5clqrIsHgZGZYtPjUxTH2e1s6yepREIpFIJBLJR4CRkRFr166lXLly/Pjjj0ycOFGnf+jQoVy/fp2zZ89iairuR/bs2fPKx9u0aRPe3t4YG4tryX379un0V6hQAaVSSY8ePVi5cqWmfdCgQfTq1Yvw8HB69erFpEmTGDduHOfOnaNBgwYEBgaiVCopV64cnTt3BtCkbiYkJDB8+HB+/PFHHBwcNPsMDw9n9uzZTJ48OUPhLD3m5uacPn0aZ2dRVb5cuXKcOXOGLVu2MGrUKMLDw1m6dCkbNmygfv36AEyePJkRI0bw66+/YmlpyZkzZxgyZAgNGogK7BMnTqRLly56x1q2bBmpqal4eHjotIeHh2Nvb0+xYsUMzjE8PBwALy8vbG31/f2KFi3KnDlzNOuzZ8+matWqB54vbQABAABJREFUXL9+nfLly+uNP3DgAPnz59ec7507d5g7d66OGObu7k7RokUz/ewkHwavVE0yK8zNzUlMTNRpi4+P1whgWfUbYty4cURGRmpegYGBAHRU/ELthD+43+M0jLgK316HCU9h8GloOBkq9wHnMiJNMeyWuLn95rxIf3QqLtL+fH4UB1Eq4cBkSIoF59Kv5YtlELc0/mHvktsH4ehvEOgn0sEy49FZ3XUjU91iAq9ARBZi2JuKDNM9hjTQfyk+VTFMfd4SiUQikUgkkg8eJycnli1bxokTJ3Tag4OD2blzJ7t378bV1VWnLyQkhKdPn+q90hvnG/L2qlu3LsnJyRm+Zs6cqTfHuLg4Bg0aRNeuXRkwYACjRo0iLCyMPn36kD9/foYMGUJ0dDRDhw6lffv2Otuam5sTEhJCv379dNonTZpkMBKtY8eOtGvXzuBnZW5urhHC1FhYWGjSPY8ePUpqaipNmjTR9Ddo0ID4+HguXrwIiEi39evX8+zZMyIiIvjzzz/p1KmTzj6DgoIYN24cCxYs0ItgCw8Px8nJyeD81P1mZmYGhTBDqOeeVihMi1oIM3S+2ZmP5MPijYhh7u7uGrFKTWBgoMZrLKt+Q5ibm5MrVy6dF4CFqTiF+CRt7jIKBTiXhM9GQqv/Qf9DUG0Q5CkFXdYKo+uijaDzGjH+1j6ICIST81Q+QQpoPC3nq8G9D2JYYhys6wYHp8LyxrB1QObjA/3EslRrsLCHCp3AItdrTSE8TghQua0ziAx7A55h6VEfQ3qGZRO1Z1hscNYC6sdAjBTDJBKJRCKRSD5GWrZsyY4dO3TanJ2duXHjBhUqVNAbX6VKFbp06UK/fv00r3z58hEWFsa2bds0XtkrVqxg2bJlrzW3U6dOUbFiRSIjI9m6dSsNGjRgzpw5lC5dmrZt23Lx4kUSExOpU6cOtra2xMTE6O1j4cKFpKamavouXbrEunXrWLNmjZ4YVrhwYUqUKJGtuUVERLBp0yZNlNfdu3dxdnbG2tpaM0Yd2aW2Pxo/fjxGRka4uLjg4ODAzZs3+f333zXjlUolvXr1omvXrlSqVEnvmGFhYdy6dQtLS0uKFi3KwIEDCQkJ0elPTEzE0tKSAgUK0LVrV+7fv6+3H6VSyY0bNxgzZgw9evQgX758WZ5vYmIiq1at0pyv+njLly/H1taW8uXLM2vWLJKSZHDFh8obCcGpXbs2+/fv11TmSElJwdfXV5MHXLt2bZYvX05KSoombPTgwYM0bPjy/lzmpsaQCAnJmdygm1pA85/1252KQcHP4P5R2NwPAk+L9qYzoFjjl55Llrh7ieWTCyIKLafFtuzw8CQkvwAUgBLuH8t8/KMzYlm5D3RYCcav/yujjQwTgpSjta45+dtIk1RHn0nPsGxinUf4ZylT4do2KN4czDKO5PzgiValU0sxTCKRSCQSieSjoESJEjx48MBg388/a+8Vk5OT6d69u07q4sKFCylZsqRmPa1Je06RkpLC0KFDGTZsGIMHDyYwMJC6devSuHFjjhw5ohGtli1bho+PDzNnzmTq1KlcvnwZCwsL8uXLp/HYAnBx0b2OTWs837hxY7Zu3apz3pnx9OlT2rZti42NjcYPzZD1kYWFBQqFQmN/9NVXXxEcHMyePXswNzdn9OjRdOrUid27dwMwa9YsAgMD+ffffw0e9/PPP6d69eoYGRlx6dIlJk2axMWLFzl+/DjGxsZUq1YNPz8/zM3NuX79OlOnTqV+/fpcvnxZEy32ww8/MGXKFFJSUmjWrBm//fZblucbHR1N165defz4Mdu2bdO0T506lQkTJpCQkMDBgweZPHkywcHB/PLLL9n6HCXvFzkihqWmptKiRQu++uor2rdvz9ChQ6latSrTpk2jffv2LFiwgNTUVHr37g1Av379+PXXXxkyZAhff/01W7du5cqVK6xdu/alj21uYgQodSPDXobKfYQYpvbDqjYQqg96tX1lRd6yorJkzDOIDoJcbm/mOJlx10csC9cT72OCITUVjAwECcYEQ8QDQCEKEeSAEAZazzB7dZqkzdtPk1QLblHxyfg/ieT7f/35tklxahaRYa8GMTYBu3yiEuumPuBYDL7cAzZ53vXMXo/nD8Tvef4quu3Rqio3NlIMk0gkEolEIvkYuHHjRrbGTZkyRS+6qEWLFjqG62krDGbGkSNHMvXoSklJoVu3bgAYGxtz+vRpbt++rWPKvn79etavX6+37ebNm6lXr54muATAx8eHatWqZWtu2eXo0aN07twZDw8Pjhw5opmbIeujpKQklEolVlZWXLt2jRUrVuDn50eVKuJae8uWLRQoUIADBw6QkpLCDz/8wIkTJ7C0tDR47Hz58mmiuCpWrEiRIkWoU6cO58+fp0qVKjg5OWnSFsuXL0/16tUpWLAge/fuxdvbG4CBAwfStm1bHj16xJIlSyhXrhynT5/W8ydT4+/vT4cOHUhNTeX48eMULFhQ01e6dGnN+2rVqpGSksLMmTOZPXt2tosNSt4fciRNMikpiYCAAJ48eQKIX9S1a9eyatUqKleuzNWrV9m3b59GnXV3d2fHjh0cO3aMSpUqsXXrVnbv3k2BAgVe+tjmJuKPP9PIsMwo2Ur4iIEQxpr9/OYitsyswLmUeP/4/Js5Rlbc9RXL8h3FUpki/NQMoY4Ky1MSLOxybArPY0U0ljo90trMGDMT7a/i20mTVBnov0hix6Ugzj14zupThp8USVS0Xwae3cDKSfjvrfEWabQfoo9Y0gvYPRrmesHyRnB5g26/utDGh1L1VSKRSCQSiUTyxli/fj2nTp3SvDLzulZTsmRJFi1aRHx8PPHx8Zw9e5b58+dr1uPj4zl06BAtW7bUbKNQKEhJScHCwoKIiAjNa9KkSTx8+FCzXrJkSZKSknSEsISEBJ31nGDr1q00atSIbt26ceTIEZ1oM3d3d549e6aTJvjw4UNApF9euXIFAE9PT01//vz5cXJy4vLly/z888/ExcVRpUoVLCwssLCw4MGDB0ydOjVDAdHLS2RaZRTh5+HhgaOjo06/k5MTZcuWpVmzZpqqlosWLTK4/YkTJ6hZsyZVqlTh3LlzFC9ePNPPx8vLi9jYWMLCwjIdJ3k/eaUQnIIFC+oYBpqbm+v9Qnbo0IEOHTpkuI+6detq/kBeBwsTYyD51SPDTMyg20YIuQ7lOr751EU3T3h2FZ6ch1Kt3uyx0hMbCk9Vn3nRRqK6ZlyYuPG3NhARpU4bTR818xokJqcSkyB8utRpkgqFAgcrM55GiSoob9NAPzo+iWDVcf2fRL3x437QeFQTr9Db8GcTke67vDGggFrDoP4E8ff0IXD8D/BbrF3f9S3krwa5VYK8OjLM1lV/W4lEIpFIJBLJB8eCBQs0tj2ZkZiYSNeuXXXa/vnnHx3j9Oz4RN2/f58uXbpw7NgxHBwcMDc359tvv6VixYoaUefFixfkyZN1psX3339Pz549Mx0TExOj49/1ujx9+pQePXowe/Zshg4dqtdfq1YtkpKS8PX1pXFjYTF08OBB8uTJQ7ly5TSeZdeuXdN4sQUFBREaGoq7uzt//fWXXoRdw4YN8fb2ZuDAgQbn5Ocn/KwzquZ49+5dQkNDM+xXKBQYGxvrmOKriY+Pp2PHjvTt2zdbqZTq+eTOnTtDQ37J+82bVx3eMGYaA/3XMPV28xSvt4FHDbiwGm7ug4bfv51jqlFHheUtCzbOYJNXK4blLSP6As9AUhyYWMDpJaKtQK0cm0LECxFKa6TQjQBzsNaKYW/DMyyXpcozLCGZoEhx3AdhcUTFJxHwJApTEyO8PHK/8Xl8kDgVFdVY900SaYaRD+H4HLixW0RXevUEs5z7In4jqNOiG0yEW/uF8LttMPTeKQRxtWeYjYwMk0gkEolEIvkYGDx4MIMHD85ynKE0SSsrq5fyCbt37x4dOnTg0aNHLFu2DE9PT4YPH87kyZPp3bs3ly5dIigoCCMjIzp37szp06czzJK6f/8+SUlJOmma6QkMDCQ+Pj5bxvBpGTduHAA//fSTXt/27dtJSUmhefPmelUyPTw8KFiwoMYiaenSpcTFxTF58mTGjBmDsbExtWrVwsvLi969e/Pbb79hZmbGmDFj8PDwoFWrVgaFO1NTU5ycnDT+bL/88gtubm6ULVuWa9euMXr0aJo3b66JNluxYgXJyclUrVqVwMBAxo4dS/ny5WnVSgSd/P7775iZmVG1alWSkpJYsGABjx8/pkePHoBI21yyZAm7d+/myJEjPH78GG9vb73zdXNzw8rKim+++YaWLVvi6urKgQMHmDVrFlOnTtX4tEk+LD54McxclV6XkPyKkWFvmxItwMgEnl0RETY2ecT62xAPbh8Qy8L1xNLGGYKvCc8kgOf3YUUzSE1TYbFoIyj7RY5NQZ0iaW9lhpGRNgrPMY1v2NusJqlUwr3QWE27z/Vgvt1wCUtTY85/3xhTY/mPzSBuFYVwBHBtO+wYCqE3Ye9YuHsYuq57t/PLimf+Ylm4PpTtAAtrwoNjcH0XlGieJk1SRoZJJBKJRCKRfOr07NlTx0B/4sSJADRr1ozatWvrjV+0aBEtWrTQiSYDGDZsGI0bN0ahUNClSxdGjx6t8bQ6fvw4VlZW5MmTR2NSHxQURN++fSlatCiFChWifv36dOrUiZ49e+qk8O3evRtPT0+NLVF2uXPnToZ9T58+JT4+3mCq4JUrVyhbtizLly9n4MCBNG3aFFtbWwYPHszIkSMBEYW1Z88eRowYQfv27UlOTqZ+/fqsWrUq2xFsVlZWjB49mtDQUDw8POjZs6fmswews7Pju+++48mTJ7i6utKiRQt+/PFHTdXMwoULM3HiRL777jusra2pUqUKx44d0xQTePLkCQEBASQlJfH0qbB+qVmzpt48duzYQatWrYiNjaVbt26az2XJkiUaYU3y4fHBi2EWJjkQGfY2sXKAQnXhzkE48YeIpjG1gm/O55hBvUHiwsF/q3hfUpWeqY56UXs++W/VFcJcPcH7LzB+fXEqOCqeJ5Hxmp+TvZXuPtX+YfB20iTNTYwwMzYiMSVVE5EG8MfBWySnKolOSCY8NpG8uTI2vJSoKN0GCtWBy+uFGHZzjxCb1NGG7xsxISqxSyE8/MysRdGMo7/CwWmiWIQyRfRbf+AFAiQSiUQikUgk+Pr6aqKFssJQmmRaEhISSEpKwtTUFAsLCx49eoRSqeTixYtUqVKF5ORkVqxYwaZNmwBRefLmzZsAGBkZUbZsWe7fv8+FCxeoXLkyzZo14+HDhwQHBxMTE8PNmzeJjY2lRYsWHD58mF69erF7927i4+NZv349c+bMwd/fn3PnztG/f39KlCjBjBkzdESi7LJhw4YM+77//nu+/z7zTCZ7e3vWrcv4IbizszP//PNPtueTPiIvq2i+9u3b0759+wz7W7duTevWrTPsHzJkCEOGDAGE4JlVKuqff/6Zab/kw+KDF8PMVAb6L+MZlpSSyi/7blC3WB5qFs26emBqqpJ//B5SvZADxfK+nNpukDJthRh2/i9tW+hNyFs6w01emWfXwMQcAnZAcjy4lAeP6qJPLYapI8P8t4lls5/BuTTkq5xjEWtf/X2OS48i+K6JKAnsYKUb5qsWw8xMjLAwzVnjR0MoFApsLUwIi9WtgHInRBslFhKdIMWw7GJpD9UGwP1jELAdjv1P/N6F34NGU1/edy4pHi7+Aw9OgDIV2swF8xwqYf3sqlg6FNb+ftcaBmf/hNAbIuUTROTkmxSoJRKJRCKRSCRvhXr16mk8rLLCUJokQGpqKg0bNiQuLo6ePXtiZycKjNWpU4egoCDy58/PTz/9hImJCcePH6dYsWIA9OnThw4dOmBlZaVJpzM2NmbUqFHkzSvux9Qiy08//cSOHTsoU6YMnTt35q+//tJ4ipmbm/PVV1/x1VdfcfXqVRYvXszVq1cpVqwYjRs3pm/fvq/1GUkknxof/J2ehak6TTL7kWF7rz5l8eG7+FwPZt+IulmOP3Y7lEnbrlIkjzUHRtZ9/bKpJVvBzhG6UVhPL+e8GBYTAkvrQ0oSmKmEhGoDtUUCNGLYMyFaBF0EhZFIG7PJ2YiYuyExKJVw+EYIINIk06IWx3K9hagwNbksTfXEsLSERCe8tbl8NNT4WohhVzZq25Y3FsKqhR24VhBpugU/y7hYRWoKbOgJt/7TthVpAF45FIKsTpFMG7lmYQe1R8D+78FvqWiTfmESiUQikUgknxxTpkzRWU8rjPn4+OiNf/LkiV6bWggDUXHw7t272Tr2uHHjND5emVG2bFnmzp2rWV+2bFm29i+RSLR88IZI5qYvHxl2MTACEFFA2UmvfPT8hWb85UeRLz/J9Fg5QKnWQnhyVglgQZdff78ACdHiZv5FBNzeL6LBlCmQECmqR6b1/0orhl37V7wvWDvHhbCklFSi4oXwd/FRBKCtJKnGwUYthr15vzA1adMxbcz1RTgphr0C+auBm6iOg4U9lP4cUMKjM8Kz7uiv8Fdr+LMZBPoJ07Yjv8C8qsJrDIQx/63/RBEHD1XO/oPjOTdHdWRY3rK67V69xDGVqv8J0i9MIpFIJBKJRCKRSD5KPvjIMHPjl/cMu6QSw1JSldwOjqGsu12m48NitKLI1guPqZDf/qXnqUe7xdB0BtzxgX8HQ9Cl198nwL6JcG6lEB9SVFFPhetDfARUHwymadL+bJzFMiYYrqvM0Eu3zZl5pOF5nDb6KlFV6CCtRxhAXlsLg+1vkrTCW2m3XNx6Fs3zOG2Z5JAYKYa9NAoFtJ4DZ5ZDzW/AqRg8vQrhdyE2RAhg1/4V1RyXNwbnMhCsitTa0BPKthfpigDtFomIrb/bwb2jQjh73ahM0IphLunEMEt78ft/WeV7YCsjwyQSiUQikUgkEonkY+SDjwxT+0tlVE0yJiGZ2f9d53ZwNCCilK4+0UZ33XganeUx0qbS7bj0hKSUHKhc+X/2zju+ifKP4++0aZuOdNOWlhYoZYjsvfcSkA0/ERUQZAuKIAoiKiCiKCiI7CUylKHI3lO2bMpeLbRAW7pnmvz+uNy1adLFkFKe9+uVF83dc3fPXdOQ++Tz/XzVduDsC0UrSc/Dz4L+CfebGAVnVks/n18HV43dI5uOgwF7oVIP0/FaH+nfmFC4+6/0c+lWTzYHC8gdJDOTtUyyUZkiDGpcitGtyz7142dHZmeYj7OG1yoWxd7GmlblJRFEOMMek6KVocNPkhAGkuhUvgPU7Add5sLwf6HqW4BKEsJUVuBaXBJsZSGs1WR4tbPkNLNSQ2yo1O0UQJciiccGQ/7nlp4GDy9LP1sK+K+WKTTTySf/+xcIBAKBQCAQCJ4y6ekvSLM4geAF4oUXw+xsJKdISjbOsNXHQ/h5z3X6LT1Bclo6V+7HmZRUXr6fuxgWlUkMi0xIZf+Vh08460wUKQfWtpASC9G3zNdf3AA/VYXvgmBOw4zOj5b4dxnopJJO9GmQGieVRvpVszxedoalJUilYS7+4Or/RKdjiSgLuVxZyyRt1VZ88lo5agd6PPXjZ0dmZ5i3sx2TOlbg1OctqVtKmoMQw54Rzr7Q8WcYfAhq9INef0DfLVLZrsoKOsyCelJXF2wdpe6OIIXzJ0TCotYwtxGcX5uxz7QkyRG5fhD8OUQK4M+MwQBr35P+jtJTwVYLLgHmcyteDzyCMuYpEAgEAoFAIBA8BYKDg9FoNIwYMSJf2/311180aNAA/ZMaJ3Lh7Fnz2J7r168TGxub530kJSWhUqkIDw/nvffeY8mSJbluM23aNAYNGpSfqQoET4UXXwyTu0lmE6B/xej8uh2ZyKzd1zgTYpr5FRyW+x93ZIIkimiNuVKn7kQ/7nTNsbbJPjfs7O/wR++MErPws3BisemYdB0cmQPrBsLhn6VlpZplrA9qAVbZdGfUuEpCnIzcZfIpY1EM+w/LIbMjszPM21mDlZUKjY01RbR2gBDDnjner0L7H6TXqIsfDD0Kw0+bB+WXaCD9e3oFLH4N7p2SnmcWww7OgL9HwJmVUhfKK1tM93F5M5z7XXKfAZRuCVYW3v5UKug4G6q+bZqvJxAIBAKBQCB4ofHx8UGlUuXpodNJecfXrl3L8zYqlYpy5cpZPHZqaiq9e/emTp06LFiwwKLwlB2tW7cmIiKCw4cP5zjuiy++wNbWFicnp2wftra2Zg0CAOLj4+nQoQMrV65UlqWlpdGpUyd+/vnnPM9Vo8mI5OnVqxcjRoxg0qRJed7+cdm/fz/Vq1dHo9Hw6quvsm3btmzHRkdH89Zbb+Hs7Iynpycff/yxidCYkpLC8OHD8fDwwNnZmX79+pGYmPjMz0Hw31MIxDBjN8lsAvSvPcxooTtn33V+PxECQF2jAylPZZLxkphT2lvqyJhTB8LHInOppMyp5bBuABj0UOUtKV8MpJt9+Y816gYsaA5bx0g5RwkPJCdY9yXg4CmNyansUaUy7Zj3rMSwREvOsOcvhjnbZzjDvJwz3riLOBnFMJEZ9t9i7wZuxc2Xy2LYnX8g4rI0DqS8vVTjf0xXt0v/alylfzNn8OnTYddX0s+1B0uCW9eF2c8joDZ0nAUa58c9E4FAIBAIBAJBASM8PByDwZDj4+bNmybblCpViqSkJLNH06ZNmTlzpsmyhIQEiyKXXq/nnXfeQafTsXXrVkaNGkWnTp0IDQ01G2tJYLO3t+fatWs0aNAgR+EOYPjw4cTHx2f7GDJkiMVr4+TkxOrVq1m9ejUGYxTJt99+i5eXF2PGjMnzNVapVNjZ2ZGWlkaTJk3Yu3cv1atLVR4GgwGdTmf20Ov1Oa7LjZs3b9K2bVtatGjB8ePHady4MZ07dzbpApqZN998k0uXLrF9+3bmzp3L7NmzmTZtmrL+ww8/ZOPGjaxZs4a1a9eyefNmRo0aledrIHhxeOHFMDkzzJIzzGCQAvIByvlo0ekNSifJHjWLAfAgLoVH2Yhbcti77Gwq7aU1Pn/KIknRytK/J5dKmV+7JsJfQwED1HgXOsyE6n2l0q7o25IoYDBIJWFhp6WQ8UYfQ6PR0HOV9LzHUmjyae6B+CZiWL2ne15GouLNr6+743/XNTI7nDM7w4xuMEA4wwoa/nXArQTYOUti1uDD4FxMKgm+uQ+SHmW4xWoNkP7NLIad/R0eXpKEsiafgHtJy64wgUAgEAgEAkGhJTU1lfj4+NwHZkKlUnHhwgXatm2LSqVCo9Gg0WiwsrJCrVYrzw8dOkSDBg0sHrNPnz6cOnWKzZs3o9FomDBhAg0aNKBp06bcuHHDZHxmYe7Ro0dmAt6VK1eIiIgwWaZWZ9zT/PTTTzk6w2bPnm02x/bt22NtbU39+vXZuHEjNjY2qNVqJkyYwL59+7C1tUWlUrF3715lm1u3bmXrjktJSSEgIACVSkW1atVo27YtNWrU4LfffsPGxsbsMWbMGObNm2dx3VdffZXr72jmzJkEBQUxdepUKlasyE8//YS7uzuLFy82G3v27Fm2bNnC/PnzqVOnDl27dmXYsGGK+y0qKor58+fz/fff07RpU1q2bMmECRNYvHgxSUlJuc5F8GLxwt8R2qrlbpLmqnFkQioxSWmoVLBqQB26V5cEMCsV1C/lib+7PQCXLLjDpm27TMUvthEcFpshhhmdYZYC4Z+ICt2gyCuQGAHrB8ABozJdexC0+0G6cbd1gFc7SctPr4QbeyHkKKg1MPAANBsHzT4D/1rSmBINpBt/61wahspimMZFyi97BsjdJG2tM15uWQP0nwdak8ywTM4woxgWn6IjKVWEVT53bB1g2En4+Aa89g04F4Wyr0nrLm+ROk1iAM+yULaNtDxzwP4Ro7W7wQdSx0iBQCAQCAQCwUvHokWLaNOmTb63q1y5MomJidlmfcXFxdG/f3+6dOmCrW3GPU5YWBjNmjXj7Nmz7N69Gx8fqTmTlZUVixcvpn79+lSvXp3169eb7TMqKormzZublTT++uuv1KhRgzNnzphtA4/vDFu4cKFFZ5b8ePVV88ZTdnZ2Ft11/v7+nDlzxmTZiRMneOutt8zGHjlyBLVajUqlYuPGjWbrv/jiC8LCwvDw8GDt2rUWZg579uwx+b2q1WoaNWrEkSNHLI719vamatWqyrJmzZpx584dwsLCOHDgAHq9nlatWpmsT05O5vTp0xaPL3hxeeHFMDulm6S5aCG7woq52ePqYMt33Ssz7+3qzO5VDS9nDWW9pTKoS+HmuWHHbkWRotOz5Xw4Or10Ux3kJZdJPmXHkL0rDNgDdYZKIfalW0m5RW2+kUoZZaq8Kf17djVsGC79XL2P5dKyvCKH6PvXeWZuGVlMrFTMRVnmal8AnGEmZZIZzjAnOzUaG+laRIhSyYKBtVrK15ORxbArW+GasWtqYBPwelXqPpkYCbF3pRy+8HNSNl613v/5tAUCgUAgEAgELzZqtZoVK1awfPly/vrrL5N1er2eXr16ERQUxNixY5XlixYtonz58nh5eXHo0CH8/PxMtrO2tmbJkiWMHTuWHj160K5dO+7cuQNAaGgozZo1IzAwkB9++MFku48++ogRI0bQqFEjs7kALF68mHLlymX7WLZs2dO6LNni7OxMdHQ0IAmCP//8s1J6mZnU1FQGDx5Mr1696NSpE2PHjiUhIcFsnL29PeXKlaNo0aIWj3fjxg1KlixpsiwgIMBiGWp2Y0G67jdu3MDLywtHR0eL6wWFixdeDNNYG8skLTjDrhvzwkoVcVKWtXrVhzYVpD+kAHcHAMJjk822TTZ2pzx15xEghecXdZGcZJYC4Z8YG3to8zV8eF7qrle1l6kQBhBQFyp2lzpFxtwBazuo/8GTHTewsSQUVOz+ZPvJAfl61QuScsyKaO1QWz//l54coK/VqHGwzXDQqVQqxR32QJRKFkxKNJDcjPH34d+l0rLAJmCjkVyWILnDzhhDQMu2BQf35zJVgUAgEAgEAkHB4NChQzkG4GcVSmQCAwP56quvzMLgN27cSHBwMKtXr8bKaCzQ6XQsXryYqVOnUq9ePZycnLI9noeHB8ePH8dgMODk5MS+ffuoXr06tWvXpkGDBmZutL59+xIdHc3ChQt54403WLRokbJu5MiRnDhxgnr16rFq1Sp27txp9pg0aRIVKlR4KtcyJSXF5FzKlCkDgJeXF/fv3wdg+vTpbNu2DVXW+1pg6NChNG3alAoVKuDl5UXv3r3p16+f2ThXV1cOHTpEvXqWI33i4+NxcHAwWebg4EBKivl9XHZj5fOxtF6j0Sjln4LCRS41dAUfWxvpDysnZ1hQJjEsM7IYEpesM1uXIYZFA+DuZIu7sQNidFIa6XoD1lbmf9TPFJUKusyXRLF930KdwVLJ2JPwamco197UdfOUkcWwagGuTP9fZXyc7Z/ZsfJDGW8tWo2aOsZmCpkp4mRHSFSSSW5YfIqOXvOPULeUJ5+89mxKSgV5RG0HnefC6rclcVhlDSXqS+uKVob75yD0uOSiBKjS6/nNVSAQCAQCgUBQIKhfvz4HDx7Mdn1iYiLbt2/H2mi4SEpKIj1dui/s168f3bp1Iz4+nvT0dFJSUmjWrBm7d+/G1tZWySOzs7Nj//79qFQqpk2bRu/evVmyZInZsZo3b46HhwdVqlRh8+bNAFy8eJFPP/2UDz74gHv37jF+/HiGDx9OhQoVOHnyJDt27OCXX37B29sbBwcHpfQSQKvVMmTIEFasWMHvv/+e43UoUqQIjRs3Vp6/9957DBo0KNvxqammZpDExESKFCnCgwcPAKmb4/DhUuVSQEAAt27d4sGDB8ybN499+/aZ7W/69OmcPHmSw4cPM3PmTAA++OADJaPryy+/zHH+mbGzszObX3JyspmoldNYkEQxS+vT0tIwGAwW9yd4sXn+9pwnRKPOyRkm2Szl8sas5CSGJRnFsPgUaZ27oy2uDpJgZDBk5GD956hUULMfjLosZSA9DZ6hEAYZYpi7oy2dqxajbilz8el54O5oy7GxLZj7VnWzdUqIfqYyyWM3IzkTGsOSf26iS8+9s4ngGVP2Nfjfr6C2hzJtJKcYZDSkODZfKpd08oZSzZ7fPAUCgUAgEAgELwQODg506tSJe/fuYTAYeO2119BqtWi1WlxdXSlevDharZa9e/fywQcfoNVqCQgIUMZotVp+/PFHi04ogISEBPR6PREREVy8eJHixU3jbgYPHswHH3wAgK+vLx9//DHdu3cnNDSUXr168fnnn+PtLWU+t23blmrVqgFSxlj37t0pWbIker2eyZMnU7duXeLj4zl58iROTk7ExsYSHx+Pr68vGk1GXnL37t05cuQIycnJJCcn88MPP3D+/HnlubxMLhcEyWHl7u5u8tzJSbrnfvXVVzl79izDhg2jS5cuVK5c2eQc58yZw7fffsuaNWuws8uIqrGysuK3335j6dKljBs3Ls+/Mz8/P0JCQkyWhYSEEBgYmOexACVLlsTPz4/79++TlpaRES6Xr1ran+DF5oUXw+xs5AB9c2fYdaMzrFQ2YpizMUA9Ltk8ED+ruObhaIeNtRUuxpypZ1IqWUi4H5tMSFQiIHVEiTIKh24FIDQ/K/a21lhZcPhZ6ih5O1I6p+Q0PZfvmzddEDwHyr4Go6/C/5ZnLJPFsFRjt6BGo3NvJCEQCAQCgUAgEBgZNmwYEydOZO/evRZD4ps3b84vv/xicd2oUaOy3a/cudHLy4tq1apRsWJFvv32W5YvX25x/Lhx4wgMDOSVV16hfPnyjB492mxMdHQ0lStXpkyZMkycOJGrV68yadIkZeyKFSvo2LGjUsaZlJSEVqsF4MGDBxQrVozq1auzZMkSUlNTefToEW+99RY6nWQK0ev1JCcnKyIcwO3bt/H19VWeP3r0CDc3NwAaNmzI2rVrOXHiBD/++COAkgU2efJkPvnkEzZt2mRRXPLx8WHnzp0sWrSIbt268ejRo2yvpUyDBg3YsWOH8jw9PZ29e/fSvHlzi2Nv377N1atXlWW7du2iWrVquLm5Ub9+fdLS0kw6Z+7atYsiRYpQsWLFXOcieLF48cUwtRygbypeJabquBsttT99rDLJLF0EPYwlkvK/kfFCDLOEwWCg46xDtP3xAImpOhJT00k1/m48nAqeGJYdnk6SGLbs8C3qTtnFxXux3DEKfABnQmKe19QEWbHTmjZ/KFoZ3APBIwh6b4Ra7z2/uQkEAoFAIBAIXjguX75s5tp6GuzcuZOEhATS0tLYtGkT1tbWXLhwgdhY84Zuer2eVatWcfbsWYoUKcKpU6dYt24der3pfa+rqysbN27k66+/5vTp07Rq1Yq3336bVq1acfPmTWbNmmUi0EVFReHiIlVU/PLLL/z8s9R5vW/fviQmJjJmzBh0Oh0zZswgPT2dhw8fEhwcTN++fZV9nDt3jipVqhAdHY1Op+Off/6hWLFigJS9lZSUxJgxY9BqtcTGxlK0aFEWLlzI9OnT2blzJ9OnT8fJyQknJyfGjRvHwoULleeffvophw8f5tKlSyxdupSYmBgaNGjA4cOHLV7T4cOHc+zYMb766ivOnz/P+++/j16vp0+fPgDMmjWL3r2lRlqNGjWievXqvPvuu5w4cYI1a9bw008/Kc0PSpQoQZcuXRg+fDgHDx5k+/btTJgwgTFjxiils4LCwwsvhmnU0imk6vTo9RldKsJipNpfrZ0aN0fLIozW6AyLtySGZckgczcKOXJu2HMrkyzgxKfoCI9NJi5Fx73oZMVBZ6e2wt7mxXkDkZ1h0YlphMUks+HMPcXtBnA6JPdvKQTPCRsNDDshPUo2fN6zEQgEAoFAIBAUANzd3bl8+XKOXQF1Oh3r1q3j0qVL1KlT56nPwdraGgcHBxNh5fr160r4PMC1a9eYNGkSpUuXZty4cUyZMoUbN24wefJkRo4cSZkyZRg/fjxHjhxRQt2DgoKYPHkyderUoWPHjnz33Xc8ePCAzp07M2LECEqXLg1AbGwsycnJeHp6otPpmD9/viIayajVav744w8GDBjAnTt3qFChAr/88gsXLlxgypQpgNQ4oFGjRnTt2hUbGxvWrVvHwIEDOXPmDF26dKFbt24sXboUg8HAzZs38fLyom/fvvz777/UqFGDX3/9lfj4eOLj45k8eTL9+vVTnv/xxx+UKFGCEydO8MEHH5CYmEhwcDAPHz60eE2rVq3KypUrWbZsGTVq1OD8+fNs375dcb/dvn2b4OBgQGqUtn79ehwcHGjQoAEfffQR06ZNo2vXrsr+Fi5cSOXKlWndujXvvPMOgwcPZuTIkU/4mxcURF742iG7TAJLik6Pva30XBa4nO2zz8PKcIaZlkmmpetJSzdt/yo7wmQxLDIhlcRUHbbWVgWiM2JBITox41o+jEvBwfj78HC0zbZ2viDSqHQRyhd1Rm8wcCk8jsvhsYQ+SlLWC2dYAcfqxRFeBQKBQCAQCATPnrZt2+Lr64u/v3+O41QqFUOHDqVs2bJPfEy5W+Tdu3fx8DDNTdbr9ezfv59z585Rq1Yt9Ho9TZo04fDhwzRr1oxvvvmGzp07o1ZL96xvvvkm3bt3Z82aNSxatIipU6dSo0YNDh06RMuWLQkPD+e3336ja9eu7Nu3j379+tGyZUvee+89Hjx4gL29PbNmzaJChQrY2dmxYcMGNBoN7du3B8DR0ZErV65Qq1Ytpavm3LlzqV27Ng4ODqxYsYIFCxZw6NAhbt26Rdu2benQoQMpKSnY29uzY8cOunfvzoQJExg+fDj169dn9OjR2NvbU716daysrExyx3JDzjUrWrQokZGROY7t1q0b3bp1s7juu+++M3nu7+/Ptm3bst2Xq6srq1atyvM8BS8uL74Yps4QolJ06RlimDH4Xha8LJFdmaSl/DG5xE/+9+r9OGpO2knVADeW96/9BGdQuIhJyhDDIuJTcLKTrnF27ryCir+7A5tHNOTYzSh6zD3M5fA4IjPlxF15EEd8ik45P4FAIBAIBAKBQFBwcXJy4vTp09y7d4+kpCSLY1QqFR4eHri6uj6VY3bt2pVly5ZRsmRJk1B2GX9/f+bPn4+zszMghct7e3ubCWcyNjY29OzZk549eyouL5VKxerVq/H29sbW1pYDBw7Qtm1bJk+ezIgRI3jvvfdYuHAhAKVKlWLRokUAtGvXjgoVKihZYp988gktWrRQyjANBgM+Pj5KZ8rKlSszc+ZMZs2axfjx47G1le7v7O3tAYiLi2Pq1KkMHDgQkNxj7777LqdPn2blypVP5XoKBE+TF/5OXm1thdpKhU5vMAm9lwWunMQKpUwyVYdeb1CC1C11pnR3tDP+K/3RLzt8G4CD1yKewlkUHjI7wyLiU5S8MPcXTAyTKest2WvvGcturVRSCeX92BT2XX5I3VIeL+y5CQQCgUAgEAgELxMqlQo/P78n3s/OnTvzNM7Z2Zk9e/bkeb/ly5fP81hnZ2dFRMvsdmvYsCE3btxQAu8XLFjAggULzLa3trY2CbH/7LPP+Oyzz3I97rBhw0hPNzePdOnSxeS5p6cnGzZsyHV/OTUcEAieJYWivk9jLJXM7OiSnWFOeXCGGQySICZj0RlmFDwKYkfEgkR0UoZ7KiI+RclWe1EFIxcHG4q6ZLQe9nW1p0ZxqY3w0BX/UnPyTm5FJDyv6QkEAoFAIBAIBAKBCZk7Pz4LRJi8oDBQKMQwuVQyc0fJeGMOWE7OMI2NNbbGvK/MpZKyGJY54iprmWRmDAaD2bKXFRNnWFyqUlr4oophAGWM7jCA4h4OtK7gozxP1xs4f0/khwkEAoFAIBAIBAKBQPCiUCjEsJycYTllhkGGcyxziH6ScT/eWg2V/V0p56PFSyu5g+RyycxkFuFeRq7cj6P593v56/Rds8yw+8byQrk744tIOZ8MMSzA3YEOlX0583krWrziBcCjBNFZVCAQCAQCgUAgEAgEgheFFz4zDMDORtL0MothcSm5Z4aBJJZFJaQq3Sel/UjiloOtNesH18MAWBvzxDwsOJxS0vSKIPcysuPifa4/TGD9qbuU9nJSlkfEpxBtFMcC3B2e1/SemMzOMH/jebg42FDEKJBGPiMxbOk/t9h96QFz3qquNIYQCAQCgUAgEAgEAoFA8GQUCmeYnVoSCkzLJGUxzCbHbS11lJSdYXY21lhZqRQhDCyX+yXrzDPGXibCje6vB7EpWQL0U7kdmQhAcXfH5zK3p0HZLM4wGVkYjXpGYtjcfdfZd+UhR27k3EpYIBAIBAKBQCAQFGyCg4PRaDSMGDEi39veuXMnz2PDwsLYu3cvc+bMYcWKFWbrO3XqxJIlSwA4dOgQDx48UNbNmjWL48eP5/lYBoOBc+fOmS0PDg4mJSXF4jYpKSl07tzZYndNgeC/pFCIYRoLzrC8BOgDaI1iWWymMkl5P/Y25pfHohhmIXD/ZSJMFsPikhUnGMD92GQi4qU3wQCPF9cZFuTlhKyHZhbD3J6hGKZL13M/Trp2odGWWz8LBAKBQCAQCASCvOHj44NKpcrTQ6eT7iWvXbuW521UKhXlypWzeOzU1FR69+5NnTp1WLBgAWfPnrU4LiUlheTkZJNlv//+O5UrV+bhw4fZnlvfvn2pWbMmzs7O+Pr60rRpUwYPHsyMGTNyvCa//vorDRo0ICQkBICEhARef/11bt68iaenp8nDycmJPn36mGx/9+5dmjVrxr59+5Rl0dHRNGvWjHXr1lk8pp2dHUlJSaxZs0ZZ1rFjRzw9PXFzc8Pa2lo5ZteuXXOcP8CtW7do3749np6euLq60rFjR27fvq2s1+v1TJo0ieLFi+Pg4ECVKlUszm3nzp3UrVsXe3t7PDw8+O2335R1Fy9epEWLFtjb2+Pn58f3339vsm1YWBg9evRAq9Xi7u7Oe++9R1xcnMX5RkVF8e677+Lh4YGLiwstW7Y0eT2kp6djZWVl8rpycnKyuC/Bk1E4xDCjMyzZojMs5/IyS84wWdyyVPqosbHGMUvJmlxW+bISHiuJNRHxqYr4BaDTS40FXOxtcLHP2aFXkNHYWNOvQUmalC3CK0WdleXP0hn2IC6FdOP1u/tIiGECgUAgEAgEAsGTEB4ejsFgyPFx8+ZNk21KlSpFUlKS2aNp06bMnDnTZFlCQoJFkUuv1/POO++g0+nYunUro0aNolOnToSGhpqN/fTTT/nggw+U5ydOnKBv377o9XpKly6Nq6srGo2GatWqmWzn6+tLs2bN+PLLLwHYu3cvoaGhHDlyBIASJUpw6dIls+P9/PPPVKhQgebNm5OWlsZHH31ErVq1cHFxISIiwuQxbdo0s+2LFSvGnDlzWLVqlbJs9OjRtG7dmp49e5qM3bx5Mx4eHlSoUIE7d+4wceJEKlSogK2tLevWrSMiIoKVK1fSoEED5Zhr1641O2ZW7ty5Q82aNdm6dSvr1q3jxo0b9OjRQ1m/adMmtm/fzty5c/nnn39o1qwZ3bt3V64NwNatW2nfvj1t27bl2LFjrF+/nqCgIADi4+Np1aoV7u7uHDhwgDFjxvDJJ5/w66+/Ktv37NmTkJAQtm/fzu+//86ePXt4//33Lc7366+/xsrKik2bNrF161YA2rRpQ2xsLACPHj3CYDCwd+9erl69ytWrV7MVTwVPRqHKDEuxmBmWW5mktN6SGGafTQ5YuaLOnAuNITVdEsFSRJmk8vO1+/Fm64u/wK4wmXHtypstc3+GYlhYTIYAdlc4wwQCgUAgEAgEgiciNTWV1NTUfLlsVCoVFy5cYPTo0WzZsgU7O6kpmJWVFWq1Go1GyhDetWsXo0ePNhFY5GP279+fU6dOsW/fPjQaDRMmTODmzZs0bdqUbdu2ERgYqIz/7LPPKF++PK1bt8bd3Z0ePXowbdo0Bg8eDMC6deuYMWMGv/zyi8lxJk+erPw8cuRIateurcwtJ6ytrVm5ciU7d+7k66+/xs7Ojg0bNuTp2lSoUIHg4GBUKqmEZv78+Sbrly9fTnp6Ojdv3qREiRJYWVnRvXt3XFxc6NmzJ1WqVAEkx158fDxJSUmcPHmSwMBAwsPDAXBzc1OueXY0atSIRo0aKc8///xzevToQUxMDC4uLtSoUYPdu3ejVkvSR5UqVdi4cSN//fUXderUQafTMWjQID799FPGjx9vtv9ly5aRnJzM0qVLsbe3p0aNGpw/f57p06fz9ttvA3D8+HFWrVpF3bp1ARg+fDhz5861ON8RI0bg7++vPF+6dCl+fn4cPnyY1q1bExUVBUC1atXQarUW9yF4OhR+Z1huZZKWukmmZu8MA1j6bi12j2pMoKeUg/UyO8NSdOlExGeIQbIImZkXOTw/J56lGHYvOkNgvPso8anvXyAQCAQCgUAgeJlYtGgRbdq0yfd2lStXJjExMdusr7i4OPr370+XLl2wtc2I1AkLC6NZs2acPXuW3bt34+PjA0hC2uLFi6lfvz7Vq1dn/fr1yjbu7u6sXbuWihUrsmTJEjQaDaNHj8bJyQknJye6du3K8ePHqV27Nk5OTqSnp3P16lWGDRumPAA+/PBD5Xl0dLTFecfHxzN//nxsbW1p164dSUlJJCcn07lzZ4KCgvD09MTd3Z2goCDatm1rcR+7du1Cp9Nl+3B0zMiNbtOmDe+//z7Tpk3jjTfeoEKFClSoUIElS5Ywc+ZMatWqxd9//01wcDCdOnWiYsWKbNmyhbCwMDw8PPLkEgOpzFCj0SjHLlq0qCKEyWg0GtLTpXv+3bt3ExYWlq2T6+LFi5QrVw57e3tlWZMmTThz5oySi/a///2PJUuWEBMTQ1hYGKtWreJ///ufxf1lFsLkucjzBqmM0tbWVghh/wGFQwyz4AyLz2M3SWdLZZJGUS07MczJTk0xNwds1eZZZS8bD2ItByP6umR8E1HYxbBHiakYDIanum/hDBMURu5EJtLtl3/YdiH8eU9FIBAIBAKBIE+o1WpWrFjB8uXL+euvv0zW6fV6evXqRVBQEGPHjlWWL1q0iPLly+Pl5cWhQ4fw8/Mz2c7a2polS5YwduxYevToQbt27ZSQ/Pr16xMUFMTixYu5ffs28fHxxMfH89ZbbzFlyhSSkpKIjo4mISEBg8GAg4MD5cqVo1y5coqrqGzZssoyGxvLlVKRkZHMmDGD119/XdkOYP369Vy7do1Ro0YxfPhwrl27xubNm5/4OqakpNCnTx9WrVrFpUuXWLlyJY8ePaJ69eoADBkyhP379zNy5EiOHDlC69atAbC3t6dcuXIULVo0x/2np6fz77//MnHiREaPHm0mgMkcPXqUc+fO0axZMwCOHDlCiRIl2LBhA6VKlcLPz48BAwYQHy9VPHl4eBASEmJyvxcbG4terycyUmp0NmPGDC5cuICbmxu+vr7Y2dkxbty4PF2X+fPnY29vT506dQDp95Kamoq9vT3FixfnzTff5NatW3nalyB/FAoxzGI3SaMYps3VGSaXSVpyhuV8eWSx7GUWw8Jiki0uL+WVYT8uDGWSlpDFsLR0g0VH3JOQ2Rn2IC6FVN3L6z4UFB7W/BvKiduPWHE07x2RBAKBQCAQCJ4Whw4dyjEAv2TJkha3CwwM5KuvvmLSpEkmyzdu3EhwcDCrV6/Gykq6d9TpdCxevJipU6dSr149nJycsj2eh4cHx48fx2AwPHZIup+fn+ICu3btGgBt27ZVlmV2Z2WmePHiHD58mLS0tHx1kHxc/vrrLxo3bsyKFSto27Yt3bp1Y/r06RQpUkQZk5iYaJKZBuDq6sqhQ4eoV69etvt+7733sLW1pXr16lSvXp1PPvnE4rijR4/SoUMHunfvrrgEw8LCiIqKYt26dSxfvpyffvqJDRs2KC67zp07ExoayuTJk0lOTubs2bN8++23gCRqGgwGunTpgru7O3v27GHjxo2EhoYq2+fEggULGDduHN999x3u7u4A1K5dm2PHjnH06FG+++47zpw5Q9OmTbMN5Bc8PoVCDLPYTTI5b84wWSyLT8nsDMs5MyzrcVNeYqEis4NJxsZaZeIG8y+kzjCNjTUOxmYKUfFPt1Qy83U1GCxfZ4HgRePf248ASeAFWHDgBlO2BKPXP11npUAgEAgEAoEl6tevn2OAfkJCAuvXr8faWvqMn5SUpDiz+vXrx9q1a4mPjyc9PZ2UlBSaNWvG7t27sbW1VcYZDAb279/PgAEDAOjdu7fFYzVr1gwPDw+qVKnC5s2bFTFk6dKlrFy5kl69eqFWq5XHvHnzGDdunElWmUajUTo5btiwAQ8PDwC6du3KmDFjcr0ezs7ObNu2TXFhAUyaNIly5crxww8/MGvWLMqVK8fHH39scftWrVqh0WiyfSQkJChje/TowXfffUf//v05duwYaWlpLFu2jJMnTypjDAZDto6unPjqq684deoUf/75J3fu3KF69epKIL3ML7/8QuPGjenYsaNJp0idTkdKSgorV66kbt26dO3alfHjx7NixQrS09OpUqUKCxYs4Ntvv8XBwYEWLVrQsWNH1Go17u7ubN26lb1797Ju3ToaN25Mu3btWLx4MXPnzuXq1asW55ucnMyAAQMYOnQos2fPZujQoco6T09PatasSaVKlejRowdbtmzh9u3bSti+4OlRSMQwU4dWii5dCbfPPTNMcobFZi6TzCUzLLvjZiY5Lf2pl84VRCw5w1zsbfF0ygg6LO5h+duIwoCbg+QOi3zM3LAt58Lo9PMhVh67gy49Q1TNel1FR0nBi0663sCpO0YxLDaZFF06X28OZu6+G5wKiX6+kxMIBAKBQCAAHBwc6NSpE/fu3cNgMPDaa6+h1WrRarW4urpSvHhxtFote/fu5YMPPkCr1RIQEKCM0Wq1/Pjjj0qofFYSEhLQ6/VERERw8eJFihcvbjZmxYoVREdH89tvv5nkbw0YMIDJkyej0+lITpbuFZKTk2ncuDEJCQl8/PHHTJgwAZAca5s2baJ///5KFlV2/PDDD1y4cEF5/tlnn3Hp0iVGjhzJsGHDuHTpkuKEykz//v25dOkSycnJJCcnM378eO7evas8T05O5vPPP8fFxQWA7du307BhQ6ZMmcLOnTu5fPkyrVq14v333yctTarSio+Pz9bJlhNFixalUqVKdOzYkS1btnDr1i2TDpejRo3i448/ZtGiRcybN8+kdNTLywsfHx+T45YpU4a0tDSlfLRv3748evSIW7ducffuXXx9fXn11VexsbHh7NmzeHt7K5lwIIXfGwwGzp07ZzbXpKQkWrVqxZ49ezh8+LAimmZHQEAAHh4e3L59O9/XRZAzhUIMs1ObOrTiMwlbjrY5i2FOljLDjIH49rY5i2HycZOzOMOu3I+j8pfb6TT7Hy7ci8nLKbywyJ0kM4tfrg42eGql57bWVvg4597J5EXFw8mYG/aYYtgfJ0M5HRLNp+vO8fqsQ0QnSvu5Z8wJ8zTuP1TkhglecK7cjyPB+EVDZEIqIVFJyIawXcH3n+PMBAKBQCAQCEwZNmwYEydOZO/evRZdXc2bN+eXX36xuG7UqFHZ7rd9+/ZYW1vj5eVFtWrVqFixIt9++y3Lly8HJKFk//79vPbaa4BUIiiH5y9cuJAJEybg5OSEq6ur2XzLli2r5E55e3uzffv2PIWwT5kyhYiIiHxdn+vXr1O/fn28vLxYsWIFALdu3WLgwIHKmKSkJDQaDc7OzoDUGbJcuXLcv3+fN954g4oVKzJjxgxWrlypiFN37941EZUeB5VKhZWVlSICbty4kVmzZrFr1y7efPNNs/H16tXj1q1bSgdLgAsXLuDq6mpSwmltbU1AQAAqlYr58+fzxhtvAFKZ6oMHD0yuoSyCZc2KAxg/fjz37t3j6NGjVKtWLdfzuXHjBhEREQQFBeXxCgjySuEQw7I4tOSSR0dba6ytLKvyMha7SablzxmWksUZdvpONCk6PWdCoukw6xDHbkZZ2rxQIJfvVfF3UZa52ttQxCiOFXOzz/V38CIj54YdvRlJzck7WXLoZr62j0nKeN0Fh8UybMUpElN1SofOGsUlu7RwhgledE4aSyRlzoZGKz/vCn7wH89GIBAIBAKBIHsuX75s0bX1pOzcuZOEhATS0tLYtGkT1tbWXLhwQSnp27RpEyVKlKBEiRIAREdHm5Rpfvnll8THxxMXF8fixYuV7Tds2MCcOXNMjuXr68v06dOxtramd+/euLm5mc3n0qVLxMXFKSIaQKVKlShRogRz5sxhyZIlJvORmTRpEitXriQiIoIhQ4YA8P3333Ps2DHWrFlDQkICiYmJ7NixQ8nvqlmzJlWqVKFfv35cunSJS5cuERAQQFpaGqVKlSIwMJDdu3eTmppKYmIilStXxtvbm5iYGBo0aMDhw4ctXtPx48ezbNkyzpw5w/79++nSpQv29vZ06dIFkJx2tWrVwt3dnWvXrimPmzel+7bWrVtTtmxZ3nzzTY4cOcLatWuZNGkSo0ePBqTg/4ULF3L27FkOHjxIp06dUKvVSvfJTp06UbRoUd544w0OHz7Mzp076du3LzVr1qRWrVpm81+xYgWdO3cmKirKZD6yGLd48WLmz5/PmTNn2LhxIx06dKBSpUq0b98+D68wQX4oFGJYRrmi5NCSXV65lUhCNt0k0/IYoG8huB8gIiGjw2K63sCGM3dznceLiuwMq1zMVVnm6mBDg9KeNCvnxeAmpZ7TzP4b3I1lkiuO3uFhXAo783lTn2h0ynz6WjkcbK05eC2Cj34/A0ivvwp+0jcpoqOk4EXn3yxi2OlMpZGX78cREpX4H89IIBAIBALBy4S7uzuXL18mNDQ02zE6nY5169Zx6dIlE4HoaWFtbY2Dg4OSSQaSy6pMmTIA/PHHH4orLCdUKhV9+vRBpVJRvnx51q5dm2O3xS+//BJvb28Akyif3bt3U6dOHezsMqp8zp49y61bt8weMo8ePWL16tX06dPH5BjOzs5s376dDh06cOzYMdq0acPKlSv59ddfFfdYdvTs2ZNq1arx448/UqRIEWrVqkW7du2oW7cuiYmJBAcH8/DhQ4vblihRgq+//po6derwxhtv4OzszOHDh5XzDQ8P58CBA5QuXdrkUbNmTUDqFrpp0ybs7e1p2rQpQ4cOZfDgwSaZa7Nnz6Z27dp07NgRT09P9uzZo5RVOjk5sWfPHjQaDa1ataJ79+5Ur16dTZs2oVKpzOYfHh7OtGnTzOYjB+67uLgwZcoUateuzfvvv0/jxo3Zt2/fY2WpCXKmUFzRjDJJU2dYbuH5kJEZFp+iw2AwoFKpFGdYXgP0s2aGRRpdPX6u9tyNTuLc3VizbQsLcrZVZX9XZZmLvS1OdmoW9an5nGb13yE7w+Tyr/x2lYxPkZxhNUq48333ygz+7V+2nJe+FfB1scfPzR7IcIbp0vXEJKXhkaksVSB4EfjXmBdmbaUiXW/gTJacsF3B9+lT33IHJ4FAIBAIBIInpW3btvj6+uLv75/jOJVKxdChQylbtuwTH1PuFnn37l0l3F5Gr9ezf/9+zp07R61atQBo3LgxlStXVsakpaXx8OFDHBwcuHDhgsm6zPNt0qRJjvN48OCBkjl16tQpRcj6+++/adSoUY7bpqWloVaruXz5Mvb29vz666/UqVOHSpUqERERQXx8PGFhYRQtWpRy5cqh1+tZt24dtWvXxtvbm8WLF3P69Gllfz/88ANLliwB4M4dqcP4pk2b6NevH4MHD+aLL75gzJgx1K5dm0WLFtG9e3ciIyOznV+/fv3o169ftut3796d4/mBlMu1adMmi+vs7OxMQv4tUapUKTZu3GhxXdGiRU3mr9fn3HyvS5cuiqtN8GwpFGJYVmeY0klSY5PtNjJymWS63kBiajqOdmpSjPvJrUwyc3nmiVtRbLsQzketyhIZLznDGpctwoqjdwgOiyUtXY+NdaEw4imkpet5aDzXckW12FpbkZqux9Uh9+teWHAzimEy8ZnKbfNCQookomk1aqoXL0rrV73ZdkHKTyrqqsHPVerEKTvDvtt2mfkHbrB6YF1qlnB/0ukLBP8JUQmp3IqUnF+1Srhz+EYkF8OkLwlc7G2ISUpj16UHQgwTCAQCgUDwzHBycuL06dPcu3ePpCTLVRcqlQoPDw+zTK7HpWvXrixbtoySJUsqIfGZ8ff3Z/78+UqullxyKJOcnEyxYsUAqXzxcUWSkJAQWrdujUqlonr16rRu3RqDwUB8fDwNGzbMdjuDwUCRIkVISEjAw8ODNWvWULNmTTp37gxInQ/79+9P6dKllW30ej1BQUFs2LABgNdee83E7TZy5EildLJJkyYsXryYKVOmMG7cOKUBwNSpU7GxsWHGjBl07979sc5ZIMiNQiKGmTq0ZGeYNg/OMHsba8WpEJesw9FOnXdnmBygn6bn++1XOHwjkkrFXJXOgtUC3Pj79D3iUnRcvR9PeV/nxzvBAkpIVCIGgxSS7+loh5ezHaGPknC1f3nEMI+sYlh+nWFG4dbR+Fod2/YVdl96QFq6gaIu9hQzOsPuRSehS9ez/2oEegNsvxAuxDDBC0OwUfgq4eFAaW8nDt+IJC1dsui3Ku/NHydDuf4g/nlOUSAQCAQCwUuASqWyGGqeX3bu3Jmncc7OzuzZs+exj6PVanN1EmUlcxmkTPXq1ZXOiJk5cOCA8vM333xjtl6lUhEdHW22PLO7bs6cOWZ5ZdnRt29fk/PZu3cviYmJtGvXjnr16pmMnTRpEvfu3cvTfgWCx6FQWJXssmR3xeWjTFKlUinuMLlkTRbV7HLJDJOdYSm6dB4ZuwDejU5Sws+LaO141Zj5dP5u4esqKTuYapRww8pKhbexa+TL5AxzN3OG5V0MS9Glk5ouvWbl12pxD0cGN5Zy1qoFuOHjrEFjY4VOb+B2VCK3IhIA+PdO9FOYvUDw33A5PA6Asj5a5X1CpkYJKcz1flwK6XrzD28CgUAgEAgEgsKBo6OjWYdLBwcHMyFMxtfX97+YluAlpVCIYWbOsHwE6AOKk0kWsfKeGZZRnik7gsKik5QySQ9HWyr6SV0WzxVCMWzjWUmpf72y9CbVpEwRHGytqVbcvFNJYSWrGJaQmp7nG3q5RBJMhdsPW5Zh76gmvFHTHysrFYGeTgAcvh6pvDbP3Y0hVZe/b4kEgueFIoZ5aymiNc27q+zvitrozn0Ql/w8picQCAQCgUAgEAheMgqFGCZncemMIoTs8MqLMwygpKfUCeKasUwnOa+ZYeoMEU7uRnk3OpkoY5mkp5MdFQqpGHb9YTwX7sWitlLR5lUfAN5vXpqzE1rxqq/Lc57df0dWMQwgITVv7jBZtJVLdWVUKhUlPB2xMi4LLCK9PncF31fGpOr0SumZQFDQuXxfdoY5mznD/FztlWX3ooUYJhAIBAKB4MVBr9fnq4wxPT0990ECgeA/oVCJYWnppgH62jw6w8r4SFbNK8YbtuT8OsN0Gc6wK/fjFFHOPZMzLDgsFl164XHybDwTBkCD0p4mIfLqQtYkIDd8Xe1xc7AhwN0BG2tJvMprqWScLNrm8jotVURyhh26btpF5ZSxO59AUJDR6w3Ke2tZHye8nTOcYVqNGq3GhqIukhgWHiPEMIFAIBAIBM+WxMREi8tTUlKy3eb1119XQt8zU7FiRZYtW5an4548eZJXXnkl2+MXFDZv3kxAQEC+t2vRooXSJbKgcvfuXby8vGjdunW+t927dy8lSpTIdVybNm1yzZTbu3cvLVq0yPccBE+XQqFc2Mpi2GNkhoFUugMZpTyyGJabM0wuz4xJTFVK4+5ESW9uzho1tmorSng44mSnJkWn5/rDhDyfU0Fn1yXJpdS+0stdx62xsWb3R03YNLyB8nrLa4i+0kkyl9ep7AyTyyJl0e1USPTjTFkg+E8JfZREYmq68n7opc1whvm6SA0iirpK/4bFWO7sJBAIBAKBQPC06NmzJ5999pnF5aNGjTJbfunSJTZt2kSHDh1Mlu/fv5/g4GCTTok5Ua1aNTw9Pfn7779zHLdkyRLUajVOTk7ZPuzs7OjTp4/Jdjt37sTFxYUqVapk+7C3t+fWrVs5Hl+v12Nl9WxkgtDQUFQqVZ4eTk5O2e5nyZIl2W73wQcfWNwmLi6Orl274uHhwf79+5kxY4bFcbdu3UKlUllclxvx8fHs2bOHqKgonJycsLa2xsHBQfm9WeLBgwe0b98erVZL5cqVOXnypLIuJSWFMmXKsHv37nzNY+3atbzyyitoNBpq1qxpss+sHDp0iNq1a2NnZ0dgYCDLly83WX/58mXatGmDg4MDPj4+fPzxx+h0+WsaV1ApFGKYjVp6saYau5PlNzOsjHeGMyxVp1ecXbk5w+TgfjlrLDOeTpL7wcpKRSmjmHEzovCIYfdjJQdHOR9tLiMLP26Otmg1NsrrLS6PzjC5nNcxFzFMdobJNC/nDcC/whkmeAGQSySDijihtrbCzcFGEXSLukrCmK+LKJN8liSlpvPJ2rNsOhv2vKciEAgEAsFz5+eff2bp0qX89ddfyrJFixZx5swZxowZYzb+888/R6PR0KFDBzw9PZXH559/joODA6+++qrJ8u3bt1OiRAkzkcbKyorDhw/zxhtvWBRxrl27phyzU6dOxMfHZ/uYOnWqxXNr2rQpp0+fzvZRqlSpXK/Pk4hher0enU5n9shcSmptbY3BYFAex48fp3jx4ibLbt68qYzv168farUaKysrrK2tUavV7Nu3j44dO5psYzAYmDBhgsV5RURE0Lx5c6Kjo9mzZw9//vknn376KZMnT36s85SJjIxk6dKlyvPdu3cTEBBAjx49iI+Px8fHh3Pnzim/N0uMGTMGDw8PDh06RMeOHRkwYICy7ptvvqFmzZo0a9Ysz3OSX2MDBw7k6NGjBAQE0LZtW+Li4szGhoSE0Lp1a2rVqsU///zDW2+9Re/evdm/fz8g/T7btm2LjY0NBw8eZO7cuSxevJhJkybleT4FmbypRQUcszLJfDrDgrycsFLBo8Q0Qh9l2FZz6yYpO8Mexptbaj2cMkoHi3s4ciY0htuRhUcMi8tnKerLgJOdDZCUZ2dYvNEZltvrVM60k+lU1Y+tF8IJiUoiOjEVVwfz3DKBoKBwOVzKtitrFM5VKhVeWg13o5MoKjvDjGKYcIY9G9acDGHV8RCO3YyiXaWiz3s6AoFAIBA8F5YvX06fPn3QaKTPHb169SI1NRWVSoWNjdRQrWTJkiQlJbFp0ybatGnD/Pnz2bVrF2fOnGHHjh2Eh4fz1VdfsXr1agYNGsTFixfZsGED58+fZ/bs2YqIlNl9lZKSwr179yhZsqSyLDw8nOTk5GzL7v78888cnVFpaWn07NnTbPn+/fupU6dOttvduHEj23UyBoPhscWwfv360a9fP4vLFyxY8Fj7XLhwIQsXLqRFixYMGjSIbt26sWTJEv78808Ajh07RmpqKg0aNLC4/e7du3n77bcpVaoUmzZtokiRIvj4+LBhwwZ69OjBiRMn+OWXX/Dx8cnXvGJiYmjdujWenp707NkTW1tbtm7dSsOGDQHpdxwdHW3ye2/Tpo1Zue2xY8f4448/KF++PEFBQXz//fcAXL9+nV9++YXTp0/na17fffcdbdu2VRxyCxcuxMfHhzVr1tC3b1+TsTNnzqREiRL89NNPqFQqqlevzpEjR5gxYwaNGjUiMjKSGzdu8Oeff1KxYkWqVavGvn37OH78eL7mVFApFM4w22zEsLwKNRoba4p7SILD2VAp6F6lygjIz2k7wGJXPw/HjFycEh4OANyKLNj14XlFl64nMdVY4qexec6zKTjI5Y5ZM8Muh8fRd/ExzhlfW3+dvsu+Kw+Vcbk5wxzt1IpYAFCpmIsioEUnpj21+QsEz4LL96VvwcpmcpF6GXPD/IzOMLlM8p7IDHsm/HVa6vwbGp2EwZC3brcCgUAgEBRGsjquBgwYwPjx402WyWIGSELK+vXrKV26NG+99Rbnzp0jIiICe3t75s+fj7u7O+3bt+f+/fts377d7HjJycl06tSJIUOGmCzftGkTtWrVyrb87XGdYUWKFKFFixbZPjILbJcuXbLoUOvUqRPXr1+3uC63zKzFixebubUMBsNjC2F5YfPmzaxZs8ZseVhYGP369aNNmzb06dOHXbt2UaRIEWV9y5YtOXXqFHfv3uWVV15h4sSJxMRkNL3L7GzL2vjgwYMHihD2119/YWtrS2JiIqtWrVLy1vbv34+Hhwe///47q1at4vr16xbnX6xYMTZu3Eh6ejp//vknr7zyCgDDhg1j3LhxZiLdv//+i5ubG0ePHrW4vz179piU7rq6ulKtWjWOHDliNvbixYtUqVLFpCy0SZMmHDt2DJBeT02bNmXevHkkJydz7do1/v77b/73v/9ZPPaLRqGw9WQXoO9om/fTK+PtxM2IBE4bc5g0autca4U16uzLKDM7w0oYnT23CkmZpJx1BcIZlhm5TFIuf5SZsfMKey4/ZM/lhyx9txYjVp3GyU7N+82CgLxdw8AijoTFJGOntsLHWYOTnZr4FF2eSzIFgueF4gzzzhDDXinqzKk70UrnWTk7LCxaOMOeNiFRiZy4LZVUp+r0RMSnUkRrl8tWAoFAIBAUTv7++288PT2V5wkJCVhZWZnkR2UWRFauXEn//v357LPPSEhIIDExkWrVqpGYmEhMTAw6nQ57e3tcXV25evUqzZs3V1xm0dHRdOvWjZSUFDZs2GAyjzfeeAN7e3s6derEd999x8CBA03Wb9++nXLlymV7Ho8ePTLLKitfvjxTp06lQoUK2W4nl3QClClThkePzGNX5syZw9dff82tW7fMHGJPI0ssPT1dceeB5ERLTU01WyZfx7xg6b69Y8eO2NjYMHnyZD7++GO+/vpri9uWKFGCKVOm8NVXX/HKK69Qo0YNAJPjFy9eXGkOcOLECTp37kyNGjVYtWoVdnbS56olS5aYXM8tW7ZQvHhx9u7dy549e/j4448tHv/rr79WGjR4e3uzfv161qxZw/37981EVABnZ2fKli2Lh4eH2bpHjx6ZudEAAgICCA0NNRvv4eFh5haMjY3lwYMHyvOlS5dSo0YNfv75ZwwGA2+99RbvvPOOxXN50SgUSoacP5OWLinPKUanll0umV+ZKeutZduF+5wNjQbA3jb3bXMqo/RwyrjZkF1nhaVMMjZZEns0NlaKECnIKHfMKlAlpWWIh8NXngIk92LooyST7XKiVBEnDl2LpKSnI1ZWKrQaNeGxGR0pBYKCSKpOzw1j45DMzrDP25enV+0Ayhd1BsDH6Hx8GJ9Cqk6PbS6uXEHe2XDmnsnze9FJQgwTCAQCwUvL66+/buIiGjZsGD4+PiaB+k2aNDHZpkePHkRHRysh6Lt371bKIt3c3CyKNufPn6dTp068+uqr9OvXj549e7J161Zl/fjx44mKimLTpk20b9+e8PBwJe+qR48etGjRgqlTp9K9e3cCAwPN9n/gwAGio6MByb02fPhwrl+/TvHixVGrpXuLq1evUqpUKTMBS87J2r59O76+5s3QLl68SFxcHNeuXaNWrVo5Xc58YzAY0Gg0JCVlfAF64sQJunXrZlJaeuvWLUXUmzBhApMnT0av17Nnzx5UKpVSSgiQmpqqCFKZ2bhxI0WKFEGlUjF69GhleYsWLejfvz9vvPGGyfi+fftiZ2enzCOrm37v3r3cv3+fxo0bM2bMGMaPH6+IcAaDgZ9++kmZc1JSEuvXr+fYsWOUKVOGRo0aKY6vrFSvXp2QkBDCwsIoWrQoSUlJ/O9//+P333/nxx9/ZPbs2Wg0Gn744QdatWpFUFCQRZcXoOSSOTg4mCx3cHAgIiLCbHy3bt3o0KEDS5Ys4c033+TIkSPMnz8fa2tJC0lMTKRt27bUqVOHTz75hDt37jB8+HCmTp1qMV/vRaNwiGGZbpzS0g2kGh1itvkQakobXQtnjKVsmjzcjOXkDPPM7Awzlknei0kmOS091y6VBR1Z7JEysgQyGc4wUzFMn+l9NCYpQ7ySGyrkViYJUM5HEg3kZg/5DesXCJ4HNyLi0ekNaDWmpb4aG2vFFQbg4WiLrbUVqel67scm4+/uYGl3gsfg7yxi2N3oJCr7uz6fyQgEAoFA8ALSqlUrQCqbi4+P5+TJk8THx3P8+HFiY2OVh7W1NSNHjgTg5s2bdO3alSlTppCcnMxHH33E1q1badOmDXfv3mXevHn8888/VKpUiZ07dxIZGakcz8HBgYkTJzJr1iwWL16c49ycnJx4++23OX/+PFWqVGHjxo0UK1YMgKCgIP755x9cXV3zfK4JCQls2rSJwYMHs2DBghzFsBIlShAWFmbiyjp48CCDBg1SnqemprJ7925FYExLSzNxgOWFL7/8ki+//NIsM0wmOjpaKU3MjJeXV76OY0lQs8Qff/xB27ZtTZY9ePAAvV7Pu+++S0xMDMuXL6dixYqoVCqSk5M5c+YMlStXznaf1tbWyu9twoQJtGnThqSkJH7//Xf+/fdfrly5wuuvv86NGzewt7fP9RxSU00b/CUnJ5sJZCCJw5MmTWLQoEG8++67FC9enC5duiglv0uXLiU8PJxjx45hb29P3bp1SUtLY+DAgQwePBhnZ+c8XbOCSqEQwzKLXmnpeiXDy1ad95aoclfEdKNyocmDM0yTkzMsU2aYu6MtWo2auGQdd6ISFUHjRSXO6AxzFiWSJmSXGRaVYN5gATLEsLyUSXap5ke6Xk+zV7yN29hYPJZAUJC4HC51rSnrrc2x7NzKSoWPi4Y7UYmExeRNDIuIT+FKeBy1Az2wtnq89teFnbR0PZeMv4PaJd05ejOKe6IUVSAQCAQvMVmD6eUA/W+++UZZlpSUZBJyXqlSJW7evIm1tTVarRadTqc4gbRaLVqtFicnJ5Nsp9dff53XX38dkMStb7/9lt69e3P48GH69u1L7969qVSpEgA1a9Y0OfaoUaOIi4tDr9ezYcMGRo8ezeXLl4mLi8PPz4+QkBBcXV1p1aoVtramjbRat26tONVCQkI4ceIEPXr0wM3NDa1Wugc9e/Ysd+/epWhR86Y6M2bM4NVXX2XKlCmUKFGCDz/8MFtHE8C+fftyDOzPWrIZHx9PdHS04l6TSU9PN1uWV9HsypUrVK9e3SzXC2DQoEHMnTvXbPmuXbvMGhCcO3cuxxJTAG9vbzMhTF6+Zs0apQvjlClT+Pnnn2nXrh1ly5alcuXKODo6mm2XlXPnzrFy5UrOnz/PN998Q69evXB2dqZGjRoUK1aMy5cvU6VKlWy39/T0xM7OjpCQEJPlISEhVK9e3eI248aNY/To0YSFheHv78/7779P1apVAem1UqZMGRMBTi4TvnbtGtWqVcv1nAoyhaIWxSaLGJamOMPy7sAK8nIydS7k4PqSyVqGmbn0JHNmmEqlooRH4ckNE50kLSOXO2Z1hkXFS8r8By1KM6RJKWqVcAckh0bm7XJCY2PN23VL4GcMGtcqJZmiTFJQcFHEMJ/cvwDIa0fJxFQdw1eeos7Xu3hzwVGWHb5lcVy63kDfxcfovegYev3LGRovN9hQqVDcYHJ5tkAgEAgELxtly5Zl7ty5uQbo//DDDxQvXlzZ7tixY8TGxhIWFsbx48f58MMPadCgAd9//z0jR46kR48eNGjQgCJFijBt2jSuXbtmduy33nqLLl26ULlyZVJSUpg+fbrZGJ1OR7169YiPj2fRokVEREQwatQoRo8ejUqlYu3atdSrV09xeiUlJSkCl8y2bds4ffo0p0+fxt/fnxo1ajB16lS6du3K6dOn+eabb2jUqJFFIezIkSNMmTKF77//HhcXF0aPHk2PHj1MMtSelPv371OnTh2TcPojR45QvHhxk2WWrqFMbGwsFStWZMyYMcTHx3Pu3DnGjRvH1atXGT58uEnJ65w5c/jkk0/4559/lDD/5s2bs3LlSgwGA+np6QwaNIhjx47lKoRlhxyML4ubIHUufe211zh69CjHjx/nvffey3U/BoOBIUOGMHnyZNzd3UlOTiYtLeNeLzExMdccNSsrK+rWrcuOHTuUZTExMZw4cYLmzZtnu52trS3FixcnLi6OFStWKCWkfn5+XLt2zcRpdu7cOVQqlcXX0ItGoRDDrK1UyMaAVBNnWN5PT6VS0aq8t/I8T5lhWfZf2ivjW4bMZZIAxY2lkrcLQUdJOadKdJI0RSldzCqGJUpvHl2rFePjNuUo5mZqbc1LmWRWtNmUZAoEBYn8iGG+ckfJ6Jw7Sh68GsGGM/fQGQWuQ9fM8w8A9l15wJ7LD9l35aEiPL9sPDK+97ja2+DvJl/fl/NaCAQCgeDlpkOHDjRs2JChQ4ei0WiUx5w5c/jyyy9Nlo0ZM4aqVavy3nvv0bt3b/z8/NBqtQQEBNCgQQNmzZrF4cOHGT9+PPPmzWPr1q1cuHCB0NBQ7t27Z7HcbuvWrezbt48iRYpw48YNfv31VxOhA0CtVrNkyRKWLl1KaGgorVq1omLFirz33nvExMTw2WefMXbsWGV8VFQULi4uJvto3bo1VapUoUqVKoo76M033+Tvv/9m3759jBw50iRvS2b//v20bduWCRMmKE61Tz75BE9PT5o2bWqS5/UkXLx4kVKlSuV7O51OR2JiIt988w0+Pj7cvHmTunXr8sMPP/C///1PcZG5u7uzZcsWfv75Z2XbevXq0a5dOw4cOGCyT4PBwLBhw7hy5QoVK1bM9thJSUkmeW9Z6dixI8uXLzdZVq9ePUDKZdNoNHTv3h0Ae3t7JY8rK3Inzj59+gDQoEED5syZw+HDh5k7dy6JiYmUKVOG69evU7du3Wy7U44cOZLVq1czd+5czpw5Q9++fSlbtqziaPv000/59NNPAQgPD1ecaDt37qRNmzZUrlyZHj16ANC7d2+Sk5Pp06cPJ06c4M8//2TkyJF07dpViGEFCdkdlpKmV26S5GD9vNLq1Qxra17yxuzUVmSu/Mlc/pi5TBJQnGE3C0GIvnCGWcbJQplkYqqO5DRJnHV3lATSrOHVeXGGZXcskRkmKEgcvh7Jz3uuseVcGEmp6Vy+n1EmmRteztLfxYO4nMWwEKOzSXaSnQmNMQs4BVh+5I7y80srhiVIYpibo60iNr6s10IgEAgELzcbNmwgOTnZ7DFo0CAmTJhgcd38+fP5+eefuXv3LvHx8Tx8+JBr164xceJEWrZsyfr161myZAk//vgjX331FS1btmTPnj34+/sDcPfuXWbMmEHlypXp3bs3AwcO5OrVq/z666/MmDGD4sWL89FHH7Fnzx4SEqR7xPLlyzN79myqVKlC2bJl+e2330hISKBbt260bt2axo0bK+cUFhZmlou1efNmNmzYwKxZs/Dw8GDSpEmcOXOGSZMm0bRpU1q2bGlSLpeSksKECRNo2bIlY8eONQmat7Ky4u+//6Zo0aJUqlSJSZMmERUV9US/h61bt1K3bt18bfPtt9/i7e3NuXPnqFSpEtu2baNbt25s3LiRxYsX8+WXX5qMDwkJ4dixY8rz119/nVmzZpmJjwkJCWi1WjZs2GCxJDMhIYHp06cTGBjIzJkzs51feHg4fn5+ZsvlsPl58+Yp+1+/fj0NGzYETDtgRkVFMW7cOH755Rdlebdu3WjTpg1t27Zl1qxZrF69GhsbG2JiYggODs7Wsff6668rr8m6deuSmprKxo0bFRHu+vXripCm0+mYNGkS1atX5+2336ZWrVr8/fffStMFf39/du3axb1792jUqBEDBgygS5cuJpltLzKFRs2wtbYiRacnMTWjVji/HclqlXRXfg4Oj811vEqlwk5tpYgdAe4OvFk7AINBuvnITAnPwtNRUohhlrHk1oo0lkjaqq1wMLoNs4phj3MdZVdeVheaQPC8MBgMDFp+UmkSUdZbq5Tk5cUZ5mF8z5QFnOy4a9xni1e8WXHsDg/jUgiPTaaoS4bjMiQqkT2XM1pCv6xuKNkZ5uZgi5+bEMMEAoFAIMgvmfPFZIoWLcr58+dJS0szKVvbvXu34jDq1asXq1atom7duowYMYKePXsquUutW7emZcuWbN68mYULF9KuXTt8fHy4cOEC/fr1Y9euXUydOpWBAwdy5swZ+vXrh6+vL5MmTSI8PBxHR0f+/PNPAAIDA0lISKBly5Zcv36dVq1aUbJkSYKCgkhJSaFcuXJs3ryZpUuXMnHiRFatWsXrr7/OW2+9RbVq1WjatCnW1tasXbuW9u3bWzz/DRs2MH36dKZMmcLmzZvZv3+/ku/VuHHjHHNhM5fXXblyhT179jB//vxsx4eFhWFra8uePXsUAalEiRJ8//33dO/eHUdHR3Q6HV9//TWzZs1i27ZtuLm5odFoCAsLo0yZMpw4cYLKlSszadIkxo8fb/E4u3btUn7+9ttvlZ9v3rypuOACAgIICgpi5syZdO7cmX///Zf79+/z4MEDRYQ8fPgwjx49MiuxvHHjBs2aNaN3795KAwaAU6dOYWVlxYEDB0xeW+7u7oSFhZnsw8rKip9++omffvrJZHm1atWUTqLZMXToUIYOHWpx3e+//678XKxYMS5cuJDjvmrUqMHevXtzHPOiUmjUDBu1FaSYChH5FcMyZ4/JWSu5obGxVsQwrUbN150tWywLU5lkbLIok7SE3F0zszNMvhn1cLRV/qPwcjb95uFxyiRFN0lBQSM5Ta8IYVo7teIK83a2w9XBNqdNAXA3umkjcxHDZGEryMuJ0l5OXAqP40xIjIkYtur4HTKbxe6+pDlZUQnS78PNIcMZFp2YRkKK7rHedwQCgUAgEECzZs3w9fXFw8NDcdDo9Xo8PDxYu3YtABMnTuTbb7+16BgCSeho37497du3JykpiaioKOzt7ZkxYwYODg44OTlx/fp16tWrxwcffMCECRP49ttvFXHH19eXefPmYW1tjaOjI7/99hv+/v4mIfRbt24lOjqakJAQ9u3bR2BgIB9//DG///47Bw8epEePHkydOpVOnTrlGO5ubW3NqFGjeO+999DpdCbHyE+A/v79++nUqZPSNTHz/uXjd+/enUOHDqFWq5XOnHLJHkhOpjp16mBlZcU///xDiRIlABg4cCAdOnTAYDDg7+/PtGnTCAwMNMkPyyuhoaEEBgby008/0a5dO2V51apVqVOnDr6+vsoyGxsbRo0aRZEiRUz2sWfPHjp27GgitAHMnj2bgwcPKh1DBc+XQvNpWC6JTEzNEAdsrPJfBbphWH0GL/+Xj9uUzdP4zLlhOTl8ijhJN3q5uR5eBIQzzDJOlpxhCRnODBn5taBs9wSZYSJAX1BQiE6SXutqKxWTu1Rk+MpTAJT1yVvLZdkZFmXhPXL7hXCWH73DtO6VFGeTn6s9lYu5cik8jrOh0bSpkFHmfvL2IwD83e0JiUriXi6h/JlJSk3nu22XafWqN3UCPfK8XUFEFuPdHW1w1tgoXY3vRSdR+gXvaiwQCAQCwdNg1qxZ+d5GrVazYcOGHMcEBgbmeX/29vaKaJa57LFUqVLcuHEDb28p1/qzzz7LVtwpWbKk2TJLIfQ2Njb06tWLXr16ASj/5oWs+WSXL18262aZlfPnzys/9+/fn65du5qNqVq1quJOOnjwICBVHFhynKnVapYvX06ZMmUUIRLgww8/5MMPP8zzueREgwYNuHLlilm2l1qtZs+ePTluO2zYsBzX5+SKE/z3FLrMsASjEGFjrcLKKn+ZYQCVirly6JNmdKxiWcXPiiZTR0nZGWQJVwdpXUJquhLw/6KSIYYJZ1hmnCx0eJQ7SWbuLipnI8k8jqjorDHPJxMInieym9bVwYbXKxWleTnpw1zlYi45babgnoMYtvTwLfZfeciG0/cUMczX1Z5K/tK+z4aaZibI5fIVfKX1uXVQjEtO45KxNP63o7dZdOgmEzdezNO8CzJRmTLDAKUbrSiVFAgEAoHgxUAWwgoidnZ2OZZIWsLNzS1P43Lab7ly5UyEsGdBdiH3gsJFobH2yIH38SnpJs+fNRp1JjEsB1FDq7FBpQKDAWKS0sxyo14k4pQyyULz8nkqZM4Mk7/NyHBmZHKGZfndP1aZpFF4FWWSgoKCLIa52NugUqn4sWdVNp8LM3Fs5YT8NxKZkGr2baCcvXc6JFoRePzc7Ek3Nks5Gxptso38pUhpLye2kHtm2Od/XWD9qbtM616ZDWfuAXApPI7ktHSTLzxeNGQnsrtDhhh2KTxOiGECgUAgEAgEgpeeQucMk8skbfKZF/a4aGzyViZpbaXC2eikikl6sUslZQHGWYhhJsjOML0BktIkUdZSmaTWTq28blQqcHiMm21LYf0CwfNEfl+T88Gc7NT0qOGvvO/lhuyeTNXpScjUCAUy/o72XXkISH9DLvY2lPXRYmttRWyyjluZ8hiTjNsHGUsB70YnWew4KbP+1F0ARv1xRnGZpesNXLiXeyOV50F8io5eC44wd5/lltoyUYlZnGHGEP0r4XHPdoICgUAgEAgEAkEBp/CIYWrJESCLA/+VM8wukzNMm4vDRy6VzGs4f0ElTgToW8TB1hrZzCKXL8rODI9MzjCVSqW4w5xs1Y9Vziu7EGNFZpiggKCUSdo/3vuCg22GSCyXF4OUGSH/HclCvBwGb6u2orS31Inn2oN4ZRtZTCtVRApjTU7T8yiH9137bATpMyHRj3Mqz5z9Vx5y6FokM3dfU9xxlsjqDGtUWgp3/fts2Atfri8QCAQCwdMgPT0990EFAJ1Oh14v/u8WCJ4mhUcMM8sM+4/EsEzOsJzKJEEqHwKUjmsvKiJA3zIqlSojN8z4OozMktkj46WVOko+bke3zM6wdL2B8Jjkx9qPQPC0iDa+r7k4PL5ILos2kQkpyrLYJB26LIKP7HACKOkpCV43IzLEMNkZ5uZgqwjP2XWUNBgMpGdxjQUaRbSzodGPcxrPnCvGTp3xKTqCw7J3r2V1hjUpWwQvrR1RCansCr7/7CcqEAgEAkEBJikpiaCgIE6fPv28p2LC6dOnTbowAvz555/Url07X/uZNGkSffr0eYoze/rodDoaNWqEu7s7CQkJ+d5epVJx69atHMdMnDiRL7/8Mtd9Ze6SKXg5KIRimHQTZPeflUlKjgJrK1W27gIZWQx78Z1hktDzOF0QCzuyO1B2hkVZcIZBRkfJ3ATU7I8jvZYMBpi3/wZ1puxiyaGbj7UvgeBpkOEMy7mrUE64O5mH6GcWxmR8XTXKz4GKGCZ9gEpL15OaLn1z6mBrnWtofGKmpia21la4OtgwsmUZwDyYv6Bw9X6G8Hf0ZlS24x4lSL8TOY9NbW1F1+pSO/PVJ0Ke4QwFAoFAICj42Nvb06NHj1w7/N26dUv60tvJKduHg4MDJUqUULbZuXMnKpUqz482bdrkOIc1a9bQoEGDp3HaZgQFBeV5nidOnLC4D/kaWXpUqVIl22OPGDGCc+fO4eLiwjvvvJOt+61EiRLs3bv3sc5vy5YtlC1bFicnJ2xtbbGzs1N+bwcOHDAbr9frGTFiBB4eHhQrVoxly5aZrH/77beZMGFCvuZw/vx5GjVqhL29PYGBgfz666952u6rr75CpVIpXTYBzp49S6tWrXBwcMDHx4e+ffsSGRmZr/kIJAqNGGabxRlm+x+JYbLo5mSnzrWbhpylE/0CO8P0eoNSiirKJM1xypLlpZQpZXWGGTtKPq6gqLGxQm0sr9xxMRyA6TuvPrWyyejEVGJecNFW8N8iO15dHrNMEsDdUfq7yCyGWeou6efqoPxc0ujiuvFQEsMSM+WNOdiqcxXD5P3bqa3YO7oJm4c3pF4pT2mfEQkF0sl79UFG5texm5Y//KTq9Mr7kHumzMIeNfwBqdTyfqxwlAoEAoHg5aBPnz4WhZpvv/2W2bNnW1y3YMECZXsPDw/i4+Ozffzzzz8mx2vWrBlJSUlmj8DAQDZs2GCyLDExkb///jvbuScnJ7Np0yaOHj3KG2+8YfKYNm1ajudtMBjQ6XRmj6zloQcOHMBgMCgPR0dHbt68abKsePHiACxbtgy1Wo21tTVWVlao1Wq++uorXFxcTMYbDAb27NljcV46nY4BAwawbNkyNmzYwN69e/n333/p0qULSUmP3+hHr9czd+5cZR+PHj3i2LFjNGzYkPj4eLp3787s2bOV31vDhg3N9vHrr79y8OBBtmzZwowZMxg2bBgREREA7Nu3j8OHD/Ppp5/meU6xsbG0bNmSkiVLcvToUd5991369OnDkSNHctzuwYMHFn+/w4YNo0mTJhw5coSFCxeyb98+3nnnnTzPR5BBoRHDbKwlYUC+EfqvyiRlZ1heRA05Sycm8cUN0I9PzQhsF2WS5ihlksmmZZJZxTDFGfaYYphKpVKEt0vGMOyYpDQWHnhyd1iKLp12Px2k7U8HRK6QIM9kBOg/vhgmOyhNnWEWxDCTMkkpM+xWpCyGSX97aisVtmorZWx2HSXljq9uDrb4utrj62qPu6Mt/u7SdufvFix3WFq6XnHBARy/9chic4Bo43lZW6lM3qtLejoS5OWE3mDqMBMIBAKBoDCzZMkSE5EmODjY5HlaWhonTpwwWda/f39l+8jIyBydYfXq1TM5npWVFTt37uTtt9/Gzs4OjUaDRqNBpVJhY2OjPF+xYgXdunXDxib7z0+///47bm5utGrVinLlyimPhIQEjh49muN5L1u2DBsbG7NH2bJlH/tavvPOO+h0Or788kuGDBmCTqfj888/V9bfvXuX1atXZ7v9zZs3adSoEVu2bGHPnj00bNiQ4sWLs2fPHq5du0atWrWydaDlhMFgYODAgUyZMoWHD6WmSzt27CAgIAA/Pz8ATpw4YVKC+s033/DFF1+Y7OfYsWP069ePWrVq0a1bN8qVK8fNmzdJS0tjyJAhzJw5E41GQ15ZsmQJ6enpzJ8/n0qVKvHZZ59Rs2ZNfvnllxy3+/DDD2ndurXZ8t9++42xY8dSqVIl2rVrx6RJk9i6dSuJiYkW9iLIiUIjhqmN4lf8f+wMkwOf8yIMKQH6BdBpkFdkkcfW2koRAgUZOBndcvEpOtLS9YqrJKsYJod+yzfcj4P8msvshFl48OYTO7pO3YnmbnQSd6OThHNEkGeUMsknyQyzIIZFKR1ZM/brl6lMsqSH5Ay7H5tCQopO+XtwsJXen3xdpLG3Iy3nUERlk+tXyc8VgIsFrKPkrYgE0tINONhao7GxIiohlesPzUUtJS/MwcasSYf8xUx8yov7f5FAIBAIBI/Ll19+SdOmTYmOjlaWnTt3jlatWvHDDz9Y3Ca/zjCARo0acfLkSaZOnWpxn6GhoYwcOZKePXsqyzQaDbVr1+bixYtoNBref/99pk2bxqeffsoXX3xh8mjTpg12dnY5nmvv3r3N3FoGg4Fr167l4Uo9HlevXmXKlClmy5OTk5kyZQqVKlXCy8uLf//9lxo1aijrS5QowbFjx6hevTp16tShf//+XL+e0Tk7PT3dxN2WmbS0NAYOHMiOHTvYt28fAQEBACxYsED5+f79+1y9epVz586xatUqDh8+bHH+xYoVY8eOHSQlJREcHMzNmzcJDAzk+++/p1y5crz22msm41NSUihevDgzZsywuL89e/bQrFkzbG0zPms2a9YsR2fYli1b2Ldvn8VyTH9/f5PnGo1GNFd4TAqNGGZWJvlfOcPUeXeGFYYA/YxOksIVZgln43WJTkxVxAGVKqNEVqZleR9+61+bT9u+8tjHcrIzFR0C3B2IT9Gx/+rDx94nwD/XM8quHsQJMUyQN+TX+5OVScoB+uZiWN1SHsiajr9bRpmki4ON4ii7GZFAYooshkl/i2V8tADsDH7A2PXnzNyOsjPM3dF03j5GES3CQmbZ8yBdbyAyPoUrRjdXaW8tVf3dANh3JcJsfIaIaJ7hJrtK5S83BAKBQCB4GdDr9Xz88cfMmTOHbdu24erqqqyrWrUqf/31Fz/99BPvvvsuaWmm92vR0dEmrqysj86dO5sdz9nZmeXLl/PFF19w8uRJk3VJSUl07tyZrl278tZbbynLk5OTOXr0KOXLlyc5OZn27dvz8OFDSpYsyZAhQ0zc4KmpqflyKOVE8+bNFbeaRqMhISGBsmXLmiy7fft2nvdnKT5o/PjxzJkzh3nz5vHXX3/h5eVlVprq6OjI0qVLWbBgAadOnWL69OnK9i1atDBxt8nB+Q8ePKBFixYcOHCAffv2KeWcFy9eZMeOHcr2W7dupWTJkpw4cYK5c+cye/Zsi3MfPHgwd+/exdHRkSpVqjB58mQSEhKYPn26RcFLrVZTrlw5M5FK5saNG5QsWdJkWUBAAKGhoRbHR0RE8O677/LTTz/h5ORkcYyMwWBg4cKF1K5dGwcHhxzHCswpNGKYXCaZIJdJ/leZYflwhhWGAH3RSTJnfJyl/5DCY5KVm1FXexusszgzrK1U1A/yxPkJctcy/w48nexo/ooXAEezyRDKK4evZ9xYP4gtGEKAoOAji/xZhd/8YLFMMl762d/dgYmdKvDJa+Xwcjb94FciU4i+XCbpYCd9UVE30INhTYNQqWDF0Tv8cdI0OF4Omc8qGslOtOiEgvF+/e3WS9SYvJOf90jf5JbxcqJJ2SLKun+umwpiynk5WhDD7EyzDQUCgUAgKOw8evSIdu3asWHDBn7//Xc6depEbGyG+3vDhg307t2bgwcPcvToUdq1a6e4j/z9/bl16xYDBw5kzJgx7Ny50+yxevVqXn/9dbPj1qtXjwEDBvDNN9+YLJ8/fz42NjbZCjIytWrVYuXKldSsWZPdu3czYMAARRBLTU3N1RmWFwwGA/v37yc5OVl5ODo6cvnyZZNlssi0e/du1Go1EyZMYPbs2ajVajZs2KDsL7t5ff755wQHB9OzZ08Tl9rEiRMZOHCgybI+ffpw4sQJE6fenj17TMbIDQvatGmDu7s7x44dU+YIMGPGDJOSyOXLlzNmzBjmzJlDw4YNeeUVy6YEV1dXjh07RmhoKDExMQwYMIDhw4czatQorly5QpUqVQgMDOTbb78FwNramm3bttG1a1eL+4uPjzcTqhwcHEhJMb/Pks+9VatWdOnSxeL+ZNLS0hgwYAB79uxh1qxZOY4VWCbfipHBYODLL7/E19cXR0dHOnfurNTkZmXBggWUKlUKOzs7atWqZaaIBwQEmKnBcjhdfrF53s6wPIgahSFAP8MZJsLzLVHUGNYdFptMRLz0Bpe1RPJpoc3kRgxwt6dOoAcAR25k310uNxJTdZy6E608fxAnxDBB3pAzqlyfujNMeg16ONrSq3ZxBjUuZbZdSaMYdisiwaxMUqVSMap1WfrVl76RuxweZ7JthjPM9O9Ufr9+VAAyHlN06aw8dgeDAS6GSR/cy3hrebdBSVq84k2KTk+/JSf49cht9HrpA7JcJuluQZyUhfR44QwTCAQCwUtCdHQ0rq6uHDlyhIYNG1KxYkW+/vprQHKMTZgwgQ8//JBixYqxb98+unTpglot/X9pbW3N2bNn+fjjjxkyZIhFZ1izZs1YvHgxAwYMACAxMVEpoRw3bhw///wz8fHxGAwGkpOT6du3L3/88QdpaWnKuKyh9gBubm40adIEDw8Ptm3bxvbt25U8rZSUFBNnWJ8+fUzyyCZNmsSqVatM3F1qtdosIystLS1fDrNmzZqZZYZ16NDB7FpnRavV5su9pFKpTEoLs+P9999n3bp1aLVak+VHjx7lq6++AuDy5cscPXpUKcv8559/qFq1ao779fX1RaPRsHHjRq5du0afPn3o168fv//+O0ePHmXBggUcO3Ys1/nZ2dmRmmr6eTI5Odnitfjss8+4fv06P//8c477DA0NpUmTJmzcuJHdu3eblJsK8k6+FaPvvvuOn376iblz57Jjxw4uX75M7969zcatX7+eoUOHMnr0aA4cOICfnx9t2rTh0aNHypioqCh+++03rl69qjzc3d0f60RkJ1hGN8mcOzs+LaqXcENjY0VdoxCRE3KWzoscoC+cYTlT1FhaFRadROgjKcTQz+3ZWFYz/w4C3B2oVUL627n2IF4R4vLL8VuP0Okz7NeiTFKQF1J1esWV+3QywzJev5E5lPvJlDRxhpmWScoU95D+DsNjTF/T2ZUTys8LgpN3z6WHxGYRrkp7O2FjbcWsN6vSuEwRktLSGf/neT5YfRrI6GTr5mj++1AafQhnmEAgEAheEkqWLMnKlSsVkebbb7/ll19+YcuWLXz22WcAipDl6enJoEGDlG3nzp3Ll19+yYMHD7h58yZWVlZcuHCB+Ph4KlasyO+//058fDxjx47F2lr6Mq58+fJotVq0Wi0+Pj54e3uj1Wq5ceMGnTt3xtnZmWLFiiljtFot69evz/EcihcvzokTJzh8+DBdu3YlJSXFzIE1ceJEEzdX1kfm85KJj4+nevXqqNVq5ZGQkEBQUJDJsryWSV65cgVfX1+Sk83vI1atWmVmhhk/fjxz5841W55Xt1Pfvn0tlmX++uuvlC5dGoApU6bw8ccf891339G2bVtOnjxJ/fr1c913UlISw4cPZ/bs2Rw/fpzq1atTpkwZihQpQseOHS1mxWXFz8+PkBDTyoSQkBACAwPNxn799ddcu3YNT09PNBqN0uigefPmtGrVCpCub+3atdFqtZw5c4Y6derkOgeBZfKlaOj1er777js+++wzxQb6ww8/8Nprr3Hz5k2TWtgpU6bQv39/5Q9u+fLl+Pv78+uvvzJ8+HBSU1NJSEigYsWKBAUFPfGJKJlhqf+tM6xeKU/Of9FaCfDPCdkx8SI7w2KFGJYjshgWHpNMSJTUvc7f7fFD8nPCKdPvwN/dATdHW8r5aLkUHsexm1G0rVg03/s8bMwLU6nAYBBlkoK8IZdIqlRP5hqVxbD7sSl8seECxT0cFLHKwyl7MSzQKIbdiEigjlwmaWva4MPbWFqZtSlEds4wuUyyIDjD/jp9F4B2FYuy9/ID0tINvOrrAkgdjRf1qcmvh2/xxd8X2XDmHl93qZhjZpj8OxKZYQKBQCB4WSlbtizz5s2je/fuaDQajh8/btGFNGzYMA4fPsymTZtwcXGhW7duvP766xQvXpwbN24QHBxM8+bNAUk4kd1Jcp5VVoKCgpg1axZt2rTJdY6xsbEcPXqUli1bcvv2bWbOnMmCBQsIDAzkk08+4ciRI7i5uT3+RUAqaYyJiSEhIcHEHebk5MT58+eVUkTA5OfM6HQ6kpOTWbZsGQDbt28nPDycJUuW0Lt3b/7++29l7BtvvIGVlRWRkZEMHjwYgEmTJhEaGsqcOXMAmDlzJo6Ojrz77ruPdU4hISF4e3tTqVIlzp8/D8Dw4cN59dVXsbGxoV27dnTp0gVnZ+dc9zVp0iQaNmxIo0aNWLdunUmWXGJiYo5dQGUaNGjAwoULSU9PV8TSXbt2Ka+bzAQHB5s8v3v3Li1atGDx4sU0aNAAgDfffJO6devy+++/Y2VVaFKvngv5unrnzp0jIiLCpINC48aNsbKyMuuGcPHiRRProaOjIzVq1FCshFFRUimXp6dnno6dkpJCbGysySMzcmZYcpoUjvxfdZME8iSEgRT0DNKNoz6T+6ags+LoHabvuIJebxBlkrlQ1EUSvu7HpXDL2L3O3/1ZOcMyfgfyMTJKJR8vN+xMSDQANYtLLjNRJinICzFJkvDirDHPx8sPHo7St5upOj1L/rnFpE3B3I2WRGV3x+wzMeTMsDtRiSQa3U6OWZxhciB+eGw2zrBsyySf75cXsclp7Lr0AIChTYP4+/0G/DGoLkW0GdfD2kpFn/ol8XSSlt14GJ+tyAciM0wgEAgEgosXL/Lrr7+i1WrR6XTMmzdPuT/NzIgRIzhw4ABarZa3336bM2fOKI6l999/n5EjRyrurKioKFxcXJ54bqGhody6dQt/f39+++03Bg8eTKlSpTh37hzr16/n33//pUePHiQmJmJv/2RfugcHB1O0aNHHCuKPiYlh+/btFCtWjFmzZtGhQwf2799PTEwMdevWBcDe3p74+Hj69u2rbFezZk0mTpxoMYz+t99+Y9q0aTRp0iTb4xoMBv78889s10+dOpUhQ4aYLKtWrRp2dnbcvn2bw4cPM2LECABsbW2zLcW8fPkyCxcu5LvvvgOk/LZ9+/axbt06du7cyZo1a2jevDl6vZ42bdqwbt06i/vp378/0dHRDBs2jPPnzzNx4kTOnTvH8OHDAVi3bh1t2rRBr9ebld+WKiXFgwQEBBAQEMCVK1c4efIkb7zxBjdu3ODatWvKIyYmJttrIrBMvhSjGzduAJg4wOzt7SlSpIhZNwQPDw8zK2VsbCwPHkgf6iMjI5V9+fn50aFDB86ePZvtsadMmYKLi4vyyNqtwSaLIJX1eUFADtA3GF6c8hS93sDY9ef4cddVfj8RooRZC2eYZYpo7bC2UpGuNyjZWwHPSAxzsjMtkwSoXVISsR5XDJPLK6sEuAJCDBPkDbmU8ElKJAGc7U3fV9L1BmXfHjlk78ki0KPEVEXgyeoMk5tbPIxLIT3TlxEZAfqmc5fLC6MTU006N/3X7L38kFSdnjLeTrxSVEtgEScq+7taHFuqiCQKXn8Yz+1IqUzb29n8w62Tkhn24rqUBQKBQCDIL3FxcaxcuZI2bdpQvXp1AgMDCQ4O5tixY5w9exZ/f3/eeecd1q1bp2Rily5dmv3791OjRg3OnTvHrl27cHNzY+zYsdy8eZOPP/5Y2X9YWBheXl6PPb+YmBjq1atH9+7d6dq1K7t27WLJkiU0atSI48ePs23bNpo2baqMj42NxdHR8fEvCFKHRVm4yivbtm0jKCiI6dOnExQUxMyZM/nhhx+4fv06vXv3Zs6cOSZli48ePWL37t3K85IlS7J161bFJZWZ27dvs2PHDoslhHq9nnXr1lGtWjV69+5tMWMNIDw8HD8/P7PlKSkp9O3blyFDhlCpUiUARo4cydixYwHzDphDhgxhwoQJyu+0WLFi/Pjjj7z//vv07t2bKVOmUK5cOdLS0ggODubevXsW5+Pn58fff//NwYMHqV69OuvXr2fz5s1K2P+9e/cIDg4262Ca3bkBdO/endKlS5s8fv3111y3F5iSL0UjPj4eKysrs9pkS90QunXrxpw5c2jXrh3VqlVj4cKFHD9+XKl1DQgI4MiRIzg4OHDr1i2++eYbGjduzLlz5yhWrJjZsT/99FNGjhypPI+NjTURxLKKX/+lMyyv2KmtsbexJiktnZjENEUcK8jEZrpZ+n7HFSWTrXzR3G2lLyPWViq8tXbci0lWHC3+zygzzFljLobVCfTASgVX7scTEpWYb1ea7CYp6y1ZvB+KzDBBHlDEsCd8T7OU9yCTUyMKWYQzGCDMmAmWVQzzcMoQqiPiUxSRSA6azy4zTKc3EJ+ie25u2NsRksO0ir9rjtcHoJSXE0dvRnE5PJ5gY9D+q77m79Va4QwTCAQCwUvG7Nmz+eCDDyhRogTvvPMOS5YswcfHB5C6B27atIlTp04xb948Bg4cSHR0NP/++y+HDx9m+PDhDB06lK+++orExER69erFkSNH2LRpE5GRkUoW2O7duxk/fvxjz9HFxYVevXqxYcMGk+qpnj17AvDgwQOSkpJwdXXl/v377N27l4EDB5rsY/z48WYB+ZnR6XRKRlpaWhrz589n4sSJ2Y6PjIxEr9fz4MEDwsLC0Gg0+Pj40L9/f9555x18fX0B+Ouvvxg0aBDfffcddevWZenSpYSFhQFSkH1gYCAHDx6kYcOGJvuX3VEgZbMBjBs3Tlm2ePFiunfvTkREBN27d8fR0ZGPPvqId999F2traxwcHDh37pxSwhkREcHRo0eVayaTlJREp06dSE9PZ8KECcry69evExsbS3h4OE5OTibb7Nq1y+x69OnThz59+pgskx1nOSHrHJYYNmwYw4YNs7iuRIkSJl/KNmrU6Ll+SVvYyJcYZmdnh16vR6fTKd01wHI3hC+//JLQ0FDq1asHQJMmTWjWrJmirGq1WmrXrg1AxYoVadKkCQEBAaxcuZLRo0dbPHZOrWOzil8FUQwD6aYtKSad6KRUAng2IsnTJHN49EOjS6hOoDtdq5kLlgKJoq723MsU0u3v/mwzw2ytrZQbezdHW2qX9ODwjUi2nA9jQCPzznvZodcblJKwsj6SGBaZkIouXZ/nUmDBy4mcg+iSQ8h9Xnm9si/7Lj+gew1/Fh68CYCd2spM3MqMjbUVWo2auGSdIkI72Jn+92ZtpaKIkx3hscmExyTj7azBYDAoXTCzim0aG2s0NlYkp+mJTkx7bmKY/F4il2DnRFAR6UPc9ovhpOj0ONpaU8LD/Btj+b1DZIYJBAKB4GWhV69e1K9fn8qVK2c7pmrVqvzyyy/8/PPP3Lp1i8DAQF555RXatGlDQEAAOp2O6tWrU65cOf755x+uXr3KK6+8AoCzszODBw/OtUNhbgwdOjTbdevXr1fyuDUaDb1796Zx48YmYyZOnMgnn3yS7T4yiy7Xrl1DpVLRpUsXs3FOTk5YWVnxww8/8PXXX6NSqWjYsCHlypVDrVabXMcRI0bwxx9/8Ouvv9KiRQtAyrXq3r0706dPR6PRsHDhQho0aPBYQk5ERAR+fn4MGjSIoUOHmpQ1vv/++3Tr1k1xialUKpo0aULbtm1N9nH16lUMBgNbtmwx0RR27drFjBkzsLa2tqhBCAo3+RLDZLthaGioor6mpKTw8OFDMyujk5MTq1evZv78+SQkJFC0aFEqVKhAu3btLO5bq9USFBSU5y4VWZEzw2T+qwD9/OJib0NYTHKB6FCWF7KG/Ws1ar7vUQWrJ8gFKuzI2UQgOTCelQPQ2Xhz7udmb5LT9FpFH6MYFp4vMSw2OU0pHwvycsrkokk1OSeBICuyoPSkzjCAn96oQnKanoj4FEUM83C0zdUV5eZgK4lhj4ximI25eObtopHEsNhkKiM5o9LSDcr2lvYZFpPMo8TUZ5b9lxthMdL5FM3D32ApL0kMu/FQcpO96uti8b1aBOgLBAKB4GXDxcUlRyEsM1ZWVsq9rVqtJiAgQPl53759eHt7A+Dt7Z1vcefatWv5Gp+ZgQMHMmDAANLS0rCxsTH7bDR//vxcA9Uzd2h85ZVXOHbsmEXDiVyON3nyZCZPnoxer89236NHj+arr74yyUtr1KgR9+/fz/O55YSnpycXL160WFb5zTff8M0332S7bYUKFdi7dy8gBftnZcCAAUoXUcHLR74Uo2rVqmFvb8+OHTuUZfv27UOlUtGoUSOL2zg7O1O0aFH27dvHlStX6Nq1q8Vx0dHRXLly5bE7S5qVSRZQMUwu53meHSWvP4zntDEoPTfkm9xyPlq+eL08y96thZ/rs3E6FRZ8M920FnN3yPUm/nGpVdKdhqU9GdjIVIhu/apk+T51J5ov/75A9zn/EB6Te7mjHCSutVOjsbHG09i974EolRTkgtxN8kkzw0D6Rs/e1ppibvbK35J7Dp0kZeQA/OycYQA+ztKHPbmjpJwXZm9jjb0F51lBCNGX/3aL5uF9V84MkylvoUQSRIC+QCAQCASPiyyEPS9UKhW2tpa/JLSxsbEoGOVEXrtR5iSyFStW7Kk0DsiJ/J6XQJAX8uUMs7e3Z/DgwXz++ecEBATg5OTEiBEjGDhwIK6urrRp04YBAwbQpUsXjh49yqNHjwgICODMmTN89NFHfPrpp0rO16ZNm7h8+TJNmjTh0aNHfPHFF2i1Wnr37v1YJ2IWoF9QyyTtpZurmOcohr294CgP41M49EkzvLQ5uw2U8GonW/rUL5njWIGET6ZyJn+3ZyccajU2/Nqvttlyb2cNNYq7ceL2IxYfugXAjuD7vF2neI77y9pVr4jWjvuxKTyIFSH6gpx5WplhmVGpVNQO9GD9qbsWXVtZkQPwE1Mlm7ylsko5RF8WmKJy6LiYeZ/ylwLPg3tGcc83D84wXxd7JZcSoIKf5Q+mcgOU+BQdBoPhmQn2AoFAIBAIBAJBQSXfLQG//vprkpKS6NGjB9bW1rz11lt89913Zl0UIiIiGDhwIA8fPqREiRJ8+umnJjXKrq6uzJ8/n3HjxuHh4UGTJk1YvXp1ntXprGR1ghV0Z1jMc7q50qXrlQyasyExtCifmxgmlz89eRbQy0Lmm9bnVVrVqaofJ24/Up6HPkrMdZusYpgklMaKjpKCXJGdrs5PuSS4ZXlv1p+6Szljhl1OuGcRzCyJYd7Gv837RoH3kfKatzxvWYSTx/3XJKToiDWWMubFGWZlpSKwiCMX7mUfng8ZzrB0vYHkNL1FV5xAIBAIBIIXn8f90ksuxRQICjP5Vozs7OyYPXs2MTExREVF8dNPPynh9rdv31YEr3bt2hEaGkpKSgqXL1/m/fffN/lDrF+/PsHBwSQlJREaGsry5cuVbhSPg406S2ZYAXWGuShOg+fjDMtcFnPR2G0sJzKCscWbYV7JnK/1LJ1hOdGrdgC/9a/NsKZS2XFoVFKu28idJD0UMUwqKRNlkoLcyC6E/kl5rYIP2z9sxJg25XId62omhpl/1+OtlcUwozMswXInyYx9Su97z6tMUs4L02rUioCVG0HG3DBbtZXyc1YcbK2Ro8Tikl+M/EqBQCAQCF50goOD0Wg0jBgxItex7du3V7KuAGbMmMG0adNMxiQkJOS4j02bNlGnTh2Sk3P/LK/T6VCpVOh00r3i5MmTzbomWmLFihU8evTI4rqVK1cSGRmZ6z727t1LSEhIruMEgqdNwVSMHoMXJTNMDlN/XplhmQOTL9yLyXW8LNq5CTEsz/hmcnA8L2eYSqWifpAnFYtJZVJ5cYZFZhEGMsQw4QwT5ExkvKmr8GmhUqko463NUzfTrO9RjpbKJI1CdXgexTB5+fMqk7wXLXeSzHsDi1LGjpKv+GjN/l+UUalUirgWJ3LDBAKBQPCS4OPjg0qlytNDFoXkjot5fZQrZ/kLvNTUVHr37k2dOnVYsGABZ8+ezdfc4+PjiY+PV54nJCQolVXZ0aJFC1JSUpg/f36+jgWwZs0a6tevn+u4EydOUL16dU6dOmWyfOXKlQwbNkypGsuJtWvX5ioQLlmyhICAANzc3MzGLly4kCZNmuR6nOyOnRdDTnx8PB988AE+Pj5oNBqqVaumrDt16hRNmzbF1dUVT09P3n77baKioh5rPoL/loKpGD0GZmJYAXWGZXTxev5iWJ6cYaJMMt94Otkp3U0DnpMYJuPvJh0/5FEenGEJsrtHeo0WMeYricwwQW5kdRU+D7IKcZZK/7yNr+n7xlLx6w+lD5XFPSz/nRYUZ1hRl7w7TNtWLIqfqz09awXkOE7+vyhedJQUCAQCwUtCeHg4BoMhx8fNmzdNtilVqhRJSUlmj6ZNmzJz5kyTZQkJCRZFLr1ezzvvvINOp2Pr1q2MGjWKTp06ERoa+ljnodPp+N///oePj49Jc7r33nsPjUajPFxcXLhw4QKjR482Wa7RaBTHVv/+/fnnn39M9n/lyhUuXbpEp06dcp3LDz/8wEcffcTt27eVZVu3bmXYsGFs3ryZihUrKstXr16Nk5OT2WPevHls377d4roJEyZw/fp1Ro0axZw5c9i0aRObNm3i77//BiAqKorx48cze/bsfF3DdevWUadOHd58800ePHiQ49j09HTatWvHwYMHWbZsGcePH2fkyJHK+qtXr9KhQwf27NnD4sWL2bt3L4MHD87XfATPh3xnhhVUzDLDCqgY5mwMLn5eLe0zi3AhUUnEJKUpbjVLiDLJ/GNtpWJMm3KERCVmW6b0X1HMXbqJjkpIJSFFh2MOpVZRxs567o6SI8xblEkK8oDBYMjVYfVfkPXYll7rsjMsLkVHQoqOS+FxAJTNJpNMyQx7Ts6wMKNo5+uad2dYkJcThz5pluu4x+0oGZWQyqzd1+hRsxjlfCxnkgkEAoFAUBBJTU0lNTUVJ6e8fz5XqVSKoLRlyxbs7KTPx1ZWVqjVajQa6f/oXbt2MXr0aI4cOWJ2zP79+3Pq1Cn27duHRqNhwoQJ3Lx5k6ZNm7Jt2zYCAzM6w3/wwQcsW7aMpKQkjh49SkxMDBcvXlTWJyQk0L17d5KSkti8eTNqdcbnnbS0NL744gs++eSTXM/JYDAAkvMtNtbUIDFnzhzS09Px8vIy2/bVV1/ljz/+oGbNmtnuPykpCZVKRfPmzZVl3bp1Y8mSJZQsWZIaNWoo3SmvXLlC69at2bt3L0WLFsXWVvrsZTAYOH36NFWrVuWPP/6gWbNmtG3bFoAuXbpw6dIlXn/9dcaMGcPbb79N+fLlczznrCxYsIDmzZvz1ltv8cEHH+Q4dtGiRZw7d44bN27g6uoKYCLy9ejRQ/m5atWqXLp0iSlTpuRrPoLnQ8FUjB4Ds26SBbRMUvucxbCsNz7BubjDnkWXuJeB/g0D+bJjhefepc1ZY6OInaG5uMOiEiQHmOwMk8s95VItgcASSWnppOj0wNPPDMsPWcsk7W3MnWFOdhnZW2ExSVy9L4lh2QX0y8H6zyvjMUwpk3z62YNOj/l/0bp/Q1l06Cazdl976nMSCAQCgeBZsmjRItq0aZPv7SpXrkxiYmK2pXxxcXH079+fLl26KGIOQFhYGM2aNePs2bPs3r0bHx8fQBLSFi9eTP369alevTrr16832d8PP/xA8+bN+eOPP6hUqZLJuvbt2+Ps7MzWrVuxt3/6nw+ioqKYP38+gwcPNnHM+fr6cubMGc6fP88rr7yilG3Gx8cTFhbGgQMHlOdRUVFs2bLFZMySJUswGAxMmTKFDh06EB8fz9mzZ3njjTf4/fffAahUqRIHDx4kJiaGHj168NFHH6HT6ShWrBgnTpwgLCyMR48esXfvXl555RUOHz7Mjh07+Pzzz83Oo0ePHnTu3Dnb89y0aROTJ0/OkzC6ePFi+vXrpwhhuZGeno6Hh0eexgqeLwVTMXoM5LI0mYLrDCs4ZZIAF+/lLIbFGJ1hTzsLSPDf4W90h4VE5ZwbFqXkw0m/a1kMi4hPITkt/RnOUPAiI7vCbNVWFjs4/ldkfY/KzgVZqogjAJvPhZOQmo6ttRXFPRwtjnV9zs6we8YySZ98ZIblFSUzLJ//F8lutduRuecQCgQCgUBQGFCr1axYsYLly5fz119/mazT6/X06tWLoKAgxo4dqyxftGgR5cuXx8vLi0OHDuHn52eynbW1NUuWLGHs2LH06NGDdu3acefOnVzn8tNPP7Fy5UrFofa0mTVrFlZWViblounp6dy/f9/sHEAqNyxfvryJE+r8+fO89dZbNGvWjGPHjinLVSoVa9aswc/Pj+PHjzN27FhSU1Pp1q0btWvXZu7cuTRo0ID58+dTtmxZduzYgVqtpm7dujRv3hxfX188PDx49dVXadOmDYMHD2b69Ok4Opp/jgsMDKRs2bLZnmdeDQs6nY6TJ09SrFgxOnTogLu7OzVq1GDbtm1mY9PS0ti3bx8zZ87M1Z0nKBgUTMXoMbBRvxgB+nJOS2wBKJOE3HPDHimZYcIZ9qJSzFXKQ7IUoh8ek8zXm4PZcOae4gzzcJIEADcHGzQ2Vso4gcASshjm7mD7XJ2QWcsksxPm6pSSvqn79YiUbVHKyylbJ3FGgP7zygwzlkk+Q2dYfssk5YYaIXloyiEQCAQCQUHj0KFDOQbglyxZ0uJ2gYGBfPXVV0yaNMlk+caNGwkODmb16tVK6Z9Op2Px4sVMnTqVevXq4eTklO3xPDw8OH78OAaDIVuXUt26dZk6dSrTpk2jZcuWFC1aFB8fH3x8fChVqpQyTi59fBLS09OZO3cuR48eRa+XnP9nz57Fy8vLxO108uRJmjVrxqBBg/j6668VdxdA/fr1uXr1KtWrV6dhw4a88847SpC+tbU1Xbp0YfTo0Xz33Xf06dOHxo0bc/bsWdatW8f169e5ePEiiYmJJuWb8+fPJzIyksjISBYvXsysWbPw8/OjatWqtGzZEn9/f/r06UNSkvRF4jfffMM333zzxNcjMjKS1NRUZs6cSceOHdm6dStVqlTh9ddf58qVK8q4Vq1aYWdnR9OmTenVqxe9e/d+4mMLnj0FUzF6DMwzw55veVp2ZJRJpj2VN6z8IncOk4OhL4VnL4bp9QbFGSYyw15cFGdYljLJzefCaP79Xubtv8HoP84QEWea+6RSqTKVSuYewC94OVHEsOfsHnXN9B5lpQK7bNzB9Ut5AvDQKOqU9c7eHi+XXsan6Eg1loL+VxgMBsKMf3dF85EZllfk/Mr8Bug/MHbijE5Me24OZ4FAIBAIHpf69evnGKCfkJDA+vXrsbaWvlRLSkpSSv369evH2rVriY+PJz09nZSUFJo1a8bu3buxtbVVxhkMBvbv38+AAQMA6N27t8VjNWvWDA8PD6pUqcLmzZtxd3e3OOfDhw8zZswYRo0axfnz5wG4e/cu4eHhXL9+XRmXkJDA+PHjzcLysz5y4osvvuCNN97Ax8eHffv2AbBjxw4aNWqkjAkJCaFhw4ZUrVqVq1ev8vbbb5vtx9nZme+++45///2XW7dusWDBAk6ePEndunXp3r077777Lh07duTLL7/k5s2btGrVirVr19KhQwdGjBjBwYMHKV26NGvXrlX26e7ujpubG/fu3WPKlCnMnDmT3r17895773H9+nUSExOZNWtWXl4GeUbuKvrOO+/Qr18/atWqxZw5c/Dw8GDVqlXKOPn8Vq5cyfbt22nevLmyraDgUmjEMLNuktbPr1wnJ2QxLC3doOTs/JfIZZIlPSU76aOE7G9m4pJ1yHqd6Cb54lLMzbIzbOLGiySkSuWPKTo9ScZSyMyihp9RDLsrxDBBNsju0ecthmlsrBU3mKOtOluXWo0SbiZl9WVzCIF31thgZRwanfTflkpGJ6Ypf59Fn2GZZH6dYbKICLnnEAoEAoFA8KLh4OBAp06duHfvHgaDgddeew2tVotWq8XV1ZXixYuj1WrZu3cvH3zwAVqtloCAAGWMVqvlxx9/zPZzSEJCAnq9noiICC5evEjx4sXzNT9PT0+KFSvGzp07zdbFxMQwb948kpOTc3x07drVrMzywIEDQEb54FtvvcWPP/6IXq9n8eLFvPnmm8pYf39/7ty5g42NDX5+fnh6emJnZ4eLiwuenp6o1Wrc3d3x9PSkSpUq7Nu3j08//RQbGxtsbGw4c+YMQ4YMoUOHDsybN48vvviCuXPnEhISQvv27YmLi+PAgQN06tSJ1FTzz18ffvghw4YNw8fHhwsXLtCjRw9sbW3p3bs3hw4dytf1zA0PDw+srKwICgpSlqnVagIDA7l//76yLCAggKpVq/K///2P9evXs3//fnbv3v1U5yJ4+hQiMcz0DcemgDrDpJs06efY5/CtuvxNvo+zdHOVmJr9jZB8k+toa11gM9gEuZORGZZx45qclq6UYDUqU0RZbm2lUnLtIKM8S4ToC7JD7kJaEHIFZVejfQ7ZZQ62aqr6uynPy/pk7wyzslIpuWH3Y1KyHfcs2HFR+oBV2ssJB9un3/jZyc6YX/mYZZKQew6hQCAQCAQvKsOGDWPixIns3bvXoqurefPm/PLLLxbXjRo1Ktv9tm/fHmtra7y8vKhWrRoVK1bk22+/Zfny5WZjjx07xr1790xC+QEGDx7MuHHjzKqMrly5YjHXKytr1qxBq5WaB8XGxjJy5Eh69uxpMmbIkCEcOHBAcbXJnRxlPD0lp/2kSZOIiIjg9ddfZ/Xq1URERBAUFMTZs2eJiIjA2toalUqFjY0NlSpVYt++fUop6rRp0+jZsychISFMnDgRlUrFN998Q4MGDbCzs2PBggW88cYbJsfdsWMHp06d4uOPPyY5ORmdTqdch8TERGxsnm41k0ajoVq1aiZdQlNTU7l27RplypSxuI3c4TM9XWQuF3QKjcJh7gwrmKdmZaVCa/xGPjbpv7dOyiUx3kYxTHYeWCLaWCLp6vD8b3IFj4+/BWeYLITZ21jTrqKPstzNwQYrqwwhWS6TDIsRDhCBZR4pmWHPv5Ra7v6YXXi+TL2gjMyLnJxhAFX8XQHYf/Xhk00un6w/dReATlVz/1D7ODxON8nEVJ2Jkyxr6bVAIBAIBIWFy5cv59u1lRd27txJQkICaWlpbNq0CWtray5cuKDkY7m6uiqB8FevXqVz584UK1bMZB9vvPEGCQkJjBs3TlkWERFBSEgIVapUyfNcEhISuHnzJnXq1FHKL2VcXV355JNPWL58OR9++KEi8DwJ48aNQ6vV4uTkZPIYNGgQW7duxcnJyWx9ZhEuJSWFoUOH8vPPP2NnZ4e7uzu+vr5MnDiRkydP8v3339OqVSsAPv30Uz799NPHmuf169epW7euUoI6atQo5s2bx5w5czh58iT9+vXDYDDwzjvvAJJwumbNGs6dO8f27dvp1q0bQUFBNG3a9AmvmOBZ8/S/bn5OZHUuFWQnk1ZjQ2yy7rnkrcg3PnJ3slSdnrR0vcUA6WijM8xFhOe/0Pi5SYJWbLKOqIRU3B1tuWu8ifVzs6eeMUMJzEPIfY1ZRaJMUpAdkUYxrEA5w2xyLpOvH+TJjJ1XcbG3wTeXEsSW5b3ZfekB2y/eZ2jToBzHPi3uRSdx5GYkAB2r+D6TY8hfysTn4/+hB7Gm7jjhDBMIBALBi4S7uzuXL18mNDTUTGCS0el0bNiwgUuXLlGnTp2nPgdra2scHBxMll2/fl3J3friiy8AWLp0Kb169aJJkybKuKSkJD7//HNiYmJYsWIF9erVQ6PR8Pnnn7NmzRoqVqyIl5dXnufi6OjIqVOnKFGihFm+1aZNm5gyZQodO3Zk9OjR6HQ6Bg0apGSpPQ6TJ09m8uTJZsuXLFnCmjVr2LhxY47bT506VQnMl1m2bBl9+/Zl+vTpvPXWW7z77rsAJllq+SUmJobg4GBiYmIA+N///kdkZCRTpkwhPDycWrVqsW3bNtzcpEoDX19fRo8eTVhYGN7e3rRp04Yvvvgi13w2wfOn0IhhL4ozDDKH6P/3zjBFDHPO+ONMTE3Hxd78esnh+bLbQvBi4mCrJtDTkRsRCZwJiaZpOS/uRks3sX6u9vi7O+Dvbk9IVJJZ7pOfCNAX5ILsDPMoQGKYo13OH9RqFHdjfPvylPR0yLUDZvNXvFCp4ExINPdjkxVX7eOSqtMzZ9912lTwoYy31uKYDWfuYTBArZLuSubf00b7GN0kM5dIguUOtQKBQCAQFFTatm2Lr68v/v7+OY5TqVQMHTqUsmXLPvEx5W6Rd+/eNenGCKDX69m/fz/nzp2jVq1aOe4nODiYlStX0rx5c2bNmkXZsmVZv349I0aMYNCgQcyYMYP3338/3/MrUaKEyfP79+8zfvx4/vjjD5YsWULXrl3ZsmULvXv35scff2TEiBFER0czZcoUUlNTUalUfPbZZyQnJytOt8TERMqWLYtKpSIlJQUnJydKlSrFmTNn8j2/zHz++edmy2rUqMG5c+fMlmfubpkTffr0oU+fPibLqlWrRnR0tMmyIUOGMGTIEIv7GDt2LGPHjs3T8QQFi0Ikhpne0BRkZ5icyfRcxDDjjY+boy021irS0g0kpuosur/km1wRnv/iUzXAjRsRCZy680gSwzI5wwDqBXqyOirETAzL6CaZjMFgyFU4ELx8RCUWJGeY9D5mn0vGlkqlol8Dy63Ts+Kl1VDF35VTd6LZGXyfXrWfrGRi87kwfthxhc3nwtj6QSOLY7aeDwegU5VnUyIJj1cmKYfnW6lAbzDNIRQIBAKBoKDj5OTE6dOnuXfvHklJlv8PU6lUeHh44Orq+lSO2bVrV5YtW0bJkiVJSzN3Y/v7+zN//nycnXOObXBzc+OHH37ggw8+UJa1adOGCxcucO3aNezt7enfv/8Tz3fatGlERERw4cIFAgICAHjttde4du0a06ZN499//2XhwoUmJZoCwYtKIRLDXpwySWd7Y2bYcwzQ12rUONiqiUlKIyHFcm6YnBnmUgCygARPRtUAV9b+G8q/d6IBCDU6vWTnV/caxdh+MZzmr3ibbCeX0yalpROdmFYgBA9BwSIjM+z5vzbkfEPHHAL0H4cWr3hz6k40Oy4+uRh2O1JyU10Kj+PCvRhe9XUxWW8wGLj+MB6A6sXdzLZ/WjxON8kHcVLWYFkfZ4LDYgl9lChEcoFAIBC8UKhUqjyFzOeGpW6OlnB2dmbPnj353n/WksFZs2ZZHKdWqylXrhyHDx826xCZH9RqNQaDAb1ej5WV+X20s7MzX3311WPvPycsubMEgv+CgqsY5ZOsYpilDKyCglZxhj2/zDBnjVq5Ycyuo2R0ojFAX2SGvfBUC5Buqk+HRJOuNyjOsGJGZ1iNEu78O74l3aqb5idobKzxdJL+YxW5YQJLPCpAzrBXfaVvVUt7Zd8h8nFoVV4Sif+5FklCPrsvZiVzM4q1J++arY9OTFPepwPcn02JJOSvXD8iPoX7sclKmWTVAFdAasDyKPG//39MIBAIBAKBKU8rn8qSECYQFFYKzavd2kqFtbELnkoFaquC+03188oMMxgMigvAyc4GB6MzIDtnmJIZVgAcH4Ino6yPFgdba+JTdPyfvfOOjqJ82/C1m94rIQQIoYfeQXqTovQqKAgIfnRBEEUFRQEBQUQFBVFAQJCuP7oUqUrvJZRASCEBkpCeTbLl+2N3JrvZTQHSgPc6Zw5k6ju7ye7MPfdzP7cfJsnCluQMA7J1d0gh+iI3TJAVrVYniyFZS2yLgo41fDn6YVsmvmq51fXTUsnHmQAvR9I1Wo7cfLaukvcNnVwB/roQQYZGa7L8niGUvqSrHQ757HAzxs1Q/p6gykCdZQwSF8PieO27ozSctZ+WX//DyTv6UP8yHg6UdNWL5CJEXyAQCAQCgUDwPPLCiGGQmRtmY6Us1mUbRSWGpWZo0Gh18hhyc4ZFGW7aisNNruDZsFIqqFPGHYDTIbHyeytlhuWEn5sI0RdYJkGVIX+mFBfRvKynI8p8fhiiUCjoYHCH7bv24Jn2FWn0dxSTnG4mrt2LSQagnKfTMx0nNzydbFEqQKeDWEOpa1b+OB3K9Uh9q/d0tVYusy7hbEdZQ7B/qBDDBAKBQPASotPpinoIBYqlfDOB4EXjBRPD9KdjV4xLJCEzQD8htXA/ZCTxTakAR1srHA0h08nplp1htw25NRXzueRIUDTUL+cO6MO51Vod1koFPi65W6q9XfQihyiHEmRFElFc7KyLdU5jfvCqIU/v4I2H2Tqp8oIkRLes7A1khuVLSJli/l4FVyIJeoFcetARnWRZDLsfpx/rKxU8Teb7uNrL45PEO4FAIBAIngc2b95skk8VGRlJo0aNzNZLTs7++y0lJYXq1atz7NixXI/34MED5s2bh0Zj+X4rO3bs2IG/v3+2Qf95YePGjSgUCr7//vtc11Wr1SgUCtRq/f3i7Nmz85TjtW7dOh4/fmxx2fr164mJicl1H4cOHSIsLCzX9QSC/OaFunuxNYhgxf2mTMoMSyhkZ5gkhjnbWaNQKHCyMzjDLGTgxKdmyJ3DKpYoWIeCoHBoFKC/oT12OxqAUu72cmlxTjgZRNPsHISCl5filBdW0DQo54GHow1xKRmcDrF80ZcbiaoMuaPvkKYBABwMeii76yBTDAsoYDEMkPMAo5PSLC6X8s3ebhogf78C+LjYUd5L/70QEiOcYcURnU7H1nPh3I0WYqVAIBDkhEaj4dEjU5f2vHnz6Nu3b7bbODo6MmTIECZOnJirQ2zu3Lns3bsXK6u8Rx8kJSUxduxYUlJSaNq0KXXr1s1xSkxMNNvH5cuXGTFiBKVKleKzzz7j5s2beT4+6EXD5s2b57remTNnaNCgAefPnzeZv379esaNG8f9+/dz3ceWLVuYMGFCjuusWrUKf39/PDw8zNb99ddfadOmTa7HMWbr1q3UqVMHe3t7KlSowKxZs9Bqs3/Y+dNPP1G+fHkcHBxo164dd+7csbhecHAwtra2+dLZU1DwvDDdJCHTGVacw/PBuEyysJ1hUidJvRiXkzPs9kO9K8zX1V5eX/B807JyCQJ9XQiK0n9hGueF5YT0e5KUTbac4OUiUZVBZLyKKiVdiE025Aq+BGKYtZWSdoEl2XIunH9uPKRpRa8n3kekwRXm5mBD66olcLW3JiY5nfOhj2loEKtDY/Xihb9XwT+E0IthiTmIYfrxVinpTKsqJdh/XV8i6uNiR4C3QQwTYkuxZOflSCZtvEizil6se/eVoh6OQCAQPDesW7eOBQsWcPjwYXnekSNH6Nixo9m6Go0GBwfT6+m5c+cyceJEAO7fv8+yZctIT0/H2traZLus4lhgYCBXrlxBp9MxduxYmjZtyty5c/M0Zicn02uGK1eu0L59e5o2bcq2bdsYOHAgr776Kvv27aNq1aom644YMYJ33nmHxo0by/Nu3rxJUFAQPXv2zPXYCxcupGLFity7d4969eoBsGfPHsaNG8euXbuoVauWvO6GDRsYPny42T4yMjKwsbHB2dm8Gmny5Mm8/fbbfPDBB6xevRp3d3fefvttXn31Vbp160ZsbCzTp0/Pc3dPgISEBCZPnsyXX35J3bp1OXHiBOPHj8fOzo4pU6aYrb9p0ybef/99li9fTvXq1Zk4cSLdu3fn0qVLZg0HPvnkE1Fi+hzxYolh1nqXS/F3huU9M+xGVCIz/neVMW0r0rJyiWc6rnQ86fg5OcOCDWJY5ZKiRPJFwUqp4KPXAhm28jQApd3z5jyRf0+EM+yl586jJN5cfpIHiSp+HtyQ86F6h1QJ5xdfDAOoVdqVLecg/PHTuaGk3L1SbvbYWClpG+jDXxfus+/6A1kMk5xh5Qqwk6SEt7NUJmkuhiWlqeXvDF83B7rWLsX+6w+wVirwcLSlvCSGiTLJIiUmKQ07Gyuc7Uwv5/Ybsu2kzDeBQCB42blw4QJNmjTB2dkZlUrFX3/9xdixYxk1apS8zvLly5k6dSq7du2ievXq8nytVktAQABBQUE5HmPo0KFymaFGo+Gtt95i0KBB/Pzzz/I6arUaGxsboqKi8Pb2NtvHe++9x/79+7G2tqZz5845Hq9t27b89NNPJvN27drFoEGDaNy4MVu2bMHR0ZENGzbQr18/WrZsyerVq032e/v2bRISTL8rli5dikajwcfHx+yYNWrUYNOmTRZLSyVSU1NRKBS0b99ente3b19WrVpF+fLladiwoSwi3bx5k06dOnHo0CFKlSqFra3+2kSn03HhwgXq1avHpk2baNeuHa+//joAvXv3JigoiG7duvHRRx8xePBgk/crN+zs7Dh58qR8frVq1eL06dNs3brVohg2Z84cRo4cyeDBgwH970m1atU4fPgwbdu2ldfbt28fJ0+ezPG1ERQvirdq9ITYPCdlkq4OeqdVYloGyw4H88tRyzZLgPWnQvnvTgyDfz31zF27pE6SkhiWozNMygsrIcSwF4k2VUrQzOBoqZDH8lenXLqOCl4OwmJTeOPnE0QlqNDp4LO/rvDrsbsA9KlfpohHVzh4GcoKY7LJ2MoNyWnlZ3BlSjlkUih/Srqah4by9HKFWiZpfj5StpmLnTXOdtZ0qF6ShuU86NewLEqlQh5fdFJ6obucBXqik9Jos+AQby0/YVKmo9XqOHJLXw7/OCWDuJSn+30VCASCF42mTZuybNky+vXrx8yZM02W/fbbb8ycOZNDhw7RpEmTZz7W1KlTiYuLy1Nel0RSUhKXLl1iw4YNlChRgqCgoGynpUuXmuRspaSk8OGHH9KtWzeGDx/Ozp07ZceYvb09f/31FyNHjqRLly6MHDmSqKgoi2OIjY1l+fLljB49Gp1OJ09+fn5cvHiRK1euUK1aNZKSkuQpMjKSo0ePyj/Hxsaye/duk3VWrVqFTqdjzpw5dO/eXT7XAQMGsHHjRgBq167NsWPHiI+Pp3///kyePBm1Wk2ZMmU4c+YMkZGRPH78mEOHDlGtWjX+++8/9u3bx2effWZ2Hv3796dXr14Wz9HOzs5M6LO3t7eY6xYXF8f58+d57bXX5HmBgYGUKlWKEydOyPOSk5MZNWoUixYtwtGx4K/hBPnDC+UMs31OyiRdDWJUWGwqc3brnzAMbloOO2vzWnLjLJlx68+zeVTTpz6/rGWSOXWTvPVAX0pXSYTnv1AoFAq+H1iPP89H0L9R2Txt45hL11HBy8HaE/d4lJhG1ZIuJKWpiTC4nJqU96RzTd8iHl3h4GUoB43JpvtibkhiWCk3feOK1lVLYGOl4M6jZG4/TERt+Lx3c7DBvRC6c3q7GMSwRHNnmCSG+RrG6mRnzebRzeTlLvY2eDvbEp2UTkh0CrXKuBX4eAWmnL4bS6JKzcXweEJiUmS33qWIeJMOoXejk6nn/3K4NwUCgeBp6dKlCx06dMDPzy9f9te2bVtGjhxJu3btuHTpktnygIAAk5+HDh3K4sWLOXz4MLdv3+batWvUrVs32/0nJSURGBgI6JsANGvWjPT0dNq2bcsPP/zADz/8YHG7/v37c+TIEQIDA7l7967Z8sWLF6NUKk2WaTQaHjx4QOnSpc3W37p1KxMmTKBp06ayqHXlyhUGDRpEjRo1mDt3rlyCqVAo2Lx5M2PGjOH06dN88803pKen07dvX9LS0tiwYQMtWrRgwYIFVK1alS+++AIrKyuaNm1K+/bt8fPzQ6FQMGTIEDp37kzDhg359ttvzcpEASpUqJDta5eVuLg4Nm/ezNtvv222THodypcvbzLf39+f8PBw+ecJEyZQo0YNevbsyaJFi/J8bEHR8kKJYc+LM8xSBleGRoedhXcjXZ0Z5HcxLI6Td2JpUdncUpsXspZJOubg+JGcYUIMe/HwdrZjRMu8f0E45dJ1VPBycMeQDfXWK/74uNgxau05FAqY3rU6CkXujRheBDKdYWnodDreWXUaGyslywY3yNNrEGlUJgn6zsItKnnzz41HbL8YSXU/V6BwXGGQ6Qx7ZKFMUgrPL5VDtmCAlxPRSencjUkWYlgRcDE8Xv7/sdvRshj2T9BDk/VCYpKp5+9RqGMTCASC54FFixbx888/ExMTQ82aNc2W//LLL3Tt2jXXkHxLSCV9KSkp7NixQw54l8okQ0JCLJZJSlSvXp0zZ85ku/zQoUMsWLAAgFKlSjF+/HiGDx+Om1vu38cZGRn8999/eHiYfzdoNBqWLVvGmDFj0Gq1KJVKLl26hI+PD15emXmpZ8+eZcqUKVy5coVvvvlGLiEEaN68Obdu3WLmzJm0bNmSN954g7lz5+Ln54eVlRW9e/dmypQprFmzhp07d3Lp0iUWLFjA7NmzKVOmDNeuXcPd3Z2EhAR5jMuXL2fevHkoFAo8PDxYtGgRpUuXpl69enTo0IGgoCDat2/PTz/9hIODQ57z1qKioujZsyfOzs5MnTrVbHlSkv6eOKvby9HRkbQ0/fXThg0b2L59u0XRU1C8Kd6q0RNiY2XIDLMq3jdmkhhlTIbacveK5CxunNgnKHfQ6XRM2XSRL7dfA0y7SUL2zjBVhobwx/obISGGCRxzyJYTvDyEGrKs/D0d6VTDl5k9a7LojbrULP3yiCCeBmdYXGoGEXGp/HPjEX9fe8B9g4sqNzKdYZkCU7c6+ifQ2y/dlxuX+BdCXhiAlyEzzFLZp+QMK+Vqn+32IkS/aLkcESf//9itzE5oh27q/+9i+K6/+0i8PwKBQGCJiRMncu7cOcqWLUtUVBSNGzdm+fLlREVFERUVRdeuXQF9CdzNmzext7fPcVqzZk2+je3q1asEBgZmO2V1MU2aNClPQhiAjY0NrVq1srhsxowZDBgwAF9fX7mJwL59+0zWDwsLo2XLltSrV49bt26ZCGESrq6uzJ8/n3PnzhESEsIvv/zC2bNnadq0Kf369eOdd96hR48efPHFF9y9e5eOHTuyZcsWunfvzoQJEzh27BiVK1dmy5Yt8j49PT3x8PDg/v37zJkzhx9++IEhQ4bw7rvvEhwcTEpKCosXL87TawBw9OhRGjTQP9A8cuQI7u7uZuvY2ekfHKanm14rqVQqHB0duXjxIu+++y5r166lZMmSeT62oHggnGFFgIONFVZKhUkJZIbGshiWksWNk/oEpWrhj1PZdFZv33zrFX8jZ1jO3SSDHyWh04GHo41cFiR4eZGcYVl/FwUvDzqdjnuGLocBXk4oFAoGv1KuiEdV+Hg42qBQgE4H1yMz25hfu5+Qp+6s92W3VabA1KF6Seysldx5lMzig7cBaFiucFw8JeTMMAvOsATTMklLlBdiWJGh0+m4ZOQM+zc4BrVGS4ZGx6XwOAD6NizDyuMh3I15trxRgUAgeFno2LEj69ato1u3bibz4+PjadmypUmHSUssWbIEf3//Zx6Hn58fBw8e5Nq1a4SGhvLFF1+YrZOamkpcXJzJvEOHDtG2bVuzTpXGaDQaTp8+TcOGDU3mHz16FEB2ug8aNIjvvvuO1q1bs3LlSubNmyevW7ZsWUJDQ1mwYAGlS5fG3t6exMRE7O3tsbGxIS4uDldXV5RKJfHx8aSnp6NWq7l+/To2NjZcvHiR8uXLc+fOHRo0aICPjw/Ozs40btyYqVOnkpiYyNGjRxk7dqyZCAXw/vvvM27cOHx9fbl69Sr9+/cHYMiQISxfvtxiCH5Wtm3bxoABA5gwYQKzZ8/Gxsa8cguQS0PDwsKoWLGiPD8sLIz+/fuzaNEikpKSTH5n0tPTOXbsGGvXruXGjRuUK/fyXTM/LxRv1egJkUQw22KeGaZQKOTcMIn0bMSwZIMbR6rAeRJB4qFRDszeq1FGmWE5d5O8el/fUaSSj/NLU/4kyB7p9ySrS1Hw8vAwMQ1VhhYrpYLSHrmLPi8q1lZKPAxZXlfvZwoR1+7n3rFPp9MRGWcI0DdyhrnY29AuUB/impqhoWZpV94qJKFRKpOMSU5HqzUtAYnKkm9mCamcM2tHyTS1xiSzSpD/hMSkkKhSY2utxMXemkSVmssR8YQ/TkGn02eTNq2gL2e5G51UxKMVCASC4kV0dDTHjx+XOxdKDBo0iD179nDhwgWT+Tdv3rSYl5WVsWPHyqKItbU11tbWXLx4kfbt28s/29vrv1d9fX3ledK0cOFCQF+Cd/LkSUaMGMHGjRupW7eu2eTu7s6YMWPMxlCjRg3UanW2U1ZhJiEhgUmTJjFw4ECT+WPGjOHo0aMMGTIEnU4nl31KSCWes2bNIjo6mm7durFhwwaio6OpVKkSly5dIjo6GisrKxQKBTY2NtSuXZvDhw/L+VsLFixg4MCBhIWFMXPmTBQKBXPnzqVFixbY2dnxyy+/MGDAAJPj7tu3j/Pnz/Phhx+iUqlQq9VyGWtKSkq2opYxUVFRDB48mPnz5/P111/nuE3p0qUJCAhg37598rybN28SHh5O+/bt+eqrr7h27RoXLlyQp4YNG9KrVy8uXLiQbxl0goKheKtGT4jNcxKgD+a5YWqN5Vp0SfySblqeRAwzftq/50oU1wwt1t0ds3eGqTValh4OBqBZxafLJhO8WEi/Jymim2SRsOr4XTosPCxnOBUFkvOntLvDc/H5WpBIpZJXjQSw65G5i2HRSemkZuj/hrK6raRSSVsrJd/0q1tor7FUJqnR6ohLNe0Ied+Qb5aTMyzAy+AMy+I8Gr7qDE3nHCjS39kXHcn9Vb2Uq9wh+NitaMIe69+LMh6ORs69lKfKuxEIBIIXCVtbW0qUKAHoH1ClpKTQp08fk3WcnJyYNGkSb775JvHxmQ+9zp07R7169Z7oeJL4pNPp0Gg08s8qlf5hU1RUlJlQNWnSJO7fv0///v3ZsGEDCxcuxMXFhZ9++kkWWlauXIm7uzvdunVj5cqVz/SaJCcnc/fuXV555RWuXLlisszd3Z2pU6eydu1a3n//faytn72g7NNPP8XFxQVnZ2eTadSoUezZswdnZ2ez5cYiXFpaGmPHjmXJkiXY2dnh6emJn58fM2fO5OzZs3zzzTd07NgRgI8//piPP/7Y4jj+97//odFoeO2117h9+7bJlJ6eTnBwME2bNiU4WH9PPGnSJL777js2bdrEmTNnGD58OF27dqVWrVqUKlXKrITV0dERNzc3AgMD8yTOCYqOF6xM0pAZVszLJME8Nyy7MknJjePtbMejxLQn6uhnLIZJ5RROtlZ0qK6vZ5YdP0bOsE1nw7nzKBkPRxuGtzTtmiF4OZHKJNM1WtLV2ufi7+tFYuOZcG49TOLYrWj6NcxbB9D85l6s/ga7sILdizNeTrbcBq5GGDnD8iCGnbwbA0Cgrwv2NqblC51q+PJe+8rUKu1GVV+XfB1vTthYKXF3tCEuJYPopDRZ6AOISjDPN8tKeW8nFAqITU7n1N1YGpf3BOByRDxpai3/3o6hT4MyBXsSLymXDd/pdcq4EeDtxN6rD7gYHoeb4WFXWU8H/L0cUSggKU3No6Q0fFyyFzYFAoHgRad69eps2rSJzZs3U6JECVatWgUgdwTcvXs3U6ZM4cKFC+zZs4fOnTvz559/Ym9vz759+/jyyy8LfIypqam0a9eOfv36sXbtWmxtbencuTNjxowhPT0dBwcHQkND+fHHH3n11Vef+XhOTk6cP3+egIAA1GrTe8ydO3cyZ84cevTowZQpU1Cr1YwaNSrHEszcmD17NrNnzzabv2rVKjZv3syOHTty3H7evHlyYL7E6tWrGTZsGN9++y2DBg3inXfeAZCFLEtERUWhUqmoUqWK2bLLly+Tnp7O9evXZUF03LhxPHr0iDFjxqBSqejRo8cTZZMJii8vmBj2fGSGAbSsXIJ7MSkkGYSo7MokJTdOCRc7rkc+oTMs0bxMZXz7yvIFsez4MQhsqgwN3+67qV+vXWVcLXS9FLx8ONhmfumlpmuei7+vFwnJXWNc9lzY3DOUwQkxLNOlaxyaHxqbQqIqw2KnYInjt6MBaF7J3HFrpVQwqYP5BVlh4O1spxfDEtOoUlIvxKWma4hL0TvFcnKGOdlZ06d+GTafDef9DRfYNaElznbWJBhK8i+ExQkxrIC4ZBBja5Vxl0tZgx8ly26wMh6O2FlbUcbDgbDYVO4+ShZimEAgEFjg+vXrhIaG8s4777BgwQKsra3Zvn07HTt25J9//uHmzZvUrFnziZ1hT4ODgwPnzp3jzp07bNq0ibNnz3LixAkSEhLo1KkTNjY2REREMHjwYBo2bEiFChXw9/dnyJAhctnitWvX5FJMS0gdECUCAgJMfn7w4AHTp09n06ZNrFq1ij59+rB7926GDBnCd999x4QJE4iLi2POnDmkp6ejUCiYNm0aKpWKnTt3YmVlRUpKClWrVkWhUJCWloazszMVK1bk4sWLz/T6fPbZZ2bzGjZsyOXLl83mb9y4Mcf9WNqXMcZ5bAqFgi+//DLPguihQ4fytJ6g6HmhxDApK6y4Z4YBTH0tkCmdqtLq63+IiEslI5syyUxnmP6JfWouYphOpyP4UTIVvJ14lKS/WfN0siU2OZ0AL0eGNQ+Q15UcP8kGwW3ruQgeJqZR2t2Bt1559vBHwYuBrbUSWysl6Rotyelq2XkgKHhS0zU8NogSDxPy1rGwILhnKIMr5+lUZGMoLnhm01QkKCqRRgGe2W53/LbeGda8kle26xQF3s623H4Ij4ycxJIrzNHWyizfMiszutfg1N1YQmNTmLs7iCmdqiJV5J0Pe1xg437ZkQTqKiWdKWno+HkvJpk7hs6RZQ3ZfgFeTnoxLDqZJhWK1++eQCAQFAccHBx47bXXWL16NV5e+s9JT09PTpw4gbW1Ne3atWP+/PmFNp6HDx8yZMgQ6tatS7NmzRg9ejSVK1eWl8+dO5c7d+5w6tQpLly4wLVr1/D0zLz+qF69ulnJozFZxa+sLFiwgOjoaK5evSo3A5DKCRcsWMC5c+f49ddf+fTTT5/tRAWCYsALJYY9T84w0LsBpNJOtQVnmE6nk51gJfKYGbbuVCifbrvCp69Xk51hY9pUJF2jpVMNX+ysM10+joYyydQMDWqNll+O3gFgeIvyJusJBI52VqSnaJ+oTFfw7Nw3ylwqSmdYqCiTlJFytiSkMsMNp8M4evMR77Qoj7uj6TphsSmExqZgrVTQuHzxEiS85Y6SmU7iSKO8sNyaqDjbWfNpl2qMXHOWc/ce8zglcz9BkYmoMjRmZaGCZ0d6iOVib4OPix0udtYkpqk5cUcvupbx0P+tVvdz5eitaPZff8CAxuIhl0AgEPTt25e+ffvKP7do0YKdO3earSdlZO3atStHp9WTYm1tnWOOY0BAAGfPns1xHxUqVKBChQpm4fKtWrXi3LlzOW4bEhKS47i0Wi1Kpfm9tKura4GVig4dOpShQ4cWyL4Fgpx4PlSjPGJjrb9of54CnqWxWiqTTFNr0Rg6fGUG6OcsRpwJ0T+JPxf6WM4M83N3YEybSlQs4WyyruQMA9hxKZI70cm42FvTv1HR5BIJii9ZXYSCwkHqPghFK4ZJAfrlvIQzzMvwWSzRuoo+jHfz2XC+P3ibLecizLb5N1hfIlmnrDvOdsXrGZTcUdLIGSYF4vt75k38LGsQXmKS0+TySgC1VscVo2w1Qf6g0+lk17iTrb5LVwUf/fe71BCnrOG9e8OQM3gg6KHsJhMIBAJB3slPIaygUSqVZh0yn2YfAsHLwgv12y5lYDkVs5uNnJDEMEtlksYuMMmNkJsz7I7hpjUkJkUWw7yz3LxJ2NsokR76/3joNgBvNSlX7G7WBEWPoyE3LFk4wwoVqaMfwIMiKpOMS0knQaV/3/MqjrzIeGUpk+xVz7TVemSceQdFuUSyYvFyhYE+jxLgkZHYeudREgAVvJ0tbpMVqYw/Njmd2GTTrMrzoXH5MEqBMakZGrkUVbreqZTlYVcZQ5lkhRLOtK5SAp0OVv93r1DHKRAIBAKBQFCceaHEsDcb+zOkaTn6N3x+AnulMskMtbkzTOry6GBjJQtUOYlhOp1OvokJjUmWy168nS0/IVAoFLLj5+YD/Xa965e2uK7g5cbRTjjDioKsZZI52eoLCumzoaSrnUkzhZcVYzHMy8mWNlV92DG+BSNbVwBMs7ckgqL03Sbrl/MonEE+AZJocuthkjxPeqhSoUTenIAehtdEq8PMfXQhLC4fRikwRmq8o1Dorw8AKvlkimGeTrYmDwWHNgsAYOOZMJPu0QKBQCAQCAQvMy+UGBbg7cQXPWrKWRnPA5IzTK01F8Mk4cvJzkp2vVkK0D904yEbTocSk5xOosHBkZyukS+YvV0sO8Mg0/EDYGetpIK3KIMSmONsyJcTmWGFi3GZZLpaS3xqRg5rFww7L90HoEkxy7oqKowzw6ROizVLu1HN1xUwdVhJPEjQzyvt7lAII3wy6pXVC3RX78ejytB/v8jOsDyKYTZWStwc9I01bhtENR/D9875UBGin99IXaYdbaxQKvUP1CoavVeSwCnRukoJynk5kqhSs+dKFDqdvnw1TS0ebggEAsHTUtgPKNVqNVoL94sFRUZG4V9zCgSFzQslhj2PZGaGmX+gSiVpjrbWcti9pTK19zdc4KMtl9lzJcpsma21Epccyh6NSyIDfV2wfo7y1gSFh6PIDCsSjJ1hUPi5YRkaLdsvRQLQS7hGAfByyny44OuamSNiqdwQQJWhkUVMH9filztS1tMBb2dbMjSZAonUMCFrzmROSCJhsEFIa12lBNZKBffjVYQZ9ifIH6QHXcbuL2NnWNksDwSVSgV96usd81vPhzN3dxBdfzjG0kN3CmG0AoFAULzYvHmzSVh7ZGQkjRo1MlsvOTn7nMWUlBSqV6/OsWPHCmKIXLhwgZo1a5rM+/PPP2nSpEmu2x47doyNGzc+8THVajUKhQK1Wv8dM3v27DyF2q9bt47Hjy0/+Fq/fj0xMTG57uPQoUOEhYU90XgFgvxAKB9FjI2h86WlMkn56a+tlezgyuoMU2VoeGwILF57wjwPpISzXY7dwCSRDaBaKdcnHL3gZcHJVjjDioL7WfKnCjs37MjNR8Qmp+PtbEfLSt6FeuziipuDDVYGN47kDAMjMSxLmaT0njnYWOFqX/zyGBUKBfX89e6wc6GPCY1JQavT/8375OAqzopUPhr8SH/z4OfuQK0ybgCcvBubz6N+uZFKHY0fZvl7OsqxC1mdYZCZbfdvcAzLjuhFsPWnQgt6qAKBQFDs0Wg0PHr0yGTevHnzTDpOZsXR0ZEhQ4YwceLEbB1i+/fvR6FQ5Hnq3LlzjuPcvHkzLVq0yPV8JkyYQFBQUK7r5cbmzZtp3rx5ruudOXOGBg0acP78eZP569evZ9y4cdy/fz/XfWzZsoUJEybkuM6qVavw9/fHw8PDbN1ff/2VNm3a5HocY7Zu3UqdOnWwt7enQoUKzJo1K0fn3U8//UT58uVxcHCgXbt23Llj+kApIyODL774gnLlymFnZ0flypWJi4t7ojEJCp/id2X+kmFjuKnKsNBNUu4WZWeNo43lzDDjzl1BUYlm+8ipRBIyHT+gb8EuEFhCZIYVDiHRyTxIUNGkghc6nY7IeL2QUsbDgfDHqTxMKFxn2Nbz+s6I3ev4CdeoAaVSgYejLdFJaabOMEOjkriUDNLUGuys9QKyVCJZ0jXnBxNFSYNyHuy79oBz9+Lw99SX21Uo4fxE45Ucc1KAvoejDU3Ke3E+NI6Td2Lo2yAzyzM2OZ2xv5+jb4My9Gnw/GR8Fhek6wDjh1nWVkoCvJy49TCJMhYaXZT1dKRxeU9OGQmT1Uq5FPxgBQKB4Dlj3bp1LFiwgMOHD8vzjhw5QseOHc3W1Wg0ODiYPoCYO3cuEydOpF27dqSmmjfVqVGjBosWLaJDhw7yPJ1Oh7V19rflKpWKnTt3UqtWLQYMGGCyrGHDhnzwwQcAbNu2jfPnz3Px4kW+/PJLed9arRYrK9Pc15EjR7JkyRJGjBjBO++8Q+PGjeVlN2/eJCgoiJ49e2Y7JomFCxdSsWJF7t27R7169QDYs2cP48aNY9euXdSqVUted8OGDQwfPtxsHxkZGdjY2ODsbO5Inzx5Mm+//TYffPABq1evxt3dnbfffptXX32Vbt26ERsby/Tp09m/f3+uY5VISEhg8uTJfPnll9StW5cTJ04wfvx47OzsmDJlitn6mzZt4v3332f58uVUr16diRMn0r17dy5duiR33xw2bBgnTpzgu+++o1KlSly9ejXH91RQPBDvUBEjd5PUWuomKZVJWsnB1akZGrRanZwTkrVzF4Cfmz33DTfRJbIJz5dwshXOMEHuCGdY4fDOqtPciU5m48imVC3pIt/01inrTvjjVB4kFq4z7OhN/ZPSbnVKFepxizvezgYxzMgZ5uZgg42VggyNjpikdPwM+WCSM6xkMSyRlKhvcIadDX1M7bJ6N1de88IkvLJ817g72hLg7cTSw8GcuGtaIrH3ahT/3YkhJV0txLCnQC6TtDW9hOtZrzSr/g2hRTYuzr71y5iIYbl1pxYIBIIXiQsXLtCkSROcnZ1RqVT89ddfjB07llGjRsnrLF++nKlTp7Jr1y6qV68uz9dqtQQEBOTquBo6dKhcZqhUKtm/fz+//fYbGzdulB8wKRQKbGxssLfXXxesWLGCLVu2sHPnzmz3u3HjRjw8PMwEubNnz3Ly5EkA7t+/z+jRo1mzZg1vvfWWvM7+/fuZOHEiV65csbjv27dvk5CQYDJv6dKlaDQafHx8zNavUaMGmzZtslhaKpGamopCoaB9+/byvL59+7Jq1SrKly9Pw4YNZRHp5s2bdOrUiUOHDlGqVClsbfXXEzqdjgsXLlCvXj02bdpEu3bteP311wHo3bs3QUFBdOvWjY8++ojBgwebvF+5YWdnx8mTJ+Xzq1WrFqdPn2br1q0WxbA5c+YwcuRIBg8eDOh/T6pVq8bhw4dp27Yt+/btY8OGDQQFBVGxYkUAszJXQfFEPOovYnIqk5RcOE621jgZPQFOzci8gLUkhrUJzPzg8nbOxRmWJTNMILCEnBkmxLAC40GCSu7it+xwMBGGEklPJ1v8DU6PwnSGabQ6EgwNOfwtOE1eZlpXLYGLnTWNy3vK85RKhfx5a5wb9jyIYbXLuGGtVPAoMY2jN6OBJ8sLA9MumwDujjY0DPDESqkgLDbVpOQ3KFJ/0R1j4fursLgcHs+vx+6itfAgqrgjPZRwzpIHOrZtJU590p7y2TTC6VqnFC0re8th+0IMEwgELxtNmzZl2bJl9OvXj5kzZ5os++2335g5cyaHDh3KUzZXXmjVqhVnz55l3rx5FpeHh4czadIkBg4cKM+zt7enSZMmXLt2DXt7e8aPH8+CBQv4+OOPmTFjhsnUuXNn7OzsUKvV9O3bl169epkIYU9DbGwsy5cvZ/To0eh0Onny8/Pj4sWLXLlyhWrVqpGUlCRPkZGRHD16VP45NjaW3bt3m6yzatUqdDodc+bMoXv37iQlJXHp0iUGDBggZ5zVrl2bY8eOER8fT//+/Zk8eTJqtZoyZcpw5swZIiMjefz4MYcOHaJatWr8999/7Nu3j88++8zsPPr370+vXr0snqOdnZ2Z0Gdvb49GY/69GBcXx/nz53nttdfkeYGBgZQqVYoTJ04AsHLlSnr27CkLYYLnByGGFTFSxoelMknZGWZnhb21ldF8IzEsxfxmom3VvIthkuPH39MRF3ubJxi54GVCEmNTRJlkgXExLE7+/4Gghxy9pXdllXKzl7ObHhaiMyxJlSl8is8GUz5+rRrnP+tAOS9T0cFSiH6mGJb3/K3Cxt7GSi6T/++O3sX15M4w0/PzcLTF2c6amob9njRyh12P1Jf0Py5CMWzaX1eYueMax25HF9kYnpYkKU/UQnOcHDNCba1ZM7wJX/XSl6xI2WMCgUAggC5dunDixAmTsr5nxdXVlbVr1zJjxgzOnj1rsiw1NZVevXrRp08fBg0aJM9XqVScPHmS6tWro1Kp6Nq1K48ePaJ8+fKMGTPGJKMsPT0de3t7rK2tGTp0KF988QXOzs4mU7du3bh+/brZ/A0bNlgc8+LFi1Eqldy9e1eep9FoePDgAaVLmzdT2rp1K9WrV2fOnDnyvCtXrjBo0CDatWvHqVOn5PkKhYLNmzdTunRpTp8+zSeffEJ6ejp9+/alSZMmLFu2jBYtWrB8+XKqVq3Kvn37sLa2pmnTprRv3x4/Pz+8vLyoUaMGnTt3ZvTo0Xz77bc4OZlfs1SoUIGqVavm4V3SC16bN2+mXbt2Zsuk16F8+fIm8/39/QkPDwfgxIkTVK1alSFDhuDl5UXNmjVZu3Ztno4tKFqEGFbE2EplkpYyw4ycYUqlAgcb8xD9rDcTttZKXqmQ6VbwzqVMUnL8VBclkoIcEM6wgudSeLzJz9/uvwnog8glV1FhOsMSVPo8QjtrJbbW4qsiK5Yy1KSHD/fjU5m08QJrTtwzygwrvs4w0LuKpIczABW8n8wZ5mnBGQbwSgUvAJYeusPDRBU6nY7rUXpnWHK6BlVG0QjsIQYX5u2HSUVy/GchM0DfKpc1LSN1oRSf5wKB4GVn0aJF1K9fn7CwMGrWrEn9+vXx9fWVpx07dgBkG5KfF5o1a8b//d//MXfuXJP5y5cvx8bGhh9//DHH7Rs3bsz69etp1KgRBw8e5P/+7//k8aSnp2Nnp7/2+L//+z9sbW1JTk42cWRt377dzMmVlJTEG2+8YfF4Go2GZcuWcfLkSTlQ/tKlS/j4+ODl5SWvd/bsWdq1a8eoUaP46quvTDpYNm/enFu3btGgQQNatmzJ22+/LQfpW1lZ0bt3b6ZMmcL8+fMZOnQorVu35tKlS2zdupXg4GCuXbtGSkqKSfnm8uXLiYmJISYmhpUrV7J48WJKly5NvXr16NChA2XLlmXo0KFyTtvcuXPNXnNLREVF0blzZ5ydnZk6darZ8qQk/XWCo6NplYSjoyNpafprvMjISFauXEnt2rXZu3cvPXr0YPDgwRw6dCjX4wuKFnGHU8RYy86wHDLDDBe8UkfJlIzMC1ipTLJJeU9srZQ0CvDAxd5GdijkFqBft6w7AO2qmdeECwQSsjNMlNUUGBfD4wDkoHFVhv4C5JUKXrIzTMoMU2u0XAiLQ20Q0bVa3TNdqFlCEsNcHYQrLK9IIfpbzoaz9VwEc3ddl8sDi7sY1qmGLxtHNqW0uwNlPR2o6PPsmWEAg14ph7ezHTceJNL3p/+4EpFAosr8O6wwSUpTE5+q//0OjU0p9OM/K8lynujTxb46iYYoAoFAAMDEiRM5d+4cZcuWJSoqisaNG7N8+XKioqKIioqia9euACQnJ3Pz5k3s7e1znNasWSPvOyUlRRaePv30U5YsWUJSUhI6nQ6VSsWwYcPYtGkTGRkZ8nqWyvQ8PDxo06YNXl5e7N27l7///pszZ84AkJaWJmeP5RczZsxgwIAB+Pr6yk0E9u3bR6tWreR1wsLCaNmyJfXq1ePWrVtylpYxrq6uzJ8/n3PnzhESEsIvv/zC2bNnadq0Kf369eOdd96hR48efPHFF9y9e5eOHTuyZcsWunfvzoQJEzh27BiVK1dmy5Yt8j49PT3x8PDg/v37zJkzhx9++IEhQ4bw7rvvEhwcTEpKCosXL87zuR49epQGDRqgUCg4cuQI7u7uZutIYmN6uun1ikqlkgUytVpNp06dmDx5Mg0bNmT27Nk0bNiQ1atX53ksgqJBBOgXMTY5OcPSM51hoBfFYpJNL2ClG4nG5T35um9t3B30NyAtK3uz81IktUu753j8nvVK82r1kmbZIwKBMbIzTJTVFAg6nU4ukxzaLIA+9csQn5pBoK8LAd5OhBlu2B8mpKHT6Zj+1xXWnwqjZmlXmlfyZt3JUFpXKcHiN+vn25gkwcLFXnw25BXpIcRFg8svOV3DBcP7WtzFMIB6/h4c+bAtGq3uid2AxiX5VkoFrobfm7KejmwZ3ZS3fjlJaGwKn/552WS72OTMZgOFRaRRftm9mORCPXZ+IH0OOz3l97b0cCM5XY1Opyu2XU4FAoGgsOnYsSPr1q2jW7duJvPj4+Np2bKlSYdJSyxZsgR/f38Aqlevzr179yyul12W1aZNm+jbt2+2+y9Xrhxnzpxh/fr1zJ07l8DAQFmseVaOHj0KZJbbDxo0iO+++47WrVuzcuVKk9yzsmXLEhoayoIFCyhdujT29vYkJiZib2+PjY0NcXFxuLq6olQqiY+PJz09HbVazfXr17GxseHixYuUL1+eO3fu0KBBA3x8fHB2dqZx48ZMnTqVxMREjh49ytixY81EKID333+fcePG4evry9WrV+nfvz8AQ4YMYfny5RZD8LOybds2BgwYwIQJE5g9ezY2NpYf/kqloWFhYSaZYGFhYfJxfXx8qFSpksl2VapU4cGDB7mOQ1C0CGdYEZNTmWRKWmY3SQBHG/2Fb6qFzDBPJ1vKeTnhZihN+aZfHc5N74C/V+7B10IIE+SGJMgKZ1jBEBKTQoJKja21kqq+LjSt6EXnmr4EGIKwfVztcLCxIk2t5Zejd9l4Rp9RcCUigWWH75CoUnM8n7OPMsUw4QzLKyUsOHHVhoB23+dADAO9kPU0ZbHGZZJuDjYmAks5LyemdNLndmQtBy4KZ1iEsRj2PDrDDA/EnrpM0vB5rtOZNuQRCASCl4Xo6GiOHz8udy6UGDRoEHv27OHChQsm82/evGkxLysrY8eOlYW0kJAQkwB6aapYsSK7d++2uCyrEJaQkMC+ffsAuHfvHh988AGVK1dm1apVvPHGG6SmpuLg4MDhw4extrbG21vfTdja2lqeOnXqxNWrV03mSdPZs2dJSEgwC/EHGDNmDEePHmXIkCHodDq5k6OEdKxZs2YRHR1Nt27d2LBhA9HR0VSqVIlLly4RHR2NlZWV3EGzdu3aHD58WM7fWrBgAQMHDiQsLIyZM2eiUCiYO3cuLVq0wM7Ojl9++YUBAwaYHHffvn2cP3+eDz/8EJVKhVqtlqsjUlJSshW1jImKimLw4MHMnz+fr7/+OsdtSpcuTUBAgPw+gP73ITw8XO6W2axZMzlMX+LatWtUqVIl17EIihYhhhUxmc4w8xIn2RlmEKscpDJJo5wPKTMsa16LQqF46qfGAkFWHI2cBIL855KhRLKGn6v8mWCMnbUV77bUXzjM3nUdjVZH4/Ke9KzrR83S+ry/+NSMfO2MlyiVSQpnWJ6xJIZJ+BTjAP38wMPRFkn/kvLCjOlUwxc3CyW3RS2GhcWmoHnOOkompz1bmaSUP6rflxDDBALBy4GtrS0lSpQA9I78lJQU+vTpY7KOk5MTkyZN4s033yQ+PvPhzblz56hXr16hjTU8PJyQkBDKli3L77//zujRo6lYsSKXL19m27ZtnDt3jv79+5OSkoKDgwOtW7dGrVbLwpD0f7Vazd69e6lRo4bJPGkKDAzk7t27vPLKK1y5csVkDO7u7kydOpW1a9fy/vvvY2397NeDn376KS4uLmZh/qNGjWLPnj04OzubLTcW4dLS0hg7dixLlizBzs4OT09P/Pz8mDlzJmfPnuWbb76hY8eOAHz88cd8/PHHFsfxv//9D41Gw2uvvcbt27dNpvT0dIKDg2natCnBwcEATJo0ie+++45NmzZx5swZhg8fTteuXeVmC++//z67d+9m9uzZnD9/ng8++ICgoCDGjRv3zK+ZoGARYlgRY52XbpIGEUwqbTB+kivdSHg45hyULxA8C7IzTNw4FQiSW6ZOGfds1xnVpqKJu2hKp6osGlCPraObA6DVYZLF9KyIMsknJzsxzN3RBnubp3PxPC9YKRXy95Cl7yN7Gyt61vWTf5aEsZgiEMPuG4lhGRodkfGpOaxd/JAeSjytq1upVMidpEXpu0AgeFmoXr06mzZtAqBEiRL89ddf1KhRQ16+e/du6tWrx8cff4yHhwedO3fmwYMHxMfHs2/fPl599dUCH2N8fDzNmjWjX79+9OnThwMHDrBq1SpatWrF6dOn2bt3L23btpXXT0hIsNhJMa84OTlx/vx5VqxYYZaXtXPnTubMmUOPHj2YMmUKS5YssZhp9iTMnj3bLMg/KSmJpUuX0rlzZ4vLdu/eLW8/b948OTBfYvXq1WzatIlXX32VRo0a8c477wAQHBwsi1lZiYqKQqVSUaVKFSpXrmwy3bx5k/j4eK5fvy4LouPGjWPSpEmMGTOGtm3bUq5cOZN8uGbNmvHHH3+wevVqXnnlFQ4cOMCOHTtMyioFxRNxl1PE5JgZlmaaGeZgYx56+zjFsjNMIMhPnIQzrECJTtJ3oynjkX12kqOtNZ90qcZ768/TopI3jQL0XWNtrZU42VqRnK4hLjVdLpV+ViRnmIudKJPMKyWMcrMaB3hy+l4sOh2UdHk+SiSfFS8nW2KT03HPpulC/0Zl+e0/fX5K0wpe7LkaRWxy4XVIlbgfpzL5OTQmhTIeuUcKFBeS0kxd40+Do501yeka8ZkuEAheeq5fv05oaCjvvPMOCxYswNramu3bt9OxY0f++ecfbt68Sc2aNQvFGebm5sZbb73F//73P7kMEZBLGB8+fEhqairu7u48ePCAQ4cOMXLkyGc6ZkBAgMnPDx48YPr06WzatIlVq1bRp08fdu/ezZAhQ/juu++YMGECcXFxzJkzh/T0dBQKBdOmTUOlUrFz506srKxISUmhatWqKBQK0tLScHZ2pmLFily8ePGZxvrZZ5+ZzWvYsCGXL182m2/c3dLSfizty5i4uDj5/wqFgi+//JIvv/wy2/X79Olj5jQUFH+EGFbE2OZQJpltN0nDfJ1Ol+kME2KYoACRBFlVhha1Rou1hVI+wdOTVxdW9zp+VCzhhL+n6Y27u6MtyempxKVkUM4rm40LaEyCTIydYa2rliBBlUFQVOILXyIp4eVsy62HmZ0ks1LDz42POgeiUEBahtYghmUU8igzyyQVCn1u1r3YFJoV+iieHilPVHJ3PQ3OdtY8SkwTZZICgeClx8HBgddee43Vq1fj5aW/iPL09OTEiRNYW1vTrl075s+fX2jjGTt2bLbLtm3bxqhRowCwt7dnyJAhtG7dOl+Pv2DBAqKjo7l69arcDEAqJ1ywYAHnzp3j119/5dNPP83X4woERYG4yylibAxlkul5cIZJYpgUoJ+UppZFNE9RJikoQByNgppTMjS4CjEsX0lIlfK5cndh1fBzM5vn5mBDRFyq7BTNlzGJAP0nxsnOWnbp1ff3ICIulaCoxOcmPP9Z8XLSi34eObgTR7fRlwys+S8EoIicYXoxrKafG5cj4gl5zjpKPms3Sci8nhDOMIFA8LLRt29fk6D6Fi1asHPnTrP1pIysXbt2YW+ff9/jt2/ffuptR44cyf/93/+RkZGBjY1NnroBv/rqq2Z5YJawtrZGp9Oh1WpRKs2v811dXXN0Rj0LQ4cOZejQoQWyb4EgJ4QYVsRIDht1DplhTrIzzJDbZMgMe2x4ou5gYyWH6wsEBYGtlRJrpQK1VkdKmiZPoo0g7zxr50YPJ/128an557JJkMokhTPsiRjfvjI3oxJpFOCBu6MNdx4l0b9R2aIeVqFQ0ccZgPIlcs8v8TQIZ4UdoK/R6oiK15dJNq3oxeWIeEJjnq+OkpnNdZ7+e18S0kQOpEAgEORMfgph+YFCoTDrgpmfWBLCBIIXFXGXU8TkVCYpOcMcszjDpBKJWJEXJigkFAoFjrZWJKjUxKdmEBqbwoK9N5jwamWaV/LOfQeCHEl8RuHJ3UH/GRCXkn9imCTQuWaT/ySwzKjWmWGp1Uq58sf/NS3C0RQuY9pUpFlFLxqU88h1Xel7q7DFsIeJKtRaHdZKBQ3LefAzcO85EsN0Ol2+OMNEgL5AIBAIBIKXHSGGFTE21pa7SaartXLppBygL2eGSc4wKS9M3KwKCp4AbycuhcczaeMF7sWkkJSmZu2Je0IMyweeNZ9LCs3PXzFMOMMET4a9jRWvVMhbaJ2Xc9GIYVKJpK+bPRUMDraQmGSS0tRP3Z2xMElTa1Fr9Q/PnkkMM2wryiQFAoGgcNFoNFhZiYoegaA4IHyQRYzUTTJdbSqGSblgkCmCSU9ypTJJqSW9pTb2AkF+83Xf2rg52HD1fgJJBjfBnUfPV9ZOcUSr1ZGU/mxlklL3vvzMDBMB+oKCRPreikvNQKM1d0YXFBGGTpJ+7g4EeDlR1tOBlHQNc3ZdL7QxPAspRtcGjjbPUCZpK3WnFmKYQCB4ufnyyy9JTs7b9WxSUhLnzp1j/fr1zJgxg4wM04eQf/75J23atMl2+7Nnz1KtWjVSUgrHkXz27Fk2bNiQp3V1Oh2hoaHs27ePH374gV27dpmtU7duXQ4dOmS2naVujtevXyctzXIuaFpaGr169TJ7/QSCwkbc5RQxkhimznIzID2ttbVSYmutX0cql0zN4gzzEmWSgkIg0NeVNcMbM/y3M3g52RIUlcjdmGQ0Wh1WytwDPAWWSUpXozP8+T+t8CQJC/mZGSY5w0Q+nKAgkEL2dTq9iOvtXDgdNyVnWGl3B6ytlMzrXZs3fznJ7ydDea1mKVpULt5OV0m8srdRPlNX30xnmMgMEwgELweHDh0iJCTEbP7WrVu5desW7du3N1tWp04drly5wm+//caNGzcIDw83Wd6vXz9q1KiR5zHUr18fb29vtm/fzhtvvJHteqtWrWLEiBE55pVlZGQwcOBAVq1aRVxcHH/++afZOiEhISxatIjk5GSLWWC9evXi//7v/7hx4wY3b94kNTXV5Nxef/31XM8pIiKCdu3asXnzZrmzZVxcHO3atWPhwoUMHDjQbBs7OztSU1PZvHmzvLxHjx4cP34cjUZDQkICHh76yIXWrVuzZcuWXMeh0+lYsmQJP/zwAyEhIZQsWZK///6bwMBAeZ309HS+/vprjh8/zu7du022X7p0KQsXLiQ0NJSAgADef/99Ro4cCcCMGTP44osvLB43NDSUsmXL8tlnnzFz5kyTZfPnz+eDDz6Qf3706BFTpkxh+/btpKSk0KlTJ4vvG+h/B+bMmUNISAiBgYF89dVXdOnSRV7u7+9PWFiYyTaPHj3C27t4X8cUN4QYVsRI3SSzlklK4fnGXfwcsmR8SJlhHkIMExQStcu48+/UdigVCqp9tod0tZaIx6n4ezkW9dCeWyQHlq2VEvundHpklkkKZ5jg+cDaSom7ow1xKRk8Ti48MexRov4ptY+L/njNKnnzVhN/fj8Zym//hRR7MUxy5T5rSacUvi+cYQKB4GVBpVKRlJTE1KlTmTt3rjx/xIgRgN71lZX09HQ8PDyoXr067dq1Y/v27TRr1oyRI0fi7++Pvb09M2bMQKVSmewTICAggHv37lkcy3///ceAAQPM5t+6dYtKlSoB0LNnTzZv3pzt+SxatIgLFy4AoNVqSUpK4tChQ6SlpdGpUycAvL29mTVrVrZONFdXVzw9PXn99dfp0aMHP/zwA3v27KFcuXKULFkS0OcGp6amZivMlSlThqVLl/LHH3/IYtiUKVPo1KmTmRC2a9cuBg8eTKlSpdBqtcycOZPZs2fLQpyVlRV79uxhzpw5HD58ONtzt8T06dNZvnw5CxYsoH79+gQHB+PsrG/so9Fo+Oabb1iyZAlRUVG0bNnSZNvLly/z448/MnfuXCpVqsTu3bsZPXo0np6e9OvXj/fee49BgwaZbDNv3jyuXbtG2bL6JkmxsbH07t2befPmyesYC1OJiYm0atUKf39/tm3bhqurK7du3bJ4Lv/88w/Dhw9n0aJFtG7dmvXr19OzZ0+uXbtG5cqV5eP9/vvvNG7cWN7O09PziV4zgRDDipzsyiQTDDeiUikDZAbop2aYOsM8RZmkoBCRfmfLezlx40EiwdFJQgx7BvIjm0sqk4zLJ2eYWqOVy7GetnRTIMgNTydb4lIyiElOp3IhHVPK1XM3+t5sXaUEv58M5WGi5XKO4oT8oMz2WcUwqUxSOMMEAsHLQefOnQG9aJKTyARQokQJNm3aJP/ctWtXAG7evEnlypWpUqVKrsczdqGlpaVx//59ypcvL8+LiopCpVIREBBgcfs///xTFnMsITnDQC+CjBs3DrVazXfffUdiYmKOY5syZYrsMvrpp5/k8S5btsxEXMmNmjVrcv36dRQKvblj+fLlJsvXrl2LRqPh7t27BAQEoFQq6devH25ubgwcOJC6desC4OvrS1JSEqmpqZw9e5YKFSoQFRUFgIeHB3Z2OT8wCwoKYu7cuRw4cEAW5Iwde6mpqaxcuZLp06dz9OhRIiIiTLYvXbo0//33H05O+izR2rVrc+DAAbZu3Uq/fv3w9PQ0EZoeP37Mxo0bTX6PYmNjCQgIkMXMrMydOxetVsv27dvlbqDS+WflzJkz1K1bl/Hjx8vjWbZsGRcuXKBy5cqkp6eTnJxMrVq1sj2eIG8IMayIsZG7SZqKYVIWU1lPB3medPEr3aRKT7g9nYUYJih8KpTQi2F3HiXTtmpRj+b5JT8cWNKNfX4F6CcZuUWEM0xQUHg62nKH5EIN0Zfck1KZJmS6q/PTWZnf6HQ6FAoFSQbx6lnC80F0kxQIBC8va9asQaVS5biOsaPn888/JyYmBoCTJ09y7949rly5AkC3bt1yPZ5KpaJXr14AJqV5O3fu5OOPP+aPP/6gXbt2Zts9iTNMonv37pQpUybXMTVq1Eg+nzVr1gB651JiYiLjxo0DwMrKiu+++y7XfR04cCDHnDRjQa9z586ULVuW2rVr89dff2Ftrf8uW7VqFT/88AM///wzfn5+gP78g4ODWb58OU2aNKFmzZr8/PPP9OnTx+wYq1evpl69erIQZmkM16/rs0GPHTtmttySo8re3h6NxvIDo4ULF1KjRg06dOggz4uNjaVWrVrZvQysWrWKTz75RBbCcqJbt26yuNemTRt+//13bGxsaNu2rXwsQJRE5gMiQL+IyS4z7EZUAqDPaZKQnWEGMexBov6D3Nc1+3pygaCgkDqx3XlkbisX5J1MZ9jTO7A88rlMUhLo7G2U8meUQJDfeDoVfkdJqcmEu7EYZvj/40LubJkXdDodb/1ygte/P0ZSmpoUuUzy2TqRiW6SAoHgZUXKgcqOqKgopk2bJv9csWJFAgMDKV++PLdu3aJkyZIEBgYSGBiIl1fOHZTj4uLo2rUrSUlJrFu3zmTZgAEDWLRoET179mTZsmVm20p5V9lNc+bMsbhNbvla33//PZcuXQLAzc1N3l9UVBRWVlbyz1Wr5v+T7rS0NIYOHcoff/xBUFAQ69ev5/HjxzRo0ACAMWPGcOTIESZNmsSJEyfkck8HBwcCAwMpVaqUxf2eOHGC2rVrM3nyZHx8fKhSpQrffPMNOt3TNegJCQlh//79FkXK1NRUlixZwsSJE03mx8TE8Nlnn+Hm5kaDBg349ddf5eOHhoZy//59XF1dadOmDV5eXrRs2ZIzZ85YPL70/r766qvY2NjwzjvvsGbNGln8ksTZ8uXLU7p0abp37y6/p4InQzzyL2LkzLAsZZI3HugFhqq+LvI8SQyTLl6j4vXOsJJCDBMUARW89U96goUY9kzkhzNMygyLT81Aq9WhfMaGBlIQvyiRFBQkXs6FL4ZJpcTGZZLS/xNUatQa7TMF0+c3aWotx2/rL3q/3XeTQMM1wbOWSTqKbpICgeAlxcHBgcWLF2e7PC0tDReXzPuvt99+G4DFixej0Who0KABgwYNwt3dHYAdO3ZY3M+VK1fo2bMnNWrUYPjw4QwcOJA9e/bIy6dPn05sbCw7d+6ka9euREVF8fnnnwPQv39/Xn31VebNm0e/fv2oUKGC2f6PHj1KXFycyTwbGxvOnTtHZGRktud379492ZElCV9paWnMmzcPR0dHunfvjr+/f7bbPwt//fUXrVu3Zt26daxcuZLg4GC+/fZbSpQoIa+TkpLCxIkT6d+/vzzP3d2d48ePZ7vfyMhIrl69yjvvvMOuXbs4evQoU6ZMwcvLi6FDhz7RGG/evEnXrl2pX78+77zzjtny9evXY2trK7v9JJYvX45SqSQhIYH//e9/vPvuu6SmpjJu3Dj5/ViwYAHTpk2jTJkyfPXVV3Tq1Ilbt26ZOdMOHjzI+++/zzfffEPLli3ZtGkT/fv35+TJk1StWhV/f39OnDiBo6MjISEhzJ07l9atW3P58uU8OQMFmQgxrIiRM8M0lp1hVUpmfhhLAfop6RoyNFpikvVimK+bEMMEhU9FH70YJpX0Cp6OhPwQwwyZYVodJKap5Z+fFkmgcxUlkoICROqCWrhlkhkmxwZM/l7iUzPwKqQw/7yQatTtcc2Je4xooc+bedYAfWn7FNFNUiAQvER88cUXtGjRAoBp06axYMECeVn//v05fvw4wcHBgN7NI4lCKpWKhQsX0rZtW1avXs3atWvZu3cvvr6+2R7r7t279OnThzlz5qBSqZg8eTJ79uyhc+fORERE8PPPP/Pvv/9Su3Zt9u/fL7t9ABwdHZk5cyaLFy9m5cqVOZ6Ts7MzgwcP5uLFixw9epSmTZvSrFkznJ2dOXLkiLy/YcOG8c033xAQEMClS5dMShuXLVtGzZo1OXHiBC1atOCjjz5i7NixeXpNO3bsaLFTpURaWmYeZ//+/enXrx+7du1iyJAhuLq6snr1aipWrCivo9PpZLEur6jVamrUqCG75Ro2bMi///7L6tWrn0gM27ZtG8OGDaNVq1b8/vvvFksaly5dytChQ7GxMb3WNs7/atGiBTExMfz0009ylhvA5MmT6devH6Av7fTx8WHHjh2y4Crx8ccfM3ToUCZNmgToy1rPnTvHzJkzWbt2LS4uLjRp0gSAWrVq0aZNG/z9/Vm/fj1TpkzJ8/kKRJlkkWMpMywuJZ0HCfoPDmNnmOTSSFdrCY1NQafTO8tEgL6gKJDKJB8mpsmlfoInJz/KJO2srWTnaH6USubHmASC3JDKJGMKSQzTanXy34dxmaSNlRIXgziUX00o8gupYQ7ov/t/PKS/SZP+3p8WqVN1knCGCQSCl4h9+/axatUqQF8O+fjxY2rUqEHz5s25dOkSe/fuZenSpbi4uMi5TACzZ8+mU6dO+Pv7M3bsWDp27Ejz5s1l4cwS3bp1Y968eSiVShwdHfn6668ZMmQId+7c4c0332TIkCHUrl0b0IsdUsB/amoqY8eOJSIiAq1Wy++//46fnx+JiYncv38fhUJBeHg4SUlJNGvWTBZswsLCCAwMZMaMGRw5coQjR47QrVs3WrVqxfnz54mIiCAgIIB58+Zx8OBBeZxRUVF8/fXXfPzxx9jZ2bF//36++uorZsyYkevrOWLECIKCglCpVKhUKqZPn05ERIT8s0qlkksHQV/G2bJlS+bMmcP+/fu5ceMGHTt2ZPz48WRk6L9/k5KS5CD7vOLj42MWJF+lShUePHiQ530sXryYAQMG8Nlnn/HXX3+ZuAMl7t27x+nTp+ndu3eu+6tfv77cTdTHxwfAZIweHh74+PhYHOOlS5fMwvXr16+fbSmki4sLlSpVyrZ7qSB7hBhWxNhKmWFGYtiNKH0HkDIeDiZPf13trWWnxqm7+g9oHxf7Zy6JEgieBld7G7wNDoq70cId9rTkR5kkGHWUzIcQ/fwak0CQE1KZZGFldSWmqZHiObO6J92d8jd3L78wdm4Zf9U/a4C+cIYJBIKXkfT0dF599VX+/vtvQF9WeODAARMH0KhRo7Cyynzg8M8///D999/zySefyPPmz5/PG2+8kW3AuiUGDRpE7969qVOnDmlpaXz77bdm66jVapo1a0ZSUhIrVqwgOjqaDz74gClTpqBQKNiyZQvNmjWTSzRTU1Nl0Uar1bJixQoGDRok7y81NZXz58/L57N582YOHTokL9dqtQwaNIg+ffrILrgqVarw999/o9WaRvhkJTg4mObNm+Pj4yPnoYWEhJhksqWmpmJvb4+rqz4D28PDg8DAQB48eMCAAQOoVasWixYtYv369bLTKiIiIkfHnSWaNWvGiRMnTOZdvXo1T10/AS5evMj777/Phg0bmDRpktwdMyvbtm3Dz89PbkCQE6dOnZLFr4oVK+Lj42MyxujoaB4+fGhxjKVLl+batWsm8y5fvkzp0qUtHisuLo6bN2+KzpJPgRDDihgba0NmmFGZ5I0HejGsaklTRVqhUBDgrVfKT97RW2lFiaSgKJHcYUIMe3ryy4Uld5TMB2eLNCZX4QwTFCCeTnoxvbCcYZLQ5WBjhb2NqbNKKpt8nFy8nGEqgzPM19We+X3ryPOlvNGnRXKWCWeYQCB4mbCysmL//v107NhRnpecnIyjoyMA1tbWKBQKWSwDmDJlCl9//TVly5Y12ddXX31FlSpVaNy4Mc2aNcv12Hv27OHw4cOUKFGCO3fusGbNGtkNJWFtbc2qVav47bffCA8Pp2PHjtSqVYt3332X+Ph4pk2bZiLKxcbGyq6rpKQkxo8fz9q1a+Xl8fHxsliWmppKu3btmD17trx8165dhISE8NVXX5mMo0aNGnz55ZcAjB071mLZ4qxZs1i/fj3R0dGMGTMGgG+++YZTp06xefNmkpOTSUlJYd++fUydOhXQO+Dq1q3L8OHDCQoKIigoCH9/fzIyMqhYsSIVKlTg4MGDpKenk5KSQp06dShZsiTx8fG0aNGC//77z+JrO2bMGIKDg3nvvfc4d+4c8+fPZ/v27UyePDnX9wVgw4YNlC1blpo1a3L79m2TyTiE/9ChQ7Ro0cKiWDZ06FD++ecfzp07x7Rp01izZo38XimVSiZNmsTMmTP5448/OHXqFG+++SZVqlTh9ddfR6vV0rlzZ7Zu3QrA+PHjWbp0KcuWLePChQt8/vnn7NmzR+70uXPnThYuXMi5c+c4cOAA3bp1w8XFhSFDhuTpfAWZCDGsiLFWSplhWvmPLcjgDDMukZQI8NKLDyfu6J1hopOkoCgp66G/eAiLTSnikTy/JKTmTz6Xez52lBTOMEFh4ClnhqXlsmb+kJkXZi7ySmLy4yf4+9l6LpzePx4n/HHePv8uhsUxcs0Z2n9ziNFrz6LV5t7lSiqTdLC1ok+DMnzdtzZVS7rQpbZfnsdpCckZlq7WmsQ0CAQCwYuKWq3mzp07sjPM0dGRli1bcv78eUJDQ6lVqxY6nY5vv/2WDh06kJSUxKZNm5g/f36OHShff/11unfvDmDWvTAiIoJFixZRp04dhgwZwsiRI7l16xZr1qxh0aJFlCtXjsmTJ/PPP/+QnKx/sFy9enV+/PFH6tatS9WqVfn9999JTk6mb9++dOrUidatW8v7j4yMlEvwbt++zcqVK2VnWKtWrbhx4wb//vsvr7/+OiEhIbRp04Y///wTgP/973/Uq1eP33//PceyxMWLF8timHR+jx8/ZsOGDWZ5XK6urvz99990796dU6dO0blzZ9avX8+aNWvMumlmZeDAgdSvX5/vvvuOEiVK0LhxY7p06ULTpk1JSUnh+vXrPHr0yOK25cuXl4PzmzZtyi+//MK6devkfLjciIqK4u7du1SuXNlskt4XgLNnz1KvXj2L+4iIiKBXr160adOGAwcOsHPnTpMmAB9++CHvvfceEyZMoHXr1lhbW7Njxw5sbGzIyMjg+vXr3L9/H4D33nuPOXPm8PXXX/PKK6+wdetW1q1bR5cuXQB9Q4Hly5fTvHlzhgwZQrly5Th16hQeHh55Ol9BJuJOp4ixNepapdbqsLFScDNHMUwvPkQlqADRSVJQtPh7SmJYahGP5PkjJikNjU5n5AzLLzEsH5xhaUIMExQ8nkbdJHU6XbZlCfnFYzkvzDxn0+MJ/34uhcfx0ZZLZGh07LgUyajWFXPd5vsDtzgQ9BCA4EfJ3I1JpmIJ5xy3kcoYHQxOtv4Ny9K/YdmcNskTxt0oU9I0uDmKZ6MCgeDFZt++ffTo0YOff/6Zt956C3d3d0JCQpg0aRIbN26UM7zc3d1RKpVERUWxY8cOfvvtt1z3/e+//+Lq6sqOHTtkp9Zbb73FH3/8QdOmTZkwYQIDBw7EwcEBgE6dOtGhQwd27drFr7/+SpcuXfD19eXq1asMHz6cAwcOMG/ePEaOHMnFixcZPnw4fn5+zJo1i6ioKJycnGRRS+o0uWvXLrZv305KSgozZ86kVatWfPvtt7z99tsEBwdz5coVfv/9d+zt9feOGzdupFatWnIQe3aEhoYSHR1NYmIiwcHBuLm5sWbNGl555RVq165NdHQ0SUlJREZGUqpUKQIDA9FqtWzdupUmTZpQsmRJVq5cyYULF+R9Lly4UM5uCw0NBfRup+HDhzN69GhmzJjBRx99RJMmTVixYgX9+vUzaTBgidatW3P+/Plc3yvpuMasWLGCFStW5LptWFhYtsv27duX47YKhYIvvviCL774wmyZnZ2dSd6XQqFg4sSJTJw40eK+mjdvzvXr13MdryB3xJ1OESOVSQKoNTpsrDJLzir5mF8kS2WSEr5uxafrleDlo6yn/ks9zIIzIiYpjbjUjFxv9l5G1Botr313FLVWR2l3/Wv4rGWSbg6GMsl8yQwTAfqCgkdyhmVodCSlqQv89y3eUELsbsEZ5vEEzrCkNDXvrT8vxxuE5LFM/EGiyuTne3kQw6Rukg7PGJifFVtrJbZWStI1WpLT1bhZeE0EAoHgRWL37t307t2bevXqkZGRwfnz54mKimL27NmMGDGCUaNGyeWCNWrUYMKECcydOzdP+540aRI3btzA29ub5cuXAzBz5ky+/vrrbHOelEolXbt2pWvXrqSmphIbG4uDgwOLFi3C0dERZ2dngoODadasGRMnTuTzzz/n66+/Zvr06QD4+fnx888/Y2VlJYfj//LLL6xZs4YPP/yQd999lyVLltC4cWN27txJbGwsJUuWRKPRcPv2bRwcHChXrlyu53b27FmGDx+OUqmke/fu1K1bl+rVq9OrVy8AvL29GTFiBJUrV5a30Wq1VKpUif/9738AvPbaa7z22msmr5dUOtmmTRtWrlzJnDlz+PTTT/n8888BmDdvHjY2NixatEjuwCgQ5DdCDCtibIycYekaLQ5YyRkelp5el/MyFcOEM0xQlJQ1OMNCLZRJDvr1FLcfJvLv1PaUcBGirTGPktJ4mKgvDZNu0J/VhSU7W1KfrUwy/HEKB67r3SvifRMUJA62VjjYWJGaoSE2Ob3AxTApqN/DwnerJJA9zoOYvPtyJCExmZ95d/Iohkl5ZKXc7ImMV3EvJvfyytQM/fWAg03+imGg7yiZnqIlWeSGCQSCl4BFixahVCrp1KkTCoUCjUZDnz59AH23vz/++EMO0h8xYgQjRoww24clVxFgFt4OmY6tvODg4CCLZlLZI+iD1+/cuUPJkiUBmDZtGtOmTTPbvnTp0qxbtw6lUimLScOHD5eD87t06UKHDh0AfW7alStXzPYREBBAVFSU2fxevXrJwpeEnZ2dSYba0qVLWbp0aZ7OddiwYSbh/IcOHSIlJYUuXbqYZa/NmjVLLh0UCAoC4YsvYqyN2kNlaLRotDrS1PoPCEsXv1KZpIQQwwRFiZQZFhmvMumIqtPpuPUgkQyNzqJQ9rITGZ/pENEYcoOePUBfv318Hm7mfz95jzMhsWbzE1UZDF15moeJaVQt6ULX2qWeaUwCQW54OulvPAojRF8Suiy5oCSBLC+Ze9Lfb7VS+u5YeW0gEmPIRqvvr8/0yJMYlm64HshnZxiAk6FUMll0lBQIBC8BSkNOs1SSb9wxEjDpKFmckISw3JDOT6K4np+Tk5Mc6i/h6OiYbRMCP79ny8gUCHLiicUwnU7HF198gZ+fH05OTvTq1SvbMLtffvmFihUrYmdnR+PGjTl79qzJ8itXrtCqVSscHByoUKECa9asebqzeI5RKBRyV6gMjVbuHAWWxTBPJ1sTB4kI0BcUJT4udthaK9FodSYCT4JKjdog8sQ/o1PpReRBvMps3jNnhjnkrczrcng8n267wqi158wCvLecDef2wyR8Xe1Z9U4jUSYpKHC8nKUujgX/OSG5MC0H6EvOsNzHEZ2kF7UaBehFrUeJabl2ZUxJV6PK0Atb9fzdAQiJyV1EkwP0C8AZ5mSn32eKcIYJBAKBQCB4CXliMWz+/Pl8//33LFu2jH379nHjxg2LbTy3bdvG2LFjmTJlCkePHqV06dJ07tyZx48fA5CQkECHDh0oX748J0+e5J133mHo0KEWbaYvOlKppFqjky98Aeyszd8ehUIhd5QE8HUTYpig6FAqFZTxMOSGGTnAjG9s8yPD6kVDaoBhzLOKYW5ymWTOr7eU7xadlMb1qASTZUduRQMwrHkApdwcnmk8AkFeKFxnmCFA38FSgH7eM/ckMay8txPeBjEvt9ywmCT9sW2tlVQ3OMry5gwruDJJJ0NHyUQhhgkEAoFAIHgJeSIxTKvVMn/+fKZNm0a3bt1o1qwZCxcuZPfu3dy9e9dk3Tlz5shhhI0bN2bt2rVoNBrZ/bVq1So0Gg3Lly+ndu3aTJs2jUaNGvHTTz/l39k9J0hiWLpGK4fl2tsoUSotd9YqZyiVdHOwwb4ALpAFgidBKpU0LoeMEWJYjkRZcIa5PqMLS7qZz61M8oGREHfMIH4BpKk1/Bes79TTsnKJZxqLQJBXpBD92EIQw6TPomcN0I82CHgY2FQAAQAASURBVFveznaUNzS1yS03TDo/T0dbuRFO+OMUk/JyS8jOsAIokyxleJgWJkrZBQKBoNDQaERpukBQXHgiMezy5ctER0ebdINo3bo1SqXSzNF17do16tWrJ//s5OREw4YNOXXqFAD//PMP7dq1M6lfbteu3UvqDMssk5QufI3bnmdFuvgWJZKC4oCljpImzrBcnEovI1mdYTZWCotO0CfBPY/OsAcJafL/j93OFMPO3YsjNUODt7Mdgb4uljYVCPIdyRlWOGJY3gL0dTqd2XJjJGeYl7Ot/H1891EuYpjh2J5Otvi62mNrrSRDY1pebomUAuomCcidLINzGbtAIBC87KhUKqytn73v3NmzZ6lWrRopKcXrIcTUqVOZNWvWU22r0+m4fPmy2fzr16+TlpZmYQtIS0ujV69eZGSIewRB0fJEd1937twBoHz58vI8BwcHSpQoQXh4uMm6Xl5e3Lt3z2ReQkICDx8+lPdlvB8Af39/s/1IpKWlkZCQYDK9KEjOsAy1LrONeg6Or6qGG9UAb8ds1xEICgvJGRYWmyrPM76xjc+D0+JlI6szzNXeRg50fVrcHQxiWEq6WRaYMQ8TM4996m6snFN45JY++7FlZe9sXakCQX7j6VyIYlhqDs4wgyiXrtaaxBVYItrQCbaEs53s8robnZTjNrEGN5mXsy1KpQJ/Qyfe3HLDpL9PxwJwgVfyMYhhD3Meu0AgELwoLFq0CIVCkadp7dq12e7nzz//xN7eHl9fX5PJwcEhx+3q16+Pt7c327dvz3Gcq1atwtraGmdn52wnOzs7hg4darKdu7t7ns6tUqVKOR6/bt26eHt7m5xbiRIlLG4XERFBu3btOHz4sDwvLi6Odu3asXXrVov7t7OzIzU1lc2bN8vzevTogbe3Nx4eHlhZWeHt7Y23t7fc8fNp0Gg0KJVKk3N3dnY2WWfXrl3UrVsXe3t7qlatatYxNCwsjK5du+Lk5ISfnx8LFiwwWR4XF8egQYNwdXXF29ubDz/80KRb5meffWb2+hvvIzExkXfffRdPT09cXFzo378/kZGR2Z6TTqdj8eLFVK1aFTs7O/z9/QkKCpKXi0z2J+OJxLCkpCSUSiV2dnYm8x0dHc2U3759+7J06VJOnDhBeno6P/30E6dPn5Y7WyQlJeHo6JjrfiTmzJmDm5ubPBm3c33ekcUwbeZFuL1N9m9N5xq+fN23NtO7Vi+U8QkEOSHd1BmXScamCGdYTkjOsIol9DfSz5oXBpmZYVpdzhlAD42cYWlqLWfv6XMcjxqJYQJBYeFViM4wybHqbsEZ5mRrJbu0H+dQapyu1pKg0v99eTvbUUEWw/JYJmk4X6kzdEguuWGpheIME2KYQCB4OZgwYQIZGRl5mt56660c99W5c2eioqJMpjfeeENeHhAQYCaCKJVK/vvvPwYMGGBRpLp9+7a8fc+ePUlKSsp2mjdvntmYYmJi8nRuN27cyPW12r9/v8m5nT592uJ6ZcqUYenSpfzxxx/yvClTptCpUycGDhxosu6uXbvw8vKiZs2ahIaGMnPmTGrWrImtrS1bt24lOjqa9evX06JFC6Kjo4mOjmbLli25jjU7Hj9+jE6n49ChQ9y6dYtbt25x6dIlefmtW7fo0aMHXbt25dSpU4wePZrhw4fzzz//AHoxrUuXLqjVao4ePcoXX3zBRx99xMaNG+V9vPnmmwQFBfH333+zbNkyfvzxRxOxKzY2lt69e8vHv3XrFiNGjJCXjx8/niNHjrB582b27t1LSEiI2etmzPTp05k5cybTpk3j3LlzLF68WBb4RCb7k/NEd2B2dnZotVrUarWJVVSlUpkJW1988QXh4eFym9Q2bdrQrl07fHx85H2lp5te/Fraj8THH3/MpEmT5J8TEhJeGEFMLpM0eiKd04WvtZWS/g1fjHMXPP+UNYhh4UZlkrEiMyxbdDqd7AxrF+hD8KO7+dK10c7aCkdbK1LSNcSnZODmYHmfUmaYn5s99+NVHLn5iKq+LlyJ0LttW1QSYpig8JBKFgs6QF+tyRSxLDnDFAoF7o62PEpM43FyOqXdLTeQiEnWi8nWSgVuDjaU99ZfgN6NTkan05k5PPdcicLD0UY+P0kMK2dohHMvFxEtRc4RzX8xTCrxjElO53FyuuyOEwgEghcVhULxxOWOb731FkFBQWg0Gho2bEiDBg1MIoOyIyQkRP5/Wloa9+/fN6mKioqKQqVSERAQYHH7P//808zFZExGRoaZaCKZTvLK4cOHmTx5MhERESiVSv7880+WLVuW5+1r1qzJ9evX5e++5cuXmyyXMsPv3r1LQEAASqWSfv364ebmxsCBA6lbty4Avr6+JCUlkZqaytmzZ6lQoQJRUVEAeHh4mBlx8kpsbCygd+S5uJhHgFy4cAFXV1e5RLR27dqsXr2aM2fO0LZtW3bu3ElQUBD79+/Hx8eH+vXrc+DAAZYsWUL//v25dOkSu3fv5ty5c3I81OnTp1myZAkffvihPIaAgIBs3XinT59m3LhxtGvXDoBp06ZlK4YFBQUxd+5cDhw4QOvWrQGoUaOGvNw4k93W1pbatWuzY8cOfvrpJ1555ZWneQlfeJ7IGVa6dGkAk1LGtLQ0Hj16RIUKFUzWdXZ2ZsOGDcTFxREREcHBgweJjIyUf1FKly5NWFiYyTZhYWFm+5Gws7PD1dXVZHpRkJ1hGh2qdKkk4tmdIgJBYSCJYdFJ6TwylA/FvmCZYTqdjsj41FyzhPJCfGoGaWq9ffqtJuUo5+VIl9qlnnm/YFQqmZq9sCCJYX0Ngvr+6w/4J0hfvl6ztCs+IotQUIh4GcokHxewGCYJYZD5d5IVDyl3LwcBX+oK6emkL3cs5+WIQqHff1ZBLyw2hdG/n2XE6jOZOWNZnGH3cgmvz8wRLZhukn6GEH3hDhMIBC86GRkZqFSqJ5oyMjJ4//33mTNnDkqlkgULFjB69GgA9uzZY1YmuWHDBrPjqlQqevbsyZgxY0zm79y5k8aNG3Pw4EGL430SZ5hOp3vic1OpVFSvXp0FCxbQtm1bOnfuzIIFC6hYsSIAr776qsm5NWrUyOI4Dxw4gFqtznZycnKS1+3cuTPjx49nwYIFDBgwgJo1a1KzZk1WrVrFDz/8QOPGjdm+fTvXr1+nZ8+e1KpVi927dxMZGYmXl9cTu8RiY2OxtbW1KIQBtG3bFmtra9atW4dGo+Hvv/8mJCSEbt26AfqM8/r168tmHtBnnJ88eRKdTsc///xDyZIlTXLS27VrR2hoqFzqGBsbi7d39g+a33jjDTZs2MCDBw+Ii4tjxYoVJg5DY1avXk29evVkISwrIpP9yXkiMax+/fo4ODiwb98+ed7hw4dRKBS0atXK4jaurq6UKlWKw4cPc/PmTbnut0WLFhw8eNCko8aBAwdo377905zHc02mGKbNfApcABe+AkFB4OZgQ52y7gDsuap/ivOiZYatOXGPpnMOsums5UzDJ0EKzPZwtCHA24nDU9oyqnXFZ94vgJvcEc/yzbwqQyOLAv0blsFaqSD4UTIrj4cA0C6wZL6MQyDIK55O+qe9BVkmufzIHUauOQPoS5KtrSxf+rjnoaPkI4Oo5e2sH7e9jZWcm3jzQaLJulci4tHpIFGl5kJYHJCZTSaVKJ4Pjcuxo6SUGZZTjuizUNFHlEoKBIKXg4EDB+Lg4PBE07vvvkvDhg1p1aoVCoWCNm3aULduXTp37kxERIRZmeS9e/fo2bOnfMy4uDi6du1KUlIS69atMxnPgAEDWLRoET179rToxvr7778JDAzMdpozZ4687tWrV5/43BwcHEhOTqZNmzb4+/tTvnx52rRpg7u7O3///TeRkZEm5xYZGZmtcJdX0tLSGDp0KH/88QdBQUGsX7+ex48f06BBAwDGjBnDkSNHmDRpEidOnKBTp06APqM8MDCQUqWe7OFxTEwM6enpODg4UK5cOd58800Tx563tzerVq1i8ODB2NjY0KlTJ+bPn09gYCCQfcZ5Wloa0dHR2S6HTPNQTEwMn332GW5ubjRo0IBff/3V5OH6J598glKpxNfXF09PT27evMl3331n8XxOnDhB7dq1mTx5Mj4+PlSpUoVvvvlG3t+TZrILnlAMc3BwYPTo0Xz22Wfs3buX48ePM2HCBEaOHIm7uzudO3eWg/JOnjzJnj17uHbtGuvXr2fgwIF8/PHHcmnjiBEjiIuLY9y4cVy5coWZM2dy+fJl3nvvvfw/y2KOpW6SDjlkhgkExY0utXwB2HnpPvDiOcOuGkoIT92NfeZ9SXlhvm6Wy7CeBeMQfUtIeWF21kpKuzvwSgUvAK5F6s/v1Wo+FrcTCAoKT4MAlZSmJk2dt3bz+6894Jejd/Lk1ExN1zBvTxCnQ/TZeCVcsi+1yHSGZS+GSeH53kb7qVJS/8T5ZpSpGBZk9PNtQ0i95AxrGOCJh6MN0UlpHA+OyfZ4BdlNEkRHSYFA8PKwefNmdDodOp2OVq1asXjxYvnn7KasYerh4eGcPHmSZs2aUb16dTNnWO3ateWIoCtXrtCwYUOcnJwYM2aMWenb9OnT+fvvv9m5cycffvghX3zxhbysf//+XLt2jQ4dOvDzzz+zf/9+s2nRokU0adIE0JcrSmM+ePAgfn5+pKen53p+xiWaGRkZHD58mOXLl1OtWjXKli1rcm5lypShfv36ZqLek/DXX3/RunVr1q1bx+uvv07fvn359ttvKVGihLxOSkoKEydONNnO3d2d48ePy69tXmnSpAmnTp3i5MmTzJ8/n4sXL9K2bVsSE/Xfz1evXuWNN95g6tSpnDx5knnz5jF+/HiOHj0KZJ9xDnphL7floC8dPXv2LDt37qR9+/a8++67LFmyRF7///7v/3j48CG7d+/mwIEDODk5ZesMi4yMZMeOHdja2rJr1y5Gjx7NRx99xG+//ZbjeLPLZBc8YWYYwFdffUVqair9+/fHysqKQYMGMX/+fDIyMrh+/Tr37+tvhqOjoxk5ciSPHj0iICCAjz/+mHHjxsn7KV26NNu3b2fcuHGsWLGCGjVqsGvXLsqVK5d/Z/ecYFImWcBPgQWCguD1WqX4alcQJ+/G8jBRZeoMS81Aq9U91x0KE1R6Qe9eLp3f8sIDgzPM1/Xp8g9ywsNJfzMfn40A+cDQSbKkqz0KhYIO1Uty7HY0oBcJavq55fuYBIKccHWwxlqpQK3VEZucTqk8iMRTt14mOimNBuU8qOfvkeO6V+7Ho9bq8HC0oXf9MrQPzF7wlVxqOeWXScu8jfK1qvo6s//6A25m6coYFGXe9Vo6hq21ku51/Pjtv3tsPRdO6yolzNYFjB6QFawz7LboKCkQCF4Szp8/z6VLl/jf//6HWp19wyEpW+yvv/5i3759aDQa6taty/vvv09CQgInT54kICBALpl75ZVXCA8Pp0WLFgDcvXuXPn36MGfOHFQqFZMnT2bPnj2yq+znn3/m33//pXbt2uzfv5+YmMwHI46OjsycOZPFixezcuXKHM/H2dmZwYMHyz8vWrSIUaNGoVAosj0/pVKJUqkkLCyMTZs2sXPnTq5du8bOnTvp3bs33bp1Y9WqVeh0OpYsWcKYMWNQKpWMGDHCLPO7Y8eOKJXZmziMhZj+/fvTr18/du3axZAhQ3B1dWX16tVyaSboSz6fNNdt+PDhsiAEsGLFCt5++225IyXo88BeeeUVAgIC2LNnD/369ePLL7+kZcuWzJ49G4BGjRpx8+ZNPv74Y44dO5Ztxjno36PclgNyLhroK+NiYmL46aefGDduHNeuXWPlypWcOnVKLkPdunUr5cqVY//+/bz66qsm+1ar1dSoUUN2BTZs2JB///2X1atXM3To0CfOZBc8oTMM9NldP/74I/Hx8cTGxvL9999jZ2eHnZ0d9+7dkwWvLl26EB4eTlpaGjdu3GD8+PFmwbKtW7fm8uXLpKWlce7cOVq2bJk/Z/WcYWudWSaZ2TlKZIYJnh/KeDhSt6w7Op0+MNo4/0cqE3qekcSw3Dq/5QWpTNLXLf+zudwc9Dfo2WUeSc6wkgYhrr2RE6x9oM9zLVgKnk8UCgU+BpeV1Nk0JzI0Wjl/698cHFUS50P1+2xc3pPpXavTLIcGEXnpbPkkzrAbWX6GzAB9gF71ywCw92oUSdl0gC3IbpIAlURHSYFA8JJx8uRJ4uLicHd3x8bGxuJkZ2cnC0mrVq3C2dkZKysrHj58yKeffmqyv61bt5pECEl069aNefPmoVQqcXR05Ouvv2bIkCHcuXOHN998kyFDhlC7dm1AL8J07twZgNTUVMaOHUtERARarZbff/8dPz8/EhMTuX//PgqFgvDwcJKSkmjWrJlJPpR0fp999lm252ZjYyO70M6ePcuJEyfw8fHh888/58yZM3KZH+iFlAkTJmQrdo0YMYKgoCA5g2z69OlERESY5JJJJYKgL/1s2bIlc+bMYf/+/dy4cYOOHTsyfvx4MjL0165JSUkmOWN5YdasWVy5ckWeevToYXE9f39/vLy8uHfvHgCXLl0yEatAHwsldZzMLuPczc0NT0/PbJcDZuWKxvuXjn/58mXAVDArW7Ys3t7eJl0vJXx8fMyC+KtUqcKDBw9yHG92meyCpxDDBPmPteEGNF2jJUU4wwTPKV1q6ev4d1yMJNFwYyfp3zkFuj8PJKTqz+dRYhrJ2dy05hUpwN7XtQDKJA1lXtllHknHlkLyy3g4ynlvnWv65vt4BIK8IDVzWLT/FhptzqWPxr/bJ+7kRQyLA8jVQQaZQlVOzrBoOTPM2BmmF8NuPEiUSzdT0tUWw/G9jMSwOmXcqODthCpDy2//hlg8XkEG6ANU9NHfcITFpsgNUAQCgeBFZtSoUTmWDh4+fBhfX1/ZnbRt2zZmzJgBYFEUevDgAb6+uV9DDRo0iN69e1OnTh3S0tL49ttvzdZRq9U0a9aMpKQkVqxYQXR0NB988AFTpkxBoVCwZcsWmjVrhru7O6AXzrKGw0dFReV4fm3btqVMGf3DmJ49e7Jx40YaNWr0xOcWHBxM8+bN8fHxkUsnQ0JCGDlypLxOamoq9vb2cuM7Dw8PAgMDefDgAQMGDKBWrVosWrSI9evXY2Ojv4aNiIjI0+tpTKlSpUzy1CTxLSt37twhOjpaFpRKly7NtWvXTNa5fPmy3DSwRYsWnDp1ivj4eHm5ccZ5ixYtuHfvHrdu3TJZXr9+fTw8LF93nDp1yuT4gMkYIiMjiY6OlpcZ06xZM7Mw/KtXr1KlShV5PCKT/ckQYlgxQCqTVGt0Rk+BxVsjeL5oZSjzOXNPn6tlpVRQ0kUvuuTUne15QHKGAYQ8Q6lkfGoGp0L0r4+vW/6XSUqZYfHZvN5ymaRLpivtx7fqs3JoI9pUFXlhgqJhRMvyuDnYcPthEl/vDeKPU6HZ5nY9Ts783T4T8ph0dfbh85AphtU1iL45IXW2jE3KvUzSyynz77e8txNWSgWJKrWcCXjzQRI6XabzG0Cp0DcckVAoFLzdVB8NMX/vDb4/kHkxLSFdE9gX0AMyHxd76pZ1R6uDFcfvFsgxBAKB4HkiLCzMJEsrJ9LT09m/fz8nT54kLS0NhUKBnZ3l67s9e/Zw+PBhSpQowZ07d1izZo3shpKwtrZm1apV/Pbbb4SHh9OxY0dq1arFu+++S3x8PNOmTeOTTz6R14+Njc1W+MmP89u5cyfp6emyAGNjY4OVlf77aNasWaxfv57o6Gi5U+Y333zDqVOn2Lx5M8nJyaSkpLBv3z6mTp0K6B1wdevWZfjw4QQFBREUFIS/vz8ZGRlUrFiRChUqcPDgQdLT00lJSaFOnTqULFmS+Ph4WrRowX///fdE57py5UqWL1/OxYsX2bFjB927d6d27dp07doVgPHjx/PXX38xZ84cLly4wPfff8+vv/4qV7r169cPLy8vhg0bxsWLF/n555/ZsmULH374IQCtWrWiQYMGvPPOO5w5c4bNmzfz/fffm7xHQ4cO5Z9//uHcuXNMmzaNNWvWyMubN29O/fr15XWOHz9Ov3798Pf3p2vXrmi1WpNM9jFjxhAcHMx7773HuXPnmD9/Ptu3b2fy5MmAyGR/GoTiUgywMSqTFJlhgueVyj7OuNpbIxk7PBxt5M5pz3uIvnGZ572nLJVMSVczdOUp7jxKxtPJlrYFID55OOb8ektlkj5GeWWl3R1om0OOkkBQ0Lja28gdVZcdvsPUrZf5eu8Ni+vGJGe6l1IzNFwMj8t2v5HxqUQlqFAqoHaZ3G8WPPNQJvnIQpmknbUV5b31DiupNPKGIS+sUYAHLnZ6d4GHo61ZKfKQZgG8174yAAv33eTa/cycMY1WR5pB7HMswOiEsW31T6jX/Hcv27zBZyVBlcGM/13lXGjupbACgUBQlAQFBVG5cuUc17l9+zYBAQF8+OGH1KxZk0ePHtGgQQMePXrEjRuZ318REREsWrSIOnXqMGTIEEaOHMmtW7dYs2YNixYtoly5ckyePJl//vmH5GT9w9bq1avz448/UrduXapWrcrvv/9OcnIyffv2pVOnTrRu3Vref2RkJD4+eb+GU6lU3Lt3L8fzGzRoEKtWrSIoKIg5c+Ywbtw4unXrxvTp0/n+++8ZPHgwjx8/ZsOGDQwdOtRkW1dXV/7++2+6d+/OqVOn6Ny5M+vXr2fNmjW5Bu8PHDiQ+vXr891331GiRAkaN25Mly5daNq0KSkpKVy/fp1Hjx7l+VwB3NzcmDNnDk2aNGH8+PG0bt2aw4cPy66/Hj16sGbNGtauXUuTJk34/vvvWbBgAWPHjgX0uV+7d+8mMjKSxo0bs3DhQtavXy83LlAoFGzbtg1HR0datGjB5MmTWbBgAX369JHHEBERQa9evWjTpg0HDhxg586d9O/fX95+9+7dVK9end69e9O5c2c8PT05ePAgTk5OZpns5cuXZ9euXRw9epSmTZvyyy+/sG7dOjmnTspkP3bsGA0aNGDbtm0vbSZ7XhHBVMUAWyujzLAMkRkmeD5RKhXUL+fBoRv6LypPJ9tcuxtmx7X7Caw7dY9hzcvL3c6KCp1OR0LqszvD/rpwn/Ohcbg52LB2eBO5VDE/cculG95DOUA//11pAsGzMLRZAJcj4giJTuFaZAKHbzxCp9OZZY1mFar+C46hUYCnxX1eMLjCAn1d8yQmZS2TvHo/Hj83B1nUB4g2uMaMyyQBqpZ04fbDJG4+SKRNVR+uR+pFsWq+rqgytJy999gkL0xCoVAwqUMVTt+N5b87MVyJiKe6n76cRHo4BgX7gKx9oA+Bvi4ERSWy5r8QxrXL+Sbwafj76gNW/RvCzQeJrHv3lXzfv0AgEDwNqamphISE4OnpiYODA3fu3OG3335j5syZJutERUXJ2VwqlYojR44wd+5cUlJS2LFjB97e3qxcuZJ27drx4Ycf8uGHHzJ48GD++OMPmjZtyoQJExg4cCAODvqIjE6dOtGhQwd27drFr7/+SpcuXfD19eXq1asMHz6cAwcOMG/ePEaOHMnFixcZPnw4fn5+zJo1i6ioKJycnPjzzz8BcsyDCgkJQaFQ4OLigk6n45tvvqF06dKyMyw9PR0rKysiIyOpUaMGAPfu3eO3335j8eLFzJs3j2HDhjFixAiGDh1K06ZNWbt2LX///TevvPIKtWvXJjo6mqSkJCIjI+VyRa1Wy9atW2nSpAklS5Zk5cqVXLhwQR7XwoUL5U6doaGhgN6FNnz4cEaPHs2MGTP46KOPaNKkCStWrKBfv34mDQbySu/evendu3eO67z11lu89dZb2S6vWbNmjo60smXLsnfv3myXW8qTM8bHx4fff//d4jIpk92Y1q1bc/78+Wz3J2WyC/KGcIYVA0wyw9KFM0zw/NKwXGZ9vIejrZxh9SRugyM3H9Fv6b+sPRHKe+vPo80lQ6igSc3QoDYaw73op3OGScHbr9X0lW9285tM8TGbMkkpQN8l/4U4geBZcLC14se3GrB5dFNsrBRExKVadGFKYpikkf2XQ4j+6RC9C6muv3uexiCVPj5OSefWg0S6fH+MTouOEBKtF8C1Wh2xyVJmmKmgLIXo34hKMvyrF8Oq+rrIyyyJYRJS7tith5mh+9L1AICddcFdrimVCoa30Af9/n3tQYEcQ3LUia6VAoGgOJGUlETt2rXx9fXFzc2NZs2a0aFDBwYNGiSv06NHDypVqkS/fv1ITEykXLlyjBkzhu7du3P48GG5W+GwYcM4fPgwK1as4Pjx48ycOZPQ0FCOHTvGO++8IwthEkqlkq5du7Jt2zZiYmI4evQoDg4OLFq0iODgYEaOHElwcDDNmjWjU6dObN68mRUrVlCqVClcXV2ZOnUqP//8s1y2aImFCxdSoUIFvLy88Pb2ZvPmzSZdF0+fPo2trS3bt2+nQ4cOzJs3j6pVq3Lp0iX279/PsGHDAL3jaO/evbz66qu89957jBw5Ut6Pt7c3I0aMoHLlyjg7O8vT4cOH+eCDDwB47bXX+Pjjj+XjTpo0SS6TbNy4MStXrqRbt268++67fP755wDMmzePCRMmsGjRomd4hwWCnBH2o2KAVCap1ugyyyRFZpjgOaRBuUyHhpezrZyPk9fMsIcJKkb8doZ0jb406Or9BP538T4965mHSBYWUni+xNM6wzJdnwUndLvnUCapytAQ/lgvLhREJ0uBID9wtLWmnr8Hp+7Gcjw4mgBv045SkhhWr6w750LjuBaZYGk3PExUseG0/mlzyxw6SBrj4aT/vNJodXKnyoeJabz1y0mDSKeUy8CzCltVffUO1qCoBDRaHZcj9GG7Nfzc5M+z0u7ZN82oXFK//S0jscg4NqGgO73WMwiGwQ+TLDrynhWp8cHDxDQSVBm42tvksoVAIBAUPCVKlJBzuzIyMrC2tjb7/Pv7779Nfr569aosgGWlVq1aXL16VQ6DzysODg5yYLpx2WPFihW5c+cOJUuWBGDatGlMmzYtz/v9/vvv+f7779FqtWi1Wrk8UKJ58+YmYevVqlVj7NixODubV2UolUrmzp1LRkYGNjY2lC1bVl62dOlSli5dmqcxDRs2DK02M+/z0KFDpKSk0KVLF5o1a2ay7qxZs+QSQYGgIBCKSzHApExSOMMEzzF1y7rLTkcPR1vcHAziTB7FsNsPk0jXaCnt7sAEQ47O/L03TMqFChvj8HzIBzGsAP+2PYzKJLM66k7ejUWVoaWkq52cbyQQFEeaV9TfZPx7O4Zr9xNklxVkimG1y7gDetdpkoUOr/P33CA5XUOdsu50qpG3rlR21lZyvpdxFllEXCrLDt+R87z8PR3lxjcSUrfKa5EJnLgTQ1KaGhc7a6r6utCnfhlmdKvO+x2qZHvsyj4GZ9iDTDGsMAR0iXJeTlgrFSSna+QmAPnJY6Py1juPnr4JiUAgEBQUNjY2eXoQkJ0QZryf/EQSwp4FpVJpJoRZwt7e3qIQZsyznp+Tk5NZB0xHR0czIUzCz8/vmY4nEOSEEMOKATZWmWWSIjNM8DzjYGtFDUMJoJdTZplkXGreMsOSDWJwCRc7RrWuSAkXOyLiUjl5N7ZgBpwHpLwwyeX2ICGNlHTzm+/cKIzmGK6GMWp1kJSu5lFiGlM2XeTQjYf8E/QQgLZVffLd9SEQ5CfNK3kBsP/6A7r8cJS+S/+Vu0ZKeV7+no7y50vE41ST7a/dT2DT2XAAPu9W/YlcVZ6GLLBL4XpnVxWDY+vEnRguhMUBmS4qY0q62lOxhBM6HfxwUN8VsmGAB1ZKBfY2VgxtXp6yno7ZHreyj/44EXGpJBvEvcKMTbCxUuLvpR9f8MP8F6seG+UYBotSSYFAIBAIBMUAIYYVA6QnzBlqnXCGCZ57utXxw8oQpi9lWMXn0Rkm3QQ621njYGtFoCFHJyYpLafNChTJGebv6SgLYiFPkRsm/20XoMvD3sZK/ux4mJDGyDVn2HQ2nIkbLvD31SgA0TlSUOypU9YdJ1sr0tRadDp9N9eoeL1bKdYQYO/lbEsZD33ZoVT+K7Hzsr6kolONktT39+BJkMofgx/pBRupRPvGg0SO3NQ3B6lb1t3its0N5Zgn7ujF+0blLQf7W8LDyVYO5ZeOXRifGcZUMjQruW2UW5ZfGDc+kM5PIBAIBHnHuJzxeSApSXzWC4o/QgwrBlgbxDC1VlsopVQCQUEyomUFrszoRJuqPkbOsLyJYVK5k6Ph5k8Snx7nUUwrCKTMMFcHa9n1duruk3e0kf627Qv4b1t6zSdvusg5Qze9uJQM7sersLVS0iKP+UkCQVFhY6Wkc81SKBWZwfH34/XuL8lh5OlkK2dwhWdxhh27FQ3Aq9WevLTEyyCG6QxVxvXKehDg5YhOB2fu6QP562UjsDWraPq31TibLpfZUcngDpNKJVMz9J89hXU9UNFw/OBHySw/cocPN1/MtwYmxp/hokxSIBC8rFy/fh17e3smTJjwRNv99ddftGjRwiRrqyC4dOmS2bzg4GASEszzOa9fv056ejrh4eGUKVMGgIcPHxIXFwdAhw4d2LRpU4GOVyB4VoQYVgywNZRJZpiUSYq3RvD84iCLWfoby9sPk9hwOlQudcoOY2cY6HPHAOJT8lZmWRAkGpxhrvY2tK5SAoBDBofIk5CaoT/3gr6xlUL0L4bFoVTA6DYV5WVNKnjiZCdKsAXFn/l9a3N2WgcaGDrURhrEMKlM0sPRljIe+rK+iLhMMSwuJZ1LhvD6lpVLPPFxswbjl/FwoKGRqGVrpaRaKZesmwHQtIIXUkWmnbWSWmXcnujYUtdJKUQ/Nd3wmVHIzrCTd2OYs/s6G8+Ec+V+fL7sWzjDBAJBccDX1xeFQpGnSa3WX5Pevn07z9soFAoCAwMtHjs9PZ0hQ4bwyiuv8Msvv1gUnrKjU6dOREdH899//+W43owZM7C1tTXp6ph1srW1ZcaMGWbbJiUl0b17d9avXy/Py8jIoGfPnixZssRs/U8++YSFCxeazBs1ahRLliwhIyODS5cu0aBBgzyfY1bUajVVq1alUqVK8ryhQ4dafM2ljpozZszI9n0JCwsDICIigl69euHs7Iy3tzcfffRRjiLjli1bqFatGvb29jRq1IizZ8+aLD9y5AgNGjTA3t6eGjVqsHfv3qc+Z0HhIxSXYoBUJpluXCYpMsMELwAVSzhhb6MkPjWDj7Zc5tv9N3NcX8oMkwQbyeVUpM4wlcEZZm9D66r6m+sTd2KeONRfVUglTx2ql8TR1opXq/mwalhjPuocSIfqeofMazVLFeixBYL8QqlU4OFkSyk3vfvrfpwKnU4nB7F7ORs7wzLLJP8NjkGn02dwPU3XVE8nO/n/VkoFpdzsTRxe1f1csbO2/Dfs5mhDzdJ6AaxuWfds18sOKTdMKlMsbKe45Ay7+SBJ7pr5KPHZS9TVGi3xRu7gkJhk1JqCdTcIBAKBJaKiotDpdDlOd+/eNdmmYsWKpKammk1t27blhx9+MJmXnJxsUeTSarW8/fbbqNVq9uzZwwcffEDPnj0JDw83W9eSkOPg4MDt27dp0aJFjsIdwHvvvUdSUlK205gxYyy+Ns7OzmzYsIENGzagM9ijv/76a3x8fPjoo4/M1p8+fToLFixApdLHGOzdu5fLly8zefJkjh07hoODAxUqVMj7m5OF5cuXc/Om6X3D119/za1bt0ymzp07M2DAAPncsy4fMWIEzZo1o2zZsmg0Grp168bjx485cOAAixYt4scff2T27NkWx/Dff/8xYMAARo4cycmTJ/H39+f1118nMVH/PX337l1ef/11Xn31VU6fPk3r1q3p1asXISEhT33egsJFKC7FAEkMS1NrSFMXjntEICgMfFztOTi5DT8cvM36U6GcyiUIX3KGOdrpf/8ll1NeyywLAilA39XBmqolXfB1tScqQcXJu7GyUywvFNaN7aQOVZiUpWvdDwPrcTokVu7SJxA8L/i56wWtyPhUElLVqA0qjadTZmaYcYD+UUOJZIvKT/e77mXkDPN1tcfaSknDgMyySEvh+cZ0quHLpfD4pyrRrGToKHnqbiyrjt9FYxCkCk0MK2HeZTY/xDDp81thKHtVZWgJe5wqutoKBIJCJz09nfT09Fw7JhqjUCi4evUqU6ZMYffu3djZ6R+aSB0a7e3131MHDhxgypQpnDhxwuyYI0aM4Pz58xw+fBh7e3s+//xz7t69S9u2bdm7d6+JaCQJUQBxcXGkpaWZdJS8desWnp6eeHl5WRzv999/z9KlS3N8DT755BOTeV27dmX37t1yg6WsHSNtbW3RaDT8888/tGnTBoD69etz4sQJ+fybN2/Oxo0bsbe3Z8OGDWRkZNC5c2d5H25ubmzYsCHbcRnz8OFDPv/8c3r27Mnly5fl+T4+Pvj4ZGbf3rx5k4MHD3Lx4kUAPD098fTMfID1+PFjNm7cyObNmwG9YHfp0iXCwsIoVaoUTZo0ITQ0lG+//ZZPP/0UpdLUJzR//nxef/11Jk6cCMCvv/6Kr68vmzdvZtiwYfzwww9UqlSJefPmAfrX/n//+x8rV67kiy++yNO5CooW4QwrBkjdJBNVmaq+EMMELwp+7g4MbxEA6Lu8aXLIoJHLJA3OSCmAP64IyySlAH0Xe33LbblU8sbDJ9pPYWWGWcLexoqWlUs8UVc9gaA4YOwMizV8DjjbWWNnbSWXSUqZYTqdjqO39CXMLZ9SDDMuk5TEtvLeTrJIll14vsSo1hXZPKop77Qo/8THrlnaFS8nWxJUamZsv8bMHdeAzAzFgsbF3oaSrnYm8x7mgxgmufncHGyo4G3IJRMdJQUCQRGwYsUKE4Emr9SpU4eUlJRss74SExMZMWIEvXv3xtY283skMjKSdu3acenSJQ4ePIivry+gF9JWrlxJ8+bNadCgAdu2bTPbZ2xsLO3btzcraVyzZg0NGzaUBaCsPK0z7Ndff0WtVmc71ahRQ1739OnTlClThjZt2lCtWjWioqIIDAykW7duzJgxg99//50333yTAQMG0KNHD/bu3Uu3bt1IS0ujXLlyLFq0KKeXm/Hjx/Pmm29Sp06dHNebOXMmffr0ybY0deHChdSoUYMOHToAcO3aNXx9fSlVKrNSok2bNkRHR3Pnzh2z7f/55x9ee+01+Wd3d3dZBJSWG/8+WVtb06pVKzNBVFB8EWJYMUByhkk33ZAZGiwQvAiU93bG0daK1AxNjnkxWcskPZwkMawYBOjb68fUxlAqefjGk+WGFXZnOIHgRaCUwRl2Py6V2GS9MCN9LpQ2iFUxyemkpKt5lJRG+ONUlApoUt7yE/Pc8HI2FsP0YptCoeDTLtXoWdePTjV8c9zeSqmgYYAnVk8hPLvY23Dwgza8176yyXz7QvzMkEL8JfLDGSblhXk62hJoyFvbfun+M+9XIBAICgtra2vWrVvH2rVr+euvv0yWabVa3nrrLSpVqmTiuFqxYgXVq1fHx8eH48ePU7p0aZPtrKysWLVqFZ988gn9+/enS5cuhIaGAhAeHk67du2oUKGCWS7X5MmTmTBhAq1atTIbC8DKlSsJDAzMdlq9evUzvx6NGjXi9OnTDBw4kKZNm+Lu7k6rVq345ZdfSE1NJT09nYoVKzJ06FDatGmDr68vgwYNwtramsDAQMqWLZvtvteuXcu///7Ll19+meMYIiIi+OOPP2TXVlZSU1NZsmSJyXIvLy+io6NJScmMV5CaAzx8aPqQ+/Hjx8TFxVG+vOnDLX9/f7m89c6dOzkuFxR/hOJSDJDFMEMpgb2NUjg4BC8UVkoFNf30WTqXwrMPZJacYU52pgH8j4uBM8zV4FJrVskbpQLuRCdzPy41p01NUIlOsQLBE+NncIZFxquISZI6SerdS24ONrgYROr7cancNXQpLOPh+NSNIryMMsMkZxhA7/plWDSgXoE7O90cbBjTpiK2Rg/EHAvxM6NtVR8UikxnXX6IYdLnt4eTLe801980/HXhPlci8iecXyAQCJ6E48eP5xiAn1XckKhQoQJffvkls2bNMpm/Y8cOrl+/zoYNG+QyO7VazcqVK5k3bx7NmjXD2dk52+N5eXlx+vRpdDodzs7OHD58mAYNGtCkSRNatGhh5kYbNmwYcXFx/PrrrwwYMIAVK1bIyyZNmsSZM2do1qwZf/zxB/v37zebZs2aRc2aNZ/pNdy9ezetWrWidu3afP7557zxxhv8+OOPHD16lK5du/Lzzz9z7tw5AM6dO0ft2rUBvQC4d+9e+vTpY3G/t27dYvz48axatQpXV9ccx7Bs2TJq1apF48aNLS5fv349tra29OrVS57XuXNnbGxsmDx5MklJSYSEhDBt2jR5bMYkJekf3js6OprMd3R0JC0tTV4np+WC4o/IDCsGSBfX0YYLfUcRni94AalZ2o1TIbFciYinb4MyFtdJksUwqZukXoCKL1JnWGY3SdDfrNYq487FsDj+DY7J9lyyIsQwgeDJkTLD4lMz5HJILydT99b1yATCHqfyMEEf4hvwDFlUns7mZZKFjb2NFY0CPDh+OwYoXDfp8BblGdDYn6M3H3H0VjSPkvLDGab/DPVwtKVmaTe61/Hjfxfv8/XeG6x+x/JNjEAgEBQUzZs359ixY9kuT0lJ4e+//5bFkdTUVDQa/TXc8OHD6du3L0lJSWg0GtLS0mjXrh0HDx7E1tZWFlDs7Ow4cuQICoWCBQsWMGTIEFatWmV2rPbt2+Pl5UXdunXZtWsXoC/l+/jjj5k4cSL3799n+vTpvPfee9SsWZOzZ8+yb98+fvrpJ0qWLImjo6Ncegng4uLCmDFjWLduHRs3bszxdShRogStW7eWf3733XcZNWpUtuunp2c+mO7cuTNXr14lOjqaqlWrEhISgru7uxxEX7VqVaZMmYJKpWL37t0mx8mOxMREevTowbhx42jfvn2O62o0GpYvX87nn3+e7TpLly5l6NChJvlnpUqVYsOGDQwbNoxly5bh5OTE+PHjOXv2rEkWGSBnwxmfN4BKpZIFMDs7uxyXC4o/whlWDJAuuKVuS+JmWfAiUquM/gnP5RzcAMlZxDApQD8xTU1GEXUfk7tJOmR+mTavqC/B+vd2dJ72odPpjDLDxMeuQJBXXOxtcDF8Hly9ry9lMM71yuwomcrdaH3ZQ3mvp78IzSq0FRXNK2VmnhVmzqBCocDZzhofQ3ZYfjrDPA3lrR90rIqVUsGRm4+IeAJ3rUAgEBQGjo6O9OzZk/v376PT6XjttddwcXHBxcUFd3d3ypUrh4uLC4cOHWLixIm4uLjg7+8vr+Pi4sJ3330nh9FnJTk5Ga1WS3R0NNeuXaNcuXImy0ePHi2X9vn5+fHhhx/Sr18/wsPDeeutt/jss8/kQP3XX3+d+vXrA/qMsX79+lG+fHm0Wi2zZ8+madOmJCUlcfbsWZydnUlISCApKQk/Pz85+B6gX79+nDhxApVKhUqlYuHChVy5ckX+WZrn7+8P6F1X/v7+1KhRA5VKRY0aNfD19cXX15fw8HB8fHwIDAxk/vz5/Pnnn7zxxhu5vu5bt27l+vXrzJ8/H3t7e+zt7Zk1axZ37tzB3t6eNWvWyOseO3aMBw8e0LNnT4v7unfvHqdPn6Z3795my15//XWioqK4d+8ejx49ol69enh4eBAQEGCynre3N3Z2doSFhZnMDwsLkxselC5dOsflguKPuCsrBmR9ii1ulgUvIrVKuwP6EH11NsJWiiFXy9lw8+tmJEDFF1FHyURVZjdJCelG9XhwtEnXn+xI12iR+gYUZv6PQPAiIOWGXb2vF9IthdyHP04hJFpfJvkszjB7Gyu8ne1QKqCChe6KhUXLSpmdam2tCv+aoISz/jV/mKjK02dcTkgB+h6Ghxv+Xo74uur3/8Dg5hMIBILixrhx45g5cyaHDh1Cp9OZTe3bt+enn36yuOyDDz7Idr9du3bFysoKHx8f6tevT61atfj6669Zu3atxfU//fRTKlSoQLVq1ahevTpTpkwxWycuLo46depQpUoVZs6cya1bt5g1a5a87rp16+jRo4dcxpmamoqLiz7D8eHDh5QpU4YGDRqwatUq0tPTefz4MYMGDUKt1j8Q1mq1qFQqWYR788032bt3L66urkRHRxMVFcXWrVvx9PSUw+knT57MZ599RufOnalYsWKur3fPnj25fv06Fy5ckKdRo0ZRtmxZLly4QPfu3eV1t23bxiuvvGLiijNm27Zt+Pn50ahRI4vLFQoFZcuWxd7enp9++on+/fubiZdKpZKmTZuyb98+eV58fDxnzpyRnWstWrQwWa7RaDh06FCuzjZB8UGoLsUANwcbk4t7EbAteBGp4O2Ekxyin2xxHalMUuqeZqVUyMH1RdFRUqfTGQXoZwpzDcp5YGut5EFCWrbnYowqPVP8E85PgeDJkDpK3nyQCICPS2aulxT4fjUigZCYZxfDAFYMbcivQxtR0tU+95ULiOp+mVkpobEpOaxZMHi76K9JVBla+XP5aYk1ygyT928oR5Vy4AQCgaC4cePGDTPXVn6wf/9+kpOTycjIYOfOnVhZWXH16lU5yN0YrVbLH3/8waVLlyhRogTnz59n69ataLWmD5Xd3d3ZsWMHX331FRcuXKBjx44MHjyYjh07cvfuXRYvXmwi0MXGxuLmps/y/emnn1iyZAmgzyNLSUnho48+Qq1Ws2jRIjQaDY8ePeL69esMGzZM3kdUVBROTk6MHTuW3bt3M2jQIBYvXiyXlz56pG80ZWVlJT9U0Wq1dO7cma1bt5qdq5ubm1nYv7e3NzY2NgQGBsrjBTh06BAtW7bM9jU+dOgQLVq0sOjO+/XXXzl//jynT59m2LBhXL9+nc8++wzQi10tWrTgv//+A/QZbBs2bGDZsmVcvHiRYcOGUbVqVV5//XVA37nz1KlTfPnll1y5coXx48ej1WoZOnRotmMTFC+EGFZMKG908e5oIzLDBC8eSqWCyiX1T6HuRlvuKCmVSTobhV9LN1BF0VEyTa0l3eBiMy6TtLexooG/BwD/BudeKimVSForFXLDDIFAkDek3DCtTt9puWttP3nZ/7N33+FNl1sAx79J05VuOmmhLbvsJauAIFsoiGwEKQpc4IKiKCgIMhUoKr2KICBbhiBDhuy9l+xNy+qmu2mbdOX+keZH03SyKe/nefI8ze99fyOhpMnJOedt4F0KgH8fxEnBsHKOTxcMq1XGnnequBQ+8TkykcvoVtcDmQy61vUofIdnTGmmkF6Hn7ZUMi7HapJ6+i8A9SuECoIgvAilSpXi5s2bBa72l5GRwcaNG7lx4waNGzd+5tdgYmKCUqk0aNgeFBRE5cqVpft37txh+vTpVKpUiW+++YYZM2YQHBzMd999x+jRo6lcuTITJ07k5MmTUrP2ihUr8t1339G4cWPee+89Zs+eTVRUFO+//z6jRo2iUiXdSsWJiYmo1WqcnJzIyMhg0aJFRsEbhULB+vXr+c9//sODBw+oUaMG8+fP5+rVq8yYMQOA9u3bc/78eWJjY+nYsSNyuVzKPFu/fj2ffvopCxYs4NixYwwdOpS0tDTS09O5fv06YWFPvqKwRqPh6tWr1K1bN985586dy3f8r7/+olmzZrRp04b4+HiOHj2Ku7vufUVKSgrXr1+XAnmdO3fmf//7H1OnTqVJkyakpaWxbds26d+ubt26rFmzhhUrVvDWW29x5coVdu/eLWXdCa8+EXV5RXg7WnHufhwgyqiEkksfUFJpMo3GsrK0UplkzpXg7C1NuQ/EvYRgmL55vlwGVrn+X/pWcOREcAxn7sUxoIl3gcdJFc3zBeGJ6TPDAD5qWg43u8cZW5VcrLGzNJXKqBVy2UtrfP+sze5Zm3Edq+KcIxPuRXKxMUelyeBRkobyztZPfJzY7NfunJlh+hVBY5JFZpggCC9Ox44dcXd3p2zZsgXOk8lkjBgxgipVqjz1OfWrRYaGhuLo6GgwlpWVxeHDh7l8+TINGzYkKyuLli1bcuLECVq1asXMmTN5//33USh074s/+OADevbsyV9//cWSJUuYNWsWb731FseOHaNt27ZERESwatUqunfvzqFDhxg0aBBt27ZlyJAhREVFYWlpydy5c6lRowbm5uZs2bIFCwsL/Pz8ALCysuLWrVs0bNhQWlVzwYIFNGrUCKVSyerVq/n999+5fv06K1as4M8//6RatWpcvnyZQ4cOERgYyI4dO/jll19YuXIl3bt3x9fXl3fffRdfX19OnjzJ/fv3i/zcTZ48mcmTJxtsMzc3Jz294M8Euft45bRjx458x0qXLk1MTIzBthEjRjBixIh89+nRowc9evQo8HqEV5cIhr0iyjk9btRrKXqGCSWUvhG2Sm38Rywl/XGAzCrHiqr6Jvovo0wyUeoXZmqUal3FTfetj75PUUFSs4N8ItAtCMWnb5Jva6FgeAvDviNyuYy3vBzYdyMKgLKllChKSPaliVz20gJhAE425gRHJxP1rDLDrB5n1zqKMklBEF4Ca2trLly4QFhYGKmpeS/gIZPJcHR0xN7e/pmcs3v37qxYsYJy5crlGcQpW7YsixYtwtZWVx7/22+/4erqahQ40zM1NaVv37707dtXyvKSyWT8+eefuLq6YmZmxpEjR+jYsSPfffcdo0aNYsiQISxevBiAChUqsGTJEgA6depEjRo1pIyur7/+mjZt2khlmFqtFjc3N2llytq1a/PLL79w4sQJTExM2LZtG9WqVQOgRo0ajBgxgkmTJnH8+HHq1Kkjbf/333/Zt2+fFNQThFeF+I18ReTscSKyR4SSyspc97udnGacGaYvkZTLDBeRsFfqPkC9jDLJhOx+YTYWxi+VXtmlWPdjihAME5lhgvDEOtRw41hQNJ1ru2OnNDUab1CulBQM836KlSQFQ/pA3LMqk3TIUSbpKJVJimCYIAgvlkwmw8Pj6cvP9+7dW6R5tra2HDhwoMjH1QeXinpsfRAtZ7Zb8+bNCQ4Olhre//777/z+++9G+5uYmBisfDhhwgQmTJhQ6HmbNGlCkyZN8hybMmWK0TZnZ2f69OlT6HEF4UUTwbBXhHeOHieWZuKfRSiZrM11H2ST1MYNmfVNmq3MFQZZWPoPUPGpL/ZDk1arZc3pBwC42hg30vYspfvQnajOID4lTcpgy4taBMME4YlZmSv4qVedfMcbeDtIPz9t83zhMWfr7GCYqujBMN1KarqMPYC0jCySsl/bS1kZ9wwTZZKCIAjPhz4QJghC/kTU5RUhMsOEN4F1doaVSpNHmWR2HzGrXMFgu+w+Yy+6Z9gfJ+/z17kQ5DL4rE1lo3FLMxNcbMyJStJwPyalwGCYKJMUhOenpoc95go5mowsg8VohKfjYlu8zLDY5DTa/nQImQz8arnzSauKBouH5FyRV18mKRroC4IgCILwspSMxholgLW5QipJsDQT/yxCyWStL5PMo4H+48www4CRQ3ZZVMILDIZptVoCdt0E4KsOPjSr5JTnPK/skqz7sSkFHu9xmaT4vy0Iz5qZQk7rqi7IZfCWV6mXfTklhj4zrKg9w84/iCMmOY1oVRrLjt/jl/13uB2pWzm4vLOVlC0G4KhvoC96hgmC8IbJzDR+D/wipKamSr3ABEHQEZ/MXiH65eBFZphQUuVVJrn/RiRdfz3GpZD47DmGmWH6jKu4F9hAX5ORJV1j30ae+c7T9w17UEjfMNEzTBCer9k9anPgy5ZUc7d92ZdSYhS3Z1hYvK4ZtT7mdTEknluRSQBUdjVcZj5nmaRWq30WlysIgvBEpk6dSnJy4f1fAVQqFf/++y9r1qxh8uTJRg3xN2/eTMuWLfPd/9y5c1StWpWUlIK/RH0SCQkJ0kqNFStW5M6dO2RlZXHnzh1A9zg/+eSTZ35eQXidiWDYK6Sel67viZejKPMQSiapgb7mcTBs3ZkQLjyM588zD7Pn5A6GvfgG+qk5GvwrCwhgeWX3DbsXU/CbGqlnmCiTFITnwspcIf52PmP6YFh0EXuGhWQHw5pW1GXS3ghP4np4ImAcDNOXSaZlZOW5oIogCMKzdvDgQZYtW2Z027hxI8OGDctz7Pz586xcuZI2bdpQtmxZbGxsqF+/Ph988AFTpkzh1q1bxbqGevXq4eTkxNatWwuct2zZMhQKBdbW1vnezM3NGThwoME+Q4cONTjO3LlzGT58OKALxNWvX79Y15vThg0bcHd3L3DO1KlTkclkHD161GD7o0ePGDhwII6OjlhaWtK1a9c89z9y5AgymYzp06fne44rV67w9ttvY2lpSfny5Vm5cqXB+MOHD/Hz88PKygp3d3d++OGHoj1A4Y0kgmGvkNFtK7NjVHP8apV+2ZciCM+FjdQz7HEwLDY74+tudnaV0izvzLD4F5gZlpIdvDIzkaMwyf9l0jO7TPJBIcEwqWeYyAwTBOE1oS9ljCti9lZYvBqAZhWdsDQ1ITU9kwM3HwHGwTClmULKlI0pRoN+QRCEJ6VWq1GpVIwcORKVSiXdBg8eTKNGjQy26W9paWk4ODhQrVo1hg8fTuPGjRk9ejQ3b94kNTWV6tWrM3nyZL7++muj83l7eyOTyQxucrmcEydO0KdPH6MxmUwmZXEBdO3aNc9r0t9mzZplcL4hQ4Zw+fJlTp8+DUB0dDRTp07l559/JjU1lSNHjtCgQYNiP28bN26kcePGfPDBB0RFReU7LyoqKs/AU1JSEm+//Tbh4eFs2rSJEydO0K9fP6N5Wq2WsWPHFngtiYmJtG3blnLlynHq1Ck+/vhjBg4cyMmTJwFdCWqnTp3IyMjgyJEjTJkyha+++op169YV81ELbwrRQP8VYqaQU7W0KPEQSi59mWTOYFhc9mpi+s9a1vn0DItPffaZYaHxqQQ/UtG8krPB9tQ03fUVlsmlz0S5H5t3ev3R29FcDUsgJU2USQqC8HpxsNK99mZkaUlMzcBOaVrg/NA43ZcCZUspqVrahn8fxJOQ/bpd2dXaaH4pKzNC41OJSU4TWX2CIDx3HTp0AGDixIn89ddfBc51dnZm/fr10n0/Pz8Abt26RaVKlahc2Xhhpdzu3bsn/azRaAgLC6NcuXLStoiICNRqNd7e3nnuv3nzZqytjV879dLT0+nbt690X6lUcvDgQSpVqgSAk5MTmzZtomrVqqxduxa1Ws3o0aMxMXn8XnTBggV4eXkV+Dh+//13WrduTf/+/fnss8/ynff555/Tvn17o+d25syZZGVlsXXrVszMdF9w16lTJ8/zZGVl4emZf3uSZcuWkZmZyaJFizAzM6NWrVps27aN+fPn07hxY7Zv386NGzfYu3cvLi4u1KtXj3379vHrr7/Sq1evAh+n8GYSwTBBEF4YfZmkQTAsV8ZX7jJJZxtzZDJIScskKkmNi43FM7kWdXomvX47QWh8KltHNqNmGTtpTB+8siosGJZdJhmZqEGdnkl8Sjqzd92kbTVX2ld35bM/LxCt0uBbwREQwTBBEF4f5goTbMwVJGkyiEnWFBoM02eGudtbUt3djn8fxAO6L/ryCnY5WuuCYbGiib4gCC/QypUrUavVBc5xcnq8cNKkSZOIiYkB4NSpU9y/f58rV64A0Llz50LPp1aref/99wHYsWOHtH379u2MGzeOtWvX0qpVK6P9unbtWmDQLjAwkAsXLkj3vb29ycjIQK1WExMTQ/PmzTExMaF58+Y8ePCAli1bShlZkyZNonfv3tjZ2dGrVy/S09PZtGlTnufZvn07MpmMZcuW5XstO3bs4NChQ+zcudPompctW8b48eOlQFhewsPDGTduHLt27aJ79+75zjtw4ACtWrUyOFarVq3YsGGDNF6vXj1cXFwMxj/99FO0Wi0ymczomMKbTZRJCoLwwtjoM8Oym9NnZWmJy9ULLHcwTGmmoKKz7puxSw8Tntm1LD12j9DsHjcXHsYZjEmZXIUEw+yVpthml35uuRhGt3nH2PBvCNO2XSPoUbLUa+dOlG5FNVEmKQjC66RUdm+v2OSCA1ZpGVlEJuk+XHrYW1LD43GWe0Vna0zkxh9AHKUm+qJMUhCEFyd3X63cIiIimDBhgnS/QoUK+Pj4UK5cOW7fvo2rqys+Pj74+Pjg6OhY4LHi4+Px8/NDpVKxevVqg7E+ffoQGBhI165dWbBggdG+u3fvls6T123GjBkG869du8a4ceOoU6cOpUqVomnTpkyYMIE+ffpw9uxZrKysGDhwIAMHDkStVjNo0CDs7e0pX748VapUyfcxFBZAio6O5uOPP+bnn382ymR78OABYWFh2Nra0rJlSxwdHWnevDlnz56V5mi1Wvz9/fnggw8K7WkWHBxskF0H4OnpSUhISIHjGo2G6OjoAo8tvJlEZpggCC+MPjMsNT2TzCwtSep0MrMMe9FYmRm/LNUqY8/tKBWXQuJpU831qa8jNjmNeQce92W4mb3imZ6+x1fu/mW5yWQyvBytuByawNi/LknbQ+NT+etciHQ/Kns1NtFAXxCE10kpKzPux6QQU0gwLDJRjVarywJztDKjuvvjTNsqbjZ57lMquyeZ/tgPYlK4HZVEKx8X8e29IAjPjaWlJXPnzs13XKPRYGPz+HVrwIABgK4ZfWZmJvXr16d///7Y29sDsG3btjyPc+XKFbp27Ur16tUZNGgQffv2ZefOndL4xIkTiY2NZfv27fj5+REREcGkSZMA6NWrF23atGHWrFn07NmT8uXLGx3/yJEjxMfHA7oAXpMmTejfvz+BgYHMnz+f8ePH8/fff0tzu3XrBkBoaCgqlYoKFSoAujLGJ6XVahk4cCDt2rWjW7duBqWhoMv4Avjhhx+YMGECZcqU4fvvv6d9+/bcvn2bUqVKERAQwMOHD6VrLYhKpUKpVBpsUyqVaDQaaTxnVp9+HJDmCEJOIhgmCMILY23x+CVHpcnIM9vAytw4YFS7rB0b/g3hUuizyQxbc/oBSZoMTE1kpGdquRWhMhhPLmLPMIBOtUpzPTwRW0tTGng7EJ6g5lJIAitO3DOaKzLDBEF4neiztwrLDAuJ02XZethbIpfLqORqjUIuIyNLS6U8+oXB4xUl9WWSn6+7wLn7cXz3fg36NSq4h40gCMKTmDJlCs2aNQNgwoQJBg3fe/XqxbFjxwgKCgJ0WU36/lVqtZqffvqJd955hxUrVvDHH3+wa9cu3Nzc8j3X3bt36d69OzNmzECtVvPFF1+wc+dOOnToQGhoKAsXLuT48ePUqlWLvXv3SqWYoAvgTJs2jblz57J06dICH5O1tTUffvgh169fx8LCgho1ajBr1iw8PDz473//K82zs7Pj7NmzXLhwgaZNm6JQPH0YYMKECQQFBbF27do8xzMydO+nv/jiC3r27AnAihUrcHFxYdu2bbi6ujJ9+nSOHz+OpaVloeczNzcnLc3w75FarZYCXvmNA0ZBNEEAUSYpCMILZK4wwSx7dUaVJsOoXxgYl0mCLjMM4FJIQpFWNStMVKLuD2OLyrrG+TcjkwyOmyJlhhUevBrWogK3pr/LvxPbsuDDt2iXnbmmP0ZOomeYIAivEwdl0YJhYdkl5+72up6O5goTanjossNqedjnuU+pHIG2zCwtl7O/7Jj5zw0iEgru5yMIgvAk9uzZI/W+ioiIIC4ujurVq9O0aVMuXbrErl27+O2337CxsSE2Nlba77vvvqN9+/Z4enoyYsQI2rVrR9OmTaXAWV46d+7MrFmzkMvlKJVKAgIC8Pf3Jzg4mA8++AB/f39q1aoFQIMGDaQG/6mpqYwYMYLQ0FCysrJYtWoV7u7uJCUlERYWhkwmIyQkBJVKha+vL2ZmZty7dw9vb29cXV25evUqH3/8MW5ubri5uUkrKXbv3p2ffvqJ3377jT59+jyT5/P777/nzp07ODk5YWFhIZVbtm7dmnbt2km9uypWrCjt4+DggIuLC5GRkcycOZOUlBQaNGiAhYUFFhYW3L9/nylTpmBhYdwj2MPDg4cPHxpse/jwoZQ5l9+4nZ0dpUqVeiaPWShZRDBMEIQXSp/5lazJIDbZeIXIvIJhVUvbYGoiIzY5TcpAeBqp6bpAVXV3O0zkMhJS06VSRshZJlm04JU8Rz+cphWd8p1naSZecgVBeH3oe4bFFNLkXt9/0d3u8Tf7P/aqzf/61KFpxbx76uizzqKT0wiJSyEtIwuAJE0Gk7ZceeprFwRByC0tLY02bdqwe/duAExNTdm3b59BQ/Zhw4YZrLh44MABfv75Z8aPHy9tmz17Nr179yYz0/iLz/z079+fbt26Ubt2bTQaDXPmzDGak5GRga+vLyqViiVLlhAdHc2XX37JmDFjkMlkbNiwAV9fX6lEMzU1FRsbG7y9vQkKCsLa2pqTJ08SGRnJjRs3MDMzo2HDhgCMHDmSTZs2ERkZabAK5dO4fv06ly9f5sKFC1y4cIF//vkHgKVLl/L7779ToUIFXFxcOHnypLRPdHQ0UVFRVK5cmeXLl3P16lVp/wsXLuDu7s6IESMMFgfQa9asGfv37zd43vft20fr1q2l8dOnT5OQkJDnuCDkJj6ZCYLwQulLJZPUGcRlZxvkbA9jnUeZpLnCROo7cynk6Usl1em6D112lqZ4O+rSpm9GPO4bJjXQNy1+CnmtMvbYZD9GRa6m0SIzTBCE18njMkkNa04/oNeCE9Lrdk76zDAPh8fBsArO1rxXxyPf/l+OUqBNw+1IXam6k7Wuj9iuq5EGqw4LgiA8CyYmJuzdu5d27dpJ25KTk6USOoVCgUwmk4JlAGPGjCEgIICyZcsaHOv777+ncuXKNGzYEF9f30LPvXPnTg4dOoSzszPBwcGsXLmS9HTDL4UVCgXLli1j+fLlhISE0K5dO2rWrMmQIUNISEhgwoQJBkG52NhY7Ox0WbgRERF4eXkxduxY/v77b3r27MmQIUPw9vYGICwsDIVCgVKpJDX18RfL48aNY9y4cUV8Bg3lbuqv70Pm6emJp6cncrmc0aNHM23aNNauXcvp06f54IMPqFy5Mh07dsTT09PoGKampjg5OeHj4wOAv7+/1ONt8ODBxMfHM3LkSK5cucK0adO4fPkyn376KQA9e/bE0dGRjz76iIsXL7Jw4UI2bNjA2LFjn+jxCSWfCIYJgvBC6RvkJ2syiM0uk6zkYm00ntvjUsn4p74GfWaYpdnjINutHE30U7N7hhU1MywnE7kM3wq6TIjcWWKiZ5ggCK+TnE3uFx0J5vTdWI4HxRjNkzLD7Avv+aJX1kH34TPokYobEYkA+FZwxM1WVxpzIzzxqa5dEAQhp4yMDIKDg6XMMKVSSfPmzTl//jwPHjygZs2aaLVa5syZQ9u2bVGpVKxfv57Zs2cXuAJlx44d6dKlC4BRK4/Q0FACAwOpXbs2/v7+DB06lNu3b7Ny5UoCAwPx8vLiiy++4MCBAyQnJwNQrVo15s2bR506dahSpQqrVq0iOTmZHj160L59e1q0aCEdPzw8XCpFrFChAvv376dNmzZ07dqVI0eO4OXlRUZGBteuXaNDhw6MGDGCsmXL0rFjR6m5fVBQUIHlnk9r7NixfPrpp4waNYoWLVqgUCjYtm0bpqamRdr/+vXr3L9/H9CVQW7dupWjR49Sv359Nm3axD///IOXl67PpFKpZMeOHYSHh9OwYUN++ukn1qxZQ6NGjZ7b4xNeb6KBviAIL5Q+a0qleZwZVresA7eyMwPyKpMEqFPWntWnHnD0ztMvjazODoZZmMqp7GrDP5cj8swMU+aRpVYUo9tWwdREzmdtKnPop0PSdpEZJgjC60SfGRaZqOZBTApAnr0e9cGwMsUIhlVwtsZBaUpcSjqbzocCUNHFmiR1OhGJaq6HJ/KWt+jxIgjCs7Fnzx7ee+89Fi5cSL9+/bC3t+fevXuMHj2adevWST287O3tkcvlREREsG3bNpYvX17osY8fP46trS3btm2TMrX69evH2rVradKkCaNGjaJv375Sk/j27dvTtm1b/vnnHxYvXkynTp1wc3Pj6tWrDBo0iH379jFr1iyGDh3KxYsXGTRoEO7u7kyfPp2IiAisrKzYvHkzAOXLlycmJoalS5fy119/kZSUxK5du8jKymLmzJkkJCQwfvx4hg8fzsyZM0lMTMTPz49atWpx4sQJqadYYQYOHMjAgQMLnOPt7W0UEJTJZEyZMoUpU6YU6Ty5V6Q8ffq0wf0WLVpw+fLlfPevUaMGJ06cKNK5BEEEwwRBeKGss4NdKnUGMdnBMC8nJd6OSkLiUnGzM26YCdC2qiumJjKuhiVyMyJJyuh6EvpgmKWpCVVcjTPDUrLHlU9QJglQxc2GuR/UA3SlmAmpujT4oqxOKQiC8KrQN7m/E6UiK/vzjf71TE+r1RIer2t4X7oYwTC5XEYD71LsvhZJ0CNdRkQlF2s0GZkcuPmIayIzTBCEZ2jHjh1069aNunXrkp6ezvnz54mIiOC7775j8ODBDBs2jO+//x6A6tWrM2rUKGbOnFmkY48ePZqbN2/i5OTEokWLAJg2bRoBAQF4eHjkuY9cLsfPzw8/Pz9SU1OJjY3F0tKSwMBAlEol1tbWBAUF4evry2effcakSZMICAhg4sSJALi7u7Nw4UJMTExQKBTcvXuXGTNm0KJFC+RyXfFXhw4dWLNmDQsXLpT6hNna2rJ//34WL14slTUKwptKBMMEQXih9JlfOTPDSinNWD2kMfEp6VLPmNwcrMxo5ePCrquRbPg3hPEdqz7xNejLJM1NTaRSndtRKrRaLTKZrNgN9AviZmvxOBgmMsMEQXiN6INhWTm+6M/dMyxJkyG9pupLHIuqYTldMEyvoou1dK5rYSIYJgjCsxMYGIhcLqd9+/bIZDIyMzPp3r07AC4uLqxdu1ZqpD948GAGDx5sdAz9SpS55WwQr6df4bAoLC0tpaCZvuwRdKWPwcHBuLrqViqfMGECEyZMMNrfzs6OX3/9Nc9j59UsX6FQFFj6KQhvCtEzTBCEFypnmaS+Z5iDlRnu9pZUc7ctcN/u9coAsOl8KBmZWU98DfoG+pamJpQtZYlMpiuN1GeqpWT3DHsWmVyuOTLdRM8wQRBeJ/om9znF58oMi0zQZYXZWiiK/ZrZsNzjMkiFXIaXoxVVS+uydW9EJD3V67wgCEJO+mwp/aIeOVeMBAxWlHyV6ANhgiA8eyIYJgjCC5Wzgb6UGWZVtDcgLau4UMrKjEdJGo7l0cS5qPSZXxamJpgrTKRshpA4Xd+blGeaGfY4002USQqC8DpRmimwMDV8qxifq2dYZKIGANdiZoUBVCtti1X266KXoxIzhRwvRyuUZiZoMrK4F5P8hFcuCIIgCIJQMBEMEwThhbLOzgxL0mQQmx0Mc1AWLRhmppBLKzXeztHjq7hy9gwDKOOg63PzMFbXIPrZBsMef0AUZZKCILxuHK0MS9fjU3JlhiXqMsOeJBimMJFTP7tJfiUXXUaYiVwm9YS8WsRSSU1GJhvOhUjXIgiCIAiCUBgRDBME4YXSN9BPSEknUa0rRyxqZljO/fUBrSeROxim7xv2MM4wGGZp9vRtFUWZpCAIr7Pcr8+5V5OMTNIFoFxs8+73WJjOtUoD8HZlZ2lbtdK6kvnLIQlFOsY/l8P5Yv1Fvv/n+hNdgyAIwouSmfnk71+fRmpqKllZovRcEHISwTBBEF4ofTArJDvwJJfpVlwsKn2poT5gVVxarVZq9qwv/9FnhunLJFOze4ZZPYueYTa6YJiZQo6JXPbUxxMEQXiRcgfDcq8mGZVdJlnc5vl6PeqX4dT41vRtWFbaVqesPQCLj91l1s4bZOXs4J+HsOzVLC88jH+iaxAEQSiIWq1GoXj6L0jPnTtH1apVSUlJeQZXZSghIYH79+8DULFiRe7cuUNWVhZ37twBYOrUqXzyySfP/LyC8DoTwTBBEF4ofZnkg+ySRHulWbGCRPpsrtQnzAxLz9RKq5VZZAe7ypTKzgyLzZ0Z9gzKJLMzw55FyaUgCMKL5pgdDNO/9sanpKPVPg5ORSQ8eZkk6JpZu9paSE2tAbrW9aBn/TJotTD/YBDbLocXeAx9gO5+TAoqTcYTXYcgCCVfYGAgMpmsSLc//vgj3+Ns3rwZCwsL3NzcDG6WlpYF7levXj2cnJzYunVrgde5bNkyFAoF1tbW+d7Mzc0ZOHCgwT65V4icO3cuw4cPB3SBuPr16xfhWTL0zTffUK5cOZRKJTVq1GDjxo0G47/88gtVqlTB0tKSihUr8ttvv+V5nIsXL1KvXj2OHj1qsH3//v1Gz72fn1++13PlyhXefvttLC0tKV++PCtXrjQYf/jwIX5+flhZWeHu7s4PP/xQ7McsvDlEMEwQhBfKKjszLC6774yDsuhZYZAjGPaEmWE5g2gWCsMyyVApM0zfM+zpvwWsVtqWnvXLMPKdik99LEEQhBdNnxlWu6wdABlZWoOAk75M0vUJyyTzYmoiZ3bP2vRv7AnAv/fjAEhUp5OexwqTCTn6mN2MKFqfMUEQ3jyjRo0iPT29SLd+/foVeKwOHToQERFhcOvdu7c07u3tbRTkkcvlnDhxgj59+uQZgNNncQF07doVlUqV723WrFkG1zNkyBAuX77M6dOnAYiOjmbq1Kn8/PPPpKamcuTIERo0aFDs5yw8PJwFCxZw/PhxWrZsSe/evbl8+bI0HhQUxOzZszl58iQDBw5k+PDh7NixQxr/999/6dWrF02aNOH8+fNGx4+NjcXDw4Pbt29Lt0WLFuV5LYmJibRt25Zy5cpx6tQpPv74YwYOHMjJkycBXQlqp06dyMjI4MiRI0yZMoWvvvqKdevWFftxC2+Gp/+kJwiCUAw25oYvO0Vtnq+nz9Z60swwTfZ+JnIZpia6TIScZZJZWVpS0p9dA325XMbsnrWf+jiCIAgvQ80yuiBYyyouXHgYjzo9i/iUdGwsdF9kRD3FapKFqefpwB8nH3AtPJH7Mcm0m3OYBt6lWPFxQ+Q5MorjUx/3MbsWnkR9r1LP/FoEQXj9yWSyYpc79uvXjxs3bpCZmclbb71F/fr1effddwvd7969e9LPGo2GsLAwypUrJ22LiIhArVbj7e2d5/6bN2/G2to63+Onp6fTt29f6b5SqeTgwYNUqlQJACcnJzZt2kTVqlVZu3YtarWa0aNHY2Ly+L3tggUL8PLyKvBxLFmyRPr5559/ZsWKFRw4cICaNWsCumw7vdq1a7NmzRr27NkjPUcbN27E3Nycbdu20bp1a6Pjx8bG4urqSsWKhX9pvGzZMjIzM1m0aBFmZmbUqlWLbdu2MX/+fBo3bsz27du5ceMGe/fuxcXFhXr16rFv3z5+/fVXevXqVejxhTePCIYJgvBC6csk9ZpVcirW/lIw7CkzwyxNTaSynNJ2FpjIZaRlZhESl0pmdh3lsyiTFARBeJ29V8eDt7xL4W5nwbJj94hIVxObnMaxO9H4VnAiKunpyiQL4uOma6R/IzyRfdej0GRkcfRONH+efUjfhp7SvJx9zK6Hi8wwQRCMpaenF7t5vYmJCZ9//jmxsbG8++67/PDDD9jb23Pv3j127tyJm5ubwfyEhATatGljsE2tVvP+++8DGGRMbd++nXHjxrF27VpatWpldO6uXbvy119/5XttgYGBXLhwQbrv7e1NRkYGarWamJgYmjdvjomJCc2bN+fBgwe0bNlSynabNGkSvXv3xs7Ojl69epGens6mTZsKfT60Wi2ZmZk4OjrmOyf3+LRp05DJZAbBwZxiY2NxciraZ4EDBw7QqlUrzMwef5HeqlUrNmzYII3Xq1cPFxcXg/FPP/0UrVZrUI4vCCCCYYIgvGBWOUoPK7pYM6xFhWLtr3zKzLDczfMBFCZySttZEBKXys3IpMfnEqs/CoIg4GGvy561V5oSkahmzekHrD3zkArOVqRn6r48cLZ5dmWSehVcrFDIZSSqM9hyMUzaPnPHDdpWc8XJWnfOhNTHZZsiGCYIQl769u0rBU2Kyt/fn2XLlqFWq5HJZLRs2RIAHx8fQkNDjYJCUVFRKJVK6X58fDw9evRAo9GwZcsWg7l9+vTB0tKSrl27Mnv2bKN+X7t378bHxyffa4uLizPIULt27RpLly5l06ZNnD9/nqZNm9KmTRtKly5Nr169aNu2rdRj7KuvvmLQoEHY29tTvnz5Ij0XUVFRTJ06FU9PTym4l1NCQgJz584lISGBjz76SNpeWAAqJiaGvXv3YmVlhaenJ++99x4TJkzIMysuODiYjh07Gmzz9PQkJCREGs+Zfacf12g0REdH4+zsjCDkJIJhgiC8UA45ViYL6FELi2IGnJ62Z5g6XddvJvd5yzooCYlL5VZ2MMzMRI7CRLRVFARB0LPP7vF45HY0AEGPkgFwsjbD9Dm8XporTKjgbM3NyCRppUgHpSlxKeksOhLMuHerApCYmrNnWBJZWVqDMkpBEIScWVYtWrSgV69ejBgxoljHCAkJITQ0lOHDhxMaGppnoMfFxYVLly5x5coVunbtSvXq1Rk0aBB9+/Zl586d0ryJEycSGxvL9u3b8fPzIyIigkmTJgHQq1cv2rRpw6xZs+jZs2eeAasjR44QHx8P6EoumzRpQv/+/QkMDGT+/PmMHz+ev//+W5rbrVs3AEJDQ1GpVFSooPsyeubMmQU+5sOHD9OmTRvS09OpVKkSa9euNQj43b9/n8qVK5OWloabmxsrVqzA3d29yM/psGHD6Nu3L1qtlhMnTvDtt99y586dPLPiVCqVwblBVx6q0Wik8dxZZvr5+jmCkJMIhgmC8EJZmytY9lEDFHI59Twdir2/ZXZmWcqTlkmm6TPDDINh+r5h+mCYKJEUBEEwpO/xGBqfarD9eZRI6lUtbSNl7JqZyJnQqRpfrL/InmuRUjAsZ5lkSlom92NTKOdk9dyuSRCE19f58+e5dOkSW7ZsISMj/9Vn9b3F/v77b/bs2UNmZiZ16tTh888/JzExkVOnTuHt7c2pU6fQarU0btyYkJAQmjVrBsDdu3fp3r07M2bMQK1W88UXX7Bz5046dOhAaGgoCxcu5Pjx49SqVYu9e/cSExMjnVupVDJt2jTmzp3L0qVLC3w81tbWfPjhh1y/fh0LCwtq1KjBrFmz8PDw4L///a80z87OjrNnz3LhwgWaNm1a5N5pb731FhcvXiQ6Oprt27fTtGlT1q1bR+fOnQFwd3fn4sWLxMbGcvjwYbp27cqPP/7IsGHDinR8fVAOoH79+tjZ2TFgwAAePXpklMllbm5OWlqawTa1Wi0FvPIbB4yCaIIAIhgmCMJL0LKKS+GT8qHPDFM/YZmkOuNxz7CcypbS/ZG8GaH70GUlgmGCIAgG7PNZ/fd5BsN8StvCBV2JZK0ydrSt7opig4zgR8ncjU6mjIOltLpl2VKWPIxN5VpYogiGCYKQp1OnThEfH4+9vX2+c+RyORqNBoVCwbJly6hSpQomJiZERUUhl8sNAlQbN27E2tqaxo0bGxyjc+fOUsBIqVQSEBCAv78/J06c4KOPPsLf359atWoBGKzymJqaypdffklSUhJZWVls2bKFMWPGcPPmTZKSkvDw8ODhw4fY29vTrl07zMzMuHfvHo0bN0ar1RIVFcXHH38sZa39/PPP9OrVi+7du/PTTz9x69YtgyBZYZRKJVWr6r54aN68OeHh4cyYMUN6bKamplI5p6+vL6mpqUydOrXIwbDc6tWrB+gyznIHw/SPPaeHDx9KmXP6VSlzj9vZ2VGqlFhYRTAmaoAEQXit6INYT5oZpk4z7hkG4OWoC4YFPVLpziOCYYIgCAbs81n919X22fcL0/Nxs5F+fsu7FLYWpjQqr/tQs+96pEGJZJPyuv49V8ISntv1CILwehs2bBharTbf26FDh3Bzc5MypzZt2sTkyZMBXZAst8jISKNG+nnp378/3bp1o3bt2mg0GubMmWM0JyMjA19fX1QqFUuWLCE6Opovv/ySMWPGIJPJ2LBhA76+vlIgLzU1FRsbG7y9vQkKCsLa2pqTJ08SGRnJjRs3MDMzo2HDhgCMHDmSTZs2ERkZabAKZXEpFIoCFyIobLwwp0+fRi6XG/X+AmjWrBn79+83OP6+ffukVSqbNWvG6dOnSUhIyHNcEHITwTBBEF4rlk/ZQF+fGZa7TLKCs65Rp74ZtNJMJM4KgiDk5JAjM8zRykzKvnKxeZ5lkrbSzw28daX1rX1cAdh7PVIqkbQxV1A3u/T+Ukj8c7seQRBKtocPH+Lt7V2kuWlpaezdu5dTp06h0WiQyWSYm+f95cDOnTs5dOgQzs7OBAcHs3LlStLT0w3m6DPRli9fTkhICO3ataNmzZoMGTKEhIQEJkyYwPjx46X5sbGx2NnZAbq+YV5eXowdO5a///6bnj17MmTIEOmxhIWFoVAoUCqVpKY+LnUfN24c48aNy/Oa9+3bx8SJEzl69CgXLlxg1qxZLF++nCFDhgBw/fp1Pv30Uw4cOMClS5f47bffCAgIkMaL4ptvvmHz5s1cunSJRYsW8cUXXzB06FBpcQJ/f3/mzp0LwODBg4mPj2fkyJFcuXKFadOmcfnyZT799FMAevbsiaOjIx999BEXL15k4cKFbNiwgbFjxxb5eoQ3iwiGCYLwWnnaYFhqmq6Bfu4yydwlNSIzTBAEwZC95ePMsEqu1gxrUR47S1PaVHV9bud0sTGnVhk7XG3NaVBOlxGmP9+Ze3E8iE0BwNbSlJoeug+Fl0ISyMrSPrdrEgSh5Lpx4waVKlUqcM6dO3fw9vZm7Nix1KhRg0ePHlG/fn0ePXrEzZs3pXmhoaEEBgZSu3Zt/P39GTp0KLdv32blypUEBgbi5eXFF198wYEDB0hO1i1IUq1aNebNm0edOnWoUqUKq1atIjk5mR49etC+fXtatGghHT88PBwXF13rkQoVKrB//37atGlD165dOXLkCF5eXmRkZHDt2jU6dOjAiBEjKFu2LB07diQ8PByAoKAggoKC8nycZcuW5dixY/j5+fH222+zceNGVq9ezeDBgwFwdHTk7t279OzZkyZNmjB//nxmz57NtGnTivx8y2Qyhg4dSqNGjQgMDGTixIn88ssv0vj169e5f/8+oCuD3Lp1K0ePHqV+/fps2rSJf/75By8vL0BX0rljxw7Cw8Np2LAhP/30E2vWrKFRo0ZFvh7hzSJSHwRBeK0os4NYaRlZZGZpMSnmimH6IFruzDArcwXudhaEJWQ32hTBMEEQBAM5e4ZVcrGhdwNPejfwfK7nlMlkrBvahMwsLVbmuretno5KyjtZERydzMGbjwCwszSlipsN5go5SeqMIjXRj0xUc/puLB1rli723xJBEF5/qamp3Lt3j1KlSmFpaUlwcDDLly83COakpqYSERGBmZnuywC1Ws3hw4eZOXMmKSkpbNu2DScnJ5YuXUqrVq0YO3YsY8eO5cMPP2Tt2rU0adKEUaNG0bdvXywtdYs1tW/fnrZt2/LPP/+wePFiOnXqhJubG1evXmXQoEHs27ePWbNmMXToUC5evMigQYNwd3dn+vTpREREYGVlxebNmwEoX748MTExLF26lL/++oukpCR27dpFVlYWM2fOJCEhgfHjxzN8+HBmzpxJYmIifn5+1KpVixMnTrBu3bp8n5/KlSuzf//+fMddXFzYunVrkZ5rb29vtFrjLymmT5/O9OnT893v9OnTBvdbtGjB5cuX851fo0YNTpw4UaRrEgQRDBME4bWSM2MrNT0Ta/PivYzpG+/nzgwDqOBiLYJhgiAI+cjZM6yyq/ULO2/uLy9A93odHJ3MlVBdbxg7S1NMTeRUc7fl/IN4TgTFsPhoMM0qOtGhRuk8jzt5y1V2XInA1EROhxqF9/wRBKFkUalU1KpVS1pV0tLSkr59+9K/f39pznvvvce+ffvo378/SUlJVKxYERsbG0aOHMnw4cOlssiPPvqIt956i+7du9O0aVOmTZtGQEAAHh4eeZ5bLpfj5+eHn58fqampxMbGYmlpSWBgIEqlEmtra4KCgvD19eWzzz5j0qRJBAQEMHHiREC3iuPChQsxMTFBoVBw9+5dZsyYQYsWLaTeZh06dGDNmjUsXLhQ6hNma2vL/v37Wbx4scFKjoLwJhLBMEEQXivmCjkyGWi1kJKW8cTBsNwN9EHXN+zI7WgALE3Fy6MgCEJOOXuGVXK1KWDm8+eZvQLw1bBEQBcMA6jlYcf5B/F8t/0ayWmZbL0YTssqLnkG1G5G6lYPvhOVBIhgmCC8aZydnaW+Xenp6SgUCmkVRr3du3cb3L969SpOTk55Hq9mzZpcvXoVU9O8V97Nj6WlpRQ005c9gq70MTg4GFdXXWn4hAkTmDBhgtH+dnZ2/Prrr3keO69m+QqFgqFDhxbrGgWhJBI9wwRBeK3IZDIpq0ud3f+rOKRgWB6ZXxWcH5fUiMwwQRAEQzkzwyq5vLjMsLzoVwDWl75LwbAy9gAkZ68cnJCazvZL4Ub7Z2VpCYnTNZEOjVc/78sVBOEVZ2pqahQIy0t+gbCcx3mW9IEwQRCePREMEwThtaMPhqWkZxR7X6lnmMI42FXe+fGHO6W5CIYJgiDk5GRtRt+GZfmoqTeO1nmvmPailM3ODNPT9zOrVcZO2qbPHF516r7R/o9UGtIydF+ohMWnGo0LgiAIglCyiWCYIAivHWlFybTiryipTs8yOEZOFXIGw0SZpCAIggGZTMaMbrWY1Ln6y74UqUxSzzY7M6y8szUe9pZYmZmw/OMGKOQy/n0Qz7Xsckq9kLgU6WcRDBME4UXJzHyy1dAFQXj2RDBMEITXjj4zTJ/lVRypBTTQd7U1xyo7SCbKJAVBEF5dZRwsyVnRpC+TNJHL+HtkU/Z+0YL6XqVon90Y/4v1F0lITZfmP4x9HAALi0/Nc5UzQRBKvqlTp5KcnFykuSqVin///Zc1a9YwefJkqd+Y3ubNm2nZsmW++587d46qVauSkpKS75xnbfLkyQwcOLDAOWq1Gnd3d5KSkgo9VkErPwrC60YEwwRBeO0onyYzLC3/BvoymYwK2X1w8socEwRBEF4N5goTSttaSPf1wTAAJ2tzSttZAvBVex+crM25Hp7Ix8vOkJGpyw5+GPv4w2hyWiaJqcUvuxcE4fVx8OBBli1bZnTbuHEjw4YNy3Ps/PnzrFy5kjZt2lC2bFlsbGyoX78+H3zwAVOmTOHWrVvFuoZ69erh5OTE1q1bC5y3bNkyFAoF1tbW+d7Mzc0NglzLli0rMBBXkMOHD2Nra8svv/yCtbU1MplMOs+7776b5z6nT5+mbt26WFtb07FjR2JiYqSxGzduULp0aaKjo4t8DX/++Se1atVCqVTi6enJ1KlTjb6kmD9/PuXKlcPS0pJWrVoRHBxc4DHXrl1L7dq1sbCwwM3NjQMHDgC67LwhQ4ZQpkwZrKysaNiwIYcOHSrytQolhwiGCYLw2rF4iswwdYY+GJZ3sMuvVmnsLE2p7+Xw5BcoCIIgPHc5+4blDIbl5OmoZOWghtiYKzh3P45Td2MBeBhnmJkRKkolBaFEU6vVqFQqRo4ciUqlkm6DBw+mUaNGBtv0t7S0NBwcHKhWrRrDhw+ncePGjB49mps3b5Kamkr16tWZPHkyX3/9tdH5vL29kclkBje5XM6JEyfo06eP0ZhMJuPOnTvS/l27ds3zmvS3WbNmPfFzcebMGQ4fPizd37lzJ82bN2f8+PGcP3+eMmXKSOfZsWNHnsfo06cPAwcO5NixY5iYmPD9999LY//973+ZOnVqoYsN5HTjxg3Gjx/PyZMn+eabb5g6dSq//fabNL5+/Xo+//xzpk6dytGjR0lPT6dLly5kZeW9mNbvv//O4MGDGTJkCGfPnmXFihXSYgQZGRloNBr++OMPDh06hKenJ507dyYyMrLI1yuUDKIpjiAIrx191lZKMTPDtFqtlE2WXzDsP29XYHCz8sjlha8oJAiCILw8Xo5KKbiVXzAMoGppWxpXcGTPtUhuRSbRtKKTQZkk6IJh1dxtn+v1CoLw8nTo0AGAiRMn8tdffxU419nZmfXr10v3/fz8ALh16xaVKlWicuXKhZ7v3r170s8ajYawsDDKlSsnbYuIiECtVuPt7Z3n/ps3b8baOv9Ve9PT0+nbt2+h15HbpUuX6NChA6NGjeLtt98GdMEwfUDv7Nmz1KhRw2AfCwsL1OrHq+4+evQIlUrFqFGjABg+fDi//vorAKtWrSI1NZXBgwcX67omTZok/VyrVi22bNnCnj17GD58OAAzZsxg6NChfPjhhwAsWrSIqlWrcujQId555x2DY8XGxjJ69Gh+/fVX/P39AQwek7m5OStWrJDuz58/HxcXF06dOkWXLl2Kdd3C600EwwRBeO3oyyTVxcgMG7z8LCFxKWRk6VKu8+oZpicCYYIgCK++nE309atJ5qeSizV7rkVyJ0oFPM4Mc7I2I1qVJproC8IbYuXKlQaBnbzkzGiaNGmSVAJ46tQp7t+/z5UrVwDo3LlzoedTq9W8//77AAZZVtu3b2fcuHGsXbuWVq1aGe3XtWvXAoN2gYGBXLhwwWCbVqslI+NxybdcblgEdubMGfz8/Pj000/59ttvATh+/DjXr1/H09MTgCNHjqDValm7di0AnTp1Mjq3g4MDGo2GY8eO0bBhQ7Zt20bVqlVJSEjgq6++YuvWrchkhu+l586dS0BAANevX8fKyirfx6WXmZmJo6MjAPHx8Zw/f94g+8zHx4fSpUtz8uRJo2DYX3/9hbW1Nf369Sv0PICUXaY/n/DmEGWSgiC8dvRZXUXNDNNkZLL3eiQ3IpKkD0KiJ5ggCMLrrShlknoVs/tB3o5SkZGZRXiC7sNwo3K6Dz8iGCYIb4ahQ4cWOB4REcGECROk+xUqVMDHx4dy5cpx+/ZtXF1d8fHxwcfHp9DgSXx8PH5+fqhUKlavXm0w1qdPHwIDA+natSsLFiww2nf37t3SefK6zZgxw2ifw4cPY2pqKt0+/vhjaWz58uW0aNGCzz77zCALKzAw0OAYO3bswM7OjoMHDzJkyBCioqKMzqNQKJgzZw6tW7fG3Nycs2fP8vXXX/PNN9/QvXt36tata7SPm5sbVatWxcLCwmgsp+TkZBYvXsypU6cYOXIkAHfv3gUwyKwD8PT0JCQkxOgYJ0+epGbNmvz444+UKVMGb29vxo0bZ7TgAcCDBw8YMWIELVq0wNfXt8BrE0oekRkmCMJrp7gN9KMSNUbbLBQiGCYIgvA683J8nF1gY1FYZpgNAEFRKsIT1GRmaTEzkVOnrD3bL4eLnmGC8IawtLRk7ty5+Y5rNBpsbGyk+wMGDAB0mU2ZmZnUr1+f/v37Y29vD8C2bdvyPM6VK1fo2rUr1atXZ9CgQfTt25edO3dK4xMnTiQ2Npbt27fj5+dHRESEFKTq1asXbdq0YdasWfTs2ZPy5csbHf/IkSPEx8cbbGvRogUHDx402DZ58mS2bNnC5s2bWbdunVTyCbpA0N9//03VqlUBOHbsGBkZGaxZs4b09HRWr16dbxnnxx9/TN++fYmPj6d06dKcO3eOzZs3c/bsWYYMGcLOnTupWLEiS5YsoVy5cvTo0YMePXrkeSw9CwsLNBoNtra2zJ8/n9q1awO6VTwBlEqlwXylUolGY/wePzw8nAsXLuDh4cHGjRu5evUqn3zyCebm5kyePBnQZQh+/PHHZGRk0KBBA/7880+jbDah5Ct2ZphWq2XKlCm4u7tjZWXF+++/z6NHj/Kcu2zZMqpUqYK5uTm1a9dm+/btBuOenp5GjQOLs+qEIAhvJstiNtCPSjJOh7c0E4mxgiAIr7PKrta42ppT38sBk0LK2yu46AJnMclpXA5NAMDDwZIyDrpVJ0VmmCCUfFOmTKFZs2Z4e3vz+++/G2Raffvtt7Ru3ZoqVarg7u7OgwcPpP3UajU//fQT77zzDitWrKBFixZEREQUeK67d+/SvXt3Nm3axHvvvcelS5ekYFhoaCgLFy5k9OjRNG/enL1799KoUSNpX6VSya+//srcuXPp2LFjnplhQ4YMYcyYMaxcubLQx12qVCmpRDKnkydP8sEHH1CtWjUA5s2bR79+/Xj48CFnzpyhatWqmJjk/+WxpaUlpUuXJisri+HDhzN79mzmz5+PhYUFd+/epWvXrlJ2V1FcuHCBkydP8v333/Ppp58yfvx4QNfjCyAtLc1gvlqtNgqQga5Bvo2NDb///jsNGzbko48+Yvjw4QZ9wrp06cKFCxfYt28ftWvXpk6dOpw9e7bI1yqUDMX+NDh79mx+/vlnFixYwJ49e7h586bUmC6nAwcOMGjQIEaOHMmZM2fo2LEjXbt25fbt29Kc2NhYVq1axe3bt6VbqVKlnu4RCYJQ4knBsCJmhkUkGH9rZC4ywwRBEF5rSjMFh8e+w5//aVykuR72usDX3mu6FcPKOFjibq8PhhXcQ0gQhNffnj17WLZsGaArh4yLi6N69eo0bdqUS5cusWvXLn777TdsbGyIjY2V9vvuu+9o3749np6ejBgxgnbt2tG0aVOCgoLyPVfnzp2ZNWsWcrkcpVJJQEAA/v7+BAcH88EHH+Dv70+tWrUAaNCggdTgPzU1lREjRhAaGkpWVharVq3C3d2dpKQkwsLCkMlkhISEoFKp8PX1xczMrNDH3axZMypVqmS0vW3btkydOhWA4OBg9u7dS5s2bfD19WXChAm0b9++SM/rggULsLGxoW/fvuzYsYP//Oc/KBQKBg8ezLFjx4p0DND1AWvUqBEjRoxg9uzZBAQEkJqaioeHBwAPHz40mP/w4cM8s+ZcXFwoX768Qc+0ypUrG6wWaWdnR/Xq1WnVqhWLFi2iTp06/PTTT0W+VqFkKFYwLCsri9mzZzNhwgQ6d+6Mr68vP/30Ezt27JBqefXOnj1LnTp1+OSTT6hVqxYzZszAxsZGavSXlpZGcnIyNWvWpGLFitItd6M/QRCE3CzNdBXeRc0Mi0zMKzNMBMMEQRBed+YKExQmRXvvqO8b9vfFMADqejpIwbDIJDXpmVnP5yIFQXglpKWl0aZNG3bv3g2Aqakp+/btMwgoDRs2zCAb6sCBA/z8889SlhLokkN69+5NZmbRF3Lq378/3bp1o3bt2mg0GubMmWM0JyMjA19fX1QqFUuWLCE6Opovv/ySMWPGIJPJ2LBhA76+vlKJZmpqqkFJZ1FptVqCg4NxcHCgbNmygK4f199//02bNm1Yv349R44cYdCgQYUeKyoqiilTpjBv3jxAl62l782VkpKCqWnBJez5USgUaLVaMjMz8fDwwNvbmz179kjjt27dIiQkhNatWxvt6+vry4ULFwwWSrh69WqBq4AqFIpi/XsKJUOxIk+XL18mOjqad999V9rWokUL5HI5J0+eNJjbuXNn7t27x759+8jMzGTFihWYmppKqz3oo+05V+sQBEEoCktT3UtXUTPDIvMok7QoYDVJQRAEoeSplB0My8zSopDL+KChJ45WZpgp5Gi1EJEgssMEoSQzMTFh7969tGvXTtqWnJwsldopFApkMpkULAMYM2YMAQEBUtBI7/vvv6dy5co0bNiwSI3Xd+7cyaFDh3B2diY4OJiVK1caNXRXKBQsW7aM5cuXExISQrt27ahZsyZDhgwhISGBCRMmGATlYmNjsbOzy/N84eHhnDp1Ks+xmJgYKlSoYNB8XiaT0bixLst25cqV9OjRAy8vLwCsra3zfVxffvklgwYNokqVKoAuC23q1KmcP3+eCRMmSM/1xo0b6dChg7RyY06JiYkMGDCA3bt3c+XKFVatWsXYsWPp27evdO7Ro0fzv//9j/Xr13P27FkGDRqEn58fNWvWBMDf31/qBde/f39MTEzw9/fn7NmzLF68mAULFjBmzBgAVq9ezezZszl16hTnzp3jyy+/5ODBg0UK/gklS7GCYcHBwYDhSg6WlpY4OzsbreSgX+WiTZs20moWK1eulIJf+iVqy5Urh4eHB126dOHSpUv5nluj0ZCYmGhwEwThzaQsZmZY3g30RRaqIAjCm0SfGQbQoYYbbnYWyOUyqXxSNNEXhJIrIyOD4OBgKTNMqVTSvHlzzp8/z4MHD6hZsyZarZY5c+bQtm1bVCoV69evZ/bs2QWuQNmxY0e6dOkC6DKucgoNDSUwMJDatWvj7+/P0KFDuX37NitXriQwMBAvLy+++OILDhw4QHJyMgDVqlVj3rx51KlThypVqrBq1SqSk5Pp0aMH7du3p0WLFtLxw8PDcXFxMThnSEgIn332GeXLl+fw4cN5XnNERAQKhQJXV1ejsR07drB+/Xr+97//Sdty9vTO2WT+8OHDHDlyxGD1zalTp6LRaHj77be5f/++lAEXFhbG9evX81zR0cLCgvT0dAYMGECDBg2YNm0an3zyCYsXL5bmjBw5ktGjR/Pf//6Xd955By8vL4N+adevX+f+/fuArgRy9+7dhIWF0bRpU6ZOncqsWbP44IMPAF38YcOGDbRp04a2bdty9uxZdu3aZRAkFd4MxVpNUqVSIZfLpSZ2enmt5LB//34+//xzfvzxR5o3b8769evp1asXp06dokqVKnh6enLy5EmUSiX37t1j5syZtGjRgsuXL1OmTBmjc8+YMYMpU6Y8wUMUBKGkscgucUxJywDg4M0ovlx/ke/fr0m76m5G83N/229mIi9yWY0gCIJQMuQMhn3U1Fv62d3egrvRyaKJviCUYHv27OG9995j4cKF9OvXD3t7e+7du8fo0aNZt26d1MPL3t4euVxOREQE27ZtY/ny5YUe+/jx49ja2rJt2zYpU6tfv36sXbuWJk2aMGrUKPr27YulpS7w3r59e9q2bcs///zD4sWL6dSpE25ubly9epVBgwaxb98+Zs2axdChQ7l48SKDBg3C3d2d6dOnExERgZWVFZs3bwaQembdu3ePf//9l4oVK9KlSxcOHjxIo0aNmD17Nrdu3SItLQ0zMzO0Wi3//PMPPj4+RiWM//zzD7179+aPP/7AzU33fjojI4OzZ8+iVCo5f/48rVq1kua//fbbRq2SnJyc2LFjh9FzNHLkyHyb6ZuZmbFmzZoCn2OZTMbUqVOlHme5nT592uB+zZo1OXLkSJ5zmzRpYlTVJryZihUMMzc3Jysri4yMDBSKx7vmtZLDuHHjGDhwIKNHjwZ0jQH//fdfpk2bxh9//IGNjY20akbNmjVp2bIlnp6erFmzRkphzH08/bFAl06ZO11VEIQ3g1JaTVKXav3P5XCiVWlsvRSeZzBMXyapNDMhJS0Tc1MRCBMEQXjT1PCwo56nPR4OSup5Okjb3e3EipKCUNLt2LGDbt26UbduXdLT0zl//jwRERF89913DB48mGHDhvH9998DUL16dUaNGsXMmTOLdOzRo0dz8+ZNnJycWLRoEQDTpk0jICBAav6em1wux8/PDz8/P1JTU4mNjcXS0pLAwECUSiXW1tYEBQXh6+vLZ599xqRJkwgICGDixIkAuLu7s3DhQqm/maWlJbVr12bBggXS6pAA3bp1Y/78+VhaWkpZXfb29vz2229G17R+/XoWL17Me++9Z3Cd//3vf0lNTcXFxcVgTBBedzJt7nzOAhw7doxmzZpx9+5dvL29AV35orW1NevXr6dr167SXEtLS37++WeGDBkibRs7diw7d+7MtxyyQYMGNGrUSKr3LUhiYiJ2dnYkJCRga2tb1IcgCEIJcOxONP1+P0VlV2t2f96Cnr8d58y9OKq42rDr87eN5teYtAuVJoO3Kztz+NYjXGzMOf1Nm5dw5YIgCMKrZs6eW/xv3236NvRkRreaL/tyBOGVUZI+b2VlZSGXy9FqtchkMjIzMw0a5eszp141kZGReZYz5kX/GAVBKJpi/W+pV68elpaWBis5HDp0CJlMxttvG34A9fDw4Nq1awbbLl++nG90PD4+nlu3blGxYsXiXJIgCG8gCykzTNczLPiRrs9C0CMVaRmGjTlVmgxUGl05ZePypQz2FwRBEAR9zzCRGSYIJZc+SKTPjsoZCANeyUAYUORAGCACYYJQTMUqk7S0tGT48OF8++23eHp6Ym1tzahRoxg6dCj29vZ06NCB//znP3Tr1o1PPvmEr7/+Gh8fHxo1asSmTZvYuXMn27ZtA2D79u3cvHmTli1bEhcXx+TJk7GxscHf3/+5PFBBEEoOZXbPsNS0LBJS0olJTgMgI0vL3ehkqrg9XmY6KlFXImltrpDKYhysXs03PIIgCMKL5y4a6AuCIAjCG6dYwTDQLSObmppKr169MDExoX///syePZv09HSuX79OWFgYAJ9++ilarZaAgABCQ0OpVKkSq1evplOnToCuVnnRokV88803ODo60rJlS/78808cHBwKOr0gCAKW+sywtAyColUGYzcjkwyCYRHZwTAXW3MalSvF9K41qFPW/oVdqyAIgvBq83B4nBmmL6ESBEF41hITE7GxsRGvMYLwiih2LqW5uTnz5s0jISGB2NhYfv75Z8zNzTE3N+f+/fvSKhEymYzPPvuMoKAg1Go1ly9fpm/fvtJxmjZtyvXr10lNTSUkJIQ//vgDd3f3Z/fIBEEosaTMsPRMqURS71ZEksH9qETdSreuNhbIZDL6N/aihofdi7lQQRAE4ZVX2s4CgJS0TOJS0rkWlkhWVpFb6gqC8BqbOnUqycnJhU8EVCoV//77L2vWrGHy5Mmkp6cbjG/evJmWLVtK9zt06MDOnTul+5UrVyY0NLRI59q0aZNRy6Gi+PTTT41WVvzhhx/yXclREN5kxc4MEwRBeNkssoNhWVq4GZEIgJlCTlpGFjdyBcMiszPDXG3NX+xFCoIgCK8FC1MTnKzNiFal8dWGS+y5FklAj1r0ekusWi4IJcXBgwe5d++e0faNGzdy+/ZtWrdubTRWu3Ztrly5wvLly7l58yYhISEG4z179qR69erFvpbo6GhcXFwoVUrXyzY9PZ26dety8OBBaU58fDyDBw/m+PHjAEyYMIEFCxbg6Ogozbl//z4nTpygTp06gG4RgGXLlvHll18W+5oKcv78ecaMGcOJEydQKBR88803jB071mheRkYG48ePZ8WKFSQmJvL222/zv//9jypVqhjNDQoKomrVqgwYMIDff//9mV6vIBSV6LInCMJrxzJHA/wrobpgWIvKzgDciswdDMvODMv+5l8QBEEQctP3DdtzLRKAU8GxL/NyBEF4xtRqNSqVipEjR6JSqaTb4MGDadSokcE2/S0tLQ0HBweqVavG8OHDady4MaNHj+bmzZukpqZSvXp1Jk+ezNdff210viZNmrB//3569+6Nk5MTTk5OREVFUatWLS5evIitrS3R0dFER0fz999/S/u1bt0ab29vpkyZwo0bN/D29qZ169ZotVpGjx7NjRs3pFvuQNzu3bupVq0ahw8fls7p5OTEpEmTWLx4scG2Jk2aFOl5u3DhAs2bN6dGjRocPXqUXbt2Ub9+/TznTp8+neXLl7Nw4UKOHDmCXC6nY8eOZGVlGc0dP368UWadILxoIjNMEITXjqmJHFMTGemZWq6GJQDwbg039lyL5EFsCilpGSjNdC9vkUnZPcNsRDBMEARByJu7nSWXQhKk+0GPVAXMfnJ3olSsOnWfT1pVopRYzEUQXpgOHToAMHHiRP76668C5zo7O7N+/Xrpvp+fHwC3bt2iUqVKVK5cudDznThxgg4dOvDZZ59J53Zzc+Ps2bNYWOT/nnTDhg0MGDCAnj178u677wKgUCiYNm0aS5cuZdWqVdLcoKAgg31XrlxJjx496N+/P/3795e2//DDD9y7d4+5c+cWet25jRgxgp49exIYGFjo3DNnztC/f3+6dOkCwMyZM6lduzYxMTE4OztL8/bs2cOpU6do0KBBsa9HEJ4lEQwTBOG1VLW0LZdCEkhUZwDwllcpnKzNiVZpuB2ponZ2k/xkjW7cxkK83AmCIAh502eG6QU9Uj2XZvq/HQrir3MhZGVpmfJejWd6bEEQCrdy5UrUanWBc5ycnKSfJ02aRExMDACnTp3i/v37XLlyBYDOnTs/8+uzt7fHzMwMGxsbnJyc0Gg0xMbGEh4ezowZM/D395fmvvXWW9LPISEhbNq0ieHDhwPwn//8h23btiGXy0lNTSUrK4vNmzej1Wrx8PCQ+or16tWL9PR0Nm3aZHQtt27d4vjx4yxcuLBI1967d2++++47RowYQenSpfntt9945513DAJhycnJDBs2jMDAwCIF2ATheRJlkoIgvJYCetTCwlT3EmZmIsfDwRIPe903bdEqjTRPnZ4J6HrCCIIgCEJe9CtK6iWpM3iU42/JsxKRoPsQvv1yOBmZxqVDgiA8X0OHDi1wPCIiggkTJkj3K1SogI+PD+XKleP27du4urri4+ODj4+PQf+u/IwYMYI6depQp04dKagGupUly5QpQ5kyZejZs6fBPlFRURw9epSPPvoId3d31q9fz/79+5k1axY1atSQblevXpX2+eGHHwzKDhMTE/npp58ICQkhJiaGuLg4QkJC2LdvH7Gxj8vAy5cvn2dPL4CTJ09ibm7OvXv3qF69Oq6urvTq1YvIyMg85w8YMID69etToUIFrKys2LhxIytXrjSYM2rUKKpXr07Xrl0Lfe4E4XkTqRKCILyWfNxsmdqlBmM3XKK6hy0mchlW5rqXNFV2NhhAarruw4aFQsT+BUEQhLyVzQ6GudiYY6aQExKXSvCj5GdeYq//siZalcbJ4FiaVXIqZA9BEJ4lS0vLAssFNRoNNjY20v0BAwYAMHfuXDIzM6lfvz79+/fH3t4egG3bthV4vh9//JE2bdoAusAagFarxc7OTmrIf/DgQSZPngxASkoKQUFBuLi40KVLFwICAvj222+pW7cuO3bsQKVSsWjRIrKyspg6dSqurq7cvn2bVatWUa1aNYNzf/LJJ0b9zNLT07G0fBz8nzlzZr7XHh4ejqmpKT/++CPz5s1DrVbzySef0KdPHw4cOGA0f+rUqRw8eJD169fj7u7O9OnT6dy5M8ePH8fCwoI///yTrVu3cunSpQKfM0F4UUQwTBCE11avBmUp72wlfaOv7xOWkpYpzdGIzDBBEAShEO/4uDCoWTlaVnFmydG7hMSlEvRIRePyhWd+FMejpMfZZlsuhopgmCC8QFOmTKFZs2aAbnXGH374QRrr1asXx44dk/pwPXjwAE9PT0DXfP+nn37inXfeYcWKFfzxxx/s2rULNze3Qs/53//+F6VSCSBlhmVkZGBqaprnfKVSyd27dzEz0/UU1Gg0ZGRkSL3CrKysUCgUZGRksHXrVkqXLs25c+eYNGkSmzdvNjjWL7/8Qp8+fQy23bhxQ+qBVpiMjAxUKhXLli2Tnos5c+bg5+dn8PwAxMXFMWPGDFasWEGPHj0AWLduHWXLluWPP/6gQYMGDBkyhA0bNuDq6lqk8wvC8yaCYYIgvNbe8i4l/Wxtrgt4JefIDNOXSVqaiWCYIAiCkDdTEzkT/XRZFYduPuLAzUcERSU/03NkZGYRm5Im3d9xJYKp79UQX9YIwguyZ88ejh49ysCBA4mIiCAuLo5mzZphb2/PpUuX2LVrF3v37mXMmDHExsZKwZ7vvvuO9u3bk5qaSq9evQgKCqJp06bs3r270HMuWbLEoIE+6LK/0tPTGThwIKArzdSLiIigcePGRsdZvHgxHh4emJgYvl6sXbuWxo0bU69ePaNg2Ndff22U+aXRFL3828XFBTMzM8qWLStt0y8eEBkZaRAMu3XrFmq1mjp16kjbrK2tqVSpEpcuXeLYsWOoVCqDPmtpaWkcPXqUP/74g5s3b+Ll5VXkaxOEZ0EEwwRBKDHyKpNUS2WS4sOGIAiCULgKLtYABEc/2xUlY5PT0GpBLgNXWwvCE9SsPxfCh43FB0BBeBHS0tJo06YN165dY/DgwZiamrJv3z769u0rzRk2bJhUAglw4MABfv75Z65cucLEiRMBmD17NqampmRmZuY+hcTJyYnExEROnDghZYHFxsZSq1YtfvzxR+rWrcuyZcsAwzJJNzc37t27Z3Q8e3t7zpw5k282Wl6LfcycOfOpMsN8fX1JS0vj/Pnz1KtXD4CrV68ik8moWLGiwVx3d3cArl27RqVKlYDHJZ/du3dnwIABfPXVVwb7DBgwAC8vL6ZNmybtLwgvkmiiIwhCiaEPhhlkhmXoyyTFy50gCIJQuPJOVoBuRclnKSq7RNLR2pzhLXW9g+buv406PROtVvtMzyUIgjETExP27t1Lu3btpG3JyclSGaNCoUAmkxlkfI0ZM4aAgACD7CiA77//nsqVK9OwYUN8fX2NzhUdHU1aWhqnTp0iOjqaxYsXk5CQQGxsLFZWVlL/sLxs3boVb29vg1tiYiINGjQw2FZQ7zPQ9QzLfZzWrVsbzBk3bhzjxo3Lc//q1avToUMHBg4cyKFDh9i9ezeff/45gwcPxsHBgaCgIJo0aUJQUBBly5ala9eufPbZZ2zfvp0zZ87Qr18/MjMz6d+/P6VLl5YWHtDflEoldnZ2+Pj45Fs2KgjPk/h0KAhCiWGV3TMsOUfPMLGapCAIglAc+sywkLhU6W/Is6Bvnu9kbU7vBmVxt7MgMlFD+8DDVBj/D+vOPnxm5xIEwVBGRgbBwcG0adOG3bt3o1Qqad68OefPn+fBgwfUrFkTrVbLnDlzaNu2LSqVivXr1zN79uwCV6Ds2LEjXbp0ATAKav/44490796dzMxMVq5cyfvvv09aWhrbt2+nefPm+R6zc+fO3Lt3z+Bma2vLmTNnDLaNHDmywMf8yy+/GB1n3759BnOCgoKkPml5Wb16NTVq1MDPz48+ffrQoUMH/ve//wGQkJDA9evXSUhIAGDlypV07NiRgQMH0qJFC+Lj49m/fz8eHh4FXqcgvCyiTFIQhBLDKlfPMK1WK5VJmovMMEEQBKEIHK3MsLM0JSE1nXsxyfi42T6T4+qb5zvbmGOuMOHT1pX4euNl7sekALDjcji93ipb0CEEQXhCe/bs4b333mPhwoX069cPe3t77t27x+jRo1m3bh3+/v7UqlULe3t75HI5ERERbNu2jeXLlxd67OPHj2Nra8u2bduws7MDYNeuXUyfPp1jx45hYmLC0qVLadq0KcuWLWPnzp38/PPPaDQaTE1NuX37NhYWupVrmzRpQnh4uNE59JlhuXuGAdy8eRNzc/Mnel7WrVtX4LiDgwOrV6/Oc6xevXrEx8dL962trfn111/59ddfi3TugwcPFvUyBeG5EMEwQRBKDOtcZZKajCxpTGSGCYIgCEUhk8lwsTEnITWd2OS0wncookdSZphulbge9csQkagmNC6V9edCuBVpXJb594VQftpzi18/qEcND7tndi2C8KbZsWMH3bp1o27duqSnp3P+/HkiIiL47rvvGDx4MMOGDeP7778HdOWBo0aNMmo+n5/Ro0dz8+ZNnJycWLRoEZmZmXzyySesWbOGatV0C3PY2Nhw+PBhxo4dy8cff4ydnR1r167lww8/xMzMjN9++w2AEydOPNXj1Gg0ODk5kZSUxD///GOUPZaZmUliYiJOTk6cOXOGcuXKPdX5BOF1JoJhgiCUGEopGKYra9Gk5wiGiQb6giAIQhFZW+j+niSpMwqZWXTRSbrAmrONLoNDYSLnszaViU9JY/25EELjU1FpMqQvdgDWnw3hfkwKu69GiGCYUKDDtx6RkpZJhxp5N1h/0wUGBiKXy2nfvj0ymYzMzEy6d+8O6FZNXLt2LWZmukD14MGDGTx4sNEx9A3vczt58qTRtjNnzkhZYnr29vbMnDlTygLr06ePUYP7p7F3715A169MEITCibohQRBKDGt9mWSa7sOLvnm+XAamJsar7AiCIAhCXmwsdM2cn2UwTJ8Z5mxtWM5krzTDJTtAdjsyyWDsRoTufkhc6jO7DqHkSc/MYujKc/x31TmiktQv+3JeSXK57mOvftXF3OWG+kDYs5I7EKZXqlQpqWG/IAgvlwiGCYJQYugb6KuyyyRzNs/Pa8lpQRAEQciLTXZmmEqd/syOGZ2jZ1hulV1tALiVIxgWrdJITfcfxqU8s+sQXl/q9Exisn8ncgqLTyU1PZMsLdwIT8pjT0EQBCE3EQwTBKHEsMrVMyw1OxhmKfqFCYIgCMVg+xzKJPPLDAOo5KpbwTJn37CbEY+DGg9jRWbYmy4rS8uHi0/RZOZ+HsYaBkcf5Lh/K/LJg2ELDwcxd/9tQuPF75sgCCWfCIYJglBi6INhKdk9w/QrSYrm+YIgCEJxSGWSmmcYDMvODHPKIzOsSh6ZYTdyBMMik9Roskv/hTfT1kthnLkXR1pGFieDYwzGcgbDcgZRi2vZsXv8sPuW9LsqPHuZma/H/+O0tGe3eIggvKpEMEwQhBLDKkfPMK1WK5VJmpuKlzpBEASh6GzM9Zlhz6ZMUpORSUKq7lh5Z4YZB8NuRiRKP2u1EBYvekG9qTQZmfyw+6Z0/0augNezygyLS9H9jpZSPtv+Wa+yqVOnkpycXKS5KpWKf//9lzVr1jB58mTS0w1fHzZv3kzLli3z3f/cuXNUrVqVlJRXq+w5MDDQaNXJ0aNHM2bMmGIdZ82aNVy4cKFIc9PT07lx4wZ///03AQEBXL9+3WA8Pj5etDgRnjuxmqQgCCWGvmdYllZXIin1DBMrSQqCIAjFoF9NMvEZlUnGqHRZFgq5DDtLU6NxfZlkZKKGhNR07CxNjQIeD2NTKOdk9UyuR3i9/HUuxKBUNnf210ODYJiKrCwtcnnxAgmpaZlSewkHK+Pf0dfdwYMHuXfvntH2jRs3cvv2bVq3bm00Vrt2ba5cucLy5cu5efMmISEhBuM9e/akevXqRb6GevXq4eTkxNatW+ndu3e+85YtW8bgwYOlVSfzkp6eTt++faUVLidMmMB3331X5GuZMWMGX3/9dZ5jWq2WTZs2MW/evDzHb9y4kecKmjt37uSnn35ixIgRRmOurq44OzszZcoUbt68yd27d8nIePz6am1tTdWqVYt8/fmJj4+nQoUK1K1bV1pdUy8tLY2AgACOHTvGjh07DMZOnTrF6NGjOXfuHK6urowYMYKxY8fmeY4lS5YwaNAgg20jRoxg7ty5AOzfv9/o96lTp05s27btaR+e8IyJYJggCCWG0swEmUz3DbpKk5GjTFJkhgmCIAhF96xXk9Q3wneyNs8zSGFrYYq7nQVhCWquhibQqLyjlOFT3smK4Ohk0UT/DXbpYQIALSo7c+jWowIzw1LTM7kYEs+5+3F0qe2Oi23+AZWc4lIeB2ytzUveR0S1Wo1KpeLrr79m5syZ0vbBgwcDuqyv3NLS0nBwcKBatWq0atWKrVu34uvry9ChQ/H09MTCwoLJkyejVqsNjgng7e3N/fv387yWEydO0KdPH6Ptt2/fpmLFigB07dqVv/76K9/HExgYaJCFNXnyZCZMmGAwJzQ0lIoVKxIfH4+5uS4jNSsrC7lcjqlp/gHPY8eOERYWxtKlS1mzZo3B2IABAyhbtiwqlYpFixbRunVrypcvD0CzZs1o1qxZns+lra0tDg4OeHh4UL9+fYKCgggJCeF///sfXl5e2NnZcfDgQb7++us8A21FNWPGDGJjYw22ZWZm8uOPP/Lrr78SERFB8+bNDcbj4+Np3749PXr0YN68eVy6dIn//Oc/uLi4MHDgQKNzxMbG0qhRI/744w9pm729vcG4h4cHBw8elLZZWYkvMl5FJe+VThCEN5ZMJsPKTIFKk0GKJlPqryJ6hgmCIAjF8SSrSaamZfLRstP4uNkyuYthtsijAlaS1Gtc3pGN50P5YfdNAnrURp2ehYWpnKYVnQiOTiYkTjQ1f1NFJulKZN+p4szh24+klUadsktuH8TogmFWZiYkp2Xiv+Q0ieoMlp+4x+rBjSlbSlnoOfTBMAcrsxJZntahQwcAJk6cWGCQCcDZ2Zn169dL9/38/AC4desWlSpVonLlyoWeL2cWmkajISwsjHLlyknbIiIiUKvVeHt757n/5s2bsba2zvf4+swwPYVCwcSJE3FxceHzzz8HkAJg5ubmUpaZv78/1atXzzfrCXSZT82bN6dOnToG2zds2MDdu3fp2LEjNWvWZO/evRw4cIB///0332MBrFq1Cg8PDwB+++03QJf9tnPnTmrVqlXgvsVx7do1lixZQrt27Qx6s6WmprJ06VImTpzIkSNHCA0NNdjv9u3bJCQkMGfOHGxsbKhduzabNm3izJkz+QbDPDw8pMBlXuOurq75jguvDhEMEwShRFGamaDSZGRnholgmCAIglB8Nk+wmuTJ4BhOBsdyMjiWAU28KO/8+IOsvoyttF3+WTpjOlRh97VI/n0Qz8fLzgBQycUGL0elwTFAV8b04+5bHLn9iGUfNcTB6s3p8fQmikzUBVO9nazwKqXkXkwKNyOScKpoTnxKmlTO27ySMzuvRkj3H8am0nvBCXaPblFotldc8pvRL2zlypWo1QX333NycpJ+njRpEjExugULTp06xf3797ly5QoAnTt3LvR8arWa999/H8CgNG/79u2MGzeOtWvX0qpVK6P9ipsZBtCjRw/efvttGjRoQLNmzYz22bp1Kxs2bGDcuHEAHD58WAocabVafv/9dwICAlizZg1nz541KgG9evWqFGADGDduHA8fPiz0OXB3dwfgjz/+kLK+bty4wYMHD6ReZT4+PtSoUSPP/cPDw6lRowYLFy6ke/fuec7Jyspi8ODBfPPNN1y4cMGgpNXa2lrqSXb06FGjfWvXro2Pjw/z5s1j9OjRXLp0iWPHjrF27do8zxUbG2vwO1LcceHVIWqHBEEoUfRv9pJFmaQgCILwhGyfoEzyYki89POfZww/IN6N1jXozhkgy620nSVj2lcBdGVvFqZyPmlVkTIO2cGwHJlhs3fdZO6BO1wMSeDInegiX6PweopK1AVvXG0t8HGzBeB6uG6BBX2JpLONObXL2kv79G/sibONOWEJas7djyv0HPrMMHtlyesXltPQoUMLHI+IiDAoN6xQoQI+Pj6UK1eO27dv4+rqio+PDz4+Pjg6OhZ4rPj4ePz8/FCpVKxevdpgrE+fPgQGBtK1a1cWLFhgtO/u3bul8+R1mzFjhtE+9evX59tvv6V///4kJiYajN2+fRt/f3/mz5+Pj48PAG+//TZqtZrZs2czbNgw1Go14eHhdOjQga1bt0r9yPTS0tIM+pjNmTOHGzduFPgcDB06VMrSKl26tHT9wcHB2NjYSPfLli2b7zEsLS3x8fGhdOnS+c75/vvvUavVRgsBFIWZmRmrV69mypQpmJub89Zbb/Gf//yHd955J8/5MTExLF68GBsbG2rVqkVAQIDBYgoxMTHs3bsXKysrqlatytdff51n6ajw8onMMEEQShSr7GBYSppooC8IgiA8GesnWE3y4sN46ee/zoXwRbsqmCl0X8YE64NhhTTA79/Yi6BHKtTpmXzauhJlHJRcCdX1iwrJDnqsP/uQeQeDpH1CRflkiZaWkUVMsi5Q5WJjThU3G3ZejZCa6OuDYZ6llNQqYweAm60F4ztWJSxezf4bUUX6HdEHw0qV8CxDS0tLqdF5XjQaDTY2NtL9AQMGADB37lwyMzOpX78+/fv3l3pE5dcU/cqVK3Tt2pXq1aszaNAg+vbty86dO6XxiRMnEhsby/bt2/Hz8yMiIoJJkyYB0KtXL9q0acOsWbPo2bOn1JMrpyNHjhAfH2+0/csvv+TPP/9k5cqVvPfee9L2r776io8++ogPP/ww/ycHXRmlVqslKSmJNm3aIJPJ8Pf3B3TBsJyZYRYWFoWWc2q1WqnstnXr1rRu3ZrLly8zatQoGjZsSNeuXSlTpgyAQY+tnOzt7Tl27Fi+5zh27BgBAQGcPHkShaL44Y2IiAj8/Pzo168fgwcP5tKlS4wePZrq1avn2dttypQpfPPNN2g0Gvbt28ekSZOIiorihx9+AGDYsGH07dsXrVbLiRMn+Pbbb7lz506h5bnCiyeCYYIglChW5rrAV84G+uaiTFIQBEEoBn2ZZHJaJplZWkwKWZlPq9VyKUQXtDI1kRGTnMavB+7wcdNy2ClNpcywcs4FB8NM5DKmvmdYKqTv9xSTnEa0SkPArpuALuARkagmRDTWL9H0iy+YmshwUJpRtbQuUHMz0jAY5lVKiW8FRwJ716F2WXuUZgrKOFgCFOl3JDb5cc+wkmrKlClS+eCECROk4AXoAlDHjh0jKEgXaH7w4AGenp6ArtTxp59+4p133mHFihX88ccf7Nq1Czc3t3zPdffuXbp3786MGTNQq9V88cUX7Ny5kw4dOhAaGsrChQs5fvw4tWrVYu/evVIpJoBSqWTatGnMnTuXpUuXFviYrK2t6d+/P8nJydK2DRs24OzsTFhYGADJycnMnTsXKysrgwylvIJY+qwxgLVr1zJixAi6d++OtbU1Go1Gygz7888/AahRowYff/wx+/fvJyIiAoA6derg6OjI9u3b8fb2Zt++fbRr10467qRJk2jXrh0XLlygXr16bNq0iaZNmxb4OPMTEhJC9+7dmTNnDtWqVXuiY8yZMwcHBwcWLlyITCajUaNGREZG8uWXX+YZDMt5nkaNGpGZmcmsWbOYPXs2MpmMChUqSOP169fHzs6OAQMG8OjRI5ydnZ/oGoXnQ9QOCYJQoliZPS6T1C8RbimCYYIgCEIx6FeTBFAVoVQyND6VmOQ0FHIZg5rpsjj+t+82jWfs41JIPKHxusyccoVkhuXFztJUarzfbd5xHiVp8Cyl5L/vVJDOLZRckdklki42FsjlMqqWflwm+ShJI/WSK1tKiUwmo2tdD+n37HEwrPDfkfgUXRakQwkuk9yzZ49U+hcREUFcXBzVq1enadOmXLp0iV27dvHbb79hY2NjsCLhd999R/v27fH09GTEiBG0a9eOpk2bSoGzvHTu3JlZs2Yhl8tRKpUEBATg7+9PcHAwH3zwAf7+/lLz+AYNGkgN/lNTUxkxYgShoaFkZWWxatUq3N3dSUpKIiwsDJlMRkhICCqVCl9fX8zMzLh//z42NjbSrXz58tjY2FCliq7s2snJCQ8PD+zt7Q3mRUcXXGLdsWNHdu3axdSpU5k8eTIajUbKDDtz5gzDhg2jZcuW3Lp1iyNHjtC5c2d69uzJzp07OXfuHAMGDKBTp05cu3ZNOubevXu5desWPXv2pE6dOixYsIBOnTrxzz//FP8fFFi8eDGRkZGMGDECCwsLLCwsWLlyJQcOHMDCwoLDhw8XeoxLly5Ru3Ztg4Uj6tWrR2hoqNHKlHmpV68eycnJBgHN3ONAvquLCi+PCIYJglCi6MskDRvoi5c6QRAEoejMFHLMs0sckzSFl0pefKjLCvMpbcMX7Sozpn0VPOwtSU3PZP7BILRaXbaZ4xNm3UztUh0TuUzKAvqyfRUp4CHKJEs2ffN8F1tdEMLL0Yo6Ze1Jz9Sy6Egw+29EAVA+j6xDD3tdVmFRAqZSZlgJbqCflpZGmzZt2L17NwCmpqbs27cPM7PHj3nYsGGYmDz+EvXAgQP8/PPPjB8/Xto2e/ZsevfubbBiYWH69+9Pt27dqF27NhqNhjlz5hjNycjIwNfXF5VKxZIlS4iOjubLL79kzJgxyGQyNmzYgK+vr1SimZqaio2NDd7e3mi1WqObvrl9ampqnuO5m7zfv3+fc+fOAXDy5El69+5NjRo1uHHjBp06dSI1NRVLS12ANSsri1GjRjF79mxAt6L77du3DQJs33zzjUEpZ2RkJB9++CHTpk1DLte9vr7//vvMmzev0EUN8jNixAiuX7/OhQsXpFuXLl1o1KgRFy5c4K233ir0GB4eHgYBO4DLly9jZWWFnZ1dofufPn0aBwcHSpUqle+4XC43WE1UeDWIT4iCIJQoOXuGaTLEapKCIAjCk7EpRhP9S9nN82uXscfURM6IdyrySauKAOy+Fgno+oXlzDwojndrlmZ+v3pYmprQtKIjfjVL42Gv+1AaGq/7oCu83u7HJNN+zmE2nQ8x2B6VlN083+Zx4/JBzXQfqhceDiYyUUPZUpa0r25cslecMsk3oWeYiYkJe/fuNSjZS05ORqnUBQ0VCgUymUwKlgGMGTOGgIAAowbv33//PZUrV6Zhw4b4+voWeu6dO3dy6NAhnJ2dCQ4OZuXKlQZN1/XnX7ZsGcuXLyckJIR27dpRs2ZNhgwZQkJCAhMmTDAIysXGxhYpWFMQrVZLWFgY69evp0qVKhw9epQmTZrQsmVLbG1tuXLlClu2bKFBgwakpKRIwTCVSsXvv//OmDFjpGMlJCRI/dZSU1Np3LixQdBv9uzZtGzZUlpdU++DDz6gW7dueHh40LNnT6NrTEhIoFmzZpw4ccJozMnJyWhxATs7O5RKJT4+PtK/bUGGDx/OlStX+Pzzzzl//jwrVqzg+++/Z/jw4ZiYmHDixAmaNWtGQoLuS49PPvmEnTt3cvHiRX788UcCAgL4+uuvpQDfN998w+bNm7l06RKLFi3iiy++YOjQoYUuuCC8eKJnmCAIJYqVmS7wJVaTFARBEJ6GjYWCaJWmSMGwC9nN82uXsZe2Na2oy7rIzNIFqp6kRDKndtXd+HdiW0xNZMjlMtyzg2EpaZnEp6RzKTSBmh52JTqYUZJtPh/Gzcgk/rf3Nl3reEiB06hcmWEA79Zww93OgrAEXaDsm45V8/ziTx8Mi0zUoMnIxLyABYX0wbCSmhmWkZFBcHAwbdq04dq1a3z66ac0b96cb775hgcPHlCzZk02bdrEnDlzmDRpEiqVivXr1zN79ux8VxUEXSmhXu6gdGhoKOvXr2fp0qVEREQwfvx4Ro4cyd69e/nyyy+ZOHEiffv2xc/Pj4YNG2JlZUW1atWYN28e48ePp0OHDixdupTk5GR69OhB+/btadGihXT88PBwXFxcnvg5OXXqFB988AFJSUkMHjyY4cOHU7ZsWbKysti4caPR6o2JiYlYWelex+7cucNnn31GREQEn3/+OZ07d2b37t1kZmbSoUMHdu7cyYIFC6hXrx4xMTEsWbKE/v37S33Y8lKpUiW++OILo+cyJSWF69ev8+jRoyd+rAWpX78+27dv55tvvmH+/Pk4Ozvz2WefMXHiRAAePXrE9evXSUlJwc7OjuTkZPr164daraZy5cosXLjQYGECmUzG0KFDSUxMpHz58kycOJFPP/30uVy78HREMEwQhBIl7zJJkRkmCIIgFI++iX5RVpQMeqRrSl3N3VbaVraUEi9HJfdjdFk55ZzyX3GtqCzNHv89szA1wcnanGiVhkVHgpl3MIhOtUrz6wf1nvo8wot3IyIRgHsxKdyJUlHJVZdho+8Z5mr7ODNMYSLn42blmL79Ok3KO+aZFQa6LC8LUznq9CzC49V4FxCQjUvO7hlWQoOpe/bs4b333mPhwoX069cPe3t77t27x+jRo1m3bp3Uw8ve3h65XE5ERATbtm1j+fLlhR77+PHj2Nrasm3bNilTq1+/fqxdu5YmTZowatQo+vbtK2VVtW/fnrZt2/LPP/+wePFiOnXqhJubG1evXmXQoEHs27ePWbNmMXToUC5evMigQYNwd3dn+vTpREREYGVlxebNmwHyXGmyqMqUKcOkSZPo3bu3wSqRn3/+OaArm1QoFNja2nLkyBHi4uIoX7488fHxREdHS5lPcrlcyip78OABFSpUICoqCldXV2xtbYmJiWHx4sV8/PHHhV7T5cuX0Wq1HDt2DFtb3etp6dKl8+3HlRd9X7jijLVr184gYzCnLl26GJx/yZIlBZ5/+vTpTJ8+vdDrFF4+EQwTBKFEsTZ/3EBfCoYV8E2oIAiCIOTlcTCs4MywjMwsYrL7LbnZWRiM+VZw4n7MAyDvnk5Py8PBkmiVhtWndec4c7fwZs/Cq+lGRJL08+5rkY+DYUnZmWE25gbzP25ajrKllDQu75hv+a1MJqOMg5I7USpC4lILDobpyyRLaGbYjh076NatG3Xr1iU9PZ3z588TERHBd999x+DBgxk2bBjff/89ANWrV2fUqFHMnDmzSMcePXo0N2/exMnJiUWLFgEwbdo0AgIC8PDwyHMfuVyOn58ffn5+pKamEhsbi6WlJYGBgSiVSqytrQkKCsLX15fPPvuMSZMmERAQIGUrubu7s3DhQoP+ZsXl4eHBgAED8h0fN24ca9asAcDR0ZFff/0Vc3Nztm3bxscff0yXLl24fPkyCxcuZOrUqWzcuBE7OzumTJnC4MGDAahYsSKffPKJQQZdQZYvX86SJUuwsLBg8uTJT/zYBKEoZNrXtMlAYmIidnZ2JCQkSFFjQRCEVafu882mK7St5opKncGJ4Bj+16cO79XJ+82IIAiCIORl2Mpz7LwawbSuNfiwsVe+86IS1TT8fh8mchm3pr+LifxxYGL7pXBGrP4XgG2fNKOGx9P198ltxOp/2X4p3GDbmW/aSKtPCq+HlLQMqk/ahf5TWZ2y9mwe0RSADoGHuRGRxIqPG/J2ZediH3vg0tMcvPmImd1q0qdh3iVq6vRMfCbuBODS5HbYZvfLK0mft7KyspDL5Wi1WmQyGZmZmQaBpLS0NING+q+KyMhIXF1dX9r5s7KyyMjIMHpu9M+nXs7nMz09HVPTkrsqqVByiEY6giCUKAaZYaKBviAIgvCEilomGZWduVPKyswgEAbgW8ERM4UcS1OTp+4Zlpcy2X3DcroWnvjMzyM8XzcjktBqH7+HufAwnqjs8kj971fOnmHFkXOhhfzos8IUchk25iWzcEgfuNFn0eXOqHoVA2HASw2Ege55y+u5yRkIA8PnUwTChNeFCIYJglCiWJnlLJPUN9AXwTBBEASheIq6mmS0ShescLY2DlY4WJmxenAjVg5qKPW0fJY8HIyDYVfDEp75eYTnS18iWdfTntpl7QE4ePMRmoxMYrNLcHOuJlkcZRx0q+mFxBUQDMvRL+xJVzwVBEF43YhgmCAIJYrSPHs1ybRMNNk9wyxFMEwQBEEoJusiZoY9ys7cya808S3vUrzlXerZXlw2jxyZYY7Zjc+vhYnMsNfN9exsvqqlbWlcXve7cv5hvPS7ZWYix175ZNk2+hUlQ+JS8p3zeCVJkdHzvGVmZr7sSyiStLS0l30JgvDciWCYIAglSs4yyVRpNUnxUicIgiAUj20RG+g/ys4Mc8ojM+x502f9AAxo4g2IYNjr6Ea4LjOsamkb6pSxB+BSSLy0kqSLrfkTZ2zpswcfxuoyw+5EJbHgUBAZmVnSnMfBsFezVPBlU6vVKBRPn9l57tw5qlatSkpK/oHJlyEwMJCRI0cabBs9ejRjxox5Ludr3LgxR48efS7HFoTiEJ8QBUEoUfRlKKqcq0mKzDBBEAShmIq6mmR0ki6Q8DKa1ns5KnG1NaeckxV9GpYF4G5MMsmagq9ZeHVkZWm5HqELYPq42VIru0zyRkQSR2/HAFDB2fqJj1/B2RqZDCIS1TxK0jBu42Vm7LjBjisR0py45DcjGBYYGIhMJivS7Y8//sj3OJs3b8bCwgI3NzeDm6WlZYH71atXDycnJ7Zu3VrgdS5btgyFQoG1tXW+N3NzcwYOHCjtM2HChCI/NplMVuBKmVqtlk2bNtGsWbN851y4cKHI59KvLJmX+Ph45HK50XNpZ2dX4H7FkZSUxKhRoxgyZIjB9pYtW+Z5vRUqVMjzOK1bty4wKJqQkMDAgQOxt7fH1taWwYMHGwQ+MzIyGDt2LG5ubiiVSjp06MDNmzefyWMUnkzJ7JAoCMIbS78CkkqTgamJLt5voRDBMEEQBKF49D3DVEXODHvxgQQLUxN2f9YCZGBnaYqrrTmRiRpuRCRS3+v5lGYKz05kopov118kSZ2BuUJOBWdrTE1kOFmbE63SsPT4XQDeqVL8VST17CxNqexiw83IJI7eecT5B/EAPIh9/CE9NkfPsJJs1KhRRhlQ+cndYD+3Dh06sHnzZoNtOYNT3t7e3L9/P899T5w4QZ8+fYy23759m4oVKwLQtWtX/vrrr3zPHxgYyIULF6T7kydPZsKECQZzQkNDqVixIvHx8Zib64L1+lUgC2pyf+zYMcLCwli6dClr1qwxGBswYAAdO3akTp06pKcXXEKul7vZfm62trZEREQYbFu2bNlTZ4+pVCoCAgKYP38+iYmJfPjhhwbjq1atIjXVsJdet27d6Ny5s9GxduzYwf79+wv8vRgwYADBwcFs2bKFhIQE/vOf/6DValm8eDEA06dPZ/ny5SxatAgPDw8mTpxIx44duX37dqHPkfB8iGCYIAglipO1GRamctTpWaRl6Bvoiz8wgiAIQvHoM8MSC+0ZpitlexmZYQB2Ofo8VSttS2TiI66FiWDY6+DzPy9wPCgGC1M5379fEzOF7v1K7TJ27LsRRXyK7nevlc/TrShY39uBm5FJLDx8l4wsLQDhCY+DADHJuoBuSe8ZJpPJil3u2K9fP27cuEFmZiZvvfUW9evX59133y10v3v37kk/azQawsLCKFeunLQtIiICtVqNt7d3nvtv3rwZa+v8MwLT09Pp27evdF+hUDBx4kRcXFz4/PPPAaQAmLm5ORYWugUY/P39qV69OmPHjs332EuWLKF58+bUqVPHYPuGDRu4e/euwTmL46effmL16tXcuHGDwYMHY21tzd69e4t1jOK4f/8+O3bsYMGCBfz8889G4x4eHgb39+3bx7179/jiiy8MtqempvLpp5/So0cPNm3alOe5rl27xpYtWzh9+jQNGjQA4Mcff8Tf35/Zs2dTqlQpzpw5Q//+/enSpQsAM2fOpHbt2sTExODs/OQBb+HJiWCYIAglikwmw7OUkluRKmmbuSiTFARBEIpJ34Oy8NUkX16ZZG4+pW05cPMRNyOTXvalCIVQp2dy5l4sAOuGNqFWdq8wgNpl7dl3IwqAii7WeDoq8zpEkTXwdmD1qQdSo36AiAS19PO5+3EAVHa1earzvMrS09OL3bzexMSEzz//nNjYWN59911++OEH7O3tuXfvHjt37sTNzc1gfkJCAm3atDHYplaref/99wFddpHe9u3bGTduHGvXrqVVq1ZG5y5uZhhAjx49ePvtt2nQoEGeJY5bt25lw4YNjBs3DoDDhw/Trl07MjMz0Wq1/P777wQEBLBmzRrOnj1L9erVDfa/evUq5ubmZGZmFjkrTE8mk9GlSxfq1avHf//7X4YPH07NmjUBSExMNHouU1NT6dmzJwDh4eHUqFGDhQsX0r179yKfs1q1apw5cwYgz2BYbpMnT2bkyJGUKlXKaHv16tXp1KlTgcEwwCCA2LJlSzIyMvj3339p06YNvXv35rvvvmPEiBGULl2a3377jXfeeUcEwl4iEQwTBKHE8SxlZRAME5lhgiAIQnGVyi4Zi1ZpyMzSYiLPu4G5tJrkS2ign1tlV10mya0IVSEzhZftRkQS6ZlaSlmZUdPDzmCsdnbfMIBWPi5Pfa638sgSDM8OhsWoNFzNXnShaUWnpz7Xq6pv375s2LChWPv4+/uzbNky1Go1MpmMli1bAuDj40NoaCiOjo4G86OiolAqHwcu4+Pj6dGjBxqNhi1bthjM7dOnD5aWlnTt2pXZs2czdOhQg/Hdu3fj4+OT77XFxcUZZajVr1+fb7/9lv79+3Pp0iWDsdu3b+Pv78/8+fOl47799tuo1WoCAwO5c+cOc+fOZdy4cXTo0IGtW7dy5swZg9LPtLQ0LCwsmD9/Pp988knBT14uXl5e3Lt3j4oVK2Jra0vdunVp1qwZWq2WkJAQ3N3dDeYnJSWhVut+Ry0tLfHx8aF06dLFOmdxFp04efIkJ0+eZO3atQbbDx8+zIIFC7h48SIHDhzId3/978KDBw+knmOJibr/V1FRusD2gAED2LlzJxUqVEAmk+Hi4sK5c+eK9ZiEZ0sEwwRBKHG8cnyDKpPpliQXBEEQhOIo46BEaWZCSlomwY9UVMoja0aTkUlCqi5D4lXIDNNn9tyMTEKr1T7xCoTC83c5JB6Amh52Rv9OtXIEx96p8vTBsDIOlrjYmBOVHbiFx5lhx4J0Tfp93Gxeid/h5yVnllWLFi3o1asXI0aMKNYxQkJCCA0NZfjw4YSGhub5/8vFxYVLly5x5coVunbtSvXq1Rk0aBB9+/Zl586d0ryJEycSGxvL9u3b8fPzIyIigkmTJgHQq1cv2rRpw6xZs+jZsyfly5c3Os+RI0eIj4832v7ll1/y559/snLlSt577z1p+1dffcVHH31k1DcrN39/f7RaLUlJSbRp0waZTIa/vz+gC4aZm5szcuRIqffapEmTuHbtGuvXry/8CcwhISGBPXv2sHXrVtasWZNnL660tDQuXLiAp6cnx44dK9bxi2vevHl06dLFoHQyOjqa/v37M2fOHLy8vArcv1GjRpQpU4bRo0ezZMkSMjMzpXJV/WObOnUqBw8eZP369bi7uzN9+nQ6d+7M8ePHpTJW4cUSwTBBEEqcnMEwC4WJ+DAgCIIgFJuJXEa10racvR/H1bDEPINhMdklkqYmMuwsX36/pQrO1shlkJCazqMkDS624gPWq+piSAKg6w+Wm4OVGUNblOdRooYG3g5PfS6ZTEYD71JsvxyOjYWCJHUGMclpqNMzOXLrEQDNK5XcrLCczp8/z6VLl9iyZQsZGfmXQOv7Yf3999/s2bOHzMxM6tSpw+eff05iYiKnTp3C29ubU6dOodVqady4MSEhIVJ54t27d+nevTszZsxArVbzxRdfsHPnTjp06EBoaCgLFy7k+PHj1KpVi7179xITEyOdW6lUMm3aNObOncvSpUsLfDzW1tb079+f5ORkaduGDRtwdnYmLCwMgOTkZObOnYuVlRUqlcpg39xyZqOtXbuWESNG0L17d6ytrdFoNAZBG41Gw2+//cb69esLfC5NTHTvxc+ePcuWLVu4c+cOPXr0oH379jg5OTF79mwGDhxISEgIp06dkkohK1asSFZWVoGPH2DQoEEsX75cur9kyRIGDBhQ6H56cXFxrFu3zmBBhIyMDHr27EmLFi346KOPCj2GUqlkw4YN9O3bFycnJ8zNzfnqq6/YuXMnLi4uxMXFMWPGDFasWEGPHj0AWLduHWXLluWPP/54ZitnCsUj0iUEQShxPEs9DoZZmol+YYIgCMKTqZGdoXMlNCHPcX2JpJO1+SvxxYuFqQneTlYAom/YK+5ydjCsZo5eYTmNe7cqP/Wug+IZZbe3qKzrS+RXyx3z7Eb9kYlqjt6JBqBZpTejb9GpU6eIj4/H3t4eU1PTPG/m5uZScGfZsmVYW1tjYmJCVFQU33zzjcHxNm7cyJ49e4zO07lzZ2bNmoVcLkepVBIQEIC/vz/BwcF88MEH+Pv7U6tWLQAaNGhAhw4dAF2vrBEjRhAaGkpWVharVq3C3d2dpKQkwsLCkMlkhISEoFKp8PX1xczMjPv372NjYyPdypcvj42NDVWqVAHAyckJDw8P7O3tDeZFR0cX+Fx17NiRXbt2MXXqVCZPnoxGo5Ga8oMu4BcVFUWLFi3yfS5NTU05ePAgAFu2bOHRo0fY2tqya9cuo9U4L1++zLx584r+j5lt+vTpXLlyRbrlzIgrii1btmBhYUHr1q2lbceOHePgwYOsW7cOCwsLLCwsGDJkCJmZmVhYWDB9+nSj4zRs2JCgoCBCQkKIjo7Gz88PuVxOrVq1uHXrFmq12qCnmLW1NZUqVTIqaRVeHBEMEwShxPFytJJ+tlCIlzlBEAThyVR3twXgSljBwbBXqbysir5UMkIEw15VKWkZ3I7S/fvklRn2PPSoX4YVHzfkm05VKW2ny+45dieG8AQ1ZiZyGnq/GauPDhs2DK1Wm+/t0KFDuLm5SZlhmzZtYvLkyQDI5cbvKSMjI42av+elf//+dOvWjdq1a6PRaJgzZ47RnIyMDHx9fVGpVCxZsoTo6Gi+/PJLxowZg0wmY8OGDfj6+mJvbw/oAmc2NjZ4e3vn+VgePnwozctr3MnJMBvw/v37Ug+rkydP0rt3b2rUqMGNGzfo1KkTqampWFpaSvN9fHwKfC4zMzNRKBSUKVMG0JUJzp8/HxcXl6d6LnMrXbo0Pj4+0s3Ornj/pzZt2kTHjh0xNX2c3dugQQOuX7/OxYsXuXDhAhcuXGDq1KmYmJhw4cIFhg0blu/xPDw8sLa2Zt68ebRt2xZHR0epJ5q+0T5ASkoKQUFBRqtaCi+O+JQoCEKJ42Fvib7PsYVYSVIQBEF4QtXddR+qroYmkpWlNRqPVr06zfP19H3Djt6JptdvJ5i7//ZLviIhtyuhiWRpwc3W4oWVssrlMt6u7Iy1uQK37GDYpvMhANT3chCZ9NkePnyIt7d3keampaWxd+9eTp06hUajQSaTGWRO5bRz504OHTqEs7MzwcHBrFy50mhFRoVCwbJly1i+fDkhISG0a9eOmjVrMmTIEBISEpgwYQLjx4+X5sfGxhY78JObVqslLCyM9evXU6VKFY4ePUqTJk1o2bIltra2XLlyhS1bttCgQQNSUlIMgmGFCQ8PJzMzE09PzyLN3759O3fu3JHKO83NdRm3CQkJNGvWjBMnTjzRYyzMoUOHaN68ucE2pVJpEGDL2cDfx8cHJycnTpw4QbNmzUhI0H1Zsnr1as6cOcOFCxcYO3Ysf/31F7NnzwagbNmydO3alc8++4zt27dz5swZ+vXrR2ZmJv37938uj0sonAiGCYJQ4pgp5Ljb6/5Ym4tgmCAIgvCEKrlaY2YiJ0mTwcO4FKPxnGWSrwp9MOzgzUecvhfL0mP3Xu4FCUYu6Zvnv6CssNxK2+neI525FwdA4/KOBU1/o9y4cYNKlSoVOOfOnTt4e3szduxYatSowaNHj6hfvz6PHj3i5s2b0rzQ0FACAwOpXbs2/v7+DB06lNu3b7Ny5UoCAwPx8vLiiy++4MCBA1LPr2rVqjFv3jzq1KlDlSpVWLVqFcnJyVKPrRYtWkjHDw8Px8XlyRdYOHXqFBUrVmTZsmUMGjSI27dvM2rUKHr16sXdu3dZtGgRlStXluYnJiZiZWVVwBEN3bhxA09Pz3wDhAC///47AwcO5K+//uLMmTO0aNGCOnXqsG7dOq5evYqXlxcpKSlcv36dR48ePfFjzU9QUBDx8fHUrVu32Ps+evSI69evk5Ki+9tw8OBBWrduTbNmzfj33385ePAgNWvWlOavXLmSjh07MnDgQFq0aEF8fDz79+8XmWEvkWigLwhCieTlqCQkLhULUxHzFwRBEJ6MqYkcn9I2XApJ4GpYokEZPsCDWN2HICcbs5dxeXmq4mbYFDsmOY2ElHTslC+/wb+gExKXCkBFF+MG5i+CPjNMr1H5N6NEMrfU1FTu3btHqVKlsLS0JDg4mOXLlzNt2jSDOREREZiZ6f6Pq9VqDh8+zMyZM0lJSWHbtm04OTmxdOlSWrVqxdixYxk7diwffvgha9eupUmTJowaNYq+fftKWVXt27enbdu2/PPPPyxevJhOnTrh5ubG1atXGTRoEPv27WPWrFkMHTqUixcvMmjQIGn1wYiICKysrKR+W3mtNFlUZcqUYdKkSfTu3dsgYKVfBfH+/fsoFApsbW05cuQIcXFxBZ7v2rVr2Nvbo1QqSUpKIiAggHfeeUcaV6vVyOVyYmJiMDMzIysri4sXL/LLL7+we/duNm/ezFtvvcX7779P//79+fvvv/n1118pXbq0wQIDT0Lftyy3ChUqoNUaZ/3mZeDAgQwcOFC636VLF4PrWrhwIQsXLsx3f2tra3799Vd+/fXXIp1PeP7Ep0RBEEokz1K6DywWCpEZJgiCIDw5falk7ib6cclpbLsUDoBvhVdnJT4vRyupQbre3ZjkfGYLL0NCqq48zuElBSjdcpRmmink1Clr/1Ku42VTqVTUqlULt/+zd99RUV1dA4d/M/QOgiCCih0VSzTWGLvYjRp7ELE3YonG+CZGjSZRY42aqLHEGmNiC/bYe489WBEsKAJK78x8f4xcmTA0NZ9i9rPWrMXcc++5547o+87O3vsUKYKdnR316tWjefPmemVrH3zwAWXKlKFLly7ExsZSokQJhg4dSvv27Tl06JDSd6tPnz4cOnSI5cuXc+zYMaZMmcLdu3c5evQoffv2zVJeqFaradu2LZs3byYyMpIjR45gYWHB3LlzuX37NoMGDeL27dvUq1ePFi1asGHDBpYvX46rqyu2traMGzeOn376CSOjF///uW5ubvj6+mabufW///0Pd3d3bG1t8fX15Ycffsgxy6tHjx64u7vj4OBAqVKlSEpK4ptvvlHGZ8+ejbm5OSYmJnh5edGmTRuaNWuGnZ0df/31F++++y4AdevW5ezZs0RGRkrgSPyrVNq8hkLfMDExMdjZ2REdHY2tre3rXo4Q4g2z6NBtpu28RuPyhfm5T63XvRwhhBAF1MrjwUwMuErzii4s8X1XOT5/301m7blBpaK2bPu4/huxm2SGTX/dJyQygeO3IzgT/JQ53arS8R33170s8UzfFWfYf+0x0z+sTLeaeeun9CrtvvqIQat1jdJrlSzEb4PqGjzvv/R9KzU1FWNj41z/HkdERGRpPP/PeTI3Yn9ZYWFhuLi4vLL58kuj0ZCWlqZkxuVFWloaKpUq10BdVFQUNjY22Z6n0WgAwxsXCPEqSJmkEOKt5F3RhY3n7tOuatHXvRQhhBAFWLFCuoyOB89K2wCSUtNZcTwYgEENS79RgTCATtV1ga/HsUmcCX7KnXDJDHuTRCWkAGBn8Xoyw1wzlUnWKfnfLJH8p7wGsHIKhOVnnrx6nYEw0AWi8hMIA5SdOHOTsStmTvcW4t8kwTAhxFupVGFr9nzSMPcThRBCiBy42VsC8CDqeTBsb2AYkfEpuNlb0NqryOtaWq5KOulaBgRFSDDsTZJRJmln8Xp6zWXuGVZbmucLIf6jJBgmhBBCCCFENtwcdJlh0YmpxCalYmNuwrFbuqbJrbyKYGz05mYvlHTSNWi/I8GwN8rzYNjryQxzsjKjjLM1iSnpVC/u8FrWIIQQr5sEw4QQQgghhMiGtZkx9pYmRCWk8iAqEc8iJpy4HQFAvTJvdlZNRmZYcEQ8kXHJhDxJkODHa6bVap8Hw15TA321WsVW//qkaTRYmMpGQy8qOTk5x4byb6P/4jOLt9eb+5+yhBBCCCGEeAO4OzzvG/YgKpHgyASM1CpqerzZ/ZaKF7JErYL4lHTaLzhGpx+Pc/rOk9e9rP+k+ftuMnbDRRJS0klN1+1fZv+aMsMALEyNsDF/ffcvaOzt7YmIiFDeP378GCcnJ8LDw/M8R0REBN99912+zj969CjLli1j4cKFWcZHjhzJpEmT8jxffo0cOZKZM2fqHfvwww//tR0eixQpwv379196nuDg4Dz1cRw4cCA///xzrnOVKVPmpdck3kwSDBNCCCGEECIHbva6YNj9p4mcuK0rkazibvfGBxNMjdUUK6Tf82zz+Zf/sinyJ12jZe6+m/x29j5/3X0KgLFahaVkZf2/ydjhMK8vc3PzHOfbtGkTZcqUoXDhwgbHf/31V1asWKH32rJlC5MnT+abb77JMrZixQoePnzIZ599Rr169XB0dKRw4cK8//779O/fn4kTJ+b5WbVaLR4eHqxevRofH598Pfevv/6a7bwxMTHs2bOH+vXrZ3vOli1b8nyvr7/+Ott5Lly4gImJCUWKFNF7WVtb612nUqkIDg7O82eT2fbt27G0tMTa2hoTExPMzc2xtrbG2tqau3fv5nu+S5cu4e3tjaWlJUWKFKFPnz5ERkYaPDc4OJi2bdvi5OSEvb09H3zwASEhIXrn/PHHH3h5eWFmZkalSpX4888/X+g5RfYkGCaEEEIIIUQOMjfRP55RIln6zS6RzJBRKplh15VHpKVrXtNq/jv2/B3GBz8cI+BiKOGxyaRrdNlgN8LiALC3NHnjdiF9mxkbG5OYmJjl5efnx5gxY/SOxcfHEx0dneN8GzZsIDY2lu7du+u9+vbtC0B8fDzXr19n2rRpxMXFERcXR1JSEtOmTcPOzk45lvmVnp6Oi4sL9erV43//+x+Ojo6sXbuW4OBgHj58CECjRo3YtWtXjmvbu3cvUVFRdO7cmZ9//jnLM+/btw8XF5csz5yYmEjXrl2znTcgIIC0tDSmTp2a5bnPnDkDwAcffEBqamqeXl988UWOz1GpUiUePXqk9xozZkyO12QnKSmJxYsXk56eDugCV0+fPqVTp07ExcVRq1Yttm/frvxZFC9ePN/38Pf3p1GjRpw8eZJly5Zx6NAhfH19DZ579+5datasya5du9i0aRNBQUF6n/3Zs2f58MMP6dGjB8ePH6d+/fp88MEH3Lx584WeXxgmPcOEEEIIIYTIQUaZ5P2nCZy/GwVAvdJOr3FFeVfKyZqD18Mp52JNZFwKkfEpnAiK5P2yhjNaxMubsu1vlh29A8CakyEUf5adB3AzLBYA29dYIvlfFR4eTufOndm5cyeFCulKnI2MjDAyMlIywQIDA+nQoQPHjh3LtjfWnTt3OHLkCGPGjMHE5PmfY2RkpJJZ1a9fPy5cuMCiRYvYsGFDjutq3ry5Ehj65JNPlOMLFiygevXqlChRIl/PuWzZMnr06IGFhe7frd69e9O5c2c6deoEgKmpbhfTjGfWarV4e3vTt29fevToke28y5cvp02bNnh6ema5X69evQBdppaxcf5CDGPGjOHgwYNERkbSpk0bSpQoweTJk/M1R05SUlLo1KkTjx49olu3btjb27Nr1y7q1KmDiYkJ6enpXLhwAS8vL+WawYMHU6dOHRo1apTn+6xdu5ZixYoBUKVKFaKjo+nVqxcJCQlYWlrqndugQQMaNGigvJ8wYQJdu3YlOjoaOzs7vvvuO1q0aKH8XixatIhDhw6xcOFCZs+e/RKfhshMgmFCCCGEEELkIGNHySM3I4hNSsPcRE2NEgWjEX2vuiV4HJvE0EZlWHMqhF9O3WX7pYcSDPuX3H+aoATCQNdn7lF0ovL+5mNdZtjr2knyv6xYsWIUK1YMHx8ftm/fniUzLy0tDT8/P95//32cnHTBbm9vbw4fPkxycjLu7u6UL1+eBg0a0KFDB7755hu9669evcrmzZuV96VLl2bJkiW5rqtChQoAPHnyhAkTJijHnzx5wtdff429vT2gHyjLzpMnT9iyZQvHjh1TjnXv3p1evXrh5eVFuXLlslyzYMECrl69StOmTQFYvXo1AwYMUEpLx48fz9y5czl9+jTBwcHKZ5Nh+/btmJmZkZaWRlpaWq5rzEytVuPn50fbtm3p1KkT48ePVwJKV69epUiRInrnx8XFMW7cOL1j6enp2d43Li6Onj178ujRI/bt24e9vT1arZbly5dTp04dAM6dO4dGo+HAgQMAVKxY0eBcCxYs4LvvviMwMBArK6ss4xnrzmBubo5Gk7cs3PT0dMzNzZV5//77bzp06KCMq1QqGjRowOnTp/M0n8gbKZMUQgghhBAiBxmZYbFJui9czSq4YG5SMPo9lXSyYkHP6lQsakvbyq4A7L76CM2zsj3xat16FuyyNdflHDyKSeL+0+fBsBvPMsNeZ/P8/7IlS5Zw8eJF5s2bl2VsxIgRJCQk6I39+eefJCUlYWdnx/379zlw4AA///wzffr0oWPHjjx9+lQ5NyUlRa/X2IMHD3LNcNq3bx/r1q0DwMTEBE9PTyXzKjY2lpIlSyrHMjK9crJmzRo8PT2pUaOGcqx9+/b07NkTHx+fLEGjY8eOMW7cONauXYuzszMAvXr1IikpCX9/f6ZOnUpSUhKHDh1iwIABTJkyJUvvqoznHjduHBYWFvl6eXt74+XlRaNGjTA1NaVu3brUqVOHSpUq8eDBgyxlknfv3mXYsGF69y9TpgwmJibKK8Pt27epW7cukZGR7Nu3DwcH3X/A2LlzJ9evX1fO27lzJ+XLl+fgwYPMnDmT33//3eBnW6RIESpUqJBrPznQZdstW7aM2rVrZ8kKyyw9PZ2//vqLKVOm8OmnnypZdY6Ojll6iMXExPD48eNc7y3yTjLDhBBCCCGEyIG7vf6XmXZVi76mlbycmiULYWqs5mlCKnefJODhlDW7QbycOxHxANQu5cjB649JTddy8f7z/lMZAVXJDHs9HBwcmD9/PsOHD2fIkCHK8YsXL/L7779z6tSpHIMXdnZ2/PLLL7Ro0YKff/6Z5s2bs3fvXuzt7UlJSdErrVSr1URERLBgwYJs54uIiKBbt24A2NjY4O/vD0Dnzp0BaNiwIU2bNs1zf7lly5bRv3//LMenT59OpUqV2LVrl5JpBjB8+HCmTZtG48aNc5x35MiRFCtWjLNnz9KpUyc2b95M8+bNAZTnnjlzprL7ZO/evXF2dmbGjBl5WneG8PBwgoKCmD17NsePH0etzpq7k5ycrBeEvHPnDh4eHsr7jIb6NWvWpFu3bnz//fdKaSjA3Llz9Uoif/nlF+bPn4+3tze+vr5UqFDB4C6hnTt3Vv5ccpKamsrQoUM5cOAAhw8fzva8AQMGsHz5cjQaDb169dLLeOvcuTOffvopPXv2pFmzZmzbto0tW7ZQsmTJXO8v8k4yw4QQQgghhMiBrYUx1ma6/4ZsY2ZMw3IFs8TQxEhNhSI2AFwJzblBuHgxGcGw0oWtKfpsF9JzwU+ynCfBsP9fycnJSnN0b29vjhw5QkpKCmlpaaSmplK6dGnOnTtH4cKFlfNSUlKyzGNkZET79u1RqVSsWrUKGxsbVq5cqdwjI2soOTmZb7/9Fm9vb6pXr87MmTOVDC9PT09+/vlnPDw8ePfdd5Xm+BkuXrzItWvXKFasGKNGjcLHx4fU1NRcn/Hs2bNcv34dHx8fQJd1lPEsarWaw4cP06hRIxITE9FqtcTFxbFlyxb69OmjnBcfH29w7tq1a1O0aFHat2/P7NmzGTdunFICmPm5AR49esSGDRsYMmSIUjpp6JXhwIEDjB07lqioKBo2bMj69euJi4vjt99+49GjRxw4cIDVq1cr2WGxsbG5fhYAM2fOZOHChXqBMI1Gw7Vr1xg7diwAe/bsITk5WenLdvz4cd555508zW/I/fv3adSoEdu2bWP//v28++672Z47efJkzp8/z5YtW7h79y41atQgJiYGgKFDhzJgwADatWuHmZkZX331FR988IGSvSdeDQmGCSGEEEIIkQOVSqWUSrbwKlJgSiQN8XKzA+DyAwmG/RuCwnXBhFJOVrg9C4aFRidlOc/O0jTLMfHvGTRoEDY2NsqrVKlSSiBr9uzZ2NjYULx4cb1zcuvRZWZmxrZt2yhWrBjvv/8+jx49UjLDUlNTefToEStWrOD06dPcunWLSpUqUbZsWUxMTAgLC+Pu3bssWrSIq1evKnNqNBpGjBjBl19+iVqtZsWKFTx48IAOHTqQkJCQ43qWLVtGp06dlHLAI0eOGHxmb29vHj9+bPCZXVxccv0sBw4cyJIlSxg6dCiLFy8mOTlZLyPuwoULJCQkULp0ab3yxX++7tzR9db79ddfSUtLw8zMjCtXrvDDDz/o3e/gwYPKxgT5kbGzZ2ZqtZpt27Ypn9E333zD5MmT6dOnDx988AFpaWmUL18+3/cCuHHjBrVr18bGxoaLFy8qPcmy4+rqSpUqVfjggw/YuXMnwcHBynMaGRkxf/58YmNjCQkJ4cKFC8TFxb1UoE5kJcEwIYQQQgghcvF+WSdMjdV8VLv4617KS8kIhl19EPOaV/J2ysgMK1XYSgmgGiKZYf+/VqxYgVarzfLq168fn332mcGxf5Y3pqens2XLFkDXv2nu3LlUrlyZjz/+mDZt2gAofb00Gg3nzp2jWbNmhIWFKWM7d+5UAkdBQUHMnDlTrwRyypQpREdH06VLFwCsrKzYunUrJUuWRKvNvs9fYmIi69at0yuRbNSokcHnOnLkCC4uLgbH4uLissz9999/c+3aNUDXQ61Vq1bUrVuX+Ph4JdMscz+zli1bGpw74xUSEoJKpcLNzQ2AxYsXM3v2bCwsLAyWRYaFhWVppP8i7ty5g0ajoUqVKsqxqVOn4uvry/79+wkODqZfv34G15AXPXv2pG7duuzYsSPfGVwqlQq1Wk16errecQsLC4oVK0ZQUBC7d++me/fuL7Q2YZgEw4QQQgghhMjF560rcGFCc94pXjB2kcyOV1FdMOxKaHSOX65F/iWlpvMgStcsv6STFW72OfSekmBYgREWFkZaWhrvvPMOo0aNYvny5RQrVowlS5YwceJEgoODlbLBjKBQXFwcDRo0YO/evUq2lUajUQJfiYmJVK5cmd9++42oqCgAoqKi+O6771i5cqVeQMbGxoYFCxZgZWVFx44dlZK+zDZs2EChQoVy7f2VV2lpaYSFhTFr1ixq1KjBoUOH8PLyonPnznh5eREUFMTq1aspX748CQkJeWrun+HevXu4urrqlS/mZMeOHVy8eJHoaF02a3YN7NPS0ti6dWu283z88cd8++23esfq1q0L6LLZ7t27x4ABA5R7ZG7GD7Bp0yZatmxpcIfIGzducO7cObp3705QUBC3bt1SXtHR0Wg0Glq2bMmmTZsA+PLLL1m1ahUXL17k8OHDdOrUCQsLCzp16gTAtWvX2LJlC1evXmXr1q20adOGjz76KNdsM5E/0kBfCCGEEEKIXKhUKixNC/7/dS5XxBoTIxVRCancf5pIsULZB2xE/mRkhdmaG1PIyjRLZpiFiRGJqbrMD9lNsmAYOHAgK1eu5P3338ff35927dpx8uRJVq1apfQOyxATE4OVlW5Tilu3bvHXX38pmWFlypTh6NGjXLt2jY4dOxIXF0fJkiVZuXIlnTp14sKFC2i1WrZu3aqXufRPI0aMUH7OHMxetmwZffv2zXOj/Zxs3bqVgQMHYmpqyvDhwxkwYAD29vbExMQoP2dISUkhOTlZee68uHbtGmXLls3xnL179wIwZ84c0tLSKFKkCFWqVOHnn3/Okr2WmprK8uXL+fbbbzEzM2P79u0G53z06JGSjZZZVFQUffv2Zdq0aUoG2ty5cwFdM/6MzzQ0NJTAwEBSU1P1ykIz5gaUjL7M5s+fz4ABAwgMDCQ0NBQADw8Pvv32W0JCQnBwcKBBgwacOHFCCZzGx8czevRo7t+/j6urK3369OHzzz/P8TMT+Vfw/xddCCGEEEIIkSdmxkaUc7HhamgMV0OjJRj2Cj0vkbTWlYFlCoYVsjKlkJUptx7rvsjbWUowrCBo2rQpn3zyCZ6ensqx9957D9Blf4WFhWFvb49Go2HDhg3K2Pbt2/nmm2+U0rkqVaowbtw4vv76a1atWkXt2rVp2LAhhQoVAnTN742MjOjTp0+O64mJiSEwMBAzMzOOHDlCr169uHXrFseOHWPt2rWv5Jk9PDz48ccfad++PUZGz/sjfvrpp4Au0GdlZYW1tTW//PILHh4e2WaGaTQa/v77bxwdHbG0tCQsLIwFCxbQoUMH5ZzExETS09OJjY3F1NSUtLQ0Tp06xezZs7ly5Qo7d+6kVKlSbNu2jZ49e9KzZ0+mTp2qbDxQp04dXFxc+OKLL+jZs6fShP7y5ctUrlwZgNu3bxMYGJgl0BgZGUnz5s2pXLmykhUGutLQlJQUzp07h7W1NQD+/v7Kbp//1KBBg1wzbUNCQpSf+/XrR79+/bI9t0aNGty+fTvH+cTLk2CYEEIIIYQQ/yFeRe24GhrDlQcxtPRyfd3LeWsowTAnXZZMRgN9gCK25thbmjwPhklmWIHQrVu3bMfu379PpUqV0Gg0GBkZUb9+fSVYcuHCBd59910qV65MtWrVmDBhAt26daN37940aNAAHx8fJk2aBECJEiX49ttv2bVrV67rSUpKolWrVgCUL1+e7t27M3/+fLy9vQ1mPb2IypUrK0EkQ3x9fTlx4gQAbm5uLF68ONtz1Wo177//vlIKampqSsuWLRk9erRyzogRI1i6dCkNGzbE0dGRqlWr8vTpUwYOHMiKFSuwsdHtgNu2bVvOnDlDx44d2bBhA7Vq1cLDw4OvvvoKHx8fpbS0cOHC9OzZk+rVqysBKiMjIzp37kz16tX11nfu3DnKlCnD2rVr9bLqfvvtN3777TdMTEwYOXJk3j88UaCotAW0WUBMTAx2dnZER0dja2v7upcjhBBCCCFEgbDyeDATA67SrIILS3u/+7qX89b45LcLbPrrAaObl+PjpmVJS9dQ/stdpGu0NPV0xtbChM3nHwBw+vOmONsa7n30ppDvW3mTmpqKWq3Wy6LSaDR6fb/S09P1xlNSUvLcM+tNlJ6ejkajydJXKyepqakYGRnl2qA+MjKSQoUKZVvymZqaqtz3n5+rEPmR7wb6Wq2Wr776iqJFiypN/MLDww2eu2LFCsqXL4+ZmRlVq1bNUr975coVGjRogIWFBaVKlWL16tUv9hRCCCGEEEKIPHG20fW7iUpIec0rebsEP8sMK1lYlxlmbKSmyLOAl4udOc62z/sM2Upm2FvDxMQkS0DmnwGff44X5EAY6J4nP4Ew0H1Oedmp0dHRMcfeZ5nvK4Ew8TLyHQybMWMG8+bNY/HixezZs4fr16/Tu3fvLOcdOHCAfv364e/vz5kzZ2jdujUdOnTg5s2bgO6/NDRv3pySJUty6tQp+vbti5+fHydPnnz5pxJCCCGEEEIYlFGiF52Y+ppX8nZ5FJ0E6JdHZvQNc7U1x9lGFxgzN1FjbiJf4oUQ4nXKV88wjUbDjBkzGD9+PO3atQNg9uzZtGrVijt37lCyZEnl3LNnz1KtWjU+/vhjAKpUqcLixYu5cOECZcuWZcWKFaSnp7NkyRJMTU2pUqUK27ZtY+HChbJlqBBCCCGEEP+SjKykKAmGvTJarZbwuGQAvfLHOiULcSb4CTVKOPDkWSae9AsTQojXL1+ZYZcvXyYiIkJp2gfQsGFD1Gp1loyudu3aERwczL59+0hPT2fVqlWYmJjQuHFjQJc51qRJE70U0SZNmkhmmBBCCCGEEP8ie0vJDHvVniakkpqua8XsZP38+82o5uU4/2Vz6pVxopyLrhF4CUer17JGkdXjx49JSkrK1zVJSUlMmjQJjUaTp/OjoqI4deoUq1atYtq0aVnG586di5+fX77WkB8Zu1i+Co0aNWLFihU5nnP48GGaNm2ap7mOHj36StYlxIvIV2ZYUFAQgF4GmIWFBYULF+b+/ft653p6ejJ16lSaNWuGSqVCrVazY8cOnJyclLlat26td03x4sWzzJMhOTmZ5ORk5X3GlqlCCCGEEEKIvMvITEpJ05CUmi4le6/A41hdQMXe0gQz4+efp0qlwt5SFxwr52LDxiH1KFbIwuAc4t8VFRWFvb298j48PJxatWrx+eefM3DgQIPXbNu2jYiIiCzHly5dSnR0NFWrVs0y9t577xEQEEBAQADXr18nLCxMGVOr1QwbNkzZITE3DRo0oHnz5nz55ZfY29sTHR2d6zWlS5fm1q1b2Y5Xq1aN+/fvY2z8PBSQnp6OnZ2dcp2fnx8eHh7Kjpf5sWvXLsqWLUurVq04ePAgqampmJvrsiU///xzPv/883zPCRAbG8v48eNJSEhgyZIl2Z4XFRWFv78/AQEBmJqa0rdvX6ZNm6bXrywoKIjRo0criTv9+vVj3rx5L7QuUXDlKzMsLi4OtVqNmZmZ3nFLS0u9QBXA/v37GTVqFLNmzeLUqVN88skndO3alevXrytzWVpa5jpPhqlTp2JnZ6e8ihUrlp+lCyGEEEIIIQBrM2OM1LoG1VEJkh32KoTHPiuRtDHL8bwaJRyU3mHi/8/FixepXLkykZGRgC67q3PnzoSFhTF27Fjs7e2xtbVFpVJx6dIl5bqEhATCw8P59NNPiYuLU17jxo2jdOnSescyXqmpqTg5OVG9enVGjRpFuXLlmDlzJrdu3SIxMREbGxv8/PxYtGhRjmu+efMmx48fV7LGIiMjSU1NzfWV8X07J3v37uXRo0fK68yZMy/82f7+++/cu3dPeb9r1y7ef/99du7cybx58+jRo4fy2bxIICwuLo4JEyZQqlQpFi1ahFarzfH8nj17cu3aNf78808WL17Mjz/+yMyZM5Xx+/fvU69ePSwtLdmzZw+HDx/G29s73+sSBV++MsPMzMzQaDSkpaXpRZKTkpKyBLb+97//4efnxyeffAJAzZo1+euvv5gyZQpr1qzBzMyMlBT9HWwMzZN5voy5QJcZJgExIYQQQggh8kelUmFnYcKT+BSiE1MpYifBmZf1OCYjGCaf5ZuoatWqtGzZkn79+rFy5Urat2+Pg4MDDx8+xN7ennv37tGvXz8+/PBDqlSpolzXtWtXoqKimDBhAhs2bMjxHpUrV2b+/PkAVKxYUdlkbufOnVSqVInSpUvna83Lli2jefPmynfe/O6ceOjQIUaPHs2DBw9Qq9Vs2bKFxYsX52uO3KxatYrBgwezZcsWihUrRlhYGBcuXOD9998HdH3Evby8lPNPnjzJuHHjOHjwYJ7vERISws6dO1m8eHGu2VuXLl1i586d/PXXX7zzzjsAnDlzhh9++IGxY8cC8Nlnn1GxYkXWrFmT466V4u2Xr2CYm5sboIumenh4ALryxfDwcEqVKqV37qVLl+jfv7/eserVq7Nr1y5lrswRZIB79+5lmSeDmZlZlow0IYQQQgghRP5lDoaJl/c4j5lh4vWZOXMmBw8e5OjRozx8+JBz587h7u4OoFQnHT9+nNGjR/PDDz8owSxbW1tWr16d6/wlSpRQfvb391d+vnnzJj/++CPbtm0DUObNSVpaGqtWrWL+/Plotdpsq6dyUrFiRWbOnMlPP/2EhYUFvXr1UgJyzZo1M1gmmVlGEkyGfwbjli9fzvDhw9m8ebOSWbVs2TK0Wi3FixcH4MiRI1hZWfHrr79iYWGBi4tLlnU+fPgQLy8vfvrpJz788EODz5GRuZZbMOzAgQO4uLgogTDQ9SWfPn06Dx8+xNramo0bN7Jx40YJhIn8lUlWr14dCwsL9uzZoxw7dOgQKpWKBg0a6J3r5ubG33//rXfs8uXLSkCtfv367N+/n/T0dGV83759eWq2J4QQQgghhHhxyo6SCSm5nCnyIqNnWGFbCYa9qezs7Pjggw9o06YNN27cUEr39u7dS+nSpUlNTSUuLo4OHTqQmvo8SJySksLHH3+c49yXL1/m+++/V957enri6emJo6MjoaGhFCtWTDn2z6CTITt27CAtLY327dtz9epVLCws8v2Kj4+nUaNGFC9enJIlS9KoUSPs7e35888/efjwoV6Z5MOHD9m/f7/eGqZMmYKJiYnyWrlyJaAL1H388ceMGjWKTZs20aJFCwBSU1P58ccfletDQkK4c+cOCQkJbN26VS9AmJmFhQWenp64uroaHM9P0CooKEivvzmgBObu37/P+fPnSU5OJi0tjZo1a+Lk5ESrVq1y7LEm3l75ygyzsLBgyJAhTJgwgeLFi2Ntbc2IESMYNGgQ9vb2tGzZkoEDB9KpUyc+/vhjxo0bh6enJ7Vr12bz5s3s2rVLiYj379+fWbNm4e/vz7Bhw9i8eTOXL19m3bp1/8qDCiGEEEIIIXTsLV7vjpIPoxN5HJNM1WL2r+X+r9rzzDApk3zbqFQqUlNTWbBgQbbnxMbG6mUjZQR+xowZA0Dt2rXp1q1bniudli1bRq9evTAxMcHLy0vpk3XgwAF8fHwIDg7GxMQkz8+QmprKoUOHuHHjBuPGjTN4bVpaGvPmzaNnz54ATJw4MUsD/RUrVjB27FiKFCnCmTNnKFeunDL2+++/o1arsbW1BWDNmjV07NiRRYsWsX//fsLDww2uzd7enmPHjuX5WXKSXV9y0GX/PXz4ENBlCU6ePBkrKyvGjh1L69atuXr1ar4+U1Hw5SsYBvDtt9+SmJhI165dMTIywsfHhxkzZpCamkpgYCChoaEADB8+HK1Wy3fffceDBw8oW7Ysv/zyC23atAF0mWMZEeLly5dTqVIlduzYoZdeKoQQQgghhHj17F5zMKzPz2e4ERbLvtGNKOlk9VrW8CqFP+sZVljKJN9YERERjBo1io8++oi2bdvqjaWnpytlgxqNhnXr1nHixAmWLVvGmDFjlPO//PJLZsyYoVw3ZMgQli9fTnR0NGlpaSQkJCjBl7CwMH7//Xdq1KjBjBkzWLVqFVu2bMHa2jrHdT569IgdO3Zw8eLFLGNz585l8ODBqFQqvRLGzNRqNWq1mnv37vH777+zfft2/v77b7Zv306nTp1o164dK1asQKvV8sMPPzB06FDUajX9+/fP0tPbkLp167J+/fosz3H06FGmTJnC6NGjSU1NZenSpUyZMoX4+HiOHz+uFyz8t2TXlxx0QbGMz+ybb75RKtuWLVuGl5cXJ06cyFLtJt5u+SqTBN0v2I8//kh0dDRPnjxh3rx5Sj+vkJAQJQquUqkYOXIkt2/fJikpicuXL9OjRw+9uRo2bMjly5dJTk7mr7/+UhrtCSGEEEIIIf49eQmGpWu0zNx9nV9O3X2l974bmcC1R7FotHAzLPaVzv26hMdJz7A33Z49e7hx4wYtW7YkLS1NeR09epTy5csr73v27MnixYtZtmwZAOfPn2fFihUEBwfz8OFDbGxsKFu2LKVLl+bOnTucPXuWRYsW8eTJE71AzJgxYxg6dCjW1tZMnToVJycnGjduTERERI7rXLlyJTVr1qRixYpZxk6dOsWECRP0yhf/+frqq68AOHfuHCdPnsTZ2ZmJEydy9uxZpWQQdEGiESNGoFbnLyTQpUsXgwG9oUOH0qtXLwDWrl1L6dKliY2NpWbNmqxevVopp/w3ZdeXHKBkyZI4OzsDUKZMGWU8I7stLCzsX1+feLPkOxgmhBBCCCGEKNjsLXMPhv10OIgFB27x+ebLSpnWq3D01vNgQEZ5YUH3OEaXfSLBsDfX7t27adWqFaArY7S2tsba2pomTZpw48YN5f369ev1rrt79y7NmjVTMrXMzMzYuXMnFhYWgK5EcuzYsXpBpZUrV7J//36GDRsGgLGxMWvWrKFevXp6PbMNWb58eZaN6DI8evQIrVab7atx48bKpgAdOnTgt99+o2bNmgYDXmFhYRQpUiQvH12Onjx5wtOnT/Hy8lLu07ZtWxYuXMiQIUPo1asX6enpNG7c+KXvlZv69esTEhLCzZs3lWP79u2jevXqODg4ULNmTUxMTDh58qQyfvXqVQC9kk/x3yDBMCGEEEIIIf5jcssMi4hL5vt9N5T3MUmGS7JexNFbz3sHZQSRCrL45DTiU3QBDmdb6Rn2JkpNTSUgIEAJhi1YsEBpoL9//37KlSun975hw4bKte7u7uzdu5eqVasqxzIHhwsVKsSVK1e4fv26MjZ27FiWLVuml0FlbGzM999/j4uLC82aNcPLyyvLOo8cOUJoaChdu3Z9oee8d+8eHh4eeTp3+/btpKSkKIEhExOTLDtGZvjrr78ICQkxOLZ//35q1qypd8zJyYmyZcuSmprKr7/+yujRo1GpVBgZGSlBxAzR0dHUr1+fEydO5Gnd/7RgwQJlh84GDRpQo0YN+vbty9mzZ9mwYQPz5s3j888/B3T9yfr168eIESPYsWMHR48epW/fvjRr1kzvz1f8N+S7Z5gQQgghhBCiYHu+m6ThYNjM3ddJStUo75/GpygBtJeRrtFy7Fak8v5tyAzLeAZLUyOszeTr1Zto7969qNXqLEEbQzK37rl79y5BQUFKZpiLiwvFixdnwYIFdOnSBTs7O+zs7Pj555/p27cvISEhREZGsm7dOpo0aZLtPXx8fJSfMwfWli1bRrdu3XLtK2ZIUlISISEhlC1bNsf7+vj4cO3aNaZOnYq/vz/t2rVj8ODBzJs3L0sD+TNnzvDNN9+wfft2jhw5YnDOR48e4ebmZnDsyy+/xMbGhkGDBgFQs2ZNdu7cqYyrVCoSEhIIDAzMtsF+bkJCQggMDFTm27x5M/3796d+/fq4uLgwc+ZMPvzwQ+X8uXPnKr3PU1NTadu2bY6bI4i3l/xrLYQQQgghxH9MTrtJajRadlx+qHfsSUIKHrx8o/srD6L17hn2FmSGSYnkm8/V1ZXPPvtMr1zw0aNHWFhYcPbsWezs7Axet337doYPH87nn39O/fr1KVmyJJMmTWLatGmsWbOGTp060aBBAwoVKoRKpeLmzZtcvnxZ6duVndTUVE6fPo2trS179uyhbNmyxMTE8Pvvv7Nv3748PVNwcDAqlQobGxu0Wi2zZs3Czc1NyQxLSUnByMiIhw8fUqlSJUAXOFq5ciULFixg+vTp9OnTh/79++Pn50fdunVZs2YNnp6eBAcHs3XrVmbOnMngwYO5desWJUqUwMrKiitXrihrSEpKYt++fVSpUiXL+iZMmMDKlSs5evSo8rlHREQQHByMWq3mzp07WFtb4+rqSmRkZJbrDTl48GCWY5k3NAAoVqwYu3fvznYOMzMzFixYIAEwIWWSQgghhBBC/NdkZHnFGAiGBUXEEZOUhoWJEZ5FbABdZtirkNEvzOZZBtXblBnmbCMlkm+qatWq8emnn+od8/f3x97engkTJvDJJ58YvO7gwYNUrVqVqlWrYmJiwuLFiyldujSDBw/mt99+Y9u2bUqJXuXKlfnkk09o1KhRrusxNjamW7duNGjQgPv37zNw4EB+/fVXPDw8qFOnTp6eafbs2ZQqVQpHR0ecnJzYsGEDK1euVMbPnDmDqakpW7dupXnz5kyfPp3y5ctz6dIl9u7dS58+fQBd0/ndu3fTrFkzhg8fjlarxcrKig8//JDg4GBmzpxJiRIlAOjfvz9Lly7F2NgYY2NjrKysuHr1qrKJXob4+HgOHTrE/v37KV26tHL83r179O7dm48++ojatWsbLBUV4v+LSvsqu2H+P4qJicHOzo7o6GhsbW1f93KEEEIIIYQoMK49iqHl3CMUsjLlry+b6439duYeYzdeonbJQpiZGHH4RjgzOlehy7vFXvq+n224xPqz92hRyYXdV8Nwsjbj7PhmLz3v67T86B0mb/ubNpVd+eGj6q97Oa+MfN8CjUaDWq1Gq9WiUqlIT0/X66uVkpKCqanpa1yhbo0ajQZj45yLvpKSkkhLS8uxBDM1NRUTE5MszynE20gyw4QQQgghhPiPsbfQfYGPTkzNslPkX3efAlC9hAOFnu06+TTh1WSGxSXrGvGXddZlnEXGJ5OWrsnpkjeaRqNl19VHALg7WORytihoMsr7VCoVQJYA0esOhIFujbkFwgDMzc1z7UWW0TNMAmHiv0CCYUIIIYQQQvzHZJRJpmu0yk6IGZRgWHEHHKx0X/afxBtutJ9fMUm6eUo4WmKkVqHVQkTcqwm0vQ6/n7vH6TtPsDAxwqdOide9HCGEEHkkwTAhhBBCCCH+Y8xN1Jga6b4KRGXK+opOTOVGWBwA7xS3p5ClLhj2qnqGxSbpMsPsLExwstbN/Ti2YDbRj05I5Zvtul3sRnuXo1ghy9e8IvG2S09Pz5LJKYR4MRIME0IIIYQQ4j9GpVJh96wE8ubjOIIj4gG4cC8K0GVuOVmbKZlhr7pM0trcGBdbXcP5sJiC2UT//L2nxCSlUayQBX71PF73csT/o1q1arF8+fI8nZuQkGDweHKy4d/7pKQkXFxc2LVrl97x0NBQzM3NCQkJydN9z507x/r16/N0rlar5e7du+zZs4f58+ezY8eOLOdUq1bN4G6OQhRUEgwTQgghhBDiPyijVLLPz2doMfcwQeFxHLkRDuhKJAEKveJgWOyzMklbcxNl98WCmhn2MFq37rLONhgbydeqgqB+/fqUL1+eatWqGXyVLFkSPz+/XOfJaKyfm4SEBCpVqsS2bdv0jsfExFCxYkUCAgKyXLNy5UoSEhJo3Lix3vGlS5dSrlw5PDw89I5HRUWxYsWKLK+tW7cyaNAgli9fbnA8Ojqabt26Ua1aNaysrChRogTe3t4MHz6cFStW5PpsudFoNCxevJhq1arleF7fvn1RqVTcv3/f4PjkyZNRqVQcPXrU4PiqVatQqVSsWbNGObZ//35UKpXeq23bttmu4cqVKzRo0AALCwtKlSrF6tWr9cbv3btH27ZtsbKyomjRosycOTPHZxIFQ+6d9oQQQgghhBBvteQ0DeO3XOFciK5fWOvKrgA4WGb0DHtFmWHPyiRtzI1xtjUDCm5mWGhUIgCuduaveSUiP9avX59tgGbDhg1ZAleG5DUYZmlpyU8//USXLl0IDAzE1VX398rf358qVapkCdDExcUxffp01Go1bm5uyvEyZcoQHBxMcnIyjo6OSkN/gIsXLxIXF8fBgwdJTk6mRYsWADg5OfH1119nm5lma2tLoUKFaN26NR988AHz589n165dlChRAhcXF0CXQZqYmIi5ef5+x5csWcLcuXO5desWxYplvwvtlStXWLVqVbbjjx8/zjHwlJSUxIQJE7Icf/LkCW5ubnqZbFZWVgbniImJoXnz5nh7e7NgwQICAgLw8/OjbNmy1KlTh/T0dNq0aUPRokU5cuQI586dY/DgwRQvXpyuXbtmuzbx5pNgmBBCCCGEEP9BNubPvwqoVHD8diQANT0caFbBGcicGfbyDfQzN+u3NjPG5VlmWHgBzQwLjdKtu6i97CJZkPj6+mJpabi/25MnT6hXr16uc2i12lyDYf3792f16tXKDo1ly5YlMTERMzMz5VpbW1vi4+OVgNPgwYMpXLgwV69eZeDAgfTs2ZNWrVoxZMgQtFotx44dw8fHh9atW+Pr66vcy9/fn7S0NL7//ntiY2NzXNenn35KmzZtAFi4cCEAwcHBLF68mFq1auX67HmxaNEi/Pz8iIqKyrZUU6vVMmTIEDp27MiGDRsMnjNq1ChatGiR7fiUKVOoWbNmltLRJ0+e4OLiQpkyZXJd64oVK0hPT2fJkiWYmppSpUoVtm3bxsKFC6lTpw7bt2/n2rVr7N27F2dnZ6pXr86+ffv44YcfJBhWwEkwTAghhBBCiP+g4U3LsvzoHb5oU4GfDgex6a8HAIxrVUHJPHGw0n2Rj0pIIV2jxUityna+3GRkhYGuZ1hBzwx7GK3LDCtqL5lhBUnt2rWVzKd/unbtmt777t27ZxvM6dWrF7169cpy/Oeff1ZKLWfNmoW/v78y5uXlxaJFi6hfv75yLOPvWnR0NA8ePCAgIAALCws+/fRTZsyYQatWrXB1dWX16tXExcUxYsQIZs6cSY0aNahUqZIyT/v27XF3d8/1+WvWrAnAqVOnlHLA2NhYYmNjlbUaGRnx/fffZztHrVq1eO+995gzZ47B8bNnz6JSqZg0aVK2cyxatIiEhASGDBliMNi1c+dODh06xK5duwyOX7p0iYULF3Lp0qUs40+ePMHJySnbe2d24MABmjRpgqmpqXKsSZMmbNy4URmvXr06zs7OeuPDhw9Hq9XqZemJgkWCYUIIIYQQQvwHNS7vTOPyui94Y7zLc+l+NPXLOFGjhINyTkaZpEYLMYmpSkP9FxGbrMsuMzVWY2ZshMuzYFhB7Rn2vExSMsMKinHjxuHu7o61tbXB8fDwcFJTn2dBLl++nEWLFmU5z8PDgzFjxugFujJkzjobN26cXkAoKiqKtm3bYmyc9Wu4nZ0dmzZtolu3bsTHx5OQkEB8fDzu7u7ExcUp81hbW2Nvb8/06dP1Sgz//PNPDh06xIcffpjt88+bNw97e3uaNWuGnZ0dnp6eAGzduhUjIyPlfW5Zb+XLl6dUqVLZjucWILp+/Trjxo1jz549Bss4IyIi6Nu3Lz/88IPBP6ukpCQ++ugjxo8fbzAAGBkZyd69e7GysqJ48eJ88MEHjB8/3uBcQUFBtG7dWu9Y8eLFlR5mQUFBlCxZMst4cnIyERERFC5cOMdnFW8uCYYJIYQQQgjxH1fU3oK9nzTMctzESI2NuTGxSWk8SUh5uWDYs8ww22flmRkN9O89SSQ5LR0zY6MXnvv/m1arVRrou0mZ5Buvc+fOnDlzhtjYWIoWLQroMrESExMpUqSIwWs6dOjA119/naWkMigoiOjoaI4fP8748eNzvO+0adPynBkG4ODggK+vLxYWFlhbW2Ntbc306dNp2LAhffr0wc7ODiMjw39PTExM+Ouvv3j48GG26wkJCVECcZ6ennh6epKcnMz06dOxtLSkffv2FC9ePMdnArI0mM+PpKQkevTowciRI6lVq1aWHSq1Wi1+fn54e3vTqVMngoODs8wxatQonJycGDlypMF7DB48mB49eqDVajlx4gQTJkzg1q1bBjPM4uLisvwZW1paKrt9xsXFZckyyzg/ux1BRcEgwTAhhBBCCCFEtgpZmRKblMbT+BR4iSSIWKV5/rMeSi7WuNiaERaTzKrjIQxokH2myZvmSXwKyWkaVCpwsZUyyTfdhg0b2LJlC9u2bWPp0qUArFmzhrNnzzJ37tx8zbV+/Xo6derEqVOnuHfvXo4N4l+Ej48PACkpKcTExGBmZkZYWBhHjhwhJiaG2NhYYmJiKFeuHJ06dQJ0TfSPHDlC3bp1qVevHtbW1hw+fBjQBW769OnDrFmz8PDw4NKlSzRq1Ei53+LFi/Hy8uLkyZPUr1+fzz77jGHDhr3SZ8qsf//+2NnZGWx8DzB+/Hhu377Nr7/+anB86dKlbNq0iQsXLmSbwVa6dGnl5xo1amBnZ4evry/h4eFZMrnMzMxISdHfICQpKUkJeGU3DmTbe04UDBIME0IIIYQQQmTLwdKUkMiEl95RMu5ZmaS1me4riJmxEaO9yzN2wyXm779J5xruL5V59v8po3m+k7UZpsa57yoo3gybN2/m7NmzADx9+pSOHTvi6+vLxo0bKVu2LKDbwbBdu3YsXrw4y/VPnz5l7ty5rFu3jgoVKvD555/nmCU1evRoxo0bp7xPTEzE29s72yDOnTt3qFmzptJo39bWlqioKIoUKcKVK1ewsbFRXh4eHsp19+7dw9PTk+7duyvlgO3atSM6OpolS5bg7e2Nh4cHQ4YMYdCgQQwfPhyAR48e8d133/HLL7/QtWtX9u7dS+PGjQkPD8+x39eLCgkJYe3atZiYmCi7O2o0GkC3Y2avXr1YunQpxsbGSjaWVqsFoGnTpjRs2JCbN28SGRmZpXSxb9++TJkyhevXr2e5b/Xq1ZX7/zMY5ubmxr179/SO3bt3TykDdXNz4+bNm1nG7ezsKFSo0At9DuLNIMEwIYQQQgghRLae7yj5csGw55lhz7+CfFjdneVH73DtUSxLjwbxaQvPl7rH/5dQpXm+lEgWJB07dsySGbZixQpOnz7N3r17cXJyom7duvTo0SPLtRqNhn79+lG3bl2aNGlC1apVqVy5MsuXL6dv375Zzm/SpAm+vr40aNBAOWaoTNLf318pXfTw8OD+/fuYmZkRFxdHTEwMw4cPp3bt2nh7exMTE0NMTAzR0dE8ffqUadOmMWbMGDQaDcuXL2fbtm1KkCwxMZHz588rZZUbNmygQoUKes/j4+PDhx9+qJRGlitXjj///DPbTQNeVtGiRQkMDNQ7dvr0aXr37s3u3bspV64co0eP1ht/8OABzZo14+eff6Z+/fqkpqbq9XUDqFChAt98842SKfdPp0+fRq1WZwmgAdSvX59ly5aRnp6ufFb79u2jadOmyvjatWuJjo7Gzs4uy7gouCQYJoQQQgghhMhWRhP9J/GpuZyZM0PBMCO1ir7vlWTsxktcuBf1UvP/f3r4rHl+UTspkSxIDGWGqdVq/P39GTlyJLVr18bNzU2vjBB0vaH69evHuXPnOHfuHACOjo6sWrWK1q1bExMTo9e/yt7eXimlyywlJYWmTZtmaTC/dOlSlixZwuTJkwkPDyc9PR1ra2tsbW2JiIjg5s2bnDlzBjs7O2xtbbGzsyMyMhJ3d3c0Gg1xcXF8/PHHtGvXTuljFh0djY2NDaALjDVp0oRvvvlGyXjasWMHwcHB/PHHH4SHhytrqVSpEpMnTwZg2LBhBpv99+7dm5o1axrcQCAnJiYmSpP+DI8ePQJ0pY2urq64urrqjZub6/6OFS9ePMd+Zq6urkp55BdffEHNmjUpVaoUp06d4rPPPmPQoEE4OjpmWX///v2VXT+HDRvG5s2buXz5MuvWrQOgS5cujB8/nj59+jBx4kROnTrFxo0blTJUUXBJMEwIIYQQQgiRrUJWuh5fryozzNrMRO94aWdduVRwRNZd5d5Uoc+a50tmWMHSsWNHZs+erZTrHT58mEmTJjFx4kSWLFnCli1bspTZnT17lgEDBpCSksLRo0f1mqk3a9aMjRs30qNHDzZt2sSUKVNo2LAhUVFRBu9vKDMss3bt2mFtba0XgOrTpw8NGzbEz89P79wpU6bw8OFDTE1NuXXrFlu2bGH9+vV4eHjQoEEDLly4wKVLl2jdujXBwcE0atSIkSNH8sknnxAQEECNGjVYu3YtVlZWesGwzBYsWKD8nFGuCBAYGIizs7PBa94EKpWKQYMGERMTQ6lSpfjyyy+V0lDQX7+bmxtbt27F39+f5cuXU6lSJXbs2EGJEiUAXV+wnTt3MmDAAGrVqkXJkiVZt24dtWvXfi3PJl4dCYYJIYQQQgghspXRx+tle4bFJukyyzJnhgGUcNQFw0KjE0lKTcfc5M3fVTL0WWaYq2SGFQgLFixg9uzZxMbGcvnyZUqXLk1sbCwODg44OTnRpk0bChcujLe3N7Vr16Z///707NmTpUuXMmvWLHx9fZkzZw729vZZ5m7Xrh0nT55kzJgxNG7cmICAANq2bftC6zQ0v6urK+fPn9cLhqWlpXHkyBG6d+8O6LK8tm7dSkJCAlOmTKFBgwbMmTMHX19fbt++zZUrV1i7dq2SZfXbb79RuXLlXAM6d+/eJSIigtjYWG7fvq2UCZ4+fTpPzzNp0qRce481atRIL9D2Tx4eHjmOA1nGv/76a77++utsz//n+hs2bMjly5ezPd/Ly4sTJ07kuAZR8EgwTAghhBBCCJGtjIBPUHjcS80Tl5y1TBLA0coUGzNjYpPTuPckgbIuNi91n/8PDyUzrEDp0aMHPXr0UMrkQNcz7PDhwwQEBNCjRw/8/PxQqVQMGDCAhQsXEhoaip+fH+3ateP999/PcX4vLy927dpFUFCQUob4qgwcOJD27dtja2urNN5PS0ujatWqdO7cmQcPHuDh4cHSpUtZvXo1Y8eOZcCAAfzwww/UqlWL7du38+TJE1xcXEhPT+fWrVtYWFgomU85OXfuHP369UOtVtO+fXuqVav2Sp9NiNdJpc0tzPqGiomJwc7OjujoaGxtbV/3coQQQgghhHgr3XuSwPvfHcBYreLiRG+szF7sv6ePWn+Bzecf8HlrTwY2KK031nb+Ea48iOGnXjXwrlTkVSz7X1V36j4eRiexZdh7VCtm/7qX86+Q71sFh0aj0duhMnMzeND1KjM1LRg7tQrx/0X2ARZCCCGEEEJkq1ghS9wdLEjTaDkT/OSF53neQN8ky5jHs1LJkMg3v29YdEKqkhlWopDla16NEOgFwgC9QBgggTAhDJBgmBBCCCGEECJHdUvpystOBr1MMEzXM8zUcrWnAAEAAElEQVTaQGZZRjDsTmT8C8///+XSgygASjhaKv3UhBBCFCwSDBNCCCGEEELkqM6zYNiJoMgXnuN5ZpiBYJhTRmZYAQiG3Y8GoIq7/etdiBBCiBcmwTAhhBBCCCFEjuqW1gXDrjyIVjK88ut5A31DZZK6csPgiDe/TPLCvSgAqrrbvd6FiP+c9PT0XHdWLKje1ucSby4JhgkhhBBCCCFyVNTeghKOlqRrtJx6wVLJjCBaTplhodGJJKWmv/hC/x9czAiGvaWN80XuatWqxfLly/N0bkKC4QBvcnKyweNJSUm4uLiwa9cuveOhoaGYm5sTEhKSv8XmgVar5e7du+zZs4f58+ezY8eOLOdUq1aNgwcPvrJ7HjhwgEmTJgFw69YtvLy8sv1MsqNSqQgODn5laxL/LRIME0IIIYQQQuSqcXlnANafvZen85NS07n2KAbQfdnOqUzS0coUazNjtFrd7pVvqkfRSTyOTcZIraJSUdlhsaCpX78+5cuXp1q1agZfJUuWxM/PL9d5/rl7Y3YSEhKoVKkS27Zt0zseExNDxYoVCQgIyHLNypUrSUhIoHHjxnrHly5dSrly5fDw8MhyzezZs3F3d8fR0RErKyvc3d2zvMzMzFi7dq3eGrp160a1atWwsrKiRIkSeHt7M3z4cFasWJHtM61YsQKVSpWvl5eXV5bPZeDAgTg5OQFQpkwZHBwcmD9/vsF7enh4vFAgburUqRQpUgRnZ2emT5+uN/bll1/Su3fvfM23aNEiypUrh7m5OZ6enixevDjbc/v27YtKpeL+/ft6x4OCgujYsSO2trZYWVkxfPhwg9dn91n269dP77zY2FhGjBjBgAED8vUsAl5sX2QhhBBCCCHEf4pPneKsOB7MvsAw7j1JoFguOymO/u0i2y8/ZE2/2rzr4UCaRlcGZaiBvkqlwsPJkisPYgiKiKesi82/8gwvK6NEspyLDZam8lWqIFq/fj3VqlUzOLZhw4YsgStD8hoMs7S05KeffqJLly4EBgbi6uoKgL+/P1WqVKFt27Z658fFxTF9+nTUajVubm7K8TJlyhAcHExycjKOjo6oVCplLCQkhE8++YRPPvmEBQsW8Pfff/Pjjz9mWa+LiwvNmzdXjtna2lKoUCFat27NBx98wPz589m1axclSpTAxcUF0P29TExMxNzcXLnOz8/PYMBw/PjxREdHZxvQymzAgAEULVqUoUOHKsfmzJlDkyZNeP/996ldu3aucxii0WhYsmQJvr6+nD59msWLF7N+/XpSUlLo0aMHrVu3pnLlyty8eZMlS5Zw6dKlPM99+fJlfvzxR6ZNm0aZMmXYuXMnQ4YMoVChQnTp0kXv3CtXrrBq1aosc9y/f5969erRtGlT9uzZg7GxMQ8fPjR4v5s3b+q9f/r0Ke+//z7du3cHdL8r3333HQsXLiQmJoZevXrl+VmEjvwLLoQQQgghhMhVGWcb3i/rxJGbEaw+GcLnrStke+6diHi2X9Z9yTtyM5xyRawBUKnAKpsgUoUitlx5EMP5u1G0qFTk1T/AM/sCw1CrVDT2dM73tRfvRwHSL6wg8/X1xdLScCD3yZMn1KtXL9c5tFptrsGw/v37s3r1akxMdD3yypYtS2JiImZmZsq1tra2xMfHKwGnwYMHU7hwYa5evcrAgQPp2bMnrVq1YsiQIWi1Wo4dO4aPjw+tW7fG19c3yz2DgoJYt24df/75p97xESNGEBUVhbe3N6ALaI0cOZKFCxcCEBwczOLFi6lVq1auz56dixcv0q1bt1zP++qrr9i3bx/nz5/X+wxr1qzJ5MmTad++Pdu2baNmzZr5ur9Wq2XQoEHs2bOHVq1acfr0abp27UrDhg0BaNKkCdeuXaNy5coMGzaMiRMn4uyc938D3NzcOHHiBFZWupLuKlWqsG/fPjZt2qQXDNNqtQwZMoSOHTuyYcMGvTk+++wzKlasyJo1a/QCmoaUKVNG7/2XX35JjRo1lIBmSEgIO3fuZPHixcybNy/PzyGekzJJIYQQQgghRJ741fMA4NfTd0lMyb6318rjwcrPV0KjiXtWImltZoxabfhLYMaOlSdfYsfK3MQnp9Fv5Vn6rDjDjbDYfF9//ZHumkpuEgwrqGrXrk2zZs0MvqpUqaJ3bvfu3Q2Wql24cIFevXoZHMtcYjhr1izi4uKUV4UKFfjzzz/1jmWIjo7mwYMHBAQEYGFhwaeffsovv/wCgKurK6tXryYuLo4RI0bwxx9/cPXq1SzPFhAQwK+//sqhQ4e4desWu3fvJjU1lR49enDv3j127dpFgwYNiI2N5dSpU/j7++Pv78/EiROJjY1V3o8YMSLXz9He3h5zc3PltX37dvr27at3zNzcnEWLFgG6INHEiROZM2cOO3bsULLkMhs1ahSDBw+mUaNGLFy4EI1Go4ylp6eTlpamvDJLTU1VAmGHDh2iePHiuLu7c/jwYaKioggNDeX06dN4enry66+/8vTpUwYNGpTl/rVq1WLUqFEGn7dQoUJKICyDubk56en6/w4uWrSIhIQEhgwZonc8NjaWjRs3Mnr06FwDYf/09OlT5s2bx8SJE5VjFStW5MyZM3Tq1Clfc4nnJDNMCCGEEEIIkSeNyzvj7mDB/aeJ7AkMo33VolnOiU5M5bdMfcWuPIh53i/MQIlkhtqlCgFw+UE0cclpBsspX1ZkXIry848HbjG3+zv5uj44Mh6AUk5WuZwp3kTjxo3D3d0da2trg+Ph4eGkpj7fLXX58uVKMCczDw8PxowZg7+/f5axzFln48aNU5rEA0RFRdG2bVuMjbP+btvZ2bFp0ya6detGfHw8CQkJxMfH4+7uTlxcnDKPtbU19vb2TJ8+Xa8Ub8OGDVhZWREfH8+7777LF198wZIlS5g6darSmwsgLS0Ne3t77Ozs8PT0BGDr1q0YGRkp7/NSApqUlMS1a9cM9jDL0L17dyVwNX78eJYsWcLu3bupXr16ttd89dVXlChRghEjRnDz5k1mz54NQLNmzfTOu3PnDgCPHz+md+/ePH78mEOHDlGiRAkAunTpwpo1ayhUqBAqlYrx48fj4eFB69at2bx5s8FnLF++PKVKlcr12UGXTbd3715lfQDXr19n3Lhx7NmzJ8vGCefPnyc5OZm0tDRq1qzJnTt3qFmzJvPnz8+SBfZPixcvxs3NTcnsA/IdUBNZSTBMCCGEEEIIkSdqtYpO77gxb/8tNv11n/ZVixKblIqNuYlyzo8HbpGQkk7pwlbce5JIdGIqV0N1jfQzn/dP7g6WSqDtXMhTGpYr/MrXH534PNARcDGUEc3KUTKPga10jVZp7l/CMed+aeLN0rlzZ86cOUNsbCxFi+oCuNHR0SQmJlKkiOGS3A4dOvD1119nKakMCgoiOjqa48ePM378+BzvO23aNL2AmZeXF4sWLaJ+/frKscxBDQcHB3x9fbGwsMDa2hpra2umT59Ow4YN6dOnD3Z2dhgZGWW5T2BgIEOGDGHVqlW0atWK0NBQPv74Y4oWLUrt2rVJSUlBo9GQnp7O6dOn8fb2xtPTE09PT5KTk5k+fTqWlpa0b9+e4sWL5/6BvoChQ4fSv39/KleuTHx8fI7nxsfH06RJEwoVKqQcO3DgAI0aNcpybsuWLWnYsCHbtm3DxuZ5r0FjY2O2b99OWFgYVlZWWFtbM3LkSNq3b4+xsTF169YlNDSUzp07M2PGDNRqNatXr87Ts9y4cYO2bdtSvXp1+vbtC+iCgz169GDkyJHUqlUrS8P/jN5gM2fOZPLkyVhZWTF27Fhat27N1atXlXLaf9JoNCxevJhPPvkkT2sTeSfBMCGEEEIIIUSedazuzrz9tzh8I5wxv19kw7n7zO1WjQ7vuHEyKJKfjgQB8FlLT+bvv8XlB9H8dPg2AKWdcw481SnlyIZz9zkZFPmvB8M0Wvjp8G2mdqqSwxXPPYxOJDVdi4mRClc7i1e+NvHv2bBhA1u2bGHbtm0sXboUgDVr1nD27Fnmzp2br7nWr19Pp06dOHXqFPfu3aNYsWKvdK0+Pj4ApKSkEBMTg5mZGWFhYRw5coSYmBhiY2OJiYmhXLlydOrUiadPn1K/fn2GDh2Kg4MDtWvXJjg4mN9++43AwEBq1KjBypUr6dChA0ZGRjRu3JhWrVop91u8eDFeXl6cPHmS+vXr89lnnzFs2LBX+kyAsiFAdHQ0Wq2Wn376iYCAAL0NCyIiIihatCgWFhY5Zpxl9vHHHzNp0qRsM6UyNgO4cOECv//+OxcvXuS9997j559/pmrVqrRo0YKNGzdmaYKfnc2bN9OnTx8aNGjA2rVrMTU1BXQ94uzs7JgwYYLB6zIy5L755hsaNGgAwLJly/Dy8uLEiRPKsX/atWsXoaGh0iD/XyDBMCGEEEIIIUSelXSy4p3i9py/G8WGc/cBOHE7ktaVXRn920W0Wuj2bjG8KxXhwPXHXH4QTXCkLqPqw+ruOc6dORj2b8gcDAP4KyQqz9eGPHuGYoUsMcqm75l4s23evJmzZ88Cuj5MHTt2xNfXl40bN1K2bFlAV3bXrl07Fi9enOX6p0+fMnfuXNatW0eFChX4/PPPc8wmGj16NOPGjVPeJyYm4u3tnW0ZYkbpXEajfVtbW6KioihSpAhXrlzBxsZGeWUEixwcHNi+fTt16tQhNDSUnj170q9fP6UUtE+fPhQrVgytVpvlfo8ePeK7777jl19+oWvXruzdu5fGjRsTHh6uV96ZnfLly+dYrpeamqqXBQcomW0hISF4eHjolYympqZibm6erxLAPn365Hp+RlP7adOm8fjxY6ytrZWNEnr06MGxY8fyFAxbsGABo0ePZurUqYwaNUq5b0hICGvXrsXExETpK5bR76xMmTL06tWLrl27Ku8zlCtXDoCwsLBs7/n777/TtGlT7O3tc12fyB9poC+EEEIIIYTIl07/CGo9iErk+qNYHkQlYmdhwpftKgJQqejzRvPONma5ZnvVLvmsb9j9aJJSs2/Q/6KiEnU9wzyL6Mqp7kTGk67JGiQwJKNfmIej9AsrqDp27MiFCxe4cOEC33zzDQArVqygWLFi7N27lwsXLlCiRAl69OiR5VqNRkO/fv2oW7cuTZo0YdSoUezbt4/ly5cbvFeTJk3Ys2dPrg30hw0bpgSEPDw8uH//PnFxcYSEhHDs2DGaNm1K3759+fbbbxk2bBgdOnTg3Xff5enTp0ybNo20tDTq1KnD4MGDKVOmDBMnTsTJyQkLCwvs7e0pV64c5ubm2NvbY2lpiZ+fn/I8Pj4+fPjhh0ppZLly5fjzzz/1Gtfn5Pr16yQlJWX7yinAdPz48Sy9wxISErLd6TOv7t27R0pKit6xpUuXYmZmRq9evUhKStLrC5eQkJBtiWJmFy9eZNSoUaxfv55PPvlELwBXtGhRAgMDuXTpkvL7lZGBuHv3biZPnkzNmjUxMTHh5MmTynUZmyBkBMX+KT09na1bt9KhQ4c8P7/IO8kME0IIIYQQQuRL5+ruXH0QjVqt4pdTd3kQlcjdZ/20yjhbK83vK2fadbFzDXeMjXL+b/HuDhZYmhqRkJLOw+ikPPfzyquMzLAKrrYEhceTkqYhNCqRYoVy/wJ+N1L6hRV0hjLD1Go1/v7+jBw5ktq1a+Pm5palN1VycjL9+vXj3LlznDt3DgBHR0dWrVpF69atiYmJYeTIkcr59vb2JCUlZbl/SkoKTZs2zZLJtHTpUpYsWcLkyZMJDw8nPT0da2trbG1tiYiI4ObNm5w5cwY7OztsbW2xs7MjMjISd3d3vcDVvHnz6N+/P5MmTcLc3Jxx48Yxd+5cHj16xLRp01ixYoXSy2rHjh0EBwfzxx9/EB4ersxRqVIlJk+eDKAXqHuVgoODOXHihLJbZoaYmJhcg2FarZY//vgj2wDR9OnTSUpKUoJRERERfPnll+zfvx8AT09PQkNDWbJkCZUqVWLx4sUsXLgQgN69e1OzZk2DGyOsX7+eYsWK4eXlxa1bt/TGSpcurWw+kOHRo0fKWMbOmf369WPEiBGYm5tja2vL8OHDadasGVWrViU6Opo2bdowY8YM6tatC8Dly5eJjIzk/fffz/EzES9GgmFCCCGEEEKIfLEwNWLah1W49yRBCYZlZE4VzxRYKl/EBitTIxJT0+nybu69lVQqFUVszQmKiCcs5t8LhhWyMqWEoyU3H8dxJyI+T8GwjOcrkYdzxZupY8eOzJ49WylrO3z4MJMmTWLixIksWbKELVu2cP36db1rzp49y4ABA0hJSeHo0aN6OzM2a9aMjRs30qNHDzZt2sSUKVNo2LAhUVFRBu9vqIF+Zu3atcPa2lovANWnTx8aNmyoZHRlmDJlCg8fPlR6VuVX48aNWbt2LVZWVnrBsMwWLFig/GyozPJFpKenM2TIELp27Zql31poaCjOzs4Gr9NoNMpnHBQUxJMnTwye9+jRIypVqqS8Hzt2LL1796ZiRV22qqWlJWvWrGHo0KHExsYyYsQImjdvDug2Isju/o8ePeLOnTtKOW1msbGx2e5QmtncuXMxMjLCx8eH1NRU2rZtq3zGCQkJBAYG6v1ZnDt3DktLy2wzx8TLkWCYEEIIIYQQ4oUUsTNHrYKUNA3n70YB6AWWzE2MWNWvNslp6XkObDnbminBsFctOkEXDLOzMKGkkxU3H8cRFB5Hgzw068/oGVbiFQfoxL9vwYIFzJ49m9jYWC5fvkzp0qWJjY3FwcEBJycn2rRpQ+HChfH29qZ27dr079+fnj17snTpUmbNmoWvry9z5swx2LepXbt2nDx5kjFjxtC4cWMCAgJo27btC63T0Pyurq6cP39eLxiWlpbGkSNH6N69OyEhIZQvX560tDSWLVuGv78/aWlpqFQqJk2aRHp6Olqtlrlz55Keno5Go+HXX3/lwYMH1K5dO8f13L17l4iICGJjY7l9+zZ2dnZ645n7Xxmi0Wj0An/x8fH069ePa9eucerUKbRaLbdu3cLZ2ZnU1FR+/PFHpZdX5msiIiLo0qULVlZWjB49mr59+2JkZISlpSWXL19W+qdFRERw6tQpvTJXQ2WsLVu2JCgoKMvx06dPZ/ssy5cvz7Yk1pBGjRplCSCamZmxYMECvSBjBldXVyIj9Xsl9uvXj379+uV6r3/uXCnyRoJhQgghhBBCiBdiYqTGxdach9FJnLqj+yJX/B+ZUzVKOORrziK25gD/TjDsWWaYvaUJpQpbA2HciYjP9TqtVqsEw6RnWMHTo0cPevTogaOjo3JszZo1HD58mICAAHr06IGfnx8qlYoBAwawcOFCQkND8fPzo127drmWqXl5ebFr1y6CgoIoVarUK137wIEDad++Pba2tkrj/bS0NKpWrUrnzp2xtbU1WJL5Kpw7d45+/fqhVqtp37491apV0xu/detWjrs+du/eXe/cxo0b4+rqyqFDh5QMrPfee4/w8HDUajX16tXjiy++0JsjMTERNzc3Bg8ezLBhw/Qy4T7++GM6d+5Merquv6BKpaJRo0a0bt36JZ9c/BeotK8q3/H/WUxMDHZ2dkRHR2Nra/u6lyOEEEIIIcR/UueFxzkb8lR5//vgutT0KPTC803dEcjiw0H0fa8kE5414n9Vei45yfHbkXzfvRrJqRrGbrzE+2WdWN0v5wyZxzFJ1Pp2H0ZqFYGTW2Jq/PbvQybft8Srtnv3bpo3b55lN02tVotGo1F2mvyn9PT0bMeEeFFv/7/iQgghhBBCiH+Nu4OF3vt/Zobll0tGZljsq892iXpWJmlrYULJwroMr7xkhgU/yworam/+nwiECfFvaNGiRZZAGOgyunIKdkkgTPwb5F9yIYQQQgghxAtzyxQMMzNWU9ja7KXmU4Jh0f9imaSFCaWe9f56EJVIUmp6jtc9iNIFw4o5SPN8IYR4G0gwTAghhBBCCPHC3OyfB4iKFbJErVa91HxF7HTBtH8jMywm8XkD/UJWptiaG6PVPm+On524ZF2wzMZcWi6L1yejGb4Q4uVJMEwIIYQQQgjxwjJnhpV4yRJJAGebjAb6ya/0i39auobY5DRAFwxTqVSULGwNwJ2IuByvTUrRBcMsTKRcS0CtWrXyvLNgQoLhQGtycrLB40lJSbi4uLBr1y6946GhoZibmxMSEpK/xeZi3LhxfP311690TiEKAgmGCSGEEEIIIV5Y5p5hxV5BMCyjTDIlTaP0+MrJxXtRnL/7NNfzYpLSlJ/tLEwA8HDUrTe3zLDEZ2WUFqaSGVaQ1a9fn/Lly1OtWjWDr5IlS+Ln55frPBqNxmDvq39KSEigUqVKbNu2Te94TEwMFStWJCAgIMs1K1euJCEhgcaNG+sdX7p0KeXKlct290Z7e3tUKlWurzJlyuS45mrVquHk5ESRIkWUV+HChXO9Lr+aNm2KsbH+36eoqCh8fHywtbXFycmJsWPHotFolPEJEyZkeZ6ZM2fqzREeHo6fnx+Ojo5YWFjQoUOHbNdw+PBhatSogbm5OZUqVWL37t1641euXKFBgwZYWFhQqlQpVq9e/fIPLt4Y8q+5EEIIIYQQ4oW52T8Phr1s83wAU2M1jlamRMan8CgmCQcr02zPjUtOo/tPJ1Gp4PyE5pgZZ5+5ldEvzNrMGGMjXSDD6Vl/sycJKTmuSQmGSWZYgbd+/XqqVatmcGzDhg1ZAleG5DUYZmlpyU8//USXLl0IDAzE1dUVAH9/f6pUqULbtm31zo+Li2P69Omo1Wrc3NyU42XKlCE4OJjk5GQcHR1RqZ6XIoeEhGBlZUVkZGSeMikzX5udvXv36n1GwcHBNGvWLNfr8mrnzp3s378/S2P8nj178vjxY/78808ePHhA7969laAYwJMnT+jUqRPTp09XrnFyclJ+jo2NpUGDBhQvXpzNmzdja2vLzZs3Da7hzp07tG7dmmHDhrFixQoWLlxIx44d+fvvv/Hw8CAmJobmzZvj7e3NggULCAgIwM/Pj7Jly1KnTp1X9lmI10eCYUIIIYQQQogXZm5ihJO1KRFxKa8kGAbgbGtOZHwKYTFJVHC1VY6fCoqksI0ZpZ6VN168F6UEqqISUnGxzT5YFfUs4JWRFQbgYKn7OSo+5wy0xIwySVMprCnofH19sbQ0/Hv65MkT6tWrl+scWq0212BY//79Wb16NSYmut+xsmXLkpiYiJmZmXKtra0t8fHxJCYmYm5uzuDBgylcuDBXr15l4MCB9OzZk1atWjFkyBC0Wi3Hjh3Dx8eH1q1b4+vrq3e//O64eOjQIUaPHs2DBw9Qq9Vs2bKFxYsX52uOF5GYmMjw4cPp3LkzmzdvVo5funSJnTt38tdff/HOO+8AcObMGX744Qe9YJiHh0e2WWrTpk1Do9GwdetWTE11QfTsAp/z58+nTJkySmBt3rx5BAQE8PPPP/PVV1+xYsUK0tPTWbJkCaamplSpUoVt27axcOFCCYa9JeRfcyGEEEIIIcRL6VazGJ5FbKhZstArma+I7bMm+jFJ3H+agFar5dbjOHosOUm3n06SkqYrnToX8rw8MqM5fnaiMzXPz2BvqfvC/DS3zDDpGfbWqF27Ns2aNTP4qlKlit653bt3N1hqeOHCBXr16mVwbMWKFcr1s2bNIi4uTnlVqFCBP//8U+9YhujoaB48eEBAQAAWFhZ8+umn/PLLLwC4urqyevVq4uLiGDFiBH/88QdXr14FdIG5pKSkfL8qVqzIzJkzady4MS1btmTmzJmULl0agGbNmumVSdasWVPvc6lVqxajRo16oc9/0qRJVKpUiTZt2ugdP3DgAC4uLkogDKBJkybcvXuXhw8fArpgWOZMsH9asWIFw4cPVwJhOTlw4AAtW7ZU3hsbG9OgQQNOnjypjDdp0kRvriZNmijjouCTzDAhhBBCCCHES/m0hSeftvB8ZfNl9A2bvus6T+IvM7trVeKS09BoITw2mb2BYbSu7MpfmXqFxSTlPxhWyCqPwTDpGfZWGDduHO7u7lhbWxscDw8PJzX1+e/R8uXLWbRoUZbzPDw8GDNmDP7+/lnGMmedjRs3jkmTJinvo6KiaNu2bZZeWQB2dnZs2rSJbt26ER8fT0JCAvHx8bi7uxMXF6fMY21tjb29PdOnT2fVqlVcvXqVypUr5/UjUNy5c4dGjRqxa9curK2tadSoEQB//vknDg4OSkYbQFpaGqGhocr78uXLU6pUqXzf8/DhwyxevJiLFy9y4MABvbGgoCBKliypd6x48eIA3L9/H1dXVyIjI5kwYQLTpk2jTJkyDB06lL59+6JSqbh79y6hoaHY2trSqFEjLl++TMWKFZkzZw7vvvtulrVkd7+LFy8q461bt84yfv/+/Xw/t3gzyb/mQgghhBBCiDdKRjDsSbwuSLX5/ANsMwWx1p+5R8tKRTh/N0o5FpOYRk4ygmH2lpkzw3Q/P82lUb/0DCvYOnfuzJkzZ4iNjaVo0aKALhMrMTGRIkWKGLymQ4cOfP3111lKKoOCgoiOjub48eOMHz8+x/tOmzZNL2Dm5eXFokWLqF+/vnIscw8vBwcHfH19sbCwwNraGmtra6ZPn07Dhg3p06cPdnZ2Wcohvby8lF5hBw4cwMfHh+DgYL1gVm5SU1M5dOgQN27cYNy4cQavTUtLY968efTs2fOFGslHRETg4+PDnDlzKFGiRJbxuLi4LJ91xvuMnTeXLFmCWq0mJiaGgIAABgwYQGJiIv7+/kr22MyZMxk/fjzu7u58++23tGjRgps3b1KoUKE83S/jXrmNi4JPgmFCCCGEEEKIN0pGMCzDqaAn2Jg//+py+GY4R29FKAEuyENmWELWzDCHjDLJ+DyWSUrPsAJpw4YNbNmyhW3btrF06VIA1qxZw9mzZ5k7d26+5lq/fj2dOnXi1KlT3Lt3j2LFir3Stfr4+ACQkpJCTEwMZmZmhIWFceTIEWJiYoiNjSUmJoZy5crRqVMnvWvnzp3L4MGDUalUpKUZDg6r1WrUajX37t3j999/Z/v27fz9999s376dTp060a5dO1asWIFWq+WHH35g6NChqNVq+vfvT0pKzn9PAFatWkXfvn2V971792bx4sV06dJFCeoZYmZmlmX+pKQk4HlQLHP/r/r16xMZGcnChQvx9/dXnnf06NF06dJFWYuzszPbtm3L0mMtu/tl3Cu3cVHwyb/mQgghhBBCiDdKBVcbAMq5WONmb0FKuobI+BRMjdRUL26PVgufb76sd01MUt4ywwyVSUYlpua4E59khr0dNm/eTLVq1ahWrRpffPEFoGuob2VlpRwvWrQogwYNMnj906dPmTt3LsOGDcPPz4/PP/88x/uNHj1ayfCytrYmMDAQb29vvWOZ3blzBycnJ6ysrChSpAjvvvsue/bsISAggMWLF7Nt2zb++usvwsLCDAamTp06xYQJEzAxMcn29dVXXwFw7tw5Tp48ibOzMxMnTuTs2bNKWSLoAj8jRozI066ZmX3wwQdcuXJFeX399dccO3aMgwcP8ttvv2Fubo65uTkDBgwgPT0dc3Nzvv76a9zc3Lh3757eXBnv/1nOmKF69eqEhIQA4OzsDKDXXN/BwQFnZ2fCwsKyXJvd/TLKP3MbFwWfBMOEEEIIIYQQb5R3ijuwcUg9NgypR9MKzsrxKu52+Dcpg0oF958m6l2TWwP9qIxgmIEyyXSNNsdg2vPMMCmsKcg6duzIhQsXuHDhAt988w2ga7perFgx9u7dy4ULFyhRogQ9evTIcq1Go6Ffv37UrVuXJk2aMGrUKPbt28fy5csN3qtJkybs2bMn1wb6w4YNU3qIeXh4cP/+feLi4ggJCeHYsWM0bdqUvn378u233zJs2DA6dOjAu+++y9OnT5k2bZpeBtijR4/QarXZvho3boy7uzugKwP97bffqFmzpsGAV1hYWLYlpDmxs7PD09NTebm6ulKzZk0CAwO5ePGi8vlPnjwZIyMjLly4wODBg6lfvz4hISHcvHlTmWvfvn1Ur14dBwcHg/c6ffq0EvwqXbo0zs7Oeg3uIyIiePz4MeXKlctybf369dmzZ4/yPj09nYMHD9K0aVNlfP/+/aSnp+utJ2NcFHzyr7kQQgghhBDijVOjhO4LcGNPZ1ad0GV/1CpZiCaeLqzqW4tPfrtIeGwyFVxtCXwY80IN9M2MjbAyNSI+JZ2n8Sl6Y5klSWbYW2Hz5s2cPXsW0GV5dezYEbVajb+/PyNHjqR27dq4ubkpzeQzJCcn069fP86dO8e5c+cAcHR0ZNWqVbRu3ZqYmBhGjhypnG9vb6+U+GWWkpJC06ZN9fqEASxdupQlS5YwefJkwsPDSU9Px9raGltbWyIiIrh58yZnzpzBzs4OW1tb7OzsiIyMxN3dHY1Gk+fnv3fvHh4eHnk6d/v27aSkpHDy5Enq1KmDiYmJ0q+sd+/e1KxZ0+AGAoZYWlri6am/wYarqyuAcrxBgwbUqFGDvn37MmfOHIKDg5k3bx4rV65UrvHz86N3797KZgOrV6/m119/BXTln5988glTpkyhaNGilCpVivHjx1OuXDlat26NRqOhdevWDBw4kE6dOjF8+HBq1arF5MmT6dSpEz/++CMajQY/Pz8A+vfvz6xZs/D392fYsGFs3ryZy5cvs27dujw9s3jzSTBMCCGEEEII8caqW8oRM2M1yWkaapbUNcF+v2xh9o5qSFBEHIdvROiCYYlpxCalcjb4Ke+VccLUWD/bxVDPMAB7S1PiUxJ5mpCCB1YG15CQIsGwt0HHjh2ZPXs2ISEhrF27lsOHDzNp0iQmTpzIkiVL2LJlC9evX9e75uzZswwYMICUlBSOHj2Kk5OTMtasWTM2btxIjx492LRpE1OmTKFhw4ZERUUZvL+hBvqZtWvXDmtra73dJvv06UPDhg2VIE2GKVOm8PDhQ0xNTfP07ElJSYSEhFC2bNlsz/Hx8cHHx4dr164xdepU/P39adeuHYMHD2bevHlKY/3AwEClLPFVUalUbN68mf79+1O/fn1cXFyYOXMmH374oXLOgwcP6NixIxqNhkqVKrF9+3ZatWqljI8dO5aEhARGjBhBTEwMjRs3Ztu2bZiYmJCcnExgYKCyK+Y777zDunXrGDduHN9++y21atXizz//xMZGV6Lt5ubG1q1b8ff3Z/ny5VSqVIkdO3YYbP4vCiYJhgkhhBBCCCHeWOYmRkz5wIsrodG8X+Z5IMLO0oR3ijtw8V4UoGugP3//LX46HERNDwfWDaiDsdHzgFhEnG4XOCdrM735HaxMeBCVSFQOO0oqPcNMJRhWEC1YsIDZs2cTGxvL5cuXKV26NLGxsTg4OODk5ESbNm0oXLgw3t7e1K5dm/79+9OzZ0+WLl3KrFmz8PX1Zc6cOdjb22eZu127dpw8eZIxY8bQuHFjAgICaNu27Qut09D8rq6unD9/Xi8YlpaWxpEjR+jevXu2cwUHB6NSqbCxsUGr1TJr1izc3NyUzLCUlBSMjIx4+PAhlSpVAiAkJISVK1eyYMECpk+fTp8+fejfvz9+fn7UrVuXNWvW4OnpyenTp1/o+TLz8/PLEuArVqwYu3fvzvaazGWNhqhUKr766iulL1pmZmZmSn+xDJ07d6Zz587ZztewYUMuX76c7bgo2KRnmBBCCCGEEOKN1rVmMSZ/4KUX3Mpg+yzTKyYxlZNBkQCcCX7KhICreueFxejK1v65U2XGjpJPcthRUoJhBVuPHj04c+YM4eHhnDp1il9++YVu3brh5uZGQEAAXbp0Yc+ePcyYMYP9+/cTHR1NaGgofn5+HDx4kJ9//tlgoCqDl5cXu3bt4tatWy8cCMvOwIEDOXDgALa2ttjb2yuv+Pj4HAM5s2fPplSpUjg6OuLk5MSGDRv0Sg7PnDmDqakpW7dupXnz5kyfPp3y5ctz6dIl9u7dq+z66Obmxu7du2nWrBnDhw9/pc8mxOuk0ua0bcobLCYmBjs7O6Kjo7G1tX3dyxFCCCGEEEK8Bnv/DqP/qrNULWZPWrqGq6ExytiBMY0o6WRFfHIalSbqMk6uftUCK7PnBTLD150n4GIo49tUoP/7WXeKS9doKf35DgD++rK5sgPl206+b70dNBoNGo1Gr/TSkKSkJNLS0rLscJlZamqqUiopREEnmWFCCCGEEEKIAisjMyw2MZXw2GS9sQfPdpx8/Oy4lamRXiAMwOHZjpJPEwxnhmVkhYH0DBMFj1qtzjUQBmBubp5jIAyQQJh4q0gwTAghhBBCCFFg2Vrovug/TUhR+oKVddZ9qc94n1Ei6fyPEkkAh2eZXk+z6RmWmPI8GGZuIl+fhBDibSD/mgshhBBCCCEKLBvzjMyuVDRaUKugXBHdjnAZwbCMzDBnG7Ms12f0DIvKJjMsKfX5TpIqlerVLl4IIcRrIcEwIYQQQgghRIFla65fAuZobaYEvSLidAGux3nIDMuugX5CijTPF2+G9PR0CmjL71y9rc8l3lwSDBNCCCGEEEIUWFamxqgzJWw525jhZK0LhkU+ywwLzzEzTJdZFpVdmWSmzDAhAGrVqsXy5cvzdG5CQoLB48nJyQaPJyUl4eLiwq5du/SOh4aGYm5uTkhISP4Wm4tx48bx9ddfv9I58+LAgQNMmjQJgFu3buHl5ZXtZ5IdlUpFcHDwq1+c+E+QYJgQQgghhBCiwFKrVUqpJGQEw3TZXv/sGeZim32ZZLYN9CUz7K1Rv359ypcvT7Vq1Qy+SpYsiZ+fX67zaDQa1Orcv0onJCRQqVIltm3bpnc8JiaGihUrEhAQkOWalStXkpCQQOPGjfWOL126lHLlyuHh4WHwXvb29qhUqlxfZcqUyXHN1apVw8nJiSJFiiivwoULK9etWLEiT/fJ/PLy8sryuQwcOBAnJycAypQpg4ODA/Pnzze4Jg8PDw4ePJjjug2ZOnUqRYoUwdnZmenTp+uNffnll/Tu3Tvfcxpy6tQp3nvvPczNzSlRogTfffed3vjff/9Ns2bNsLCwwM3NjVmzZmU7V0hICB9++CG2trY4OjrSqVMnvQDokydP6Nu3L46OjtjZ2dG8eXMuXbr0Sp7jvyb3bSWEEEIIIYQQ4g1ma2FMdKIus8vZxhxHq2eZYc9KH5/3DMuhgX58KlqtNktfsCTJDHurrF+/nmrVqhkc27BhQ5bAlSF5DYZZWlry008/0aVLFwIDA3F1dQXA39+fKlWq0LZtW73z4+LimD59Omq1Gjc3N+V4mTJlCA4OJjk5GUdHR73f0ZCQEKysrIiMjMxTqWFe+t7t3btX7zMKDg6mWbNmAPj5+RkMGI4fP57o6OhsA1qZDRgwgKJFizJ06FDl2Jw5c2jSpAnvv/8+tWvXznUOQzQaDUuWLMHX15fTp0+zePFi1q9fT0pKCj169KB169ZUrlyZmzdvsmTJklcSRIqKiqJFixZ07tyZH3/8kUuXLjFw4ECcnZ3x8/MjLi4Ob29v6tWrx5EjRzh+/DijR4/G2dmZXr16ZZnvs88+o1SpUhw8eJDo6GhGjx5Nu3btuHDhAmq1mm+//Ra1Ws327dvRarVMmDCBli1bcu3aNWxtbV/6ef5LJBgmhBBCCCGEKNBszU2ARACcbc1wyugZFpuXBvq6rLKUdA0JKelYmel/RVJ6hkkw7K3g6+uLpaWlwbEnT55Qr169XOfQarW5BsP69+/P6tWrMTHR/X6VLVuWxMREzMzMlGttbW2Jj48nMTERc3NzBg8eTOHChbl69SoDBw6kZ8+etGrViiFDhqDVajl27Bg+Pj60bt0aX19fvfsZGeXv9/PQoUOMHj2aBw8eoFar2bJlC4sXL87XHJldvHiRbt265XreV199xb59+zh//rzeZ1izZk0mT55M+/bt2bZtGzVr1szX/bVaLYMGDWLPnj20atWK06dP07VrVxo2bAhAkyZNuHbtGpUrV2bYsGFMnDgRZ2fn/D2kATdv3iQ6Opo5c+ZgY2ND1apV2bx5M2fOnMHPz49Vq1aRlJTEypUrsbCw4N133+XKlSvMmTPHYDBsxowZFCtWTHm/YMEC3nvvPW7evEn58uUZMWKE3vjKlStxc3PjxIkTtGjR4qWf579EyiSFEEIIIYQQBZrtP8okHZ9le0XEp6DVapUySUMN9C1MjDA11n0tMtREX+kZJmWSb4XatWvTrFkzg68qVarondu9e3eDZX8XLlygV69eBsdWrFihXD9r1izi4uKUV4UKFfjzzz/1jmWIjo7mwYMHBAQEYGFhwaeffsovv/wCgKurK6tXryYuLo4RI0bwxx9/cPXqVUAXBEpKSsr3q2LFisycOZPGjRvTsmVLZs6cSenSpQFo1qyZXpmkocCUvb095ubmymv79u307dtX75i5uTmLFi1S1jlx4kTmzJnDjh07lCy5zEaNGsXgwYNp1KgRCxcuRKPRKGPp6emkpaUpr8xSU1OVQNihQ4coXrw47u7uHD58mKioKEJDQzl9+jSenp78+uuvPH36lEGDBmW5f61atRg1alSOvz//VLVqVTw9Pfnxxx9JTU3l3LlzHDt2jM6dOwO6EklPT08sLCyUaxo1asTFixcN9kjLHOgCMDc3V54/L+Mi7yQzTAghhBBCCFGg2Vo8/1pT2MZcaaCfkqYhPC6Z2CTdl2dnAz3DVCoVRWzNufskgdCoRIoV0s8akgb6b49x48bh7u6OtbW1wfHw8HBSU59vpLB8+XIlmJOZh4cHY8aMwd/fP8tY5qyzcePGKU3iQVdS17ZtW4yNs34Nt7OzY9OmTXTr1o34+HgSEhKIj4/H3d2duLg4ZR5ra2vs7e2ZPn06q1at4urVq1SuXDmvH4Hizp07NGrUiF27dmFtbU2jRo0A+PPPP3FwcFAy2gDS0tIIDQ3Vuz4pKYlr165l28MMdMHEjMDV+PHjWbJkCbt376Z69erZXvPVV19RokQJRowYwc2bN5k9ezaAUqaZef0Ajx8/pnfv3jx+/JhDhw5RokQJALp06cKaNWsoVKgQKpWK8ePH4+HhQevWrdm8ebPBzL7y5ctTqlSpbNdmiKmpKb/88gvvvfce//vf/9BqtYwfP17p+ebo6Mi9e/f0SrBjYmLQaDRERkZStGjRHOdfsmQJ7u7ulCtXLttxCwsL6tSpk691ixcIhmm1WiZPnszixYuJjo7G29ubn376icKFC+ud5+HhYXCniyZNmrBv3z4Aihcvzr179/TGw8PDlUZ6QgghhBBCCJEbvQb6tmZYmBphZWpEfEo6gQ9jATA3UWNjZvjrT0knK+4+SeBORDy1SznqjSVJA/0Cr3Pnzpw5c4bY2Fgl+BAdHU1iYiJFihQxeE2HDh34+uuvs5RUBgUFER0dzfHjxxk/fnyO9502bZpewMzLy4tFixZRv3595VjmHl4ODg74+vpiYWGBtbU11tbWTJ8+nYYNG9KnTx/s7OyylEN6eXkpvcIOHDiAj48PwcHBesGs3KSmpnLo0CFu3LjBuHHjDF6blpbGvHnz6NmzZ57nzWzo0KH079+fypUrEx8fn+O58fHxNGnShEKFCinHDhw4oATsMmvZsiUNGzZk27Zt2NjYKMeNjY3Zvn07YWFhWFlZYW1tzciRI2nfvj3GxsbUrVuX0NBQOnfuzIwZM1Cr1axevTrfz/Xo0SPatm3LRx99RP/+/bl06RKffPIJlSpVonv37nTs2JGvv/6ab775hjFjxnDjxg2lwX5Opa0ZcZeffvqJLVu2GAygLl26lC+++ILvv/9e77MSeZPvYNiMGTOYN28eK1aswNHRkf79+9O7d2927Nihd96hQ4f0ouppaWk0bNiQ7t27K8eePHnC2rVrqVWrlnJM/hCFEEIIIYQQ+fHPMkkAJxsz4iMT+Ds0BgAXW/Nsm4eXdLLi0I1wgiKyfknP6BlmLplhBdaGDRvYsmUL27ZtY+nSpQCsWbOGs2fPMnfu3HzNtX79ejp16sSpU6e4d+9elrK1l+Xj4wNASkoKMTExmJmZERYWxpEjR4iJiSE2NpaYmBjKlStHp06d9K6dO3cugwcPRqVSZSklzKBWq1Gr1dy7d4/ff/+d7du38/fff7N9+3Y6depEu3btWLFiBVqtlh9++IGhQ4eiVqvp378/KSmGd1zNi4wNAaKjo9Fqtfz0008EBATobVgQERFB0aJFsbCwyDHjLLOPP/6YSZMmZft328XFBYALFy7w+++/c/HiRd577z1+/vlnqlatSosWLdi4cSNdunTJ8T6rVq2ib9++yvvevXuzbNky5syZg4ODAz/99BMqlYratWsTFhbGmDFj6N69O9WqVWPp0qWMGDGCCRMm4OTkxEcffcSCBQuyjX1ERUXRu3dvDhw4wJYtW2jXrp3eeFJSEsOHD2flypX8+OOPDBw4ME+fldCXr2CYRqNhxowZjB8/XvkDmT17Nq1ateLOnTuULFlSOTcjPTHDsmXLsLCwUHaeSElJIT4+nsqVK+e6vasQQgghhBBCZEe/TFIXDHO0MiUkMoG/H+qCYYaa52coXdgKgKDwrMGwjDJJS8kMK/A2b97M2bNnAXj69CkdO3bE19eXjRs3UrZsWUBXdteuXTuDzeSfPn3K3LlzWbduHRUqVODzzz/PMZto9OjRjBs3TnmfmJiIt7d3ts3379y5Q82aNZVG+7a2tkRFRVGkSBGuXLmCjY2N8jIULDp16hQBAQFMmDAh2zVNmDCBr776inPnznHy5EmcnZ3p0qULEyZMYM2aNdy4cQPQBVxGjBhhsBQ0Q/ny5XPcnTI1NVUvCw6eZ0OFhITg4eGhl/GUmpqKuXn2QWtD+vTpk+v5Wq2WIUOGMG3aNB4/foy1tbWyUUKPHj04duxYrsGwDz74gCtXrijv7ezsALh06RJVq1bVW0P16tV58OABT548oVChQvTp0wdfX18ePHiAq6src+fOpVKlSgYz8MLDw2nUqBGWlpacP39e6eOWITExkRYtWvDw4UNOnDiRY8mpyFm+gmGXL18mIiKCVq1aKccaNmyIWq3m5MmTesGwzNLS0vjmm2/4/PPPlT/wJ0+eAEhJpBBCCCGEEOKlZGSG2VuaYGas+7Lt+Kxv2N+h0QA422Rtnp+hpJOuh1RQRFyWsSTpGfbW6NixY5bMsBUrVnD69Gn27t2Lk5MTdevWpUePHlmu1Wg09OvXj7p169KkSROqVq1K5cqVWb58uV7GUIYmTZrg6+tLgwYNlGOGyiT9/f2VgJCHhwf379/HzMyMuLg4YmJiGD58OLVr18bb25uYmBhiYmKIjo7m6dOnTJs2jTFjxijXP3r0KMfnb9KkCe7u7oCuDLRDhw6MGzfOYHAuLCws2xLSDNevX8+1Z1h2jh8/nmU3xYSEhGx3+syre/fu4eLigqmpqXJs6dKlmJmZ0atXL/766y+9CraEhIQ8lZTa2dkpAbDM3NzcOHfunN6xy5cvY2VlpXe+kZERxYsXJy0tjSVLlhj8nQEYMmQIdnZ27N+/X2mOn9mXX36pbAggVXUvJ1/BsKCgIAC9oJeFhQWFCxfm/v372V63YcMGoqKi9H7ZIyMjlbkcHR2pUaMGX3/9dZYdPDIkJyfr7bYQExOTn6ULIYQQQggh3lK2Frovs5mzvzKa6N9+lu1V2tlw03SAUs8yw+5GJpCWrsHY6HlwIFF6hr01DGWGqdVq/P39GTlyJLVr18bNzS1Lb6rk5GT69evHuXPnlMCHo6Mjq1atonXr1sTExDBy5EjlfHt7e5KSkrLcPyUlhaZNm2bJZFq6dClLlixh8uTJhIeHk56ejrW1Nba2tkRERHDz5k3OnDmDnZ0dtra22NnZERkZibu7u96Oi7m5d+9enssPt2/fTkpKCidPnqROnTqYmJjk2OMqP4KDgzlx4oSyW2aGmJiYXINhWq2WP/74gw4dOhgcnz59OklJSUrQMyIigi+//JL9+/cD4OnpSWhoKEuWLKFSpUosXryYhQsXArrSx5o1a+aYDfdPQ4YMoU6dOowaNQpfX18uX77Mt99+y5AhQzAyMiI5OZk1a9ZQs2ZNYmJimDZtGsbGxnz88ccA3L59Gx8fH9asWYOrqyt//PEHM2bMyBJfcXBwwNHRkV9++YWPPvqIJ0+eKAlGoNtYIbfgpdCXr2BYXFwcarUaMzP9FGNLS0uD24Jm+PHHH+nTp4/edqLFixfn5MmTWFpaEhwczLRp02jYsCGXL19WotWZTZ06la+++io/yxVCCCGEEEL8B3gWsUGlgqru9soxJ2tTvXPaVHbN9voituaYm6hJStVw72kiJZ2slLGEVOkZ9rbo2LEjs2fPJiQkhLVr13L48GEmTZrExIkTWbJkCVu2bOH69et615w9e5YBAwaQkpLC0aNH9SqbmjVrxsaNG+nRowebNm1iypQpNGzYkKioKIP3N5QZllm7du2wtrbWKx3s06cPDRs2VNoNZZgyZQoPHz7Uy4DKSVJSEiEhIUo5qCE+Pj74+Phw7do1pk6dir+/P+3atWPw4MHMmzcvX035s5Oens6QIUPo2rVrln5roaGhODs7G7xOo9Eon3FQUJBeICizR48eUalSJeX92LFj6d27NxUrVgR0sYs1a9YwdOhQYmNjGTFiBM2bNwcgMDAw2/tnp0aNGmzfvp0vvviChQsXUrhwYUaOHMmXX36pnPPjjz/y999/Y2lpSbt27Vi2bBlWVrp/Y6KjowkMDCQ6OhoTExPS0tIYNWoUo0aN0rvP6NGjmTlzJo8ePWLmzJnMnDlTb/zDDz9kw4YN+Vr7f12+gmFmZmZoNBrS0tL0/oImJSVlG8H9+++/OXLkSJaaaxsbG2rXrg1A5cqVadSoEcWLF2fdunV8+umnWeb53//+xyeffKK8j4mJeeXNCoUQQgghhBAFj5ebHaf+11QpjYTnmWEA5VysKV/ExtClAKjVKjwcrbj2KJY7EXF6wbCMzDDpGVZwLViwgNmzZxMbG8vly5cpXbo0sbGxODg44OTkRJs2bShcuDDe3t7Url2b/v3707NnT5YuXcqsWbPw9fVlzpw52NvbZ5m7Xbt2nDx5kjFjxtC4cWMCAgJo27btC63T0Pyurq6cP39eLxiWlpbGkSNHcixDDA4ORqVSYWNjg1arZdasWbi5uSmZYSkpKRgZGfHw4UMleBQSEsLKlStZsGAB06dPp0+fPvTv3x8/Pz/q1q3LmjVr8PT0VO6RW+9vjUajF/iLj4+nX79+XLt2jVOnTqHVarl16xbOzs6kpqby448/Kr28Ml8TERFBly5dsLKyYvTo0fTt2xcjIyMsLS25fPmy8kwRERGcOnVKr8x1+fLlWdbVsmVLpeots9OnT+f4PNnx9vbG29vb4JiZmVmWMsrMqlevrhc8zdgZNDv5yQQUOTPcuS8bGTtAZE7ZS05OJjw8nFKlShm85vfff6dChQpUqFAhx7ltbGwoU6YMISEhBsczGghmfgkhhBBCCCEEgLOtOUbq5+Vnjpkyw9pVKZrr9aULP+sb9o8m+tIzrODr0aMHZ86cITw8nFOnTvHLL7/QrVs33NzcCAgIoEuXLuzZs4cZM2awf/9+oqOjCQ0Nxc/Pj4MHD/Lzzz8bDFRl8PLyYteuXdy6deuFA2HZGThwIAcOHMDW1hZ7e3vlFR8fT+fOnbO9bvbs2ZQqVQpHR0ecnJzYsGEDK1euVMbPnDmDqakpW7dupXnz5kyfPp3y5ctz6dIl9u7dS58+fQBdDGD37t00a9aM4cOH693j1q1bpKWlZfvq2rWr3rmenp4EBQVx6NAhnJ2dUalUvPfee9jb2+Pi4kJCQgJffPGF3j0SExNxc3Nj/Pjx3Lp1ixEjRmBjowtsf/zxx3Tu3BljY2OMjY1xdXXF09OT1q1bv/TnLt5+Km1uocdMEhMTcXR05Pvvv2fAgAEA/Pnnn7Rt25ZHjx4ZbOBWrVo1Wrduzbfffpvj3FFRUZQoUYKvvvpKr946OzExMdjZ2REdHS2BMSGEEEIIIYSek0GRdP/pJAAHxjTSy/YyZObu6yw4cItmFVyoU6oQ7asVxdnGnA8XHudcyFMW+dSgpdd/pyePfN96O2g0GjQajV5llyFJSUmkpaVhbZ19b73U1NSXKpXcvXs3zZs3z9KwX6vVotFosu1Jlp6e/sr6lQmRIV9lkhYWFgwZMoQJEyZQvHhxrK2tGTFiBIMGDcLe3p6WLVsycOBAOnXqBOgCXBcvXmTq1KlZ5tq+fTvXr1+nUaNGPH36lEmTJmFjY0Pv3r1fzZMJIYQQQggh/rMquNpS2MaMasXscw2EwfMm+nsDw9gbGMaDqEQmtqtEgpRJigJMrVYb3C3ynwztXPhPL9szrEWLFgaPq1SqHINdEggT/4Z8BcMAvv32WxITE+natStGRkb4+PgwY8YMUlNTCQwMJDQ0VDk3ozb2nXfeyTKPvb09S5Ys4YsvvsDR0ZFGjRqxfv16HBwcXuJxhBBCCCGEEALsLEw4/XlTNHmsg/Esop/9FBKZAGQqk5RgmBBCvDXyVSb5JpG0XSGEEEIIIcSrtOPyQ24/jmPWnhuUd7Fh96gG1Pl2H49iktj2cX283Oxe9xL/38j3rVevoJT7paSk5HmXSiEKqnw10BdCCCGEEEKIt1Xryq60quwKQGhUIgCJzzLDzKWB/ltn8uTJxMfH534iEBcXx19//cW6deuYNGkSqampeuNbtmyhUaNG2V5/7tw5KlSoQEJCwsss+ZWbO3cu/v7+esc++eQTPv300xyv69y5M0uXLs1y/P79+zluNpCZVqulUaNGFC5cOMvnmV9btmyhTp06eseuXLnCZ599lusOjeK/SYJhQgghhBBCCPFMUXtd76TY5DRiklJJlJ5hBd7BgwdZsWJFltemTZsYPHiwwbHz58+zevVqmjVrRrFixbCxsaFGjRr07NmTr776ihs3buRrDdWrV8fJyYmtW7fmeN6KFSswNjbG2to625eZmRl+fn7KNePHj0elUuX5NW3atGzvr9Vq2bx5M/Xr18/X872I77//nps3b+Lo6MjEiRMNnnP58mXatm2r99q8eXOe5tdoNMybN48hQ4ag1WoxNjbO9jNxd3fP05z79u2jdu3a2NjY4OrqyvDhw0lKSlLGnzx5Qt++fXF0dMTOzo7mzZtz6dIlg3Olp6czYMAA3N3dsbKyolatWhw6dEjvnL///ptmzZphYWGBm5sbs2bNytM6Re7y3TNMCCGEEEIIId5WlqbGOFia8DQhlbuRCaSkawCwkMywAispKYm4uDjGjRunFwjq378/oMv6+qeUlBQcHByoWLEiTZo0YevWrdSrV49BgwZRvHhxzM3NmTRpEklJSVmCSx4eHoSEhBhcy4kTJ+jevXuW4zdv3qRMmTIAdOjQgQ0bNmT7PHPnzuXChQvK+0mTJjF+/Hi9cx48eECZMmWIiorCzMwM0AWH1Gp1jo3wjx07RmhoKD///DPr1q3TG/P19aVy5cpcv36dx48fExgYyN69e9FoNLRv3145Lzk5Wa8h/+3bt3Fzc9ObKyAggP/973/s3bsXe3t76tSpQ8WKFfHx8dE7LzIyksuXL/PDDz8oz3779u1s159ZlSpV+P7771m0aBHp6ekkJycbzBK7evUqbdq0ydOcgYGBDBw4kHfffZfAwEAGDRqEhYUF06dPB3Q91tVqNdu3b0er1TJhwgRatmzJtWvXspQbp6WlkZyczJo1a7C2tmbatGm0a9eOmzdv4uLiQlxcHN7e3tSrV48jR45w/PhxRo8ejbOzM7169crTekX2JBgmhBBCCCGEEJkUtbfgaUIqQRHPS+ikgX7B1bJlSwC+/PLLHINMAIULF+b3339X3rdt2xaAGzduULZsWcqVK5fr/YKDg5Wfk5OTCQ0NpWTJksqxR48ekZSUhIeHh8Hrt2zZgrW1dbbzp6am0qNHD+W9sbExX375Jc7OzowaNQpACYCZmZkpganevXtTqVIlxo4dm+3cy5cv5/3336datWp6xzdu3MidO3dISEhgwYIFBAYGEhYWxrlz5/j111+V7Kj79+/j5eVFVFRUtvfYvHkzvr6+rFy5kvfeew+AX3/9lS5dupCamkqfPn30zrexsVH+HDZs2EBwcDAqlcrg3CqVijZt2jBz5kx++eUXJk+ezAcffICxcfahj/z0cctcUlq1alWOHDnCnj17lGDYiBEjKFasmHLOypUrcXNz48SJE1l20zQzM2PVqlXK+4ULF+L8f+zdeVyN6f8/8Nep1GmPihTKMkQokV1kp5AtIoWYLGEs2QaRLGWLMTFDliwTQlN2kb2yzERZW0QhpEXbOdU5798f/bq/3c45lRnzsV3Px+N+PObc13Vf93XfXZl6d13vq3ZtxMbGYvDgwQgODoZIJMLevXuhrq6Odu3aISEhAZs2bWLBsE+ABcMYhmEYhmEYhmEqMNZTx/2X73Hz6TsAgKaqMtRUWIaZr92+fft4S9rkMTAw4P7b29sb796VjYHY2Fg8e/YMCQkJAIBBgwZVeT+RSIShQ4cCAE6fPs2dP3nyJBYtWoSQkBD07NlT5rqPnRkGlOXwsrW1hY2NjdwljhERETh69CgWLVoEALhy5Qr69u0LiUQCIsLOnTvh7++PP/74A7dv34aFhQXv+vv370NNTQ0jRozgjv79+3Oz66pDKpVi9erVWLNmDfbu3YsRI0ZwZfb29ggJCYGzszNu3ryJjRs3Ql1dXW47ZmZmMjO8wsLCsHbtWsTExAAoWxobHh4OHx8fiEQi3ky1cuPGjcOOHTt458LDwzFx4kTcuXMHpqamVT6TRCKBvr4+97liIAwAd1+JRFJlW1Jp2SzU8vYePHgAc3Nz3nvo0aMHgoKCIBaLuYAn88+wf9EZhmEYhmEYhmEqMNEr++XzTMJrAEDrenoKZ6IwXw8PD49KyzMyMnjLDRs3bgxzc3M0bNiQW7pmbm4Oc3NzXgBEnpycHDg4OCA/Px8HDx7klY0ePRoBAQFwdHTEb7/9JnPtuXPnuPvIO9asWSNzTdu2bbFs2TK4uLjg/fv3vLLExES4ublh27ZtMDc3BwDY2tpCJBJh3bp1mDJlCkQiEV69eoX+/fsjIiICe/bs4bVRXFwsN6BUviSy/GjSpAlyc3N55/T09PDgwQN06dIF27dvx6VLl3iBsHKDBw/GtWvXcObMGTRr1qzS3GCFhYV4+PChwvKKiAj16tWDSCTijj179shN2q+vr49mzZpBV7fynWPFYjHCw8Nx6NAhzJs3T2G9HTt2QF1dXSa5/4eeP3+O6dOno3v37ujcuTPXl7S0NF7g7/3795BKpVyQlvnn2MwwhmEYhmEYhmGYCsqT6GfmiwEAVg30PmNvmE9FXV0dW7duVVguFouhra3NfXZ1dQUAbN26FRKJBG3btoWLiwu3W+KJEyfktpOQkABHR0dYWFjA3d0dzs7OOHPmDFe+dOlSZGVl4eTJk3BwcEBGRgaXQN7JyQm9e/eGn58fRo4ciUaNGsm0f/XqVbnLEOfNm4dDhw5h3759GDJkCHd+wYIFmDBhQpVL69zc3EBEyMvLQ+/evSEQCODm5gagLBj24Uyk+/fvIyAgQO5su+fPn+PEiROYNm0aAGDDhg0wNTVFcHBwpUtNu3Xrhvj4eCxevBhFRUWoWbMm7t+/zy1zlEql8Pf3R2hoKPz8/BAfHw8lpU83x6dLly64fv16pXWaNWuGJ0+eQFVVFWvWrJFZ/lhu586d+Pnnn7F582bUqlVLbp19+/Zh4sSJKC0thY2NDQ4dOsQF3ocOHQpfX1+sWrUK8+bNw5MnT+Dv7w/g45Z2MvKxYBjDMAzDMAzDMEwFxnr85VlW9fU+T0eYT2bFihXc8sElS5Zg/fr1XJmTkxOuX7/OJWZ//vw5GjRoAKBsqePGjRthZ2eH4OBg7N+/H2fPnoWRkZHCez19+hTDhw/HmjVrIBKJMHfuXJw5cwb9+/fHixcv8Pvvv+PGjRto3bo1IiMjebN8NDQ0sHLlSmzduhW7d++u9Jm0tLTg4uKCgoL/y2139OhRGBoa4uXLlwCAgoICbN26FZqamryNAuTlJCufNQaU5fCaPn06hg8fDi0tLW4G2Llz5xAYGIgLFy5ARUVFYeL558+fIzAwkAuGzZkzBwKBAJmZmQAgN5H9vn37cOzYMWhpaWHLli0AypY7WlhYcMtTy3fRHD16NBYuXIjQ0FA4OTlV+p4AICUlhffMpaWlcjcyqI5Tp04hOzsb8fHx8PHxwe3bt3mz/0QiEWbOnIm9e/ciMDAQP/74o8K2Bg8ejLi4OLx+/Rp//PEHrKyscOHCBbRr1w5WVlbYuXMnZs2ahWXLlsHAwABjx47F1q1bFQbXmOpjwTCGYRiGYRiGYZgKPgyGtWHBsK/e+fPnce3aNYwfPx4ZGRnIzs5G165doaenh3v37uHs2bOIjIyEl5cXsrKyuGDYqlWr0K9fPxQVFcHJyQnJycno0qULzp07p/BegwYN4nKKaWhowN/fH25uboiOjsaECRPg5uaG1q1bAwBsbGy464qKijBv3jzk5eVBKpUiPDwcXl5eePz4MfLy8mBiYoK0tDTo6emhb9++UFVVxbNnz3jJ+T9UMQdaRW/fvlVYBgADBw7E2bNn4ePjAw0NDS5HVWZmJlq0aIHXr1/D3d0dkyZNglAoRGlpqUwbEokEKioqMDc354JZHxKLxdzMrujo6Grl6QIAVVVVeHh4wN/fv1rBsEaNGiEpKYn7HBISwput9zEaN24MAGjXrh3MzMzQs2dPLF++HE2bNkVRURH69euHV69eITo6GtbW1pW2paurC11dXVhYWKBnz5548uQJNm7cyAXXJkyYAFdXV7x48QJ169ZFQEAALCwsKt0RlKkeljOMYRiGYRiGYRimApMKwbC6ukLU1pHNlcR8XYqLi9G7d28uiFWjRg1cuHABqqqqXJ0pU6bwlp9FRUVhy5YtWLx4MXdu3bp1GDVqVLUSopdzcXHBsGHDYGlpCbFYjE2bNsnUKS0tRefOnZGfn49du3YhMzMT8+bNg5eXFwQCAY4ePYrOnTtzSzSLioqgra3NJZP/8EhLS+PqySv/MBD27Nkz3LlzBwAQExODUaNGoWXLlnj06BHs7e1RVFQEdXV1jBkzBqtXr4aJiQl3bUFBAVatWoWCggKUlpaitLSUm9FVWlqqMBAGlAWlVFVVoaqqivDwcLi6uiIxMRHDhw+v8r1OnjwZy5cvr7Lef6l8+Wb5eFi6dClevnyJ2NjYKgNhitr7cGwpKyujQYMGEAgE2LFjxz+e0cbwsWAYwzAMwzAMwzBMBYZaaqihXJa3hy2R/DYoKysjMjISffv25c4VFBRAQ0MDQFkQQiAQ8GZ8eXl5wd/fX2aHwNWrV6Np06Zo3749l+y8MmfOnMHly5dhaGiIlJQU7Nu3TyZ5u4qKCvbs2YO9e/ciPT0dffv2RatWrTB58mTk5uZiyZIlvKBcVlZWlUneq0JEePnyJY4cOYJmzZrh2rVr6NSpE3r06AEdHR0kJCQgPDwcNjY2KCwsVLi7o7KyMi5fvozp06d/dB9cXV1RVFQEkUiE9PR0WFtbIz09nQvmlSsPslVcXmlsbAwHB4dq3ad8mWT5UT6j7UPR0dHo2rUrcnNz5bYzbtw4nDhxAgkJCQgLC8OkSZPQtWtXNG/eHABw8OBBDB06FFlZWUhKSuKOjIwMAMCiRYu4HT0PHjyIdevWITY2Fnfu3MG8efNw6dIluLu7AyibNRcUFIR79+7h2rVrcHR0hIqKCmbMmFGtZ2Yqx4JhDMMwDMMwDMMwFSgpCWCkW/aLMguGff1KS0uRkpLCzQzT0NBAt27d8Pfff+P58+do1aoViAibNm1Cnz59kJ+fjyNHjmDdunWV7kA5cOBADB48GIBsDqwXL14gICAAlpaWcHNzg4eHBxITE7Fv3z4EBATA1NQUc+fORVRUFJfzq0WLFggMDISVlRWaNWuGAwcOoKCgACNGjEC/fv3QvXt3rv1Xr16hdu3a//idxMbGokmTJtizZw/c3d2RmJiIWbNmwcnJCU+fPsWOHTt4ie7fv38PTU1Nhe3t2LEDERERXN616hIIBBAKhbzk/MnJybx7379/HzVq1ECNGjUQHBz8Ue0DgLa2NpYsWYL8/HzesX37dpm6b9++xcOHD1FYWCi3LT09PUyZMgXt2rXD3LlzMWTIEN5GChkZGVi/fj1++OEH3uHp6ck9W/k7atiwIY4ePYrevXujT58+uH37Ns6ePcsL2AYGBqJDhw4YMmQIDAwMEBUVVenXgak+ljOMYRiGYRiGYRjmAzamtfAy5yXszP95wIH5Mpw/fx5DhgzB77//jrFjx0JPTw+pqamYM2cODh8+zOXw0tPTg5KSEjIyMnDixAns3bu3yrZv3LgBHR0dnDhxgpupNXbsWISEhKBTp06YNWsWnJ2duVlV/fr1Q58+fXDq1CkEBQXB3t4eRkZGuH//Ptzd3XHhwgX4+fnBw8MDd+/ehbu7O4yNjeHr64uMjAxoamoiLCwMAOTuNFld9erVg7e3N0aNGsULRM2ePRtA2bJJFRUV6Ojo4OrVq8jOzkajRo2Qn58PiUSCjIwM3hJTExMTJCUlQVlZGVKpFOnp6TK7TwJlObJUVFQQERHB7VhZUUZGBnbu3MnNjlJWVoaNjQ1u3rwJoGwjhPLNC7KysiAWi6GhoYGYmBjUrFlT7rPq6+vDx8eHd66goAB5eXnQ0tJCTEwMl1x/8ODBvA0NPvTLL7/gl19+UVgulUoVlgHA4cOHuf/u1KkTYmJiFNZVU1Pjlq4y/wH6SuXm5hIAys3N/dxdYRiGYRiGYRjmG1MqkVJWvvhzd+Oz+ZZ+35oxYwadPn2arKysyMLCgkJDQ2n8+PEkkUjo9evXNHToUHr48CEREe3YsYOMjY0pODiY14abmxtt27ZNpu0OHTqQnp4eNWnShKKiooiIKDk5mdLT06vVt8LCQq7u69evKS8vj4iIkpKSSENDgxYvXkxisZhWrlxJAAgAGRsb05EjRyptNy0tjQBQUVFRtfrxIWdnZ+5++vr6FBQUREREBw4cIABkYmJCqampMte5uLiQQCAgoVBIGzdulNv22rVrSVdXl2u/4qGlpUXjx4+nkpKSKvtY3hcAVKtWLTp58iRXFhUVRZaWlgqvjY2N5a7V09Pjno/5fgiI5Oxp+hV4//49dHV1kZubCx0dnc/dHYZhGIZhGIZhmG/Gt/T7llQqhZKSEogIAoEAEomElyi/uLiYN8vpS/H69WvUqVPns91fKpWitLRU5t2Uv8cvRfnX959cR0S8scB8P9gySYZhGIZhGIZhGOabVR4oKQ/gfBj8+BIDYQA+ayAMKHtv8t7NlxQIA/CPAmH/5jrm28C++gzDMAzDMAzDMAzDMMx3gwXDGIZhGIZhGIZhGOY/JpFIPncXGIb5/1gwjGEYhmEYhmEYhvnu+Pj4oKCgoFp18/Pz8ddff+GPP/7A8uXLUVJSwisPCwtDjx49FF5/584dNG/eHIWFhf+my/+5U6dOoUGDBlXWGzFiBHbu3ClzPj09HXp6etW6FxGhR48eMDQ0lHmfHyssLAwdO3bknUtISMCCBQvwlaZJZ/5jLGcYwzAMwzAMwzAM8826dOkSUlNTZc4fO3YMiYmJ6NWrl0yZpaUlEhISsHfvXjx+/Bjp6em88pEjR8LCwqLafbC2toaBgQEiIiIwatQohfX27NmDSZMmQSgUKqxTUlICZ2dn7NmzhzsXGRmJ4cOHo2HDhgqve/z4MR4+fAgzMzOFdf5pMvp/YvPmzUhMTIS+vj68vb2xevVqmTrx8fFYtGgR75y7uzuGDh1aZftSqRRbtmxBbm4utm3bhho1aiicnWdiYiLzNa5MSkoKfvzxR4wfPx4uLi7c+aysLMybNw9//vknSktL0b59e2zYsAGtW7cGAKSmpsLT0xMxMTEoLS1F9+7dsWXLFpiamsq9T05ODjw9PREeHg5VVVVMnDgRa9eu5b5GYrEYXl5eOHDgAEpKSjBy5Ej88ssv0NDQqPazfK9YMIxhGIZhGIZhGIb5ZolEIuTn52PhwoVYu3Ytd37SpEkAymZ9fai4uBg1a9ZEixYt0LNnT0RERKBz587w8PBAgwYNIBQKsXz5cohEIl6bAGBmZoZnz57J7Ut0dDRGjx4tcz4xMRFNmjQBADg6OiI0NFTh8wQEBCAuLk7mvJ2dHcLCwhRe17JlS4Vl5aoKhqWlpeHx48d48+YNHj58iMjISEilUgwePJirIxaLecG85ORkmJiY8NoJDw/HokWLEBkZCT09PXTs2BEtWrTgBZYA4N27d4iPj8evv/4KoOzZk5OTq3wOAGjdujU2b96M7du3QyKRQCwWy50ldv/+fdjb21erzaSkJKxduxaHDx9GXl4exo8fzytfvXo1lJSUcPLkSRARli1bhv79++PRo0fQ0dHB8+fPYWNjg+XLl+P9+/eYNWsWnJycEBsbK/d+Y8aMwZs3b3Du3Dm8ePECbm5uMDAwwPz58wEAs2fPxpkzZxAaGorS0lK4urpCTU0NgYGB1Xqe7xkLhjEMwzAMwzAMwzDfrP79+wMAli5dWmmQCQAMDQ1x5MgR7rODgwMA4MmTJ/jhhx/QtGnTKu9XcRaaWCzGy5cveTO2MjIyIBKJFM7QCgsLg5aWlsL2y2eGfejKlSsySwUrSklJqbLvRFRpMCw2NhZbt27Fw4cP8fr1a9y5cwchISEQiUQAypZJtmzZEjk5OQrbOH78OFxdXbF371506dIFABASEoKRI0eipKQEEyZM4NXX1tbmvg6hoaFITU1VuKOlQCCAvb091q9fj4MHD8LHxwdDhgyBiori0MeHu4tWJjIyEm/fvsWVK1fQpk0bmfJZs2ahfv363Oe9e/fCxMQE0dHR6NevH2xtbWFra8uVL1u2DE5OTsjNzYWuri6vrXv37uH06dP466+/uHvdunULv/76K+bPn4+srCzs2LEDhw8fhp2dHQDA29sbs2fPxoYNG6Curl7t5/oesWAYwzAMwzAMwzAM883bt28fF7RRxMDAgPtvb29vvHv3DkBZEOjZs2dISEgAAAwaNKjK+4lEIm453+nTp7nzJ0+exKJFixASEoKePXvKXPdPZ4YZGhqid+/eCq+rGAx79OgRmjdvrrCuvGCTqakpUlNTMWLECIwYMQL9+/fnZtdVh1QqxerVq7FmzRrs3bsXI0aM4Mrs7e0REhICZ2dn3Lx5Exs3blQYzDEzM5OZ4RUWFoa1a9ciJiYGQNnS2PDwcPj4+EAkEslddjpu3Djs2LGDdy48PBwTJ07EnTt35C5d9PDwwJQpUxQ+Y8VAGADuvoqWZ0okEgiFQmhqasqURUVFoU6dOrygW8+ePeHn54dXr17h5s2bkEql6Nu3L69cJBIhLi4OnTp1UthPhgXDGIZhGIZhGIZhmO+Ah4cHNm/erLA8IyMDAQEBuHbtGgCgcePGMDQ0hFgsRmJiIiwtLWFubg4A0NfXr/ReOTk5GDFiBMRiMcLDw3llo0ePhrq6OhwdHbFu3Tp4eHjwys+dO8fdR57s7GwMGDCAd65Fixbw8/OrdCmkhYUFF+xr2rQpsrOzZeps374dq1evRmpqqswMMXkzxsRiscyMpg+XSQqFQty4cQPu7u5IS0vDpUuXYGNjI9PW4MGDce3aNQwbNgwnT57E5s2bUbNmTbnPUlhYiGfPnlUa0CtHRKhXrx6SkpK4cyEhIThz5oxMXX19fTRr1kzmmcopmpGmyI4dO6Curi4zY08ikeDu3btYuXIlvLy85M5cS0lJkckBV765QXp6OlJSUlC7dm1eIK1iOVM5FgxjGIZhGIZhGIZhvnnq6urYunWrwnKxWAxtbW3us6urKwBg69atkEgkaNu2LVxcXLjdEk+cOCG3nYSEBDg6OsLCwgLu7u5wdnbmBV6WLl2KrKwsnDx5Eg4ODsjIyIC3tzcAwMnJCb1794afnx9GjhyJRo0aybR/9epVbhnixYsXMXPmTCQnJ8PU1JQLqiQmJqJx48YyAaxVq1YBKAu4GRsby7T94MED5OXlISkpCe3bt1f4roCyXFsBAQFyZ9s9f/4cJ06cwLRp0wAAGzZsgKmpKYKDgytdatqtWzfEx8dj8eLFKCoqQs2aNXH//n3uuaRSKfz9/REaGgo/Pz/Ex8d/0oT/Xbp0wfXr1z9JWzt37sTPP/+MzZs3o1atWtz5yZMnY9euXZBKpRg3bhwWLlwo9/r8/HyZRPjln8VisdxyoVAIgUAAsVj8SZ7hW8aCYQzDMAzDMAzDMMw3bcWKFejatSsAYMmSJVi/fj1X5uTkhOvXr3OJ2Z8/f87NsBGJRNi4cSPs7OwQHByM/fv34+zZszAyMlJ4r6dPn2L48OFYs2YNRCIR5s6dizNnzqB///548eIFfv/9d9y4cQOtW7dGZGQktxQTKAt2rFy5Elu3bsXu3bsrfSYtLS2MGzcOCQkJsLKywokTJ1CvXj0AQJMmTXDjxg0ucFcdBQUFOHnyJKZOnYqdO3fKDYadO3cOgYGBuHDhAlRUVBQmnn/+/DkCAwO5YNicOXMgEAiQmZkJAHIT2e/btw/Hjh2DlpYWtmzZAqBsuaOFhQW3PLU8Yf3o0aOxcOFChIaGwsnJqcpnS0lJ4eVhKy0tlbuRwacgEokwc+ZM7N27F4GBgfjxxx955T4+PpgxYwaePn2KTZs2oW3btoiNjYWOjg6vnpqaGoqLi2XaBsrGibzykpISEBHbTbIa/jd7pjIMwzAMwzAMwzDMZ3L+/Hns2bMHQNlyyOzsbFhYWKBLly64d+8ezp49i+3bt0NbWxtZWVncdatWrUK/fv3QoEEDTJ8+HX379kWXLl0q3dFw0KBB8PPzg5KSEjQ0NODv7w83NzekpKRgzJgxcHNzQ+vWrQEANjY2XIL/oqIiTJ8+HS9evIBUKsWBAwdgbGyMvLw8vHz5EgKBAOnp6cjPz0fnzp2hqqrKu2+/fv1gZWUFKysrpKWl4fbt26hVqxYaN27MnVdSUsKrV6/k9jsgIAAWFhZYs2YNjhw5gocPH8rUyczMRIsWLdCyZUusW7cO+/fvh1AohIqKCu/o0aMHN6OrZcuWCpcXisVilJSUoKSkBNHR0XLzdMmjqqoKDw8P+Pv7V6t+o0aNkJ+fzx3lY+FTKyoqQt++fREVFYXo6GiZQBgA1K1bF61bt8aQIUNw+vRppKamIiQkRKaeiYkJ0tLSeOfKPzds2BAmJiZ4/fo1SkpKuPLnz58DgNwZhQwfC4YxDMMwDMMwDMMw37Ti4mL07t0b586dAwDUqFEDFy5c4AWUpkyZwttZMCoqClu2bMHixYu5c+vWrcOoUaMUJkSXx8XFBcOGDYOlpSXEYjE2bdokU6e0tBSdO3dGfn4+du3ahczMTMybNw9eXl4QCAQ4evQoOnfuzM30Kioq4i3pBICzZ88iLi4OcXFxqF+/Ptq1awc/Pz8MHz4ccXFxWLt2LWxtbVG3bl2Z+8fExGDNmjXYsGEDdHV14eXlxe1yWNGYMWOwevVqmJiYcOcKCgqwatUqFBQUoLS0FKWlpdyMrtLSUm5WlzwhISFQVVWFqqoqwsPD4erqisTERAwfPrzK9zp58mQsX768ynr/S0uXLsXLly8RGxsLa2vrKusLBAIoKSnJHU9du3bFs2fPkJiYyJ27cOECrK2tUbNmTXTp0gUlJSW4dOkSr9zQ0BCtWrX6JM/zLWPBMIZhGIZhGIZhGOabpqysjMjISN7OewUFBdxyMhUVFQgEAi5YBgBeXl7w9/eX2SFw9erVaNq0Kdq3b4/OnTtXee8zZ87g8uXLMDQ0REpKCvbt28ebzVN+/z179mDv3r1IT09H37590apVK0yePBm5ublYsmQJLyiXlZUlk+T9w5lhQFnwKiIiApcvX8acOXOwYcMGmf5duXIFAwcOhLe3N5fYfuHChTAwMICdnR1SU1MrfT5lZWVcvnwZ06dPr/JdfMjV1RVFRUUQiURIT0+HtbU10tPTZWZElQfZKi6vNDY2hoODQ7XuU75MsvyYNGmS3B0mo6Oj0bVrV5kgYHUdPHgQQ4cORVZWFpKSkrgjIyMDQFmwLDg4GHfv3sWVK1cwbNgwqKurY9iwYQDK8tO5ubkBAGxtbdG2bVtMnDgRt2/fRmhoKC84a2ZmhmHDhmHmzJm4du0azp07B29vbyxYsIAX1GXkY8EwhmEYhmEYhmEY5ptVWlqKlJQUbmaYhoYGunXrhr///hvPnz9Hq1atQETYtGkT+vTpg/z8fBw5ckTuTo8VDRw4EIMHDwYgmwPrxYsXCAgIgKWlJdzc3ODh4YHExETs27cPAQEBMDU1xdy5cxEVFYWCggIAZTtCBgYGwsrKCs2aNcOBAwdQUFCAESNGoF+/fujevTvX/qtXr1C7dm3ePU+dOoXw8HBs3boV+vr68PX1xd27d+Hr6ws7Ozv06dMHbdu25eqLxWJ4e3ujT58+WLx4Mby8vLgyJSUlREREcEv6fH19ectHP7Rjxw5ERERUunxUHoFAAKFQCDU1Ne5ccnIyL8n+/fv3UaNGDdSoUQPBwcEf1T4AaGtrY8mSJbxlkvn5+di+fbtM3bdv3+Lhw4coLCz86PsAZUtw169fjx9++IF3eHp6AigLYK1evRodO3bE6NGjoaOjg+joaNSpUwcA8OzZM255qkAgwPHjx6GhoYGuXbti7ty5WL9+PW/WXFBQECwtLdGvXz+4urpi6tSpmDNnzj/q+/eGJdBnGIZhGIZhGIZhvlnnz5/HkCFD8Pvvv2Ps2LHQ09NDamoq5syZg8OHD3M5vPT09KCkpISMjAycOHECe/furbLtGzduQEdHBydOnOBmao0dOxYhISHo1KkTZs2aBWdnZ6irqwMom73Vp08fnDp1CkFBQbC3t4eRkRHu378Pd3d3XLhwAX5+fvDw8MDdu3fh7u4OY2Nj+Pr6IiMjA5qamggLCwNQlheqoKAAffr0QXJyMvr27YuGDRuiSZMmEIvFMDc3x6lTp7B3716sXLkSISEhGDRoEFxcXGBtbQ07OzsoKyvj6NGjcmdYaWlpITw8HJs2bcKaNWtw6tQpnDt3DhKJBBkZGbwlpiYmJkhKSoKysjKkUinS09N5Aa5yurq6UFFRQUREBHr37i2TSywjIwM7d+6Eu7s7gLJZZzY2Nrh58yaAso0QyjcvyMrKglgshoaGBmJiYlCzZk25XyN9fX34+PjwzhUUFCAvLw9aWlqIiYnhkusPHjyYt6FBZeRtAiCVSiu9xt3dnXs2edatW8f7XL9+fZw9e1ZhfT09Pbn5xphqoK9Ubm4uAaDc3NzP3RWGYRiGYRiGYZhvyrf0+9aMGTPo9OnTZGVlRRYWFhQaGkrjx48niURCr1+/pqFDh9LDhw+JiGjHjh1kbGxMwcHBvDbc3Nxo27ZtMm136NCB9PT0qEmTJhQVFUVERMnJyZSenl6tvhUWFnJ1X79+TXl5eURElJSURBoaGrR48WISi8W0cuVKAkAAyNjYmI4cOcK1kZKSQiUlJbx2GzduTOvWrSNXV1dKTk4mIqLi4mLav38/eXp6klQqpf3791N+fn61+pmTk0OZmZl04MABAkAmJiaUmpoqU8/FxYUEAgEJhULauHGj3LbWrl1Lurq63PNUPLS0tGj8+PEyzyNPeV8AUK1atejkyZNcWVRUFFlaWiq8NjY2lrtWT0+PgoKCqn4JzDdFQCQnnPkVeP/+PXR1dZGbmyuzBSnDMAzDMAzDMAzzz31Lv29JpVIoKSmBiCAQCCCRSHg5lYqLi2V2ZvwSvH79mls+9yUpf49fivKv7z+5johYfq3vFFsmyTAMwzAMwzAMw3yzygMl5QGcD4MfX2IgDMAXGQgD8EUFwgD8o0DYv7mO+Tawrz7DMAzDMAzDMAzDMAzz3WDBMIZhGIZhGIZhGIZhGOa7wYJhDMMwDMMwDMMwDPMfk0gkn7sL1VJcXPy5u8Aw/zkWDGMYhmEYhmEYhmEYOUQiEVRU/n2q7Tt37qB58+YoLCz8BL36dAICAuDp6ck7N2fOHHh5eVV63YgRI7Bz506Z8+np6dDT06vWvYkIPXr0gKGhIUpKSqrdZ3nCwsLQsWNH3rmEhAQsWLAAX+megcx/jAXDGIZhGIZhGIZhmG9aQEAABAJBtY79+/crbCcsLAxCoRBGRka8Q11dvdLrrK2tYWBggIiIiEr7uWfPHqioqEBLS0vhoaamhvHjx3PXLFmypNrPJhAIsHbtWoX3JyIcP34cXbt2rbSfn8LmzZuRmJgIfX19eHt7y60THx8PBwcH3nH8+PFqtS+VSrFlyxZMnToVRAQVFRWF76RevXrVarO0tBTz58+HkZERNDQ00L9/fzx+/Fhh/VOnTsHKygpCoRDNmjXDnj17eOW2trYyfbl9+3a1+sL8O1/tbpLl0d33799/5p4wDMMwDMMwDMN8W8p/z/pWZtXMmjVLZgaUIh/uNvmh/v37IywsjHeuYnDKzMwMz549k3ttdHQ0Ro8eLXM+MTERTZo0AQA4OjoiNDRU4f0DAgIQFxfHfV6+fDmWLFnCq/PixQs0adIEOTk5UFNTA1AWHFJSUkKNGjUUtn39+nW8fPkSu3fvxh9//MErc3V1RatWrfD48WO8efMGDx8+RGRkJKRSKQYPHszVE4vFEAqF3Ofk5GSYmJjw2goPD8eiRYsQGRkJPT09dOzYES1atICLiwuv3rt37xAfH49ff/2Ve/bk5GSF/a+odevW2Lx5M7Zv3w6JRAKxWCx3PN+/fx/29vbVatPX1xd79+7Fjh07YGJigqVLl2LgwIFITEyU2Z0yMTERQ4YMwYIFCxAcHIyLFy/C3d0dpqamsLOzAwBkZWXBz88Pw4YN466rX79+tfrC/DtfbTAsLy8PABsoDMMwDMMwDMMw/5W8vDzo6up+7m78awKB4KOXO44dOxaPHj2CRCJBu3bt0LZtWwwYMKDK61JTU7n/FovFePnyJRo2bMidy8jIgEgkgpmZmdzrw8LCoKWlpbD9kpISODs7c59VVFSwdOlS1K5dG7NnzwYALgCmpqbGBabc3NxgYWGB+fPnK2x7165d6NatG6ysrHjnjx49iqdPn6KwsBBbt27Fw4cP8fr1a9y5cwchISEQiUQAypZJtmzZEjk5OQrvcfz4cbi6umLv3r3o0qULACAkJAQjR45ESUkJJkyYwKuvra0NBwcHAEBoaChSU1MhEAjkti0QCGBvb4/169fj4MGD8PHxwZAhQyr92lcV/Kzo1q1bcHFx4YJ/a9euhaWlJd69ewdDQ0Ne3bi4OOjo6MDX1xdAWXAuODgYt2/f5gXDmjdvzgVCmf+drzYYZmxsjLS0NBARGjRogLS0NOjo6HzubjGMQu/fv0f9+vXZWGW+eGysMl8TNl6ZrwUbq8zXonysPn/+HAKBAMbGxp+7S/9aSUnJRyevV1ZWxuzZs5GVlYUBAwZg/fr10NPTQ2pqKs6cOQMjIyNe/dzcXPTu3Zt3TiQSYejQoQCA06dPc+dPnjyJRYsWISQkBD179pS598fODAPKcnjZ2trCxsZG7hLHiIgIHD16FIsWLQIAXLlyBX379oVEIgERYefOnfD398cff/yB27dvw8LCgnf9/fv3oaamhhEjRnBH//79MWnSJIX9/JBUKsXq1auxZs0a7N27FyNGjODK7O3tERISAmdnZ9y8eRMbN26Eurq63HbMzMxkZniFhYVh7dq1iImJAQBcunQJ4eHh8PHxgUgk4s1UKzdu3Djs2LGDdy48PBwTJ07EnTt3YGpqKnPNqFGjsGrVKkyfPh1169bF9u3bYWdnJxMIAwA7OzuoqKjg4MGDGDVqFC5cuIDU1FQMGjSIq5OdnQ0DA4NK3hrzX/lqg2FKSkqoV68eN31XR0eH/WDBfBXYWGW+FmysMl8TNl6ZrwUbq8zXQldX95sZq87Ozjh69OhHXePm5oY9e/ZAJBJBIBCgR48eAABzc3O8ePEC+vr6vPpv3ryBhoYG9zknJwcjRoyAWCxGeHg4r+7o0aOhrq4OR0dHrFu3Dh4eHrzyc+fOwdzcXGHfsrOzZWaotW3bFsuWLYOLiwvu3bvHK0tMTISbmxu2bdvGtWtrawuRSISAgAAkJSVh69atWLRoEfr374+IiAjcunWLt/SzuLhYbkBJLBbLzBz8cJmkUCjEjRs34O7ujrS0NFy6dAk2NjYybQ0ePBjXrl3DsGHDcPLkSWzevBk1a9aU+w4KCwvx7NkzNG/eXOF7KkdEqFevHpKSkrhzISEhOHPmjExdfX19NGvWTOFsSFdXV5w5cwaNGzeGQCBA7dq1cefOHbl1DQwMsGfPHjg4OMDFxYULOpZ/DQoKCiASidCjRw/o6uqiVatWWL58Obp161blMzH/3lcbDGMYhmEYhmEYhmGYqlScZdW9e3c4OTlh+vTpH9VGeno6Xrx4galTp+LFixdyl+nVrl0b9+7dQ0JCAhwdHWFhYQF3d3c4OzvzAi9Lly5FVlYWTp48CQcHB2RkZHAJ5J2cnNC7d2/4+flh5MiRaNSokcx9rl69KncZ4rx583Do0CHs27cPQ4YM4c4vWLAAEyZMwLhx4yp9Rjc3NxAR8vLy0Lt3bwgEAri5uQEoC4aVL70sd//+fQQEBHBLJCt6/vw5Tpw4gWnTpgEANmzYAFNTUwQHB6Np06YK+9CtWzfEx8dj8eLFKCoqQs2aNXH//n1umaNUKoW/vz9CQ0Ph5+eH+Ph4mVxd/0aXLl1w/fp1heU+Pj64dOkSjhw5AmNjY/j6+mLQoEG4ceOGTLDw/v37GDVqFBYuXAhHR0dERUVhxowZaNq0Kbp16wZVVVVER0dDU1MTGRkZ2LJlC3r16oXY2Fi0adPmkz0TIx8LhjEMwzAMwzAMwzDfvL///hv37t1DeHg4SktLFdYrD7z8+eefOH/+PCQSCaysrDB79my8f/8esbGxMDMzQ2xsLIgIHTt2RHp6Orc88enTpxg+fDjWrFkDkUiEuXPn4syZM+jfvz9evHiB33//HTdu3EDr1q0RGRmJd+/ecffW0NDAypUrsXXrVuzevbvS59HS0oKLiwsKCgq4c0ePHoWhoSFevnwJoGz20datW6GpqYn8/HzetR+qOBstJCQE06dPx/Dhw6GlpcXN9jp37hwCAwNx4cIFqKioKEw8//z5cwQGBnLBsDlz5kAgECAzMxOA/I0Z9u3bh2PHjkFLSwtbtmwBULbc0cLCAgkJCQD+b6OC0aNHY+HChQgNDYWTk1Ol7wkAUlJSeM9cWloqdyODymRnZ2PNmjUIDg7mlngePnwY9evXx/79+2WWjPr4+KBbt25YtWoVAMDGxgZPnjzBokWLcO3aNdSoUQMdO3YEALRq1Qp2dnZo2bIldu7cyW0YwPx3vvpgmJqaGry9vWWi1AzzpWFjlflasLHKfE3YeGW+FmysMl+Lb3msxsbGIicnB3p6egrrKCkpQSwWQ0VFBXv27EGzZs2grKyMN2/eQElJiRegKg/clAc0yg0aNIjLC6WhoQF/f3+4ubkhOjoaEyZMgJubG1q3bg0AvOWCRUVFmDdvHvLy8iCVShEeHg4vLy88fvwYeXl5MDExQVpaGvT09NC3b1+oqqri2bNnvOT8H1KUj+rt27eV5qoaOHAgzp49Cx8fH2hoaEAsFkNNTQ2ZmZlo0aIFXr9+DXd3d0yaNAlCoVBucFEikUBFRQXm5uZcMOtDYrGYm9kVHR0tN0+XPKqqqvDw8IC/v3+1gmGNGjWq1jLJyjx58gQikYi3uYCWlhZ++OEHmaWpAHDv3j3eLpEAYG1tjcOHD8ttX0VFBa1atVK4EynzaX26+YSfiZqaGpYvX/5N/mPNfFvYWGW+FmysMl8TNl6ZrwUbq8zX4lseq1OmTAERKTwuX74MIyMjbmbY8ePHsXz5cgCQuxTv9evXMon05XFxccGwYcNgaWkJsViMTZs2ydQpLS1F586dkZ+fj127diEzMxPz5s2Dl5cXBAIBjh49is6dO3OBvKKiImhra3PJ5D880tLSuHryyj8MhD179ozLfRUTE4NRo0ahZcuWePToEezt7VFUVAR1dXWMGTMGq1evhomJCXdtQUEBVq1ahYKCApSWlqK0tJSb0VVaWqowEAaUBaVUVVWhqqqK8PBwuLq6IjExEcOHD6/yvU6ePJn7+vwvlG8m8eDBA+5cYWEhkpOTee+jnImJCa8uAMTHx8utC5Rt9PD333+znSX/R776YBjDMAzDMAzDMAzD/FtpaWkwMzOrVt3i4mJERkYiNjYWYrEYAoFAYQDxzJkzuHz5MgwNDZGSkoJ9+/ahpKSEV6d8JtrevXuRnp6Ovn37olWrVpg8eTJyc3OxZMkSLF68mKuflZWlMMl7dRERXr58iSNHjqBZs2a4du0aOnXqhB49ekBHRwcJCQkIDw+HjY0NCgsLFe7uqKysjMuXL390HjagLCF9UVERRCIR0tPTYW1tjfT0dC6YV648yFZxeaWxsTEcHByqdZ/yZZLlR/mMtg9FR0eja9euyM3NlSmrX78+HB0d8dNPP+HkyZO4desWxo4dC4lEAhcXFwBlede2bt0KAJgxYwb+/PNPrFmzBnFxcdiyZQuCgoLg6ekJALh9+zaWL1+Omzdv4tq1a3BycsKbN28wY8aM6r085l9hwTCGYRiGYRiGYRjmu/fo0SP88MMPldZJSkqCmZkZ5s+fj5YtW+Lt27do27Yt3r59i8ePH3P1Xrx4gYCAAFhaWsLNzQ0eHh5ITEzEvn37EBAQAFNTU8ydOxdRUVFczq8WLVogMDAQVlZWaNasGQ4cOICCggKMGDEC/fr1Q/fu3bn2X716hdq1a//jZ42NjUWTJk2wZ88euLu7IzExEbNmzYKTkxOePn2KHTt28BLdv3//Hpqamgrb27FjByIiIpCcnPxR/RAIBBAKhbxAYnJyMu/e9+/fR40aNVCjRg0EBwd/VPsAoK2tjSVLliA/P593bN++Xabu27dv8fDhQxQWFspta9++fRg4cCDGjx+P7t27IycnBxcvXuRmez18+JBb5jhkyBDs27cP+/fvR4cOHbBlyxasX7+eCxrq6Ojgzz//hJ2dHYYNGwapVIpbt26hcePGH/2MzMf76nOGMQzDMAzDMAzDMMzHKCoqQmpqKmrVqgV1dXWkpKRg7969WLlyJa9ORkYGVFVVAQAikQhXrlzB2rVrUVhYiBMnTsDAwAC7d+9Gz549MX/+fMyfPx/jxo1DSEgIOnXqhFmzZsHZ2ZmbVdWvXz/06dMHp06dQlBQEOzt7WFkZIT79+/D3d0dFy5cgJ+fHzw8PHD37l24u7tzuxZmZGRAU1MTYWFhACB3p8nqqlevHry9vTFq1CheIGr27NkAypZNqqioQEdHB1evXkV2djYaNWqE/Px8SCQS3nsBypYEJiUlQVlZGVKpFOnp6XJnyunq6kJFRQURERHcjpUVZWRkYOfOnXB3dwdQNuvMxsYGN2/eBACsWLGCW5qalZUFsVgMDQ0NxMTEoGbNmnKfVV9fHz4+PrxzBQUFyMvLg5aWFmJiYrjk+oMHD+ZtaPAhLS0t/PrrrwoT3Jf3s9zYsWMxduxYuXWbNm2Kv//+W+G9mP8YMQzDMAzDMAzDMMx35M2bN6SiokIACACpq6vTxIkTqbS0lKvTp08fUlJSIldXV3r//j3Vrl2bGjduTJs2bSKRSMRr7969e/TDDz/QlStXKDk5mdLT06vVj8LCQq7u69evKS8vj4iIkpKSSENDgxYvXkxisZhWrlzJ9dXY2JiOHDlSabtpaWkEgIqKij7mtXCcnZ25++nr61NQUBARER04cIAAkImJCaWmpspc5+LiQgKBgIRCIW3cuFFu22vXriVdXV2u/YqHlpYWjR8/nkpKSqrsY3lfAFCtWrXo5MmTXFlUVBRZWloqvDY2Npa7Vk9Pj3s+5vvxVQfDpFIpLV++nOrWrUsaGhrk6OhIb968+dzdYr5TcXFx1KZNG7p69SrvfGBgIJmZmZFQKCQ7OztKTk7mlYeGhpK5uTmpqalRu3bt6Pbt2//LbjPfmbt371KfPn1IXV2d6tSpQ+PHj6fMzEyunI1X5ksREhJCrVq1InV1dapfvz6tWLGCpFIpV87GKvMl2rt3LwGgffv2cefYWGW+FEFBQTKBh+nTp3Pl3/NYLS4u5v0/RpG3b99W2c6nlJGR8Unb+1gSiYTEYrHM+eq8q/8liUTyj6+rGPxkvi9fdc6wdevWYcuWLfjtt99w/vx5PH78GG5ubp+7W8x35q+//oKTkxM6deokM831yJEjmD17Nnx8fHDt2jWUlJRg8ODBkEqlAMoSNI4ePRoeHh6IjY1FgwYNMHDgQOTl5X2OR2G+A56enujRowdiYmIQFBSEy5cvw9XVFQAbr8yX5dGjR1i8eDFiYmLw888/w8fHh8vtwcYq8yUSiURYtmwZ7xwbq8yXJCsrCx06dEBiYiJ3lO/E972P1Ro1asgs15Pnwx0Y5bXzKdWpU+eTtvexlJSUeEshy1XnXf0vydvps7rXKSsrf+LeMF+Nzx2N+6ckEgkZGBjwpl6ePn2aAFBKSspn7Bnzvfn555/JxcWFLly4QAB4M8PatGlDM2fO5D4/fPiQANDFixeJiGjo0KE0ePBgrjw7O5vU1NRo165d/7sHYL4rz58/530+cOAAKSkpUUFBARuvzBdt4MCBNHToUCJi/7YyX6bFixfTiBEjeDPD2FhlviSLFi2iYcOGyS1jY5VhmO/NVzszLD4+HpmZmRgwYAB3rnv37lBSUkJMTMxn7BnzvVm5ciX27dsnk8AyJycHf//9N2+Mmpubo27dutwYjYqK4pXr6enB2tqajWHmP1O/fn3eZ6FQCKlUysYr88WTSCTQ19dnY5X5It27dw/btm3Dpk2buHNsrDJfmqysLLkzm9hY/d+RSCSfuwsMw/x/X20wLCUlBQDQsGFD7py6ujoMDQ2Rnp7+ubrFfIcUTRN++vQpAP4YBYAGDRogPT0d2dnZyMnJUVjOMP81IkJQUBA6dOiA169fA2DjlfnyFBQUICgoCLGxsfD09GT/tjJfHJFIhLFjx2LJkiWoV68ed56NVeZL8+7dOwQFBUFbWxutW7eGv78/SkpK2FitgkgkgoqKyr9u586dO2jevDkKCws/Qa/+O6dOnUKDBg2qrDdixAjs3LlT5nx6ejr09PSqdS8iQo8ePWBoaIiSkpKP7SpPWFgYOnbsyDuXkJCABQsWgIj+VdvMt+mrDYbl5+dDSUlJZrtWDQ0NiMXiz9Qrhvk/+fn5AMrGZEXlY7Sqcob5L5WUlODHH39EVFQUtm7dysYr80USCoXQ0tLCnDlz8Ouvv8LS0pKNVeaLM3v2bBgYGOCnn37inWdjlfnSrFixArdv30ZkZCRGjx4Nb29vLFq06LsZqwEBARAIBNU69u/fr7CdsLAwCIVCGBkZ8Q51dfVKr7O2toaBgQEiIiIq7eeePXugoqICLS0thYeamhrGjx/Puy4yMhK6urqwsrJSeKirqyM1NbXS+0ul0n+cg+tjbd68GYmJidDX14e3t7fcOvHx8XBwcOAdx48fr1b7UqkUW7ZswdSpU0FEUFFRUfg1r/jHjOq6evUqBAIBfH19uXNEhNWrV6NevXpQV1dH7969uYk88qSlpcHBwQGampowNjbG+vXreeU5OTlwcXGBjo4ODAwMMH/+fC6XH/Pv/PsQ92eipqYGqVSK0tJSXqReJBLJ/EPNMJ9DeaC2uLiYd758jFZVzjD/lfT0dIwaNQopKSm4ePEi2rVrh5s3bwJg45X5ssTFxSE3Nxe3b9/GzJkzkZCQAEdHRwBsrDJfhp07d+LYsWOIi4uT+eWR/RzAfGlatGjB/XeHDh0gkUjg5+eHkSNHAvj2x+qsWbPg6elZrbpVJVXv378/wsLCeOcqBqfMzMzw7NkzudeWb0bwocTERDRp0gQA4OjoiNDQUIX3DwgIQFxcnMx5Ozs7mX5V1LJlS4Vl5aoKhqWlpeHx48d48+YNHj58iMjISEilUgwePJirIxaLIRQKuc/JyckwMTHhtRMeHo5FixYhMjISenp66NixI1q0aAEXFxdevXfv3iE+Ph6//vorgLJnT05OrvI5AKB169bYvHkztm/fDolEArFYLHeW2P3792Fvb1+tNssREebPny9zPiAgAP7+/ti+fTsaNGgALy8v2Nvb4969ezIbLEgkEtjb28PY2BhXr17FnTt3MGXKFDRo0ABOTk4AgDFjxuDNmzc4d+4cXrx4ATc3Ny4oxvw7X20wrPybKT09HWZmZgDKvunevn0rk7uJYT6H8jGalpaGxo0bc+fT0tLg5OQEAwMDqKmpIS0tjXddWloa2rZt+z/tK/P9ePLkCezs7NCqVSvcvXsXtWvXBsDGK/NlMjc3B1D2S5uGhgYmT56M6dOnA2BjlfkyrFq1Cu/evZNZPjZx4kSYmpoCYGOV+XJZW1ujoKAARkZGAL79sSoQCD56uePYsWPx6NEjSCQStGvXDm3btuXlTlOk4uwrsViMly9f8v6dyMjIgEgk4n6P/VBYWBi0tLQUtl9SUgJnZ2eZ81euXJFZKlhRZTOUyhFRpcGw2NhYbN26FQ8fPsTr169x584dhISEQCQSASj7/bxly5bIyclR2Mbx48fh6uqKvXv3okuXLgCAkJAQjBw5EiUlJZgwYQKvvra2NhwcHAAAoaGhSE1NVZiqRiAQwN7eHuvXr8fBgwfh4+ODIUOGVPq1/yc7Su7cuRNSqVRmSemaNWvw888/cwHPP/74A40bN8bp06d5AUMAOHnyJB49eoTIyEjUrl0b1tbWuHDhAn799Vc4OTnh3r17OH36NP766y+0adMGAHDr1i38+uuvLBj2CXy1yyStra2hrq6O8+fPc+cuX74MgUAAW1vbz9gzhiljYmICMzMz3hh98uQJ0tPT0atXLygpKaFTp0688vIZEL169focXWa+A2PGjEGnTp1w6tQpLhAGsPHKfPlUVFRARNDV1WVjlfliREZGIiEhAXFxcdwBlAXJzpw5w8Yq80W7efMmatasifr163/zY7WkpAQikeijjpKSEsyePRtr1qyBkpIS1q9fj6lTpwIAzpw5I7NM8tChQzL3FYlEcHR0xLRp03jnT548ifbt2+PixYty++vo6Ij8/HyFh5+fn9zrDA0N0bt3b4VHxQDbo0eP5C4XdHR0RHJystwyMzMzjBgxApcuXUL37t3h5eWFS5cucQHVqkilUvj6+sLFxQW7d+/mZj8BgL29PUJCQuDp6YmpU6eiqKhIYTtmZmYgIt5x/PhxdOjQAUSEEydOICMjA+Hh4dzXQSgUyhyTJ0+WaTs8PBwGBgYKZ/YBwKtXr7Bo0SIEBgbygnKZmZl4+/YtF7gCynLrNWzYkFuFUVFUVBSsra15P5P37NkTsbGxICJERUWhTp06vPZ69uyJ58+f49WrVwr7x1TPVzszTF1dHVOnTsWyZcvQoEEDaGlpYdasWfDw8ECtWrU+d/cYBgAwZ84cLFq0CFZWVmjYsCFmz54NBwcHtGrViisfOnQobG1t0bFjR6xYsQLNmjXDwIEDP3PPmW/RkydPcOfOHSxcuFDmL4OGhoZsvDJfjPfv38PT0xMuLi4wNjbG3bt3MX/+fDg7O3M5xNhYZb4EFWfRVFS3bl00btyYjVXmizJjxgzY29ujbt26iIyMhL+/P1asWAElJaVvfqw6Ozvj6NGjH3WNm5sb9uzZA5FIBIFAgB49egAom7X84sUL6Ovr8+q/efOGt2w0JycHI0aMgFgs5oIy5UaPHg11dXU4Ojpi3bp18PDw4JWfO3eOmx0tT3Z2tswMtRYtWsDPz6/SpZAWFhbcjqJNmzZFdna2TJ3t27dj9erVSE1NlZkhJm/GmFgshq6ursy5isskhUIhbty4AXd3d6SlpeHSpUuwsbGRaWvw4MG4du0ahg0bhpMnT2Lz5s2oWbOm3GcpLCzEs2fP0Lx5c4XPW46IUK9ePSQlJXHnQkJCcObMGZm6+vr6aNasmcwzVWzLzc0NY8aMkZkZqaurCxUVFV4gjYiQn5+PN2/eyLSVkpIid2MKsViMzMxMheVA2Qy8unXrVvHkTGW+2mAYAKxevRpFRUVwcnKCsrIyXFxcsG7dus/dLYbheHp64u3bt5g2bRpEIhGGDBmCrVu3cuWDBg3C5s2b4ePjg+zsbPTs2RMnTpz4R1N1GaYqGRkZAMDlBqnol19+YeOV+WIIhUKUlJTA1dUVubm5MDU1xYwZMzB37lwA7N9W5uvBxirzJSkoKMDYsWMhEonQtGlT/P777xg3bhyAb3+sVsy/1b17dzg5OXHL7qsrPT0dL168wNSpU/HixQu5y/Rq166Ne/fucTkuLSws4O7uDmdnZ17gZenSpcjKysLJkyfh4OCAjIwMLoG8k5MTevfuzeVzk5cC6OrVq9wyxIsXL2LmzJlITk6GqakptxwwMTERjRs3lglgrVq1CkBZwM3Y2Fim7QcPHiAvLw9JSUlo3759pe/k/v37CAgI4JZIVvT8+XOcOHGCmxW3YcMGmJqaIjg4GE2bNlXYZrdu3RAfH4/FixejqKgINWvWxP3797nnkkql8Pf3R2hoKPz8/BAfH/9JE/536dIF169fV1ju7++PtLQ0/PnnnzJlNWrUwODBg+Hn54fOnTvDzMwMK1euxOvXr+V+r+Tn53PByXLlAdXyzSvkbVxRXs78S8QwDMMwDMMwDMMw37i//vqL9PT0KCcnh0pKShQe5cLCwmj69OkEgPT19cnX15caN25MT58+JSKimJgYio6OJiKitLQ0MjU1JSKi8PBwmj9/PkkkEiooKKC6devS6dOniYgoPT2dNDU16e7du0REdPPmTa6s3MKFCwkAaWpqVnoEBwdz11haWlJaWhr3uXHjxpSdnf1R7yc/P59q1apFU6dOpcmTJ8utc/bsWRoyZAhpaWmRnp4ejR07Vm69q1evkoWFBfdZKpUSEdHbt29JURgiODiYHB0deeeioqJ47bi5udG6detILBZT3bp16dChQ0REdPz4cerQoQPvOktLSyIievr0KQkEAt67U1NTIzc3NyIiio+PJxMTk0reTJkzZ86QlpYW3bt3jztnampKK1eu5D5nZGRQz549CQApKSnRqFGjqFWrVrRs2TKZ9gYMGEAuLi68c2fPniUA9O7dO5o6dSp17dqVV/748WMCQHfu3Kmyv0zlvtqcYQzDMAzDMAzDMAxTXbGxscjJyYGenh5q1Kgh91BTU0NpaSkAYM+ePdDS0oKysjLevHmDn3/+mdfesWPHeLnUyg0aNAh+fn5QUlKChoYG/P394ebmhpSUFIwZMwZubm5o3bo1AMDGxgb9+/cHABQVFWH69Ol48eIFpFIpDhw4AGNjY+Tl5eHly5cQCARIT09Hfn4+OnfuDFVVVd59+/XrBysrK1hZWSEtLQ23b99GrVq10LhxY+68kpKSwnxTAQEBsLCwwJo1a3DkyBE8fPhQpk5mZiZatGiBli1bYt26ddi/fz+EQiFUVFR4R48ePbgZXS1btlSY8F4sFqOkpAQlJSWIjo7mNh+piqqqKjw8PODv71+t+o0aNeLlXduzZ0+1rqto7dq1KCwshI2NDZd37NmzZ1ixYgW3LLROnTq4cOEC3r17h4yMDOzfvx/Pnz/n5f0qZ2JiIndjCl1dXdSqVUthOQCZ5ZPMx2PBMIZhGIZhGIZhGOabN2XKFJnE6xWPy5cvw8jIiFuSd/z4cSxfvhyA/HxZr1+/rlbyeBcXFwwbNgyWlpYQi8XYtGmTTJ3S0lJ07twZ+fn52LVrFzIzMzFv3jx4eXlBIBDg6NGj6Ny5M/T09ACUBc60tbV5bZw9e5bbzKN+/fpo164d/Pz8MHz4cMTFxWHt2rWwtbWVm2sqJiYGa9aswYYNG6CrqwsvLy84OTkhNzeXV2/MmDFYvXo1txM5ULYEd9WqVSgoKEBpaSlKS0tx6dIlWFhYoLS0FAkJCQrfTUhICFRVVaGqqorw8HC4uroiMTERw4cPr/K9Tp48mfv6/C/s3bsX9+/f522aYmxsjOnTp3MbqJSrVasWDA0NceDAASgpKaFv374y7XXt2hU3b97kveMLFy5wG1N07doVz549Q2JiIq/c2tpaYS41pvq+6pxhDMMwDMMwDMMwDPMppKWlwczMrFp1i4uLERkZCRUVFbi6ukIgEEBNTU1u3TNnzuDy5cswNDRESkoK9u3bB1dXV9SoUYOro6Kigj179sDS0hKpqakYOnQoWrVqhcmTJyM3NxdLlizBwYMHufpZWVkySd779evHtVk+g2jMmDFo164d7O3tMWfOHOzbt0+mf1euXIGjoyO8vb25xPYLFy7E+fPnYWdnh2PHjlX6XpSVlXH58mUkJiZi586d1Xp/5VxdXTFq1Cje+4uKipKZEVU+W4+IuHPGxsZy857Jk5KSwttNEygLUn4oOjoaXl5eOHnypMz7LU9eX1GNGjVgYGDAbXhQPiZq166Nq1evYv78+di0aROX62vRokUAgDVr1mDkyJFYsmQJJkyYAG9vb8TGxuLo0aO4cuUKAMDW1hZt27bFxIkTsWnTJqSmpmLLli3Yu3dvtZ6ZqRybGcYwDMMwDMMwDMN89x49eoQffvih0jpJSUkwMzPD/Pnz0bJlS7x9+xZt27bF27dv8fjxY67eixcvEBAQAEtLS7i5ucHDwwOJiYnYt28fAgICYGpqirlz5yIqKgoFBQUAynaEDAwMhJWVFZo1a4YDBw6goKAAI0aMQL9+/dC9e3eu/VevXqF27dq8vp06dQrh4eHYunUr9PX14evri7t378LX1xd2dnbo06cPbwdEsVgMb29v9OnTB4sXL4aXlxdXpqSkhIiICNStWxetW7eGr68vsrKyFL6XHTt2ICIiAsnJydV72f+fQCCAUCjkBRKTk5N5Sfbv37/PLWMNDg7+qPYBQFtbG0uWLOEtk8zPz8f27dtl6r59+xYPHz5EYWHhR98HAFJTU+Hk5IQ2bdogMDAQ27dvx8SJE3nPVv6ONDQ0cPr0abx69Qrt27fHxo0b8ccff6BDhw4Ayt7N8ePHoaGhga5du2Lu3LlYv359tWbNMVVjM8MYhmEYhmEYhmGY70pRURFSU1NRq1YtqKurIyUlBXv37sXKlSt5dTIyMrjcXCKRCFeuXOFyR504cQIGBgbYvXs3evbsifnz52P+/PkYN24cQkJC0KlTJ8yaNQvOzs5QV1cHUDZ7q0+fPjh16hSCgoJgb28PIyMj3L9/H+7u7rhw4QL8/Pzg4eGBu3fvwt3dHcbGxvD19UVGRgY0NTURFhYGoCwPVkFBAfr06YPk5GT07dsXDRs2RJMmTSAWi2Fubo5Tp05xzxUSEoJBgwbBxcUF1tbWsLOzg7KyMo4ePQoHBweZd6SlpYXw8HBs2rQJa9aswalTp3Du3DlIJBLeewHK8l8lJSVBWVkZUqkU6enpcmfK6erqQkVFBREREejdu7dMLrGMjAzs3LkT7u7uAMpmndnY2ODmzZsAgBUrVnBLU7OysiAWi6GhoYGYmBiFSwf19fXh4+PDO1dQUIC8vDxoaWkhJiaGmzU2ePBgvHv3TsGokZWamsr7PGnSJEyaNElh/cOHD/M+t2zZEtHR0Qrr169fH2fPnq12f5iP8Ply9zMMwzAMwzAMwzDM/96bN29IRUWFABAAUldXp4kTJ1JpaSlXp0+fPqSkpESurq70/v17ql27NjVu3Jg2bdpEIpGI1969e/fohx9+oCtXrlBycjKlp6dXqx+FhYVc3devX1NeXh4RESUlJZGGhgYtXryYxGIxrVy5kuursbExHTlyhGsjJSWFtwsmUdlukuvWrSNXV1dKTk4mIqLi4mLav38/eXp6klQqpf3791N+fn61+pmTk0OZmZl04MABAkAmJiaUmpoqU8/FxYUEAgEJhULauHGj3LbWrl1Lurq63PNUPLS0tGj8+PEyzyNPeV8AUK1atejkyZNcWcXdJOWJjY3lrtXT06OgoKCqXwLzTREQVVh0yzAMwzAMwzAMwzDfkZKSEqioqCjc8bBcZmYmDAwMKm2nYh6wf+v169eoU6fOJ2vvUyGiKt/V/5JUKpW7wUF1riMiKCsr/we9Yr50LBjGMAzDMAzDMAzDMAzDfDdYAn2GYRiGYRiGYRiGYRjmu8GCYQzDMAzDMAzDMAzDMMx3gwXDGIZhGIZhGIZhmO/OzJkz8fDhw4+65s2bNzA0NMSQIUOqVf/Zs2d4//4971xpaSkSEhI+6r4VOTo6Yvv27bxz69atw8WLF/9xmwzzvWHBMIZhGIZhGIZhGOa7tHTp0mrXJSJ4eHigSZMmuHDhAs6ePVvlNVu2bMHo0aMhkUi4c+vWrcPo0aNRWlrKqxsQEAAHBwfe8WEgTZHs7Gw4ODjgwoUL2L9/PwQCgcJjyZIl1Wrz2LFjsLS0hFAoRKNGjeDr6wupVKqw/rZt29CwYUOoq6ujZ8+eSElJ4ZUfPXoUzZs3h1AohI2NDe7cuVOtfjDMf4EFwxiGYRiGYRiGYZhvVkJCgtyg0C+//IKjR4/KLUtKSpJpZ/78+bhz5w4iIiLwyy+/YOzYsbh3716l916zZg0A4MGDBwCA5ORkbNiwAYcOHYKKigqvblxcHOrUqYMpU6ZgypQpOHnyJIqLi6v1jCtXrkS7du1w9+5djB07FiUlJXKPOXPmVKu99+/fY+7cuZg3bx5u3bqFRYsWwdfXFxs2bJBb/8iRI5g9ezZ8fHxw7do1lJSUYPDgwVzwLDo6GqNHj4aHhwdiY2PRoEEDDBw4EHl5edXqD8N8amw3SYZhGIZhGIZhGOablZCQgK5duyIjIwMAIJVK8ebNGxgZGQEAMjIyuP8GAC0tLTx69AhNmjQBUDYj7Oeff0ZQUBAuXbqE5s2bAwCWLFmC33//HadPn0bbtm159wwNDcXIkSOhrKyssF9EhHHjxmHPnj0AgPHjx6Nly5aYN28eAEAgEGD69On49ddfFbZx5MgR5OTkoEGDBmjTpg309fWhpKR4zsu8efMgFArh6+ursA4AiMVi5Obmonbt2ty5H3/8EfHx8YiOjpapb21tjW7dumHz5s0AgEePHqF58+a4ePEi7OzsMGzYMEgkEvz5558AgJycHBgZGWHbtm2YMGFCpX1hmP8CmxnGMAzDMAzDMAzDfLNq1qyJMWPGQCgUorS0FKNGjcKPP/4IoVAIoVCI6dOnY/jw4Xj//j2EQiHc3Nygo6MDAMjNzcXw4cNx8OBBREVFcYEwAPD19cWPP/6Irl274rfffpO5b/fu3VFaWqrw8PPzq7Lvy5cvBxHxjiFDhmDbtm0gIowYMQLXrl3DgwcPYGhoiNWrV3PPVfG4cuWKTNtOTk4YOnSo3PuqqanxAmEAIBQKecs9y+Xk5ODvv//GgAEDuHPm5uaoW7cuYmJiAABRUVG8cj09PVhbW3PlDPO/xoJhDMMwDMMwDMMwzDfLxMQEgYGBeP78Obp164bc3FwcOnSIKz98+DC0tLTQunVrnD9/HkFBQahduzYiIiJgYWGBt2/f4ubNm2jRooVM276+vti5cyfmzJmDTp06/avE+Io8fvwYBQUF1apbWlqKJUuWQCQScUePHj3k5vpq1KgRmjVrVq12c3JyEBoaip49e8qUPX36FADQsGFD3vkGDRogPT0d2dnZyMnJUVjOMJ8DC4YxzHdE0apotlqa+ZLIG49sjDIMwzAM828cPHgQlpaWaN68Oc6fP4+aNWtyZZqamjh06BDmzJkDe3t7bNy4EQCwa9cueHh4wNPTE3Xq1FGYkP7x48eIj4+HoaEhhELhv+rnggULoKKiwssnNnXqVGzduvVftSvP2rVrsXbt2irrZWRkoH///tDS0sLChQtlyvPz8wEAGhoavPMaGhoQi8VVljPM58CCYRVYWVnh9u3b//j6Bw8eyOwIUpWP2WqXiGSmpSYnJ8tN7liVly9fYt++fR99HfN5icVimb/qCAQCpKam8s4VFhbKvf7HH3/Eli1beOdCQ0MV/rWonLzEoMnJyXJ3tykpKZE5d/36de5/gh/j/Pnz+Ouvvz76OubzKioq4n3es2cPevTowTunaIxmZWWhVatWSE5O5p13dHTE77///kn7CZT98PbXX3/hjz/+wPLly2XGb1hYmEzfGYZhGIb5uqxZswYTJ07E0qVLsWvXLhARb+ZU+TFz5kzs3r0bP//8M3bs2IFjx45h6dKlUFZWRvfu3WWWKxIRJk6cCH19fTRq1Ajh4eFcnjEAuHLlitwli+XHokWLZPrq5+fHLaMsN3v2bKxfv17hz08fWrlyJbS0tLgjMjLyH7+7q1evom3bthAIBLhy5Qr09PRk6qipqQGATLJ/kUgEDQ2NKssZ5nNgwbBKeHp6Qk1NjftHRFVVVWHkvLS0FDNmzICTk9NHzWD4mK12Dx8+jE6dOnE7kQBlfzk4evToRz5Z2S+iU6ZM4bX1saraalcsFmPmzJnQ19eHjo4O3N3def+AExFWrFgBY2NjaGpqYujQoXj79i3vHv92e94rV66gbdu2EAqFsLCwkNn+OCEhAba2tlBXV0ejRo0qDRB+iuf5t+bOnQtvb+9K6+zcuRO9evWSOw6nTp2K5cuXc8lDi4qKMGvWLHh5eSlMtJmfn4/Bgwfjjz/+4M6VlJTA0dFRbjJPR0dHzJ07lwuIvHv3Dn379q32ttAVxcTEwNPT86OvK1dUVITZs2ejbt260NLSQufOnXH58mVenarGQGxsLLp06QKhUAhTU1P4+/srvN/EiRMhEAh4072XLVsm89fD9evXc+W7du2SKa/4zBcuXECHDh2gra2NunXrYubMmRCJRHLvL5VK4evrC1NTU2hoaMDKygrHjh3j1bG1tZW537/5I8CHCgsLYWFhUenOSlKpFAMHDsS2bdtkymrVqoUBAwbAy8uLOxcWFoZHjx7ByclJpn69evVQr1491KhRA3Xr1uU+lx+Ghob44YcfeNfs27cPvXv3Rv369aGtrY22bdtizJgxWLFiBZ48efKPn720tBTz58+HkZERNDQ00L9/fzx+/Fhu3eTkZKiqqmLSpEncOYlEAiUlJd7XRktLi3ddgwYNZL5+mZmZ3PWTJ09GvXr1oKmpifbt28uM94ru3buHvn37QkNDA0ZGRpgwYQLevXvHlV+8eFHmXg4ODv/4/TAMwzDM5zJp0iTExMQgMDAQ6urqlR7r1q3D5cuXMXbsWAgEArntFRYWQiKRIC8vD3///TdMTU1l6pibm2P79u1coO327dv49ddfecG3ixcvwt7evsr+Ozg4QEdHB0FBQdV63qVLlyI/P587evfuXa3rPnT8+HH07t0bY8eOxZUrV3ibDFRkYmICAEhLS+OdT0tLQ6NGjWBgYAA1NTWF5QzzWRDDsbS0pFu3bnGfp06dSlu2bOE+z507l9asWaPw+sLCQmrXrh2tW7eu2vcUi8U0YMAAunfvHhERJSUlkb6+PiUkJMitHxgYSCYmJpSdnU3Jycmkrq5OJiYmZGJiQgDI2NiYTExMSEtLi0aOHElERGpqanIPFRUVUlVVlVuWmZlZab9zc3PJzMyMgoOD6d69e/T777+Tmpoa+fv7895fw4YN6eLFi3Tu3DkyMjKiqVOncuV+fn5Uq1YtCg8Pp+vXr1Pz5s1pwIABXPnhw4dJTU2NgoOD6fbt29S1a1eysLAgiURCREQ3btwgFRUV2rRpE8XFxdGwYcOodu3a9P79eyIiSklJIU1NTZo/fz7du3ePpk6dSurq6vT06VPuGYyMjMjV1ZXu3r1LK1euJCUlJYqOjpb7zP/2eT6FBw8ekLa2Nh07dow7B4B7poSEBNLQ0KDAwEDuHejq6vIOoVDI+6ympiZT59mzZ7z7xsTE0JAhQ0gqlRIRka+vL/Xs2ZP7WlSUlZVFjo6ONHHiRCIi8vHxIV1dXTIxMaGaNWuSUCjkxqySkhKdPHmSgoODFY5TZWVlueeHDh1a5fvasmULOTo6UlRUFN2+fZucnZ1JU1Oz2mMgOzubdHV1yd3dneLi4ig4OJiEQiHt3r1b5l7x8fGkrKxMACgtLY07P336dBo2bBglJiZyR3Z2Nle+bt066tChA6/87du3XPkvv/xCO3fupLi4OPrjjz9IR0eH5s+fL/d5w8PDqVu3bnT69Gn6+++/afbs2TJj2sLCgvz8/Hj3E4lEVb7Lj+Hj40ONGjWirKwsIiLavXs3de/enStftmwZGRgYcOPM19eXN/60tbVJU1OT+6yjoyMzbu3t7Xn31NfXp9evX8v0JSAggKZPn847FxERQTNmzKBVq1ZRx44dac6cOfT48WMqKioiIiJvb29asGABEREdP36c1/fKeHt7U+3atenPP/+k27dv04ABA6hRo0Zyv0+cnJwIALm7u3Pn3r59SwDo0qVL3NcmOTmZd52mpiYdOHCA9/Urb18kEtG4ceMoKiqKbt26RcOHDydtbW3KyMiQ299u3brRqlWr6O7du3TixAlq2LAhDRw4kCs/cuQImZiY8O718uXLar0LhmEYhvkauLu709y5c6usd+TIEd7PA+PHjycABIDat29P79+/p+DgYPLz8+PqRERE0Pv37+nq1at0//59evLkCenq6tKdO3e4OmfPnqWoqCjus5ubG+93SQDcz4WnT5/mfnYaMmQIbdu2jXfdpk2biKjs55GVK1fy+t+vXz/uPnPnzqWff/65ymd+9eoVaWpq0ubNm6usS0RkZmZGixcv5j4/fvyYAHC/5/bo0YPGjBnDlefk5JBQKKTw8PBqtc8wnxoLhhHR+vXrqV+/fqStrU2dOnWiSZMmERGRo6MjhYSEcPWqCoYR/V8w4vnz55XWO3LkCAEgZWVlhYeSkhK5ubnJXFtQUEBpaWnUtWtXWr16NRER3b59m3R0dEgikVBpaSk1b96cjh49yrtOJBLRlClT6N27d7zz7969o4ULF3K/CFaHSCSS+cVz8uTJ1LFjR65NFRUVXtBm27ZtJBQKqbCwkCQSCRkYGNDGjRu58tOnTxMASklJISKiNm3a0MyZM7nyhw8fEgC6ePEiERENHTqUBg8ezJVnZ2eTmpoa7dq1i4iIZs+eTZaWllx5SUkJmZiY0LJly4iIaPPmzWRoaEhisZir06FDB3J1dZV53k/xPJ/Kvn37SFtbmx48eEBE/xcMe/36NTVs2JAmTJjwye5lb29PSkpKlY5TALz/iZfLyMig6OhoqlmzJiUlJRERkaenJxeYuHbtGtWtW5cKCgp4192+fZuWL18u015oaCj9+eefH9X/D78Pi4uLSUNDg7Zv305EVY+BmzdvEgAuwEpUNu6mTZvGa1cqlVLXrl1pxIgRMsEwZ2dnmjNnjsI+Llq0iIYNG1btZ5o2bRq1adNGbtnLly+ppKSEd+6HH36ghQsXcp/r1q37n//QIZVKqU+fPtS3b1+SSCS8YNihQ4dIVVWVIiMjP9n9cnJyCAA1atSIGjduzB1jx46lAQMGUL169cjS0pIsLS1l/p1zc3Pj/TBJ9M+DYQMHDuR9re/evUsA6M2bN7x6586dI1NTU7KxseEFw8p/aKw43ioSi8W8Hyqr8ubNGwKg8Pvmw++PAwcOkJKSEvc9+dtvv5G1tXW17sUwDMMwX6N/GgyTSCRUWFjI+7lr2bJl5OXlRURlf5BWU1Ojt2/f8gJVGzdupFatWpFUKqUXL17Q+fPnqU6dOpSamkpEZT+X+Pn5UUlJCZWUlPCCYRVVFQxTVVUlTU1N3nHlyhUi4gfDFi5cyPs5saLffvuNhEIhPXnyhPeHscTERBKLxZSUlEQdO3bkfs7fsmULaWpq0uHDh+nWrVvUtWtXcnBw4NoLDw8nZWVl2r59O8XFxdHQoUPJ0tKSSktLq3z/DPNfYMskAdjY2GDEiBHQ09NDr1690K9fP2RnZyMqKgq2trZcPaFQiOfPn1e6DDItLQ2FhYXw8fGp8r7/ZKvdwsJCTJ06FWPGjIGHhwe8vLzw7t07TJw4EfXr14enpyfy8vIwc+ZMDBs2jHetmpoa3r59y1uWA5RNo718+TIvSSPw77bavXr1KqRSKfr27cuV9+zZEyKRCHFxcYiPj0dmZiZve93u3btDSUkJMTExn2R73qioKPTv358rV1FRga2tLa+8Z8+eUFVV5fVR3va+//Z5PiUXFxcMGDAAcXFxvPN//vknGjVqJLOtc3FxMTIyMmSOnJwcmbavXbsmk4sgKCio0nFqYWEh005cXBy6du2KS5cuYf/+/WjcuDFOnjyJQ4cO4dy5czh37hx0dHSwZ88emTwBtWvXxubNm/Hnn39y5/Ly8uDp6YkXL17w6r569Qr6+voKlwrXr1+f97k8GWn5OK1qDFhaWsLc3ByBgYEoKSnBnTt3cP36dYwYMYLX7vbt27nvzQ9lZWXBwMBAbv+qU/4hiUQCfX19uWV169aV+T7+cAvs7Ozsj7rfPyEQCLB79268fv2aW8JXbsuWLdi2bRt69erFO5+bmyt3nH6YVDUrKwu3bt3inQsPD8fQoUNx6NAhJCUlISkpCd27d4elpSWCg4Nx69YthIeHIyEhAUKhEN7e3vD09ISnpydiY2Nx6NAh7vOHS6k/vI+BgQGePXsmt3zUqFE4ceIEUlJSUFRUhO3bt8POzg6GhoZcnYKCAkyZMgUBAQEyYz8rKwuqqqrQ1taW235WVhYAVPvrV75sXdF4+fD7QygU8pa6f+zYZBiGYZjvhZKSEtTV1Xk/dyUnJ6Np06YAyn42HDhwoMz/R2fNmoWDBw9CIBDA2dkZYrEYU6ZMgaOjI/cz+IIFC1CjRg3UqFHjH/WtXbt2CAsL4y2TzM/PR7du3WTqJicny+RpLZeRkQGRSISmTZvihx9+4B1PnjxBbm4uHj58iNzcXABlKYbmzJmDadOmwc7ODqamprz0I4MGDcLmzZvh4+ODTp06obi4GCdOnICysvI/ek6G+bdYMAxlOXQmTZqEWrVqYciQIejbty9GjRqFMWPGoG7duly9YcOGISwsDEpKSpg7d67ctg4fPgxHR0eEhIRwW8x+KjExMWjTpg1yc3Nx/Phx9OzZEwEBAWjRogUcHR0RFxeH4uJi2NraQltbW27C8m3btkEqlXJld+/eRUhICA4ePCjzS/S/2Wo3JSUFtWvXhqamJlenQYMGAID09HQu91fF7XXV1dVhaGiI9PT0T7I9b0pKyr8qr+jfPs+ndvDgQTg7O/POTZ48GSdOnJD5H+eNGzfQvHlzTJo0iTscHBzg4uICANx4BYDRo0fj+fPn/7hfUqkU/v7+6NGjB3766ScsWLAABgYGcHFxwdSpUxEWFobw8HD89NNP8Pf3h56enkxwuX79+ti0aRPvf8wrVqxAhw4dZIJN6urqXJC0Og4fPoz379/Dzs4OQNVjQFVVFQcPHsSKFSugpqaGdu3a4ccff+SuB8q2ul64cCG2bdsmN+/au3fvsGzZMujq6qJt27YICgriPfO7d+8QFBQEbW1ttG7dGv7+/nI3IRCLxQgPD8ehQ4cwb968aj1vbGws4uPjue/LgoICbnvt2rVro1evXrh69Wq12vpYJiYmuHPnjkzQPDIyEhMnTpSpP2vWLPTt25c3Ti0tLREREYGcnBxuZ6a//voLM2bM4K4rKSnBpk2bMGrUKMyZMwf9+/eHn58f7ty5gxkzZsDAwABGRkYQCoXQ1dUFADRu3Bjm5uZo2LAhEhMTUadOHZibm8Pc3Fxh4AgoCyo1a9aMa+dDrq6uaNu2LRo3bgxNTU0cO3ZMJgfdrFmzYGFhAUdHR5nr3717h+LiYqirq8PU1BRjxozhbY5Rns+rYcOGMDExweDBgxXmZnv+/DmmT5+O7t27o3PnzgqfqRwRISgoCB06dOCCdO/evUNkZCQ0NTXRvHlzLFy48B9thMEwDMMwn9ulS5fk7gIZFBSEDRs2yC2rmFNVX18fDx48QGJiokzS/aKiIsTGxuL06dOwtbVFaWkpdu/ejZ9++gkAoKWlxeUkVVJSQsuWLZGamoq///4b7dq1w9KlS9GmTRu8efMGQqEQmzdv5pLzt23blvvZPi0tDe/evUNGRgYePnzI2w2zIgcHB94f6AHg7du3ePfuHXJzc3H37l0uJ+nhw4dx+PBhue0sW7ZM7oYBRISWLVvC2toaOTk5sLa2BlD2x1AfHx+8ffsWeXl52L9/v0yy/enTp+PFixcoLCzEiRMnUK9evY/7QjLMJ6RSdZXvi1gsxpMnT9ChQwcsXbqUV2ZtbY2XL18qvDY/Px9hYWE4ffo0jIyMsGbNmk+2+5lEIsHMmTMxa9YsTJs2DWlpaejevTv69OmDK1eucEGrnTt3IioqCn5+flixYgXu3bsHoVCIevXqISsri/tl/cPkhy1btuT+u0+fPjh+/Hi1ttkFyv5q4OjoyNtqNz8/X2bWg1AohEAggFgshkgkgpKSErezSLnqbr9bWXn5TBR5fai4fW9V5RX92+f5FK5cucKbmVZOTU0N5ubmvHNisRhRUVHcLnjNmjXDiRMnuPLQ0FDs2bPnk/SropiYGBw4cADXrl1Dy5YtsWXLFuzcuRMTJkzA77//zr3Dv/76C4GBgRg7dixGjhyJ1atXIzIykkvmXW7ZsmW89ismE9+7dy+GDx+O69evV6tvJ06cgLu7O7y8vNC8eXMAVY+BjIwMODg4YOzYsZg0aRLu3buHOXPmwMLCAqNHj4ZIJIKzszN++ukntG/fHpcuXZK5744dO6CkpIT3798jPDwckydPRlFREZckf8WKFfj5558hFotx4cIFeHt7482bN7wk+82aNcOTJ0+gqqqKNWvWoF+/flU+b2xsLAYPHoyRI0dyMyRVVVURHR0NTU1NZGRkYMuWLejVqxdiY2PRpk2bar3HqkyePFnhRhQVtxqXSqUwNjbmBXumTZuGKVOmcJ+rStZORJgzZw7U1NTg5OSEXr16oUuXLjh79iwWLlwIVVVV5OXlQUNDA5cuXeJmQrm6ugIAtm7dColEgrZt28LFxYX7ga3i90pFXbp0qXS8+fj44NKlSzhy5AiMjY3h6+uLQYMG4caNGxAKhTh06BAiIiIUBrA6dOiAmzdvQk1NDY8ePcKKFStgZ2eHe/fuQVtbGw0aNEBMTAw0NDSQmpqKtWvXonv37oiPj+d+mNy3bx8mTpyI0tJS2NjY4NChQwqT/5YrKSnBtGnTEBUVhStXrnDnp0yZAmdnZxARoqOjsWzZMiQlJSE0NLTS9hiGYRjmS2Nrayuz23VVKv5cb2trC1tbW7Ru3VruRka1a9eGt7c3NzPs+vXr3AY+EydOxIgRI6ChocH9LqasrAwvLy/UqVMHQNmGSkDZjLKKKgbk3N3dcf78eQgEArRt21Ym4FUZb29vbNu2DQKBABYWFjJ/VGeY79LnWJv5pXn37h3t37+fateuTUKhkObPn09DhgwhgUAgs9ZaIBDQpUuX5LazbNkyatmyJRGV5WLR1NSkhw8fyq175MgREggECpOGlye4r5gzTCqVcokXqzoiIyN5669NTEwoJibm0720/+/KlStkbGxMHTt2pFevXnHn/fz8qF69ery6xcXFBICOHj1Khw4dIgAy+Y3q1q1LGzZsoNjYWALArUEv16lTJ5oxYwa9fv2aAMjkHXJ2dqZBgwYREZG6ujrt3LmTV75o0SJq1aoVERE1b96clixZwiv/7bffSFtbW+Y5/+3z/K+ZmppyebyioqJIKBRSs2bNuMPY2JhLQD5kyBD6448/iKhsnFQcs/b29qSiolLpOBUIBLycYVKplJYvX17lGG3atCkRETdOz58/T+bm5p/8XUgkElq6dCkpKyuTt7c3twkAUdVjYP78+WRhYcG7ZuXKlWRiYkJERGPHjqUePXpwzxAVFSWTM+xDEydOpBYtWigs9/HxIU1NTd49k5KS6NatW7Rr1y4yMzMjZ2fnSp85MDCQ1NTUaPLkyVRcXKywXklJCTVr1kwmB9r/QlRUFJmamnKf3dzcqE6dOrxxqqmpSUeOHOHyARKVjZMOHToQEdHy5cvJxMSEnj17RrNmzSIdHR1ycnKiy5cvU+vWremnn34ie3t7AkD6+voUGhrK3a+oqIgaNmxIdnZ21LJlS2rdujX3b9g/yRmWlZVFQqGQDh8+zJ3Ly8sjPT092rFjB8XFxZG2tjadO3eOK+/evTsvZ9iHnj17RgKBgNdmRe/fvyc9PT3exiU5OTmUkJBAFy5coEmTJpGOjg5vY5gPpaWlUefOncnIyEjh5iHlgoOD5eZAYxiGYRjmf0MqlcrdmKc6JBLJP76WYb5FbGYYyvK8iEQiEBFOnToFOzs7SKVSNGvWDGfOnEHjxo0BlEXmhw4dChsbG5k2MjIysGHDBm6aaf369TFhwgR4enoiMjJSpn75Vrs//vgjACAhIQGxsbFwd3fn6ly9ehUZGRncZ4FAAIlEAqFQyDu/YcMGTJ48GTo6OgCAjh07oqSkhLf+WiwWf/L12MePH8fo0aMxa9YsrFq1irc8z8TEBK9fv0ZJSQl3vnz5XaNGjVBQUACgbImhmZkZ18e3b9+iUaNGvO15y99/+WcnJ6dKt+dt27Yt14fKtu+tqryif/s8n1vLli1x/vx57nN4eLjCKdEVjRw5EitWrODeaWBgIPr27YsmTZpwdQICArglo0DZOC0qKsKUKVO42YWJiYm4efMmxo4dCwB49OgRN2utfFz+F2NUKpXC2dkZly5dwunTp9GnTx9eeVVj4N69e7C0tOTNrLG2tsaLFy/w7NkzHDhwADVq1OCWz5bnW2rSpAnGjRuHHTt2yPTJ2toahw4dUthna2trFBQU4N27d1yeifLvgXbt2sHMzAw9e/bE8uXLub8+VjRv3jz89ttv2LVrF8aMGVPp+1FRUUGrVq0U5sD6X1uwYAEmTJjAfR49enSl9SdPnowJEyagQYMGsLKyQmRkJPfv882bN5GbmyuzTLPcqlWr0K9fPxQVFcHJyQnJycno0qULzp0794/6/uTJE4hEIlhZWXHntLS08MMPP+DevXu4fv068vPzMWjQIK68uLgY165dw/79+/H48WOZbdkbNGgAfX19hV8fbW1tNGnShFeuq6sLXV1dWFhYoGfPnnjy5Ak2btyIgwcPyu2znZ0dWrVqhbt37yp8V+XKl0E8e/aMlweNYRiGYZj/jfIlnP+EvHQeDPM9Y98RAMLCwnD16lUYGxtziYuVlJQwdepUrF69GkDZL+rTpk3DihUrZJZVlZSUYPTo0ejWrRsGDhzInff19cXDhw8REBAgc8/U1FQ4Ozvj2rVrePDgAdTU1DB37lz89ddfXJ2ioqJq/cKxbNmyKpfi5efn8/Jd/VsZGRkYN24c1q1bB39/f5k8VV26dEFJSQlv2diFCxdgaGiIVq1awdraGurq6rwAzeXLlyEQCGBrawsTExOYmZnxyp88eYL09HT06tULSkpK6NSpE688NzcXt2/f5hJzd+3alVcukUhw6dIlXvnFixd5ycUvXLggk9j7UzzPp9K5c2doaWlVeXyY9ysjIwNbt27ljtOnT1d5rzdv3qBevXpo27Yt9uzZg+LiYmRnZ8PFxQWlpaUAyoI/IpGIm+KtSFxcnNxfxivKy8v7pGMUKAveXbhwAdHR0TKBMKDqMWBiYoIHDx7wromPj4empiaMjIzw8OFD3Lt3D3FxcYiLi8POnTsBAGfPnlW4icbNmzd5wUR55TVr1kStWrXklpfn9qvY53InTpzA1q1bceHChSoDYUDZv11///13pf35WPHx8dUao/Km9l+6dIk3TisuoZTH2NgYqqqqUFJSwk8//YQ+ffpAIBBAT08PderUQZ06daCnpwc9PT0oKSlx7UVFRWHLli1YvHgx19a6deswatQoue+1OoyNjQGAN14KCwuRnJwMExMTrF69Gg8ePODGSlxcHNq1a4ehQ4ciLi6Ou76ilJQUZGZmKvz65OTk4MmTJ5V+/SpuGPGhMWPGoFOnTjh16lSVgTCgbGwqKSnJ5NljGIZhGIZhmK8NmxkGKPwFfPr06ejatSu2bt2KyMhINGvWjDdrody0adPw9OlT3Lx5k3deV1cX27Ztw4gRI9CkSRMu/83Tp08xYsQIpKenY+fOnbCyssJPP/0Eb29vjB8/Hnfv3sWrV6+gpKSE0aNHIzY2VmbGQLnU1FSUlJTwdsP7UFpaGkQi0UcnKFy0aBEAYM2aNTJl4eHhkEgkGDBgAJKSknhlDRo0gJmZGYYNG4aZM2dix44dKCwshLe3NxYsWABlZWWoq6tj6tSpWLZsGRo0aAAtLS3MmjULHh4eXBBgzpw5WLRoEaysrNCwYUPMnj0bDg4OaNWqFVc+dOhQ2NraomPHjlixYgWaNWvGBSRnzpyJ9u3bw8fHB8OGDUNgYCCkUinGjx8PAJg0aRI2bNgAT09PTJ8+HcePH0d8fDz++OMPAMCxY8fw+++/49SpU5/keT6FGzduVKte+ey0csrKyrx8W0KhEHl5eZW2sW3bNty9exe9evXChAkT4OjoiAULFuD48eMICAjA7NmzkZmZiYcPH2LChAmVzjS7f/8+tLW1QUQK/5qVmJgos7tdVXJzc2Fvb49169ahU6dOMuUHDx5Enz59IJVKeeO0PI9eVWNg6tSp6NixI2bPng1XV1fEx8dj9erVmDp1qtxcbeUzNhs3bswl9R8/fjzc3Nygq6vLJVQv37AAAGbMmAF7e3vUrVsXkZGR8Pf3x4oVK7i/3o0bNw6jRo2CmZkZkpKSsGDBAnTt2pXLe+bm5gYbGxt4enri4MGDaN++PWrVqsV7XmVlZTRs2BC3b9/GiRMnMHDgQBQXF2PDhg148+YNLyH9v9WqVatqJVm/dOkS971YTk1NjTdOqztT0NjYmNv0QCgUcjul6unpISMjA0KhkPc94eXlBX9/f5nxVv7Hj/bt23MB34qio6Ph5eWFkydPyiTRr1+/PhwdHfHTTz9BRUUFtWvXxurVqyGRSODi4oK6devKbPSgoaEBXV1dbhzt3r0bpaWlaN++PdLS0rBw4UK0bt2a+3/HyZMn8fjxY/To0QPZ2dlYvnw5tLW14ebmBqBsvL948QK2trZQUVHBH3/8wc2K/LD/r1+/xp07d7Bw4UJuA5ByhoaG0NXVxc8//wwbGxs0atQIsbGxWLBgATw8PCrdZIBhGIZhGIZhvgqfe53ml8TS0lImt8qpU6cIABkZGVFmZiav7P379zRixAjS1tamuLg4he36+vpSjRo1aMuWLSSRSGj+/Pk0dOhQIirLk7Np0yYiKlvHHR8fT0REtra2dOLECfL29iYrKysqKCggIqLMzEyu/suXL6lnz57UvHlzqlmzJg0bNowOHTpEv/76KyUnJ3P33759O1lZWX30+xg5ciSNHDlSbtmKFSsIgNyj/Bmys7Np1KhRpKGhQXXq1KEVK1bw8iCJRCKaOnUq6ejoUM2aNWnGjBkkEom4cqlUSkuXLiUDAwPS0tKisWPHUnZ2Nq8fW7duJWNjY1JXVyd7e3uZXE1Hjhyhxo0bk5qaGnXr1o0SEhJ45ZcuXaKWLVuSqqoqtWnThq5cucKV/fLLL9SgQQOuT//2ef6XPswZVp5jqdyRI0e4nGGZmZlUWFhIRP+XM6ykpIRMTEzozz//JCIiANy7T0lJodzcXEpJSSEDAwMqKCigFi1a0OrVq7n2L168SBcvXiQiojNnzpC+vj5ZWlpS/fr1ae7cuXT69GluHJfr2LEjBQQEfNRzvnz5kmrVqsX180MNGzaUO0bbtm3L1alsDBARnT17ltq1a0dqampUr149WrZsmUxuuHLycob17t2bdHV1SVtbmzp27EinTp3iXTNhwgSqVasWaWhokJWVFQUHB/PKPT09ycTEhNTU1KhRo0bk5eVFOTk5XLmNjQ3NmzePiIjs7OzkPq++vj4RET1+/JisrKxIQ0ODDA0NafDgwfTo0aOqXvN/Ql7OsG3btvHq2Nvb05EjR0gqlVJ6ejoR8XOGERG9evWKy+FGRFxuMSIiXV1dKioqIqKy74mnT58SEXFjU9F9Kzp27BiXM+zPP/+kWrVq0cuXL+XWzcvLo2nTppGBgQGpq6tTjx496M6dOwrb/jBn2NGjR6lhw4akpqZGZmZmNG3aNN6/edeuXSNzc3MSCoVkYmJCY8eOpRcvXnDlN27coA4dOpCWlhbVrFmTunfvTufPn+fKK/b/8uXLCv8N/+WXX4iI6Oeff+ZyabZo0YI2btzIy0XJMAzDMAzDMF8rFgyjsmBAWloamZiYUFxcHJWUlFBUVBSNGzeO9PX1yc/Pj0aNGkW1atWi+fPnU2RkJBUWFpKjoyOZmprSvXv3qrzHsmXLSElJiSIiIsjQ0JAuX75MRETTp0+nqVOn8uo+ffqUtLW1KSMjg0pLS2nChAn09OlTio+Pp6NHj5Kvry8NGDCANDQ0aOrUqSQSiSgnJ4d+++036tSpE+no6NDEiRMpOjqasrKyqEGDBvT777//J++O+d9xd3eX2dBB0VExqb28YNiuXbvI0dGRiMo2kHj58iU9ePCAVFVVKTU1lf78809q3Lgxl2RTU1OTYmNjeW34+/tzAbW4uDjy9PSkoqIiunz5Mu3du5dmzZpFFhYWVL9+fS4BeEJCAnl5eZGRkRGZm5vTqlWr6M2bNxQaGkpaWlqUlZX1X75C5j9WWlpa7TEqFAqrDIbZ2tpSWFgYERGlpqbSu3fvyMfHh3r06EFERJMmTSI1NTUCwG3oUNl/q6qq0k8//cS7h6Jg2PXr1yk+Pp4mTpxIgwcP/sRvimEYhmGYL8GMGTPowYMHH3XN69evycDA4JP8fDBr1ixu055y+/bto/379//rtj909+5dmXNJSUmUm5v7j9rLzs4mANwfHomIcnNzafLkybw/3DLMl4oFw4hoyZIlBICsrKzo/fv3ZG5uTs2bN6fly5fzZoPFxsbSlClTqG7durRlyxZKTk7+qF21yv+hffLkCXfuzp071LBhQ1JXV+d+SdTR0SEfHx+Z61evXk2dOnWiSZMm0d69exXeOz4+njw9PWnHjh2UmZlJ7u7ubOeQ74yimWHnz5+nTp06Ubt27bggw5kzZwgAKSsr0+DBg0kqlVJpaSlvduHKlStJW1ubG6MaGhrUqFEjun37Nu++xcXF1LVrVxowYAAtWLCALly4IHc3w5KSEjp69CgNGjSIXr58SaGhoTIzophvW2Uzw2bNmkWdOnWiXr16cf8GOzs7EwDS1tb+pGNFUTCsQ4cOpKenR02aNOHtlsowDMMwzLdjxowZNHz48GrXl0ql5OjoSB07diRNTU06c+aM3HoHDhwge3t73pGYmChTT14w7LfffiMlJSUKDg6mq1evKpzJDYDGjh0rt4+mpqa8n5fy8vLI1NSUDh48yJ0rLi6mli1b8lZ3lPuw7ytWrJCpIy8YlpeXR02bNiUbGxvKzs6mXr16Vdr/8neye/duql+/Punp6dHMmTN599m5c2e1dvb+sG9jx44lbW1t0tfXJy8vr0p/Hw4MDCQzMzMSCoVkZ2fH+z2IiCg0NJTMzc1JTU2N2rVrJ/M7EPN1YsEwOT5civchqVTKWx7HMAzDMAzDMAzDfJni4+MrDcpUFqipaN68eVS/fn16+/Yt7dq1i/T19eXOuPL29iZ7e3uKiIigiIgIql27tkw6HiL5wTAiojFjxtCMGTOIqOyPyPKOzZs3yw2GnTt3jnR1dbk0KOViYmJoyJAh3O+xvr6+1LNnT7lBIgB04MABioiIoJ9++olbDVKRvGAYUdkMNENDQ3r69ClJJBKF/dfX16fExERKSkoifX19OnnyJF2/fp0aN25M4eHhRFS2gqVu3bp0//59mftXZsCAAdS2bVuKjo6m0NBQ0tTUJD8/P7l1Dx8+TGpqahQcHEy3b9+mrl27koWFBfdebty4QSoqKrRp0yaKi4ujYcOGUe3aten9+/cf1Sfmy8MS6Muhp6dXafk/3c6WYRiGYRiGYRiG+d/T1dXlNjuSSqV48+YNjIyMAJRtglT+3wB4G/oAABHh559/RnBwMC5dugQDAwNMmDABycnJ6N27N06fPo22bdvyrjEzM+M2wVFXV8eePXtgY2Mjt29+fn5Yt24dmjRpgsLCQvz222/cZkrlO4l/qLz8Q0FBQXB2doa6ujoAwMHBAadPn+Z+h61RowavvqqqKiQSCaKiotCjRw/ufN++fWFgYIDMzEwkJibCzMwMz549k7mfuro6NDU1kZ+fj/HjxyMwMBDx8fHcbvOK+lnur7/+Qs+ePblN0IYNG4ZHjx5h0KBBWLBgAcaNG4cWLVpU2kZF9+7dw+nTp/HXX3+hTZs2AIBbt27h119/xfz582Xqr1mzBh4eHhg3bhwAYMeOHWjevDkuX74MOzs7rFu3DgMHDsRPP/0EoOz9GhkZITQ0VO7meszXo/KRyTAMwzAMwzAMwzBfsZo1a2LMmDEQCoUoLS3FqFGj8OOPP0IoFEIoFGL69OkYPnw43r9/D6FQCDc3N+jo6AAo2718+PDhOHjwIKKiorgdvQHA19cXP/74I7p27Yrffvut0j6MHz8eVLYyiztmzZqFBQsWgIgwb948xMXF4ebNm9DS0sLRo0e5/lU89u3bp/AeWVlZCAsLw6RJk3jng4KCUFpaqvCwsLCo8h2mpqby+p6dnQ0AKCoq4nYS37t3L4qLi1GnTh00a9ZMpu/NmjWTabdevXq4ffs2Xr16hezsbFy6dAnNmzdHdHQ0zp8/j2XLlslc4+TkhKFDh8rtZ1RUFOrUqcMFwgCgZ8+eeP78OV69esWrm5OTg7///hsDBgzgzpmbm6Nu3bqIiYnh2qtYrqenB2tra66c+XqxYBjDMAzDMAzDMAzzzTIxMUFgYCCeP3+Obt26ITc3F4cOHeLKDx8+DC0tLbRu3Rrnz59HUFAQateujYiICFhYWODt27e4efOm3BlKvr6+2LlzJ+bMmYNOnTohISFBYT/S09Px+vXravVZIpHAxcUFIpGIO8aPHw+JRKLwmv3798Pc3FxmltqnQET466+/ql1fLBYjKSmJ63tqairEYrFMvU6dOqFXr14wNjaGvr4+LCws0L9/f0ydOhWbNm2CpqamzDWNGjWSG1gDgJSUFDRs2JB3rkGDBgDK3n9FT58+BQC59dPT05GdnY2cnByF5czXjQXDGIZhGIZhGIZhmG/awYMHYWlpiebNm+P8+fOoWbMmV6apqYlDhw5hzpw5sLe3x8aNGwEAu3btgoeHBzw9PVGnTh0IBAK5x+PHjxEfHw9DQ0MIhUIAQGBgIFRUVKCiosItL1y2bBmWL1/+nz1jUFAQ3N3dP0lbRkZGUFFR4dp79uwZunbtirS0tE/SfkU7duzAu3fv8O7dO+zevRtbt26FiYkJ2rRpgz59+qB+/foYP348ioqKAABr167F2rVr5baVn58PDQ0N3rnyzx8G48pntMmrLxaLqyxnvm4sGMYwDMMwDMMwDMN8s9asWYOJEydi6dKl2LVrF4iIN+Oq/Jg5cyZ2796Nn3/+GTt27MCxY8ewdOlSKCsro3v37jLLHIkIEydOhL6+Pho1aoTw8HA0adIEADBt2jRuGaKpqSkA4KeffsLu3burPato79690NLS4o6goCCFdW/fvo3Hjx/DxcVFpmzy5Mlyl1yWHw8ePJC5JiMjA6Wlpdw9zczMMGDAAKxZs6ZafQeAZs2acX1v3LhxpXVr1aqFmjVr4uXLl1izZg1++eUXuLm5YfLkyUhOTkZhYSG2bt1a5T3V1NRQXFzMOycSiQDIBrXU1NQAQG59DQ2NKsuZrxsLhjEMwzAMwzAMwzDfrEmTJiEmJgaBgYFQV1ev9Fi3bh0uX76MsWPHKtw4rbCwEBKJBHl5efj777+5YFdVWrdujU6dOmHTpk3Vqu/m5ob8/HzuqGzWV1BQEIYNG8ab8QYAI0eORExMDBfw27hxIxISEnhBwI0bN3JLCStTHix88+ZNtfr/+PFjru/JycnVumb27Nnw9PSEkZER7t+/DycnJ6iqqsLNzQ3Xr1+v8noTExOZ2Wvlnz9c7mhiYsIrr1i/UaNGMDAwgJqamsJy5uvGgmEMwzAMwzAMwzDMN8vQ0BBWVlZISkrizepyd3fH3Llzeefi4uLQvn37Smf+TJ8+HSoqKtDR0UGNGjXQq1cv7Nu3D/7+/lX2xdfXFxMnTvyUj4eioiL88ccfMonz37x5g3r16qFt27bYs2cPiouLkZ2dDRcXF5SWlgIo21lTJBJxuz9Wpnv37ti2bZtMwO1TOX/+PP7++2/Mnz8fIpEIpaWlICIAZQHID3fClKdr16549uwZEhMTuXMXLlyAtbW1TL9NTExgZmaG8+fPc+eePHmC9PR09OrVC0pKSujUqROvPDc3F7dv30avXr3+7eMynxkLhjEMwzAMwzAMwzBMNQUFBaGwsBAlJSWIjY2FtrY2kpKSkJmZydUhIm6ZZEVdunSp1u6NgOwyyf3798sNCIWGhqJWrVqws7Pjnd+2bRt+/fVXAMCECRNQWFiIBQsWoLS0FAEBAZBIJHj79i0ePnyICRMm8K6VSCQoLS2FVCrlnR8/fny1glIAf5lkkyZN5CbDLycWizF9+nT8+uuvUFNTQ61atWBsbIyVK1fizp072LBhA/r27QsAWLRoERYtWiS3HVtbW7Rt2xYTJ07E7du3ERoaii1btmDx4sUAgOTkZHTq1ImbqTZnzhxs3rwZR44cwe3bt+Hu7g4HBwe0atWKKz906BB+++033L17FxMmTECzZs0wcODAar0D5svFgmEMwzAMwzAMwzAMU01KSkpQV1eHiooKdy45ORlNmzblPgcGBqJGjRqoUaMGl0D/YzRr1gy7d+/mLZPMz8/H2LFjZeoGBQVh4sSJvGWdpaWl2LFjB8aPH8+rq6KigiNHjuDHH3/E8+fP0bJlS2zbtg3379/n5QMzMjJCjRo1/nFC/rFjx+LFixe8vt+/f19hfT8/Py5hfrng4GAcOXIEvXv3ho2NDTejLjk5WeGyS4FAgOPHj0NDQwNdu3bF3LlzsX79egwfPhxA2cyuhw8fIjc3FwDg6emJOXPmYNq0abCzs4OpqSn27dvHtTdo0CBs3rwZPj4+6NSpE4qLi3HixAkoKyv/o/fCfDkEVD7vkGEYhmEYhmEYhmG+MZcuXZKZNVWVW7duoV27dgCAqKgojBo1CtevX0f9+vV59YgI9+7dw8CBAxEdHY2mTZti9erVePfuHTZs2AAAGDx4MPz8/NC8eXO8fPkSNWrUgJKSEoYPH44BAwZgwYIFAIDly5cjJycHAQEBcvuUlZUFiUQCoVCImTNnQk1NDfPmzUPz5s2RmprK5cACgPDwcMyZMwdPnjyBkpIStLS0cPHiRbRv356rU54f7cSJE7h79y527tyJX375BVpaWnj27Bn09fVx4sQJXLhwAZs2bUJRUREyMzOhra2Na9euwcnJCQUFBVwQTiAQIDs7G3p6ejJ9l0qlePXqFbS1tZGeno7WrVsjPT0dRkZGH/V1YZhPRaXqKgzDMAzDMAzDMAzzdbK1tUVRUdFHXVO+k2D59ba2tmjdujW3M2FFtWvXhre3NzczrHxJXrnw8HDuv5cvX44dO3YAAJo2bYrRo0dXu087d+7kAmdmZmYIDQ3Frl270LdvX14gDADs7e3RsmVLKCmVLQZbuHAhevfuzS17JCIYGRnh8OHDAABLS0v88ssvAID8/HyuHQcHBzg4OAAoC8aZmpqCiKCmpoZFixYp3GRAHnNzc+Tn50NNTe3/sXfncTVt///AX6d5HhSiQiUyRIlMKdMVmcqcmyRcU1SUDFHoJlNSJPN8cZUhKbNEZC5ThEqFyNA8ns7790ef9s9xTpPr8/3c667n47Ef956119p77b0r57zPWu+FqVOnskAY8z/FRoYxDMMwDMMwDMMwzP8hgUDABaoa2o6I/qfT9AQCAXg8XoMCYUBVAK76uhvalmF+NBYMYxiGYRiGYRiGYRiGYf41WAJ9hmEYhmEYhmEYhmEY5l+DBcMYhmEYhmEYhmEYpgHKysr+111gGOYvYMEwhmEYhmEYhmEYhqmBmpoaPn78yL3+8OEDNDU1kZOTU+9jfPjwAc7OzmIT8Nfm0qVLkJCQwMaNGxvUThwTExOcPXtWqMzLywuPHj36y8dmmH8aFgxjGIZhGIZhGIZhflp8Pp9L+F6fTU5OrtbjHT9+HK1bt0bjxo3r3QdNTU08ePAAO3furHeb3NxcTJ06FX379oW/vz/evn0rtt7ixYu5VR+HDRuGsWPH1vscWVlZ6N+/Px4+fAg/P79a70t9+x4WFoY2bdpATk4ORkZG2LZtW411iQgrVqxA8+bNoaioCDs7O5Eg49atW6Gnpwd5eXn0798fqamp9b4+hqkJC4YxDMMwDMMwDMMwPy0pKSmUlJSIbE5OTvDw8BAqKyoqQl5eXq3HCw8PR0FBASZMmCC0OTs7AwA2b94sEkiSlJREYmIi5s6dK7JPR0dH5BylpaUYMWIEOnTogEuXLsHGxgYjR44U27ebN2/C2NgYM2fOxKhRoxATE1Pve7Nt2zY0atQIL168wNKlS1FRUSF2GzVqVL2O9+jRI4SGhiIgIAC3b9/GlClTMGvWLBw7dkxs/XXr1iE4OBjbtm3DhQsX8Pz5c0yePJnbf+zYMbi7u2PlypW4fv06KioqMGLECAgEgnpfI8OIw4JhDMMwDMMwDMMwzE8tJycHVlZWKC4uhpycHOTk5CApKQlJSUnudVpaGkxNTVFQUFDjcdLS0nDt2jWMHz8eRkZG3Na4cWOcPn2aqzd8+HAuwPbmzRt8/vyZe52amsr9/6VLl0TOkZ+fj5EjR6KyshJHjx4Fj8fDtm3boKqqCmtra7HTM83MzDBs2DD0798fAODk5CQSdEtKSsKQIUPA4/Fw9+5dBAQEICUlBXFxcRg9ejR4PB6kpKTEbjwer173WVtbGzdv3sSoUaPQqVMneHl5YeDAgTh+/LhIXYFAgHXr1sHb2xvDhw9Hr169EBgYiJiYGKSlpQEAVq9ejRkzZmDSpEkwMzPDjh078OTJE1y9erVe/WGYmrBgGMMwDMMwDMMwDPNT09XVha6uLhwcHEBEIvv5fD6cnJzQp08faGpqAgAGDRoEOTk55OXlQUdHB507d0ZgYCBsbW3x+++/w9fXl9tmzpwJWVlZAEC7du0wcOBAyMnJITMzE3379sXWrVshJycHPp+PXr16wdfXF5KSktDV1cW4ceO4fjx//hw9evQAn89HTEwMlJSUAABycnKIjIyEqqoqTE1Ncf369Vqvd+/evSAioa1z586IiYkBEaFr1644e/YsMjIy0LRpU0yfPp0LCn69vX79WuTY5ubmcHd3F3veRo0aQVFRUahMTk4OlZWVInUfPXqEjx8/YsiQIVyZlZUVJCQkkJCQgNzcXDx48EBov5GREZo1a4aEhIRar59h6sKCYQzDMAzDMAzDMMxPb8eOHUhKSkJwcLDIPldXVxQXFwvtO3/+PEpLS6GqqoqsrCxcuXIFe/bswZQpU2BnZ4cvX75wdcvLy7lcYwMGDMC8efMQGxuLHj16YMiQIVi0aBEAQElJCdevX0d0dDR69eoFSUlJBAYGorKyEmvWrIGJiQn69euHc+fOQUVFRaiPCgoKiI6Oxrhx42BlZYVJkyYJ9eFbDx48qPd0woqKCuzcuROlpaXc1qpVK7GBw7Zt20JfX79ex01PT8fFixe5EWtfq879paenx5XJy8ujcePGyMrK4kaHfb0fAFq0aIGsrKx6nZ9hasKCYT+QuD8U4sr+Lvh8/t+6f/9WBQUFKC8v/8vHqaio+AG9YX52RUVFQq8busIRwzAMwzDMP4W6ujpCQkKwbt06offbSUlJOHbsGCIjI6GgoFBje1VVVfzxxx+wtraGjIwMfvnlF+Tm5gKoCoZVjwyrqKjAkiVLYG1tDW9vb2zatAkSEv//o7eBgQESEhKgp6eHbt264fLlyygsLMSRI0dw5MgR8Hg8SEtLi01iLyUlhWHDhuHixYvIz8/nRmGNHz8eUlJSaN26NYCqz6Fjx46tMVfXX3HgwAHMnTu3znopKSkYNGgQunTpwuVT+1phYSEkJCS4+1ZNQUEBZWVlKCws5F6L288wfwULhn2lpKRE6LWTkxN8fX2FyoqLi8W2jY2NRd++fYU+SObm5qJDhw548OCB2Da2trZih5f27t0bYWFhDer7w4cPkZ6eXmc9HR0dvHz5EgBw8OBB9OvXr842kZGRyMzMrHFfRkZGg/r6b7FlyxY8f/68we38/f0xevRo7nV1Ys+6eHt7w9vbG0DVz6mWlhaePXtWa5usrCycPHlS7L6MjAxERkbWed7c3Nx61WPqZ+HChXB0dPyvHNvDw0NoFaDt27fDyclJqM748eNx8+bN/8r5GYZhGIZh/heqAyuFhYUYNGgQrl27hvLycvD5fFRUVMDAwAD37t1D48aNuXrivpyWlJTEiBEjwOPxsH//figrK2Pfvn3cOapHhg0fPhz79u3D6dOnMWvWLKHRVtWbhIQE9u3bh0mTJsHa2hpv377F/fv3MXLkSACAj4+PyDRHIoK+vj40NDTQr18/nDp1CjIyMgCAo0ePgs/nc5/1eDweXF1dsWrVqnoPgJg2bRqUlJS4LSUl5bvv+YkTJ2Bubg4jIyPExMRw/fyarKwsBAIB+Hy+UHlpaSkUFBS4INm3z6J6P8P8FSwY9h+3b9+Gubl5rckS3717h3bt2okNbllYWKCiogLbt2/nynx9fWFsbAxTU1OR+rdu3cKpU6cwYcIEofK7d+/ixo0bGD58uEib0tJS5ObmCm3Vo7t+/fXXBs+bDg8PR+/eveus9+rVK3Tr1g0XLlwQKo+Li4ODgwP3B7chBAIBtm3bBhMTk1rr1Wep3WrXrl0Dj8eDn58fV3b79m2YmppCSUkJNjY2+PTpE7fv2bNnaNasGT5+/Njg/tclMTERbm5uYv/o10YgEODw4cNivzmpVllZWeezjo6Ohry8PNq2bVtrvS9fvsDNzQ0eHh5C/wgVFxfD1tYWUVFRdfa5oKAADg4ONQZ96+Pq1avo2bMn5OXloauri/nz59cYeK724cMHTJgwQeh5A8CLFy/Qp08fKCkpwcLCQmjp5Y8fP6JZs2bfFaSsi7e3N7S1tWFiYiJ2MzY2RqtWreo8jkAgEPrmUJyGLA0u7pzp6em4fv06dHV1kZiYiCtXruD69esQCAR49uwZ2rdv/513QVhhYSHc3NygpaUFOTk5dOnSpdb6R44cQefOnSEnJwctLS1cuXKF25eZmYlhw4ZBUVERzZs3x/r167l9AoEArq6u0NDQgI6ODvbv3y903EmTJsHHx+eHXBPDMAzDMP88M2bMgLKyMrfp6+tzgazAwEAoKyujRYsWQnXmz59f6zFlZWURFRUFXV1d9OnTB9nZ2VzwZvXq1Thz5gysra0hLy9f41Yd0Llw4QLatWtXY5L6wsJCCAQCpKWlISsrCy1atKjXdU+ZMgUZGRn1ek8PADt37uSCgYWFhWjTpk292n1r8+bNmDBhApYvX45Tp05BWVlZbD1tbW0AEJryWFZWhpycHOjr63P7vx2YkZmZWe9pmgxTExYM+4+uXbtCS0sLkydPFhs5r6yshIODA3R1ddGxY0cAwJAhQ6CmpgY1NTVoamoiMTERy5cv58p27NiB6Oho7rWamhoCAgJQVFSEmTNncuerXoGkffv28PDwQNOmTTFgwACh1UmuX7+OgIAAtGvXDi1btkSnTp3QsmVLXL9+HeHh4Xj8+DHmzJkDTU1Nbqv+lmL58uUIDw8Xup78/HxcvHgRY8aMqfPeuLu7Y+vWrXjx4gVX9uDBA4wePRoHDhwQO/+7Njt27ICxsTHmzZvHDX2tSV1L7VYjIixcuFCkfMKECXByckJ8fDwkJSXh7+/P7Zs9ezZWrlzJJcj8kXx8fCAQCGBoaMitwCIhIQFJSUmRlVlu3brFtTt16hSICCNGjKjx2F5eXrCzs0NRURHu3r0rdhRRREQEtyJMbYyNjZGQkCA0NbOsrAy2trZo06aNyAjFzp07C31bpKSkBCMjI1RUVHABqG+3+gxh/u233+Dg4IDbt29j/fr12LdvHzw9PcXWzc7Ohru7OwwNDRERESH2WL1798aNGzfQrl07oeN4enrC2dm5ziDh9/L09ERiYqLY7evATm3qEwwDqoJ+4r4t/Hr7NoBd7cSJE3BxccHo0aNRXl4Od3d3uLi4IDs7GxUVFVBVVW3QdYtTWVmJoUOH4vr169i/fz/u3LlT65vKnTt3Ytq0aZg+fTru3r2L/fv3o2nTpkLH4vP5uHbtGlasWAEvLy/8+eefAKqG6l+/fh0xMTEICgqCi4sLF+S+evUqbt68icWLF//la2IYhmEY5p9JXDJ5IsLUqVPh5eUldt/mzZuFjlFZWcnNqMjPz0dQUBCMjY0xd+5cDB06FEBVvisAMDU1hYmJicgxDQwMcPr0aaGyoKAg9O3bt9b+GxsbQ1JSEoaGhpg8eTLU1dUxf/58XLx4sdZ2SkpK2LNnD3r27Pl9N+47JCUlwd3dHUePHsX8+fNr/TzSpUsXyMvLC71nvXr1Kng8HiwtLaGtrY1WrVoJ7U9JSUFWVhYGDBjwX70O5ucn9b/uwN+FhIQEDh8+DGNjY/z+++/cdLNq8+bNw7Nnz3D37l1IS0sDAGJiYhp8HiLCqFGjQER49uwZHB0dsXDhQvTu3Rvnzp3D7Nmz8eTJE8yZMweTJ08WWjnj4sWLWLVqFa5fvw4HBwfs3bsXubm5cHd3R69evXDt2jVISEjgxYsX6N+/PxekysjIQPPmzYX6sXfvXpSVlYkdqaGoqIj3799zH0S/Vh1wKisrg0AgwK+//srtMzc3x+XLl+u8B2FhYXByckJubi6OHj1aY71vl9oFgMDAQAwZMgRpaWlCiRR37twJgUAg9C1JTk4OCgsL4erqCgCYNWsWtmzZAgA4dOgQSkpKMG3atDr721C7du3C3bt38e7dOzRp0oQrd3BwgImJSa1THtesWYPx48dDUlJS7P6DBw9iy5YtuHz5MhQVFVFYWCgyfPnDhw84efIkSktLxSYHPXbsGE6dOoUTJ04IlR86dAhA1T/0paWlkJeXF0raef/+fcTFxeHdu3cwMjLiymfPng1ZWVls3LgR5eXl3Gi4Fy9eoFmzZiI5AMS5ePEidHV1AVT9Y//q1SuEhIRwz+tr9+/fR1JSEk6dOiUyxQ+oGg145swZKCgowN3dHWPHjgUAXL9+HVevXsWTJ0/q7M/3CgoKwpEjR8Tu+3b4d02IqF7BsIZavXo1zp8/j1u3bnHJWR0dHXHp0iVs3LgRW7ZswenTp5GdnQ0dHR3k5+dj5syZWLt27Xedb/fu3Xj06BFSU1OhpqYGoOrZivP582fMnz8fW7Zs4YLd1V86AMCZM2fw7NkzXLx4EU2aNEGXLl1w6dIlbNmyBePGjcPt27cxdepUmJubw9zcHGvXrkVaWhpUVVUxe/ZshISEcNMWGIZhGIZhGuL9+/fg8/kwNTWFrKwsPn/+DHd3d+jo6MDHxwcTJ06EtLQ0jhw5wgXDfrQXL15wCfqr3yfeu3cPAwcO5OpUTzf8dtXGr9Ov1GXatGmYOXOmUJm496WTJ09Gt27d4OLiIrLv6NGj3ACSb2cQVedK8/T0xJkzZ6CqqopZs2Zh+fLlaNGiBZSUlODq6ooZM2agUaNGAID58+dj8eLFMDExgZ6eHtzd3TFs2LAa31cyTH2xkWFf0dTUxM6dO3Hjxg2h8g8fPiAqKgrR0dFo1qyZ0L6cnBxkZ2eLbN+OLnv16hVevnwJHo+HAQMGIDY2Fqqqqli2bBmSkpIAVAV0Dh06BA0NDfj7++Px48d19nnTpk0YNmwY2rdvj+XLlyMnJwdjxozBhg0buODCt/h8PhdU+vpbiV69euHUqVMoLCzkAi3V2+fPn3Hx4kXudUlJCY4fP46CggKurDoQVttSu0DVVFBPT08uqFiTupbarfbu3TssXrwYoaGhQt88qKuro6ysDPHx8aioqEBUVBTatWuHvLw8eHl5idT/Ee7duwc3NzccPXpUKBBWH0ePHsWtW7dqnP9+8OBBODs7Y+fOnbV+uxMUFITS0lK8f/+ee7bbt2+HnZ0diAhjxozBgQMHhJ7vhQsX8OXLF+7ZXrp0CVlZWSLDpB8+fIj+/fvj9OnTqKyshLu7OyoqKhAQEIDVq1fD1tYWRIQzZ87AysoKV69eBVAVRK1tity3P6s1Lb8MVI3IvHz5co3foOno6HD9O3XqFNq1awc+n49Zs2Zh06ZN/7U3KQDQpk0bDBw4UOxmZWUlVDcsLEzstMagoCDs2rVL7D5xwT8dHR2R0XiWlpYi9fT19aGpqQltbW2UlZVh0qRJ2Lt3L3R1dZGSkoIdO3bg1KlT8Pf3x+vXr6Gmpoa1a9ciMjISmpqaYpfVrs2ePXswdepULhBWm/DwcCgpKQkF17925coVdOnSReh3qn///rh16xaICDo6Orhw4QJKSkqQnJyMtLQ06OvrY8OGDTAyMhL6G8IwDMMwDFNfv/32G1q0aIEePXogNDQUL1++RNu2bbF//348fvwYkydP5j7TfJ3M/keTkpKCgoKCUGDq1atXQtMYx48fD2lpaS6BfkP169cPcXFxQu//CwsLxU7JTE5OrvG9YXZ2NtLS0mBoaCiyFRUVIScnB8nJyVxKFH9/f9jZ2WHcuHEYPnw4fvnlF6F0GC4uLpg/fz5mz56Nfv36oWXLljhw4MB3XSPDfI2NDPvG0KFDMXjwYKGyJk2a4Pnz52JHFnTr1g16enpCf/jOnj2L7OxsXL9+HWFhYTh79iz27NkDPp+PgIAApKWloUePHkLHCQ4OxuvXr3Hnzh2h8+zatQsAcOnSJbH9XbhwIZcEv1+/ftixYwe8vb0xbty4Gq/xjz/+wJcvX7ilaqu9ffuWm5f9tdjYWMyaNQvq6uq4du0aJCUlkZqaCnd3d6xcuRKrV6/GoEGDuPp1LbVb3wBUXUvtAlWjaCZPnoyJEyfCzMxMqL2UlBQ2btyIAQMGoLy8HF27dkV0dDSWLl2K0aNHi83l9lfp6upix44dePjwocjPUVlZGY4dOya0KIOUlBRyc3ORn58PT0/PGgMHZ86cQWhoKPbt2wd7e/saz19QUID9+/dDRUUFaWlpXPCgpmebk5MDDw8PnDx5EufOneN+Lnft2oXo6Gh4enrCzc2NC9D16dMHp06dQmBgIJSUlHD8+HFoaGhASUkJkyZNQkREBHJzcxEYGIhz585x39jo6OgIjfSpTXl5Ofbv31/j9Nu6fn6CgoIwYcIE2Nvbo3Xr1jhz5gyCgoKgr68vNhffj2JnZ4eBAwdCR0dH7P6KigqYm5tzr52dnUVyBgJVIwj5fL7YEWbictAVFhbi8ePHXG6w2NhYsaMPx48fjzt37sDIyAhFRUXg8Xjw8PCAoqIiVFVVceDAATx8+BAGBgZ49+4dF/jX0NBA27ZtGzR1ks/n4969exg/fjxGjBiB69evQ19fH7///jusra1F6ickJMDY2BgbNmxASEgIpKSkYG9vj5UrV0JaWhqpqalil9QuKyvDx48fMWvWLJw4cQKKioqQlpZGSEgIioqKsHHjRty9e7fe/WYYhmEYhvnagAEDMH/+fKFZEdU5lwsLC/H+/XuoqalBIBAI5WP29fXFihUrxB5T3PvR6oEI1TQ0NBAXF4ePHz9CSUlJqC6fz0d4eDh4PB4MDAwAVH1GOnHiBGxtbfHu3TuMHz+eq189Wj4/Px+vX7+Gurq62H6JS0Xz7t07Lsn9s2fPuBQ7t2/fFnsMoGp2wO7du2vcP2LECKE8zrKysggNDUVoaKjY+jweDytXrsTKlStrPCbDfA8WDPuPtm3b1hjdDggI4P6fz+dzUxSrbd26VegP5Ld/sL61YcMGbNiwQaS8R48eCAoKEgmUfW3BggUoLy9HZGQkN81PTk4OoaGheP78Obp06YLAwEBoaGhg1KhRYgN4fD4fQUFBmDt3Lj59+gQNDQ18/vwZb968EbqOFy9eYNGiRbhw4QJ8fX3h6urKTd9r06YNkpOTsXHjRowaNQpWVlbYuHEj2rRp88Mi9XUttQsAa9euRWZmJk6dOiX2GM7OzrC3t0dubi6aNWuGe/fu4eTJk7h79y6mT5+Os2fPonXr1ti9e7fIh+3v0aRJE0yYMAFBQUEYM2aM0M9JbdMkly1bBiMjI5HRZHl5eXj+/Dmys7MRExMjMrroW0VFRbC2toaKigpu3LiB7t27A6iaWlidywCoWowhKCgIq1evxsCBA/H06VOhYNmhQ4dw5coVzJs3D2FhYVi7di0XuDEwMIC2tjYOHz6M6OhoeHt7Y+HChWjXrh38/PwwadIkfPnyBW/fvuWCYfPmzavX/SsoKMDEiRPx5s2bGle5rMuQIUOQk5ODDx8+QFtbG2/evMG6detw8+ZNLFq0CEeOHEGTJk0QFhZWZ0L3+lizZg327duHV69ewdDQEEDV79jr16+5NyhfW7VqFbS1tXHu3Dmxwa1Hjx4hPz8f8vLy9Zpi+j1sbGywZMkSFBUVcWW6urpYs2YNsrOz8fLlS+6bxt69eyM+Pr5Bx//06RPKy8sREhKCxYsXw9vbG9u3b8fw4cPx+PFjkWSs7969Q2JiIrS1tXH8+HE8efIEc+fOhaysLHx9fVFYWCiS2686QFtWVobGjRvj9u3bePv2LRo1agQ5OTnY2trCw8MDKSkpGD58ODftU1xuQYZhGIZhGHG+Dip9KysrCx06dIBAIICkpCQsLCy4aYPe3t5YtGhRvc/z7Ze9U6dORVRUFLS0tERmS0hISMDQ0BD79+/n2n2dvqdZs2aIi4vjXltbW+PFixeQkJDAoEGD0LVr13r3a+rUqYiJiYGkpCS6d+/ORtszPxUWDPuP+q4u5+vri/T0dKEyGxsboQ+1da2CB1QF2AIDA4WCH6mpqfj111+FpnG9ePECSUlJXJBqw4YNQjnD4uPj4eXlhZcvX+L06dPo1asXFi9ejF9//RXKyspiR5Q5OzvDwMAAx44dQ2RkJKZMmYKLFy+iS5cu3Ai34uJi9OjRA8OHD8fz589FpocCVVH8RYsWYeLEiZg3bx4CAgJq/Ragob5ealdK6v//qFYvpXvu3Dn4+fnhxo0btU59q16tRSAQYNasWVi3bh22bt0KOTk5pKWlYcuWLXBxccGZM2d+WN8bqmvXrvDw8BBK8h0REQFXV1dkZ2fDzc2tzkAYAGhpaWHVqlU4duwYtmzZAnd3d5SVleHatWtYs2YNV8/V1RU3btxARESEUK6Br/Xr1w8PHjzAxo0b4enpicGDByMgIAChoaHo2bMnzM3NYWpqijZt2iAwMBAFBQXIzMxEs2bNMGXKFAwfPhyDBw/GoUOHalxB5mtPnjzBmDFjIBAIEB8fX6+VF2siIyPDjdBydXWFm5sbrl69iufPn+P58+eIiYnBpEmTfkj+MC8vL8yYMQNdu3blpjZnZWVh2LBhSExMbNCxbt68icrKSlhaWiIiIgITJ078y/2rtm/fPly5cgUHDhxAZWUlVqxYwY0+CwgIgIeHB6KjozF+/Hi0aNECnTt3rtdxW7duLfQ38dWrV9zvq6OjI6ZOnQqgKkHqmTNncOTIESxfvlzoGHw+H8rKyti5cyckJCRgbm6Op0+fYv/+/fD19YWsrKzYJbUBCE0rrs6NGBUVhZcvX2LHjh3o1q0bzp8/D3V1dfTu3Rt9+/YVGqHHMAzDMMy/186dO7+7rZGRESorK1FRUcEtlFWteqGs79WiRQvcu3fvu9t/LSUlhUud8m3+r9jY2FrbRkdHQyAQABCfO4xh/slYMOwHOHr0KDciBECN06S+9dtvv8HPzw9A1YiYzp07Y+XKlUJ5c/T19WudFvbo0SMoKysjMTGRy7sUEBAAW1tbLFmyBB06dODqJiYmoqCgAEDVtw8ODg5Yv349nJycsHPnTqEP3goKCnj+/DnCw8Ohr68PRUVFFBcXQ0JCAnJycsjPz4eCggKkpKTw5csXvH79usE5sury9VK71YGRr5faDQgIQHFxMbp168a1KSsrw4oVK+Dn58d9WK62bds2KCsrw97eHubm5ti1axekpKQwbdo0+Pj4/NC+N9SkSZOEXk+cOBGXLl2Cv78/l3fra18nqgeqchQkJSWhc+fO4PF4GDp0KGbOnIkHDx4gOTkZrVq1Ehr1FxAQABkZGSgrK6NRo0aorKxEYWEhVFVVUV5ejvLycigpKaGkpASLFy/Gq1evICMjg6ysLPz+++9wcXHhgpFdunRBaWkpevXqhYqKCsycORMnTpyAqakpli1bVudISQC4ceMGhgwZgpEjRyI0NLReberj7NmzePr0KQ4fPgwHBwc4OztDVlYWtra2mD59OnJzc+uV06o+MjMzYWJiAqBqSqS0tDTCwsIwb948bqns4uJi8Hi8GoPvPj4+cHZ2hqWlJWbPnl3j6M7v8enTJzRp0gQzZ87E9OnT4ePjAzc3NwBVi2wAVcHUwsJChIeHi118QZxz586hoqKCe928eXNUVlZCQkJCKGeFlJQU9PX18f79e5FjNGnSBDweT+hNVps2bbi62traQqvZAlX3W1VVlUuuWq2kpATz5s3D3r17cefOHZiZmXEj0UaOHIkbN26wYBjDMAzDMD9MXXmQ/w6q889+DxYEY35WLBj2H6GhofWaPlNeXi4yWuPQoUNCU3i+/mBYH9nZ2ZgwYQIKCwvx+++/Q15eHs+ePUNoaCiISGyup2rTpk3DtWvXYGBgIDI6LTU1FQoKCtzSvx8+fBAaXTFu3DisWLECjo6OePDgAf7880+hY1df04wZMxAUFAQPDw+0bt0aM2fOxODBg+Ht7Q0LCwvuA6+4KV9/xddL7U6fPh2A8FK7Xbp0ERmFN2DAAIwdO1ZkFZQPHz5gxYoVXGCptLSUe07FxcU/7B+xqVOnYt++fdy3LwcPHuT2CQQCHD58WGTIdMeOHUVGEHl7e2Pr1q1QVVXFtWvXRM5TPT106NChePXqFVq3bg0JCQluNI+ioiLc3NwwZ84cfPjwQShPGVC1uEBpaSlUVFTw8eNHPH78GNOmTUNCQgKOHDmCixcvYufOnVywtvrZHjhwgPuHdPDgwVxetFatWiE8PBxdu3bF8ePHAQAWFha4fPlynf/wlpaWYty4cZg6dSoCAwNrrdsQpaWlcHFxwfbt2yEjIyP0zAUCAcrKyv7SN3bf0tXV5Z5j9ciwmTNn4tq1a/j1119hY2ODRYsW1Rjc2rhxI1JSUnDs2DGoqKhAT08PM2bMwN69e2u9h3JyckL52IiIC8p9bf78+Xj79q3Qsar7+3Vi/mHDhmHv3r31HkIvbiqotLQ0unTpgoSEBO7vZXl5OV6+fCk2n2GvXr2wYsUKlJaWcvfnyZMnXBDLwsIChw4dQl5eHpe77NKlS2KX1Pbz80OfPn1gaWmJ48ePC/09/pG/6wzDMAzDMAzD/HOxMO9/zJ49W2TlDHHbkiVLRNoqKCgIreRWH4sWLYKfnx8uXLiAIUOGYOPGjdDX18fmzZsRFhaGt2/fIiEhAa9fv671mFJSUuDxeFiyZEmNq348efIEhoaGePz4MTQ0NITa+vn54eDBg5g2bdoPGyEzefJkbN68+bva3rx5ExYWFsjLy4O8vDy31O65c+cQHx8vtNRuixYtYGRkJLRJS0tDU1NTaBQUAHh4eGDq1Klo27YtgKoP1ytXrsSDBw/g7e0ttADAX7Fr1y5uSePqKZ7V28SJE7FmzRqhMj6fL3YqXfv27WtNWJ6ZmQlJSUk8fvwYsrKy2LdvH6ZNmyZUZ+HChUhJSUFZWVmtSffrq6ioCMrKyiIrFyopKSEjIwOWlpZQUlISqqOsrIxr165h8+bNYpNyAkBcXBzevHmDsWPH4uXLl0JbcXEx8vLyYGFhgZs3bzaov/7+/ujevTuXiN/CwgLr16/HvXv3sHLlShgbG/+wEWjA/x8ZZmJiIpQk3sPDA4sWLcLly5dx6tQpLFiwQKRtcHAwlixZgoMHD0JVVRU8Hg/79u3D2bNn4ejoKJTb61vZ2dlCv/tFRUX1zvFV3d/IyEiu7MOHD0KreX79O9kQHh4e2L59O8LCwnDv3j1MnToVRARHR0cAwn8nHBwcICkpicmTJ+Pu3bvYtWsXtm3bBk9PTwDA2LFjoaGhgSlTpiApKQnbt29HRESEyBcYz58/x65du7Bu3ToAVSvbXr16FcePH8fFixcRHh4uNoDGMAzDMAzDMMy/CxsZ9gM4OjoKBV+8vb0BVI2csbCwENvmzZs3WLRoER4+fIjIyEi0bNkSQFVgLTo6GosWLUKfPn2wbNkyTJw4kRstERISAl1dXQQFBSExMVEkACLOxYsXoaOjIzK6JCEhAfPnz8ewYcMQEhICOTk5eHl5/eVpWcnJyd89ZfLrpXZVVVXh7++PkpISjBs3DpKSknBwcOA+6NZXXFwcrl27hqdPn3JlK1euxKRJk2BpaYnevXtj//7939Xf/wvKyspISkpCUVERFBUV8f79e5w7dw4LFizAzJkz4eTkhObNmwsFQPLz8zFlyhQ0bdoURIRffvkF27ZtE5rO21DfrnLzta9HhokTGRmJ5ORksfuys7MBVI0O+tbp06dhZmaG5ORk5OTk1LuvL168wLZt25CUlMSVzZ07F4mJiejXrx86duz4w5+5rq4uEhISkJmZiQcPHsDNzY3LA2dlZYWBAwciIiJCKH9adnY25s2bh/PnzyMmJkbo74WWlhauXbsGGxsbGBsbY8WKFRg/fvwPHYH57ciwpKQknD17FsbGxggLC4OLi4vI72R9jR8/Hp8+fcLq1auRnZ0Nc3NznDt3jlvB6Ou/E6qqqjh//jxmz56N3r17Q0tLC2vWrOFGlSkoKCAmJgbTp0+Hubk59PT0cPjwYW6BiGqzZ8+Gj48Pd1wdHR1s2rQJc+fOBQCsXr1aJFDOMAzDMAzDMMy/EDF05coVUlRUrNcmLS1NkydP5tq2bNmSkpOTudelpaUkIyNDubm5RET04sUL+vDhAw0dOpR8fX2JiMjFxYUaNWpEAQEBVF5ezrXt3r073bx5k3udkJBAPXr0IDU1NUpKSqI1a9bQgQMHuP2bN2+mJ0+ekI+PD0lLSwv1k8fjUVpamsi1amtr071798jT05Pk5eUpODiYiIhu375Nenp61Lx5c1q9ejUdPHiQFBUVSVZWlju2tLQ0ycrKkqKiIklKSpK8vDx3LgUFBVJVVf0Rj+On9euvv9K6devqrOPj4yNUdv/+fdLR0SEA3NatWzf69OmTUL2lS5fS0qVLKSIignR1dWnw4MGUm5tLBQUFNGHCBJKSkiJHR0d69OgR93MCgBQVFUleXp4kJCS4Zy4lJUWKiookIyNDMjIypKioSEeOHKmx3y1btqQ7d+589735J4uPjycTExNSVFSkTp06ka2tLf3222/UsmVL2rVrF82cOZNat25Nq1evpubNm9OMGTPo6tWrFB4eTgoKCtS7d296+vRpjcf/+PEjzZo1i6SlpcnT05OIiACQpKRknZuEhAS1bNmSiIjS09PJ3t6e9uzZQ0RETZs25c5ha2tLx48fJ0NDQ4qIiKDU1FRSV1cX+nvEMAzDMAzzb3XgwAGysrKqcf/Tp08pIyOj1mO8f/+epkyZQiUlJQ0698WLF4nH41FgYGCD2onTuXNniomJESpbuHAhPXz48C8fm2H+adg0SQB9+/at1xTJmqZJAlU5iPr16wdLS0s4OjpyIygsLS3RpEkTPHz4EKNGjQIADBkyBElJSfDy8qo1f0337t1x8+ZNxMTEoFOnTli4cCEcHBy4/XPmzEH79u0BoNZpkt86cOAAbty4gTt37nAjJrp164bk5GR4eHjg5s2bXA6z0tJSlJeXo7CwEOXl5SgtLUVhYSH4fD6Ki4tRWFgIgUCAoqIi5ObmNui+M/VjamqKzMxMlJaWoqSkBOXl5bh9+7ZI4nAAePv2LTw9PeHr64vo6GioqqpCSUkJhw8fxqVLl5CRkcElyy8sLAQRobCwEMXFxVx5dW6twsJClJWVoaysDIWFhbUuLf1vZmpqitOnT6OgoABJSUk4ceIEli1bBjU1Ndy+fRtNmjTB/fv3sWjRIjx69AgGBgZISkrC4MGDsWPHDly7dg3t2rWr8fgaGhoIDQ3FixcvsHr1aq782bNnIlNuv93OnTvH1XdwcMC9e/dgaWnJlV24cAFaWlp48OABsrKyMHDgQIwaNQp6enrYtGkTUlNT/zs3jWEYhmEY5n/AwsICbdu25VJFfLvp6ekJ5VKtj71796Jbt24i+Ze/pampiQcPHjRoBcvc3FxMnToVffv2hb+/P96+fSu23uLFizFs2DBuGzt2bL3PkZWVhf79++Phw4fw8/Pjku2L2xrSd4FAgG3btonNZfs1IsKKFSvQvHlzKCoqws7OTmRGyNatW6Gnpwd5eXn079+fvUdlfggeEdH/uhPM/63qR/69K4owf28CgYCt+sLU29c/L3w+/4cuKsAwDMMwDPN3YmFhgc2bN9cYoAkPD0dUVBT27t0rVH7w4EHs3LkTsbGxXFlKSgpcXFxw9+5dbNmyRShH7+bNm7lBB/Whra2NrKwsobLS0lIMGjQIysrKiIqKgpOTE54+fYqLFy+KpK7o27cvevbsid69e+PDhw+YN2+e2PQmJiYmCAgI4BbBAoDCwkKYmZnB398fo0aN4vLGfmv8+PEYMmRIvdL07NixA0FBQXj58iV0dXXx8uXLGuuuXbsWa9aswd69e6GhoYFp06ahVatWiI6OBgAcO3YMkyZNwo4dO9C+fXu4ubnhy5cvePjwIfvMw/wl7FPPvxALgv3c2D8KTEN8/fPCAmEMwzAMw/zsHB0doaCgIHbf58+fxeax/dqTJ08QGBiIAwcOYNy4cUhOTkbTpk1F6g0fPpwbLfb582fIy8tDXl4eAPDu3Ts0a9YMAHDjxg1ugaFq+fn5GDt2LCorK3H06FHweDxs27YNw4YNg7W1NU6fPo3GjRsLtTEzM8OwYcOQnp4OoCon7L59+0T6NWTIEADAnTt3cPHiRQwaNAhxcXHcNdT0frAhnyHDwsLg5OSE3NxcHD16tMZ6AoEA69atg7e3N4YPHw4ACAwMxJAhQ5CWlgY9PT2sXr0aM2bMwKRJkwBUBdratWuHq1evol+/fvXuE8N8i31qZhiGYRiGYRiGYf4VunfvjoEDB4rdOnXqVGvb+Ph4dO7cGZ8/f8atW7dw8OBBsYGwdu3aYeDAgZCTk0NmZib69u2LrVu3Qk5ODnw+H7169YKvry8kJSWhq6uLcePGcW2fP3+OHj16gM/nIyYmhlv9XE5ODpGRkVBVVYWpqSmuX79ea1/37t0LIhLaOnfujJiYGBARunbtirNnzyIjIwNNmzbF9OnTIScnJ7K9fv1a5Njm5uZwd3ev8dx3796Fp6dnrSmBAODRo0f4+PEjF6ADACsrK0hISCAhIQG5ubl48OCB0H4jIyM0a9YMCQkJtR6bYerChgEwDMMwDMMwDMMwP71FixZBR0eHCzB9KycnBxUVFTW27927N1JTU2vNzwwAAwYMwIABAxAbG4vRo0fDwcEBixYtAgAoKSnh+vXrsLOzw6VLl3D06FEEBgaisrIS69evh6+vL5ydnbFp0yaRUVoKCgqIjo6Gp6cnrKysMHHiRAQHB9fYjwcPHqBz5871mjlSUVGBnTt3CuWoNjIygrisSm3btoW+vn6Nx6rvKLLq3F96enpcmby8PBo3boysrCykpaWJ7AeAFi1aiEwrZZiGYsEwhmEYhmEYhmEY5qc1ZswY3LlzBwUFBWjevDkAIC8vDyUlJdDS0hLbxtbWFtevX8fVq1e5svoEeY4dO4aRI0fCx8cHGzZsQEBAgMgoKgMDAyQkJMDJyQndunXDsWPHYGZmhiNHjuDIkSO4cOFCraOqLl26hOHDhyMoKAiKiooAqnJ6VfdPTk4ORISxY8fi999//+ELYR04cOCHHKewsBASEhKQlZUVKldQUOAW8ap+LW4/w/wVLBjGMAzDMAzDMAzD/LTCw8Nx8uRJREVFcSsiHjx4EHfv3kVQUFCN7QoLC8Hn8/Hnn39i//79iIqKAgD4+PggKysLu3btEmmjqKiI4cOH49GjRzh9+jQsLS1RWloqUk9CQgL79u3D4sWLYW1tjYcPH+L+/fvg8Xi4cOECfHx84OvrK9LOwMAAGhoa6Ny5s1DOrKNHj2LMmDFIT09Hx44dwePx4OrqilWrVmHcuHH1CuRNmzYNM2fO5F4XFxfX2eavkJWVhUAgEFnEqbS0FAoKClyQrLy8XKhd9X6G+StYzjCGYRiGYRiGYRjmp3fixAmYmJjAxMQES5cuBVCVUF9RUZErb968OWbMmAGgakqjmpoaFBQUICUlBTU1NaipqWHp0qU4f/48EhMTubLqTVpaGqtXr8aZM2dgbW3NJc4Xt1UHdC5cuIB27drVGLAqLCyEQCBAWloasrKy6pymWW3KlCnIyMjggnh12blzJwoLC7mtTZs29Wr3vbS1tQFAaMpjWVkZcnJyoK+vz+3PzMwUapeZmVnrNE2GqQ8WDGMYhmEYhmEYhmF+enZ2dkhMTERiYiJ+//13AFWJ5nV1dXHx4kUkJiaiZcuWsLe3r/U4TZo0QVBQEEaPHo2HDx+K7Dc1NYWJiYlIAnsDAwOcPn1aqCwoKAh9+/at9XzGxsaQlJSEoaEhJk+eDHV1dcyfPx8XL16stZ2SkhL27NmDnj171n5j/ke6dOkCeXl5XLhwgSu7evUqeDweLC0toa2tjVatWgntT0lJQVZWFgYMGPC/6DLzE2HTJBmGYRiGYRiGYZif3okTJ3D37l0AwJcvX2BnZwcJCQm4uLjAzc0N3bt3h7a2dp3BqevXr2Py5MlISUlB//79ERoaKrQi5I/24sULlJeXQ05OjkuGf+/ePQwcOJCrUz3dsLKyUqjt6NGj632eb6dJAhCbfH/y5Mno1q0bXFxcGnIZAICbN2/C09MTZ86cgaqqKmbNmoXly5ejRYsWUFJSgqurK2bMmIFGjRoBAObPn4/FixfDxMQEenp6cHd3x7Bhw2BsbNzgczPM11gwjGEYhmEYhmEYhvnp2dnZITAwEK9fv8ahQ4cQFxcHX19f+Pj4YMeOHTh58iSeP38u0o6IUFFRgYMHDyIoKAgpKSl4+/Yt1qxZA319fTg6OmLv3r1wc3PDL7/8Uu/VFOtLSkpKZGXJV69eCU1j/DpJfnVS/Ybo168fZs+eDXNz8zrrJicno0mTJg0+B1C1YmdycjKKi4uhqqoKf39/lJSUYNy4cZCUlISDgwPWrVvH1XdxcUFOTg5mz56N0tJSjBw5Eps3b/6uczPM13gkbq1UhmEYhmEYhmEYCk/OKAAA+xNJREFUhvkJbN68GYGBgSgoKIC+vj4MDAxQUFAAgUCAIUOGIDo6GuXl5TA1NcXhw4cxbdo0TJw4kQs2zZgxA9u3b4eOjg7mzp2LGTNmQFVVlTv+06dPsWLFCoSHhyM8PBxJSUlYsWJFvfunqKjIrZwIVCXov3btGv78808oKSkJ1eXz+QgPD8eyZcuQkZEBHo+HIUOGYMaMGbC1tcW7d+8wfvx4xMXFAQDS0tKgqqqK/Px8mJqa4uzZs+jevTsAoG/fvnBzc4Otra3Yfr17945Lct+3b194e3tjwoQJ9b4uhvk7Y8EwhmEYhmEYhmEY5qf16dMnAICGhgZXdvDgQcTFxSE9PR329vZwcnICj8dDSkoKtm7dipEjR3LTJS9cuID79+9j/vz5kJaWrvE8WVlZ0NHRAZ/PB5/Pr3f/eDwet3IiAGRkZMDOzg5JSUki0x4lJCRgaGiIrVu3Cq0mWZM2bdrgxYsXkJCQwKBBgxAVFQVJSUkAdQfDbGxsEBMTA0lJSXTv3h3R0dFCQUCG+SdjwTCGYRiGYRiGYRiG+UlVJ+sXl/+rLgKBAID43GEM80/GgmEMwzAMwzAMwzAMwzDMvwYL7zIMwzAMwzAMwzAMwzD/GiwYxjAMwzAMwzAMwzDMX0ZEInnO6uvrRQQY5r+NBcMYhmEYhmEYhmEYpp7Mzc2xe/fuWuu8e/dOpOz58+dYt25dne1sbW3FJuDv27cvzp49W68+3rt3D0ePHq1XXSJCRkYGLly4gJCQEERHR4vUMTExQWxsLPc6IiICzZo1w7dZl9atW4cBAwaIPc/Dhw8BANevX4eFhQWAqsUCSktLAQCGhoa4c+dOvfrMMH+V1P+6AwzDMAzDMAzDMAzz32ZhYYGcnBzIy8uL3Z+XlwcrKyvs3bu31uMIBIJaE8p/+vQJ7du3R0xMDHr06MGV79mzBxkZGbUee8+ePeDxeJCSqt9H9dzcXJw8eVKkPD09HUFBQSgqKhLbVzs7O/z22294/vw5UlJSUFJSwu0bO3YsbGxsaj3vhg0bMHz4cPB4PK6MiLB9+3bMmDFDbJvJkyfDy8sLOjo6AKru45gxY+Dq6op+/frh06dPMDY2rs9liygoKIC3tzeKi4uxY8eOGuvl5ubCxcUFkZGRkJGRgbOzMwICArh7VFZWBk9PTxw6dAgVFRUYO3YsQkJCoKCg8F39Yv6+WDCMYRiGYRiGYRiG+Vc4evQoTExMxO4LDw9HVFRUnceoKximoaGBZcuWYcKECbh8+TL69+8PAHj//j0UFBTQqlUrofqKiop48uQJKisrERYWhmPHjnH7DAwMkJeXB6AqWDd+/HhIS0sDABYsWIAZM2agsLAQsbGxKCsrg7W1NQBAU1MTfn5+KC4uFttHFRUVNGrUCDY2Nhg5ciRCQkJw9uxZtGzZEk2bNgUA8Hg8lJSUQE5OTqjtmTNnkJCQgJSUFBw/fpwrNzMzQ3p6OtasWYOAgAAuUDZixAjs3r0bPj4+WLJkCbZv3w4A2LFjB+Tk5DBx4kTs3bsXxsbGIueqS2FhIdauXYutW7ciPz8fkyZNqrX+xIkT8eHDB5w/fx5v3rzB5MmToampiYULFwIA3N3dcfbsWYSHh4PP58PR0RGysrIIDQ1tUL+Yvz8WDGMYhmEYhmEYhmH+FRwdHWsc5fP582f06tWrzmMQUa3BMABwc3NDVlYWZGRkkJiYiGPHjiE0NBRXrlzBq1evoKKigsaNGwMAFzQ6dOgQ2rdvj+7du8PJyQmTJk3Cq1evuGP27dsXixYtwuDBg4XO5eLiAj6fj02bNqGgoKDWfnl6emLo0KEAgK1btwKoGkW2bds2mJub13ntb9++xZQpU7B+/XqMHz8eDg4OOH78OCQkJGBkZITAwECMGzcONjY2OHHiBFq2bMm1tbW1RceOHZGdnQ0AGDlyJPr37w8ej4ejR4/izZs3QtfWpk0bBAcH19qf169fIyYmBtu2bauz7sOHDxETE4P79+/D1NQUAHDnzh1s2bIFCxcuxOfPn7Fjxw78+eef6NevHwDAx8cH7u7u2LBhQ40jCpl/Jh59O8mXYRiGYRiGYRiGYX4yFhYWaNeuHTfy6VvPnj2DkpISN01ywoQJ9c67VW3Pnj1wcnISKiMiGBsbY+nSpbC3t4etrS0GDx6MmTNncnVKS0vRoUMHHDlyBDIyMhg3bhxWrVolNPLq8uXLaN++PbS0tLiyTZs2oWnTpkhNTcX9+/fr7J+lpSWaNGmCW7du4cCBAwCqphiGh4djypQpAABJSUls2rRJaGSYiYkJgoKCkJubiwsXLmDLli0AAD8/P7Rt2xZ9+vRBYGAgVq1ahby8PGzfvp2bhqipqYkTJ05g7ty5AKqCjpWVlVwwcM6cOVi6dCmWLl0KAwMDZGZmYuPGjdi/fz/MzMzQsWNHbN++HaNHjxa5HiLigol9+/ZF69atsXPnTrHXvmnTJqxevZoLxgHA+fPnYW1tjbdv3+L27dsYNWoU8vPzoaioCABISUlB27ZtcePGDfTs2bPO+8v8c7CRYQzDMAzDMAzDMMxPb9GiRdDR0YGSkpLY/Tk5OaioqOBe7969G2FhYSL1WrVqBQ8PD7i4uIjsU1BQwPjx45GXl4devXph+fLlCA8PR1FREcaNG1dj38rKypCbm4uxY8ciMzMTFy5cQO/evdG+fXuEhobCz88Pr1+/hpubG8LCwhAbG4sFCxZAXV0dQFVQ5+rVq2IDRtWCg4OhpqaGgQMHQlVVFUZGRgCA06dPQ1JSkntd26g3W1tblJeXo3///igqKkJxcTH33/z8fKxbtw5SUlJQVVWFmpoaDA0NYWdnBzs7OxgbGyMgIACvXr3CmzdvYGZmBldXV2zcuBHS0tLo3LkzxowZg5iYGLRt2xbDhg1Dbm4ujIyM0KxZM7H9+TpnWV1SU1Ohp6cnVNaiRQsAQFZWFlJTU9GkSRMuEPbtfubnwoJhDMMwDMMwDMMwzE9rzJgxuHPnDgoKCtC8eXMAVfm3SkpKhEZZfc3W1hZ+fn4iUypTU1ORl5eHGzduwNvbW2zbOXPmICoqihupFRkZiU+fPqFt27YAgOzsbNy4cQPr168HAOjo6CA2NhafPn3C/v37ER8fz+UZa9OmDUpKSuDs7Axvb2/k5eVBRUUF3t7eCAoKgoyMDABAWloa9+/fF7uKZbXXr19zifmNjIxgZGSEsrIyrFmzBgoKChgxYgQX/KmNmZkZpk6dCiUlJW7r0aMHMjMzoaamJjbYuGPHDoSEhCAgIADl5eVITEzEjBkzsHHjRixYsABmZma4f/8+xowZg/v376NTp04AADU1NcTHx9fZp/ooLCwUeZ7Vr8vKysTul5OTA4/HQ1lZ2Q/pA/P3wYJh9VBQUIBPnz4hJycH79+/R/PmzdGlSxdu//Hjx8Hn84Ui/eXl5Rg/fjyCg4Ohq6vboPOlp6fDxMQEubm5ddbNzc1FXFwcRowY0aBzMP8crVq1wt69e9G3b9//dVf+dT58+IDIyEhMmzZNqPzgwYOwsrKq1+92eno69PT0RJadrk1JSQmioqIwduzYBveZYRiGYRiGERYeHo6TJ08iKiqKm0J38OBB3L17F0FBQQ061tGjRzFq1CjcunULmZmZYt8PWlpaIjU1FSkpKQCAzZs3Y926ddz+KVOmoF+/fnB0dARQNS0RAOLj47F+/XrEx8fjw4cP4PP5cHZ2xrJly5CTk4OysjIoKytDQkICt27d4kYwJSUl4dq1a+jZsyd69eoFJSUlxMXFAagK9kyZMgUbNmxAq1at8PDhQ6HPFdu2bUPHjh2RkJAACwsLeHl5Yc6cObXeAwMDAxgYGICIUFxczOUpe/r0KfLz87mtoKAAv/76K/T19eHk5ISpU6fi7t27GDlyJDIzMyElJYW1a9cCqBrh5ezsDH9/f8TExGD27NkNei71ISsri/LycqGy0tJSAFX3Sdz+iooKEBFbTfInVHvWv3+RadOmYdiwYejbty+6du0KQ0NDNGnSBDIyMmjevDn69euHOXPmYOvWrbh16xbXjoiwbNkyyMrKCh1PRkYGOjo6NX5bICEhgVatWqFJkybcnPLBgwfXa/WSrxUUFMDBwQEPHjxo2AV/JSQkBG3btoW8vDxat24tMhSYiLBixQo0b94cioqKsLOzQ05OTo3HIyJs3rwZbdu2haysLFq0aIFnz54BqAreTZgwAU2bNoWysjL69++PR48ecW2Liorg4OAAVVVVGBoa4ty5c0LHtrKywp49e777WsUJDAyEjo4ONDQ0oKioCB0dHZFNVlYWhw4dqvEYBQUFUFJSwrVr15CYmAgej1ev7esgS9++fetcxvl7FRQUwNXVFdOnT/8hx0tKSkKXLl1w/fr1WuvFxcXBzMwMcnJy6NChg8jzBIAjR46gc+fOkJOTg5aWFq5cufJD+hgdHc194/X1pqioCC0tLbH7Fi9eLHQMWVlZrFu3DmfPnuXK7ty5A1dXV6Eh9NV4PB7S09P/ct/5fD5cXV0RGRn5l4/1rYb+Ptf2DAUCAfz8/NCyZUsoKCjAxMREKK8FwzAMwzDM38mJEydgYmICExMTLF26FEBVQn1FRUWuvHnz5pgxY4bY9l++fEFQUBDmzJkDJycnLFmypM5zEhEUFRWhqanJbTIyMlBSUuJeq6urg4gwffp08Pl8jBgxArNmzYKUlBRCQ0OxYcMGWFlZ4c2bN9zItq+n8mVmZsLIyAi+vr6Ii4tDXFwchg8fDktLSzx48ABv3rxBq1atsGbNGly+fJlrl52djbVr12Lx4sWQlZXFxYsX4e/vD19f31qvKSAgAKqqqlBRUYGBgQH69OnDlR84cACXL1/G8+fPkZ+fDz6fD6DqM1fz5s0xcOBAfPnyBTo6OtDS0uKmP3bp0gWFhYVYv349EhMT/yuDPbS1tZGZmSlUVv1aT08P2traeP/+vdD7/IyMDACAvr7+D+8P8z9GDBERxcTE0MmTJ+nSpUt048YNsrOzoxYtWlBOTg5Xp7i4WKTd8ePHqXv37kRE5OvrS7KystwmIyND0tLSQmX79+8nIiJFRUUiIjp27BhNnjyZBAIBqaio0JMnTygtLY1UVVVFztWpUydSVFQU2hQUFEhOTk6kvHorLS2t89pdXV3p1KlTlJiYSKtWrSIAFB0dze1fs2YNNWrUiCIjIyk+Pp7atWtHQ4YMqfF4S5cupSZNmtD+/fvp8ePHdOrUKcrMzCQiohcvXpCrqyvduHGD4uPjycrKinR1damsrIy7h9bW1nT//n3aunUrNW7cmAQCARER7d27l3r37s29/tFCQkJo1qxZIuWVlZWkqalJ79+/r7Ht9u3bydDQkHtdUVFRr62yspJrY2VlRXv27BE5dsuWLenKlStCZbt376ZPnz7VeU0FBQW0bNky0tTUJBkZGZo6dWqdbWpz7949Gjt2LMnLyxMAunbtWo11U1NTSVFRkRYuXEgPHz6kWbNmkby8PKWlpXF1duzYQYqKihQSEkKPHj2ic+fO0ZMnT/5SH+vSrFkzSkhIqHH/o0ePSENDg9uUlZVJSUmJey0tLU2KiopCdZKTk4mICIDQ9RERpaWl0bd/aktKSigsLIz4fD4NGzZM7O+ujIxMjb/Xz58//+7rb8jvc13PMDIykvr06UMxMTH04MEDcnd3JwkJCbp58+Z3949hGIZhGOa/4cSJE0LvhQ8cOECurq5UWVlJbdu25T739ejRQ+S9N1HVZwI7OzsaOXIkERF9/PiRmjVrRrt27RJ7vj179tDIkSPp2rVrpKqqKrRJSUmRvLy8UNm1a9eEPhsQEfn7+wu951RUVCQ5OTmhshcvXtCpU6fI0NCQevfuTb/++ivNmDGDjh07Rh4eHjRgwACKiYmhDh060JEjR7j+V1ZW0oABA2jevHmUlpZGTZs2JSKix48f07Jly4io6r1tSUkJERF17tyZuy/l5eXE5/OpoqKCPn36xL3fffbsGd2+fZsuXrxIERERtGfPHgoKCqLjx48TEdGZM2eoc+fOVFFRQUREf/zxB/3yyy/c9QYFBREAWrBgQX0fqxArK6taP+/ExsYSAEpJSeHKFi9eTF26dCGi//++/fz589z+bdu2UePGjYnP539Xn5i/LxYM+8bjx4+pe/fuZG9vLxQIu3PnDhkaGlJQUBBXxufzqWPHjnT16lWKj4+nmJgYIqoKQBgYGFBERAQRkdjgzbfBsJSUFAJAfn5+tHDhQpKTk6PVq1dz27t37yg3N5f70F1t1qxZ5ObmRkTEBZSIiFJSUqigoOC77kH79u3J3d2diP5/ICgwMJDbHxMTQwAoNTVVpG1ycjJJSkpSbGxsvc51+/ZtAkBJSUlERGRjY0Nnzpzh9jdu3Jg+fPhAnz9/pubNm9PDhw+/65rqw93dndTU1MjAwEBoCw4OJikpKercuTN17tyZNm7cKNK2e/fuFBAQ8JfOX99g2MqVK0ldXZ0ePHhQ5zEfP35MXbt2pYiIiDr/caiPpUuXkoODA126dKnOYJi7uzt17tyZe11RUUHa2tq0fPlyIiL69OkTKSsr0969e/9Sn+piYGBAHTt25J6fuE1RUZHu3LnDtSkrK6P169dTZWUlFRQUCAWnFRUVqaCggHJzc8nX15f7x5yofsGwsrIyGjJkCJmamtKXL1+orKyM7t+/L9RmzZo1ZGtry9Wv9ubNG3r37t1334uG/j7X9Qzfvn0rdP1ERIaGhrRo0aLv7iPDMAzDMMx/w4kTJ6hRo0bc+78WLVqQq6srEVV9Kf7rr79ScHAwjR49WqRtaWkp/frrryKDJS5cuEDS0tJiPx9UB8PEGTlyJG3dulXsvvT0dDp16hStXLlS6EviW7duUaNGjUhXV5cOHjwo1ObQoUMUHBxMaWlpXDBs586dtGLFChowYAAdP36c5s6dS8bGxlyfTp8+TQYGBlRYWCgUDPvanDlzuPd61cGw06dPU9OmTUlJSYlUVVVJV1eXOnToQADIxsaG7O3taebMmeTl5UX+/v40ZcoULrh08OBBMjAwoNmzZ9PJkydJV1eXHj9+zJ1v6dKlBIA8PDy4stzcXOrduzfduHFD7P36mrjPOyEhIeTo6EhEVZ/LzczMyMLCgu7cuUPHjh0jRUVFCg8P5+qPGjWKjIyM6Nq1a3Tu3DnS0tKi9evX13lu5p+HBcP+IzU1lebPn08GBga0e/duevToEcXFxdGpU6do2bJlpKKiQoGBgUKBraCgIBo/fjwREZmbm9OWLVuIiGjKlClkbW3N1XV2dqbt27cLne/bYNj27dtJQ0ODevfuTV27diVJSUnq3bs3tz179ozi4uKoWbNmFBkZSXw+n9zc3GjatGlUWlpK/v7+NGTIEBIIBBQVFUXNmjWjqKgoIiLy9PQkU1PTet+Ltm3bkp+fHxERJSYmEgChIFxxcTFJSEjQH3/8IdJ28eLF1LVr13qf6+bNmwSAGzn222+/kYuLC1VUVFBcXBw1bdqUBAIBzZgxg+bPn1/v434PAwMDOnv2LGVlZRER0cuXL7l/8N69e0fv3r2juXPn0sqVK4XaPX78mKSkpOjdu3fE5/OppKSkQVv16D0rKyvatWuX0MgxgUAgFAxbsWIFNWrUiAuevH37lho1aiT0B/xrX/+81icYFhISQrq6ulRYWFjr8aoDPLUFw0xMTMjLy0uozN7engYNGkREVd+yNGvWTCSY8qMZGBjQ+vXraevWrTVuhoaGQsGwkpISsrCwoBkzZogNhuXn59OIESO43/9qAOjly5dCz/DrYFhBQQENHz6cTE1N6fPnz0RE9PTpU9LR0eH+Rqxfv56GDRtG+fn5tH//fjIzM6OSkhJKSEigVq1a0bZt24io7mclTkN/n+t6huIYGxuTp6dnvfvEMAzDMAzzf6F6ZFheXh49fPiQvLy8qGfPnuTj40MCgYCbhVP9WaDanTt3yMTEhNq3b08ZGRkix42MjCRFRUXq06cPNyCgtLSUevfuzQWbviUuGFZRUUHNmjWjRo0a0aBBg2jp0qWUnZ1NpaWltGnTJtLQ0KBTp07Ry5cvydzcnMaNG8e9n1yxYgWZmppyI8MOHTpEnp6eZG1tTRs2bKDAwECKiIigV69e0ciRI+nUqVOUlZXFzZaoKRj2tU6dOtGVK1eorKyM8vPzRQZ81DTpTF9fnxITE7nXnz9/JjMzMwJA3bp1o3v37hER0YYNG0hFRYV2795NysrK5OvrS5WVldznnVOnTtXaPyLxn3c8PDyoW7du3OuMjAwaNGgQycrKUosWLUSew5cvX2j8+PGkoKBATZs2pRUrVvzXZiYx/1ssGPYfAQEBJCUlRc2bNydTU1MaNGgQtWnThgDQoEGD6NWrVyJtLC0tSU5OjuTk5Khz587E5/Pp8OHDpKurK/SNQVxcHCkpKQkNx+TxeNS2bVtq3rw5TZ48maytrbkgS03TJImqRlJNmDCBLl++TC1atCBTU1OSkpKiKVOmUHFxMX3+/Jn69+8vNIJq06ZNNGnSpDrvQW5uLvn5+ZGWlha9efOGiKqmgQIQmW7ZtGlTWrt2rcgx+vXrR87OzjR//nxq3LgxGRoa0vr160X+gAgEAnr27BlZWloK9S0tLY0MDQ2Jx+ORiooKnTp1im7fvk26urqUn59f5zV8r2PHjlGnTp0oIiKCtLS0KCQkhDp16kSHDh0Sqjdr1iwKDg4WKnN3d+e+YQkJCSEADdpatmxJRFV/vL/dd+XKFWrZsiWdPn2aRo8eTc2aNRMaRfTlyxfq1asXxcfH13mN9QmGHTt2jAYNGlTnMOD6BMNUVFQoLCxMqMzLy4vat29PRFVB40GDBlFAQABpa2tTy5YtadGiRVReXl7ntTSEgYEBHT16lGJiYmrcjI2NhYJhRFVD3zdv3iw2GPb582daunSpyNRpcc+3+l69fPmSOnbsSL169eLeuFR79eoVDR8+nB4+fEg6OjpkampKCgoKZGNjQ58+faLy8nIaPHiw0IjL+j6rrzX097muZ/ithIQEAsCNkmUYhmEYhvk7CAkJIT09PdLU1CRzc3Oyt7enYcOGkY2NDYWEhNCQIUNowIAB5OHhQdra2uTj40PPnz8nT09PkpCQICcnJ/ry5UuNx3/06BFZW1sTj8ej06dPk4ODA9nb25Ofnx/17NmTEhIShL4Afv36Nfd+kM/nU1FREZWVlXGfwap5enqSpqYmWVlZCX0GKC8vp1mzZpGOjg59+vSJunfvTllZWZSSkkKTJk2izMxM6tq1KwUHB5OrqysNGDCAsrOzKTc3l0aOHEm//vqr0KyAmoJhr1+/pnv37lFsbCwpKiqKzGb42refU4iqUuQoKipScXEx3bp1i+bNm0daWlrk4OBAz58/p1WrVtHkyZPJ0dGR1NXVuc8WV65cITU1tRpH1jHMj8CCYf9RPe+ZqOqPgb29PbVt25ZOnjxJREQTJkyg3bt3C7UpLi6mwsJC0tfXp/j4eEpISCAJCQnuw2zbtm1JV1eXyzPUp08fLij09ciwCRMmkLq6OpcHqLZg2KdPn2jBggU0ffp0evz4Mdna2tLhw4cpMTGRlixZQsnJyWRqakpnz56t97Wnp6eTjIwMASAtLS2hOdL79+8nCQkJkTZ6enq0atUqkXIjIyNq0qQJLVq0iO7cuUOBgYEkKSkpNP1v1apVJCkpSQBo8ODBQoFDoqpAWWZmJpWXl1NlZSV16dKF/vzzTzp06BC1a9eODA0NudxrP8LTp09JU1OTy5NWHdBq3rw5vXz5ksrKyqikpIQKCwvJzMyMTpw4wbUtKysjTU1NioyMFDnu8uXLacyYMfXuR23TJNXV1al3795/aYrcj5gmWa0+wTAJCQmR57RixQoyMDAgIqLBgwdTkyZNaMqUKXTr1i3avXs3KSoqko+Pzw/pYzUDAwPq3LkzmZmZ1bgpKSlxwTAXFxehPAz12aqnKtc2TVJdXZ1mzpwpNO2xWlFREfn5+dGwYcMoIyOD7O3tKTg4mLKysmjGjBmUk5NDnTp1oj/++OMvfTPV0N/nup7h1xISEqhJkyY0duzY7+4fwzAMwzDMf8PHjx/p48ePQmUHDhyg6dOn0y+//EK7d+/m3mM9f/6c3Nzc6MqVK/TkyROKi4ur93levXpFAoGAbGxsuGDXli1bqF27diQvL8/lhFVRUSE5OTmSkJAgAMTj8ej27dsix7t8+TKXTkacBw8eUFZWFo0fP558fX3JwMCAtm3bRoMHD6Zbt24REVFUVBQ3+IDP51OHDh2oa9euQvnJagqGHT9+nNTV1UlDQ4Ps7e1rfR+6a9cu0tDQEMqDpqyszM04OnnyJPn7+4u8V66oqCB3d3eRlB2vXr3iZjoxzH8DC4Z95cWLF+Ti4kKtW7emsLAwoREq9+7dIw0NDVq8eLFQGy8vL+6DcEVFBUVHR1NQUBD17NmTUlJSyNramvbt20c5OTlkYmLCTQf8dppkSUkJRURE0OHDhyk4OJgUFBTo8OHDdPjwYW6KnJeXFykrK9OgQYPI29ubpKWlqUOHDtStWzcyMjLiEpEHBweTtLQ0DR8+vF6jqcrLyyk5OZni4+Np9erVpKCgwA0XPXr0KAEQmcrWrFkz2rBhg8ixWrduTf369RMqGzNmjFBZTk4OPXr0iGJiYsjOzo60tLTo9evXYvsWHBxMgwYNopSUFDI0NKR3797Rq1evqGnTpiJDmL/H58+fqVGjRuTt7U03b94kc3NzatKkCf3555+0YsUKUlVVpZMnTxIAkpSUpIEDBwqNqjl27Bg1a9ZMZHROaWkpNWnShK5evVprAv1vpzHWFAxzdnb+yyOm/q+DYfLy8rRz506hssWLF5OxsTEREQ0cOJAMDAyE/iH28PAgPT29H9LHatXTX2/evFnjZmpqKjIy7HvUFgyrKbnqxo0bqXHjxtS1a1das2YNKSoqUuvWral79+7Uvn17UlNTowULFtCxY8dISUmJevXqVa+ffUlJSaGNqOG/z3U9w2qhoaEkKytL06dP/+Ej+xiGYRiGYf5paksDUlpaSoWFhVRSUkLl5eU/ZAret4n3v/1sIu7LWIb5t5P6q6tR/iwWLlyIdevWgcfjoVOnTti8eTPWrl2LoqIiVFRUQEJCAsrKyggLC8ObN2+wa9cuxMXFYePGjZg5cybGjBkDc3NzLFy4EO/fv4eOjg4MDQ0hJycHCQkJaGpq4v79++DxeACA4uJiGBkZoaCgAL/88gvk5OQQGxuL4uJiFBYWgs/n4+LFiwCA9u3bo2/fvsjKysLvv/8OFxcXnDt3Dn5+fujSpQtKS0vRq1cvVFRUYObMmThx4gRMTU2xbNkyKCkp1Xnt0tLSMDIyAgD06tULJSUlWLlyJWbOnAltbW0AQFZWFlq1agUAKCsrQ05OjtjlZZs0aYLWrVsLlbVp0wYnT57kXlcvIdyxY0cMGjQIBgYGCAsLg7+/v1C77Oxs+Pn54fr167hw4QKGDx8OLS0tAECfPn1w584drn/fS11dHWfOnEGPHj3w9u1bTJw4EVOnTuXu25QpU6CrqwsiEtt+165dcHJygqSkpFB5WloaPnz4ACsrq1rPf/nyZfTr16/Ofk6aNAnS0tL1vKq/h5qWLq7+uWnSpAl4PB4kJCS4/W3atMH79+9/eF927dqFkydPYsSIEVBTUxPZ/+HDB+53s9rgwYNx48YNyMjIiD1meXk5evXqhbNnz9arD87OzmLL3759ixkzZsDHxwcPHz5EcXExunfvDgDo1KkTGjdujBEjRsDf3x+3bt3C1KlTxV7Dtx4/fixS1tDf57qeIQB4eHhg27Zt2L17NyZOnFhnvxiGYRiGYX52UlI1f8yWlZWFrKzsDz3f1++nAYh8Nqnp/SzD/JuxYNh/TJ48GaamptDW1oaamhpUVFRw9OhRPHr0CAcPHkRJSQnk5eWRnp4OZ2dn5ObmIikpCR07dkReXh7Mzc1hY2MDoCoQoqurK3KO6g/bFRUVUFdXx7NnzxAeHo6oqCgAQHBwMAAgPT0dZ8+exc6dO4XaHzhwgDvG4MGDMXjwYABAq1atEB4ejq5du+L48eMAAAsLC1y+fFnkA359SElJobKyEgDQpUsXyMvL48KFC5g+fToA4OrVq+DxeLC0tBRp26tXL5w7d06o7MmTJ2jTpo3Yc/F4PEhKSnLn+9qCBQswc+ZMGBoa4vTp06ioqOD2FRcX/7DgUI8ePTBz5kzs378fMjIy8PLyAo/Hg6ysLMrKykBEkJOTQ3l5OcaNG4e9e/cCqAooXLhwAZs3bxY5ppGRUY0BNAAQCASQlZWFjo7Od/U5OzsbysrKUFRU/K72/xcsLCxw4cIF+Pr6AgAqKysRGxuLhQsXAqj6WVmxYgVKS0shJycHoPafle91+vRpzJgxA9LS0nj58qXYOpqammjSpIlI+f79+2Frayu2zcmTJxEWFvbd/UpLS0PLli2xZs0a7ve0S5cu6NKlCwCgb9++cHFxwZgxYxATEwMej4f27dvjxo0b9fq9rg5wf62hv891PcOoqChs3rwZcXFxMDc3/677wDAMwzAMwzAM839Nou4q/w4dOnSApaUl5s6di4KCArRq1Uoo0GBlZQVvb2+0aNECly9fhqamJtzd3XHv3j3s3bsXHh4eUFFRAQDExcWhc+fOIuc4efIkYmNj8eHDB65ufRUVFUFZWRlKSkoiW0ZGBiwtLaGkpCRUR1lZGdeuXcPmzZsxefJkscdNTk7GvHnzcOXKFTx8+BBhYWFYu3Yt90FZXl4es2bNwvLly3Hu3DnEx8fD1dUVM2bMQKNGjSAQCDB48GAuCDd79my8evUK8+bNw/3797Fu3TqcPn0aCxYsAABs2rQJW7duxb1795CQkIDJkyfjzZs3mDRpklC/Ll++jFu3bmHx4sUAqj6UHzlyBBcvXkRERARu376NHj16NOge1iU4OBi5ublYtGgRfHx8kJubi9WrV8PNzQ25ubkIDQ0Vqr9nzx706dMHBgYGDT7Xu3fvUFlZiRYtWtRY58qVK8jNzRW77+DBgxg5ciTy8vJgYWGBmzdvNrgP4hw/fhyDBw+GQCBocNtvfxbmzZuH27dvY+XKlXj8+DHmzp0LgUAAJycnAICDgwMkJSUxefJk3L17F7t27cK2bdvg6en5Q64FAI4dO4YpU6agR48eMDQ0xNq1a5GYmIjExETcuXMHc+fORVFREaZMmSI2MDlt2jTo6OiI3aZNm1brufl8Pk6fPl3j/rlz58Lf3x96enpif6+vXbuGSZMmif29PnTo0Hc9q4b+Ptf1DP/44w+Ym5ujUaNGePnyJbelpaXVu08MwzAMwzAMwzD/19jIsP948eIFrK2tMW7cOPTq1Utk/6FDhzBs2DDcu3cPR48ehYqKCuLj43HlyhVcv34dN2/exJAhQ+Dq6oo7d+4gIiJC5BjXr18Hj8dD7969xU5Jqo2ioiIKCwvF7vt6ZJg4kZGRSE5OFrtPQ0MDaWlpGDt2LEpKStC6dWusW7cOM2bM4Or4+/ujpKQE48aNg6SkJBwcHLBu3ToAVaPckpOT8fbtWwCAnp4eoqOj4ebmhm3btqFVq1b4448/YGFhAQDQ19eHt7c3PDw8oKioiG7duuH69evo2LEjd77y8nLMmTMHISEh3Ighc3NzuLu7w97eHioqKti3bx80NDQadA9/JCLCnj17sHLlyu9q/+zZM7Ro0ULsEOlLly5h1apVuHXrFlJSUsS2z87Ohra2NoqLi5GcnIycnJzv6se33r59i+TkZFRUVDR4+Pa3PwumpqY4fPgwFi1aBH9/f5ibm+P8+fNQVlYGAKiqquL8+fOYPXs2evfuDS0tLaxZs+aHTbU7fPgwoqKicObMGWhoaMDR0RELFy7E/PnzYWlpiVOnTmHUqFGIjY2tcbrtzp07GzwyrKKiArt374a/vz9kZWVx5swZse2rn2F6errY/V+PDBNn8+bN3/WsGvL7XNczzM7OxrVr12BoaCh0Dg0NDXz8+LHefWIYhmEYhmF+DkQEgUAgMlWzPgoLC+uV5odhfoj/acayv4mLFy+SpqYmhYSEEBHRhw8fqLy8nObNm0fTpk3j6n3+/JksLCyoQ4cOlJmZSSNGjKChQ4dSSEgIJScn061bt0hLS4s2bdrEtZk4cSL5+fkRn88na2trCg0NpSFDhtCyZcuosrKS9u7dS87OzmRlZUUAatyqE9qL07Jlyx+S/PvfKD09nWRlZUlSUpKkpKRE/l9KSookJSW5/5eQkCBZWVkKDw8nNTU1Ki4urtd5njx5Qm/evKEvX75QRkYGDRo0iJycnLj9fD6fDAwMqFGjRqShoUErVqygDx8+EBFR+/btKTg4mKubn59P3bp1o/Xr1//Ym/GTEQgElJycTBEREbRy5UqysbGhli1bkp2dHXl6epKJiQkpKyuTubk5OTg40MKFC+n06dNce2tra1JWVq5xBUllZWWytrbm6lcnym/UqBG1a9eOdu/eTaWlpfThwwcCQA8fPuTqvnz5khQUFOju3bs19t/KyoqOHTv237k5DMMwDMMwzHfr1q1bjYsjVXv79q1I2bNnz2jt2rV1ths5cqTYJPxWVlYUExPTsM7WwcvLS+yq4nUJDw8nLS0tkQUA1qxZQ1ZWVmLbVK+Mee3aNerduzcREb1+/ZpKSkqIiEhLS0vsqpoM89/ARoYBePToEcLCwjB69GgAwKBBg5CYmAgNDQ0cO3aMq6euro4LFy7Az88P6urqOHXqFLcvMjIS9vb28PPzw7x587hyR0dHTJgwAStWrIC2tjaGDx+OS5cuwdHRERoaGqisrMT27duxffv2WnNMfU9knalby5YtUVpa2uB2EydOxMSJEyEvL1+v+vb29nj06BGICFJSUujVqxd+//13bn91Hrlx48ZhyZIlQt+IzJo1C15eXnB3d+fKOnfuDEdHxwb3+9+Ex+Nhw4YN4PP5MDU1xdKlS2Fubi6U0DQvLw937tzBw4cPkZKSAlVVVaFjNCRnWEVFBVq1aoUVK1bAwcGBS2TauHFjTJw4EV26dOF+xyUlJTFmzBguPxjDMAzDMAzz32dhYYGcnJwa38Pn5eXBysqKyxFcE4FAIJK0/mufPn1C+/btERMTI5TaZc+ePcjIyKj12Hv27AGPx6s1Cb84ampqyMvLq7OegYFBjXl0AcDExARZWVlC56+srISqqqpQuw0bNmD48OFCuWyJCNu3bxeaZfS1yZMnw8vLi0tPIhAIMGbMGLi6uqJfv3749OkTjI2N67yGr5WUlGDJkiU4cuQICgoK0KlTJ6xevbrGhcxyc3Ph4uKCyMhIyMjIwNnZGQEBAdzzLCsrg6enJw4dOoSKigqMHTsWISEhUFBQaFC/mH+A/20s7u/r2+Vp61JRUUGpqan1qvv1Urc/Yild5p+joqJCZKnjajWVM/8c7BkyDMMwDMP8ffXu3ZsePHhQ4/5jx47R5MmT6zyOqakp7du3r9Y6GzZsoJYtW9KrV6+oZcuW1LJlS5KTk6NGjRpxr6u39u3bE1HVe0ldXV1KSEjgjqOvr8/NTJCSkiIVFRXutb+/P1ePz+dTRUVFndu371e/HRnWuXNnkXuUlpZGBgYG3OuoqCji8XgiMycGDRpEkpKSpKGhwc140dDQoClTphAR0YkTJ6hdu3bcyLCwsDDq06cPCQQC2r17N3Xp0qXOe/+t4OBgsrW1pStXrtDdu3fJ3t6eFBUVKS0tTWz9IUOGkJmZGd28eZPCw8NJUVGR1qxZw+2fNWsW6enp0eXLl+n8+fOkpaVFs2bNanC/mL8/NjKsBrVF+sWRkpKCnp5evep+Pcrre1Z7ZP65avuGh43+++djz5BhGIZhGObvzdHRscZRPp8/fxabP/pbRFTn50U3NzdkZWVBRkYGiYmJOHbsGEJDQ3HlyhW8evUKKioqaNy4MYD//5nw0KFDaN++Pbp37w4nJydMmjQJr1694o7Zt29fLFq0CIMHDxY5X0Pfh169ehULFizAmzdvICEhgZMnT2Lbtm11tnv79i2mTJmC9evXY/z48XBwcMDx48chISEBIyMjBAYGYty4cbCxscGJEyfQsmVLrq2trS06duyI7OxsAMDIkSPRv39/8Hg8HD16FG/evBG6tjZt2iA4OLjW/tja2mLu3Lnc63379kFNTQ3nzp0TGaH28OFDxMTE4P79+zA1NQUA3LlzB1u2bMHChQvx+fNn7NixA3/++Sf69esHAPDx8YG7uzs2bNhQ71lBzD8Dj6iWuXkMwzAMwzAMwzAM8xOwsLBAu3bt0LRpU7H7nz17BiUlJW6a5IQJE3D06NEGnWPPnj3cytvViAjGxsZYunQp7O3tYWtri8GDB2PmzJlcndLSUnTo0AFHjhyBjIwMxo0bh1WrVnGrfAPA5cuX0b59e2hpaXFlmzZtEkn1UR8FBQV48uQJtm/fDnl5eUyaNAkmJibo27dvrdMkT548iQsXLmDLli0AAD8/P7Rt2xZ9+vRBYGAgVq1ahby8PGzfvp2bhqipqYkTJ05wQavPnz+jsrKSCwbOmTMHS5cuxdKlS2FgYIDMzExs3LgR+/fvh5mZGTp27Ijt27dzaY1qQ0RQU1PD6tWrMXv2bKF9mzZtwurVq7lgHACcP38e1tbWePv2LW7fvo1Ro0YhPz8fioqKAICUlBS0bdsWN27cQM+ePRt8n5m/LzYyjGEYhmEYhmEYhvnpLVq0CDo6OjWuWJiTk4OKigru9e7du8WuHt6qVSt4eHjAxcVFZJ+CggLGjx+PvLw89OrVC8uXL0d4eDiKioowbty4GvtWVlaG3NxcjB07FpmZmbhw4QJ69+6N9u3bIzQ0FH5+fnj9+jXc3NwQFhaG2NhYLFiwAO/fvxcKjtVXWloa+vbti7Nnz0JJSQl9+/YFUBUcUldXh7S0NFeXz+dzq43b2tqivLwc/fv3R1FREYqLi7n/5ufnY926dZCSkoKqqirU1NRgaGgIOzs72NnZwdjYGAEBAXj16hXevHkDMzMzuLq6YuPGjZCWlkbnzp0xZswYxMTEoG3bthg2bBhyc3NhZGSEZs2a1eu6/vzzT+Tn53Mju76WmpoqMpurRYsWAICsrCykpqaiSZMmXCDs2/3Mz4UFwxiGYRiGYRiGYZif1pgxY3Dnzh0UFBSgefPmAKqS5ZeUlNQYSLK1tYWfn5/IlMrU1FTk5eXhxo0b8Pb2Ftt2zpw5iIqKwv379wFULbb26dMntG3bFgCQnZ2NGzduYP369QAAHR0dxMbG4tOnT9i/fz/i4+PRv39/AFVTBUtKSuDs7Axvb2/k5eVBRUUF3t7eCAoKQqdOnbhFmq5cuQIHBwekp6cLBbPqUlFRgatXryIlJQWLFi0S25bP5yM4OBgTJ06EmZkZpk6dCiUlJW7r0aMHMjMzoaamJjbYuGPHDoSEhCAgIADl5eVITEzEjBkzsHHjRixYsABmZma4f/8+xowZg/v376NTp04AqhYGiI+Pr9d1REVFYerUqfD09ES7du1E9hcWFoo8z+rXZWVlYvfLycmBx+OhrKysXn1g/jlYMIxhGIZhGIZhGIb5aYWHh+PkyZOIiorCzp07AQAHDx7E3bt3ERQU1KBjHT16FKNGjcKtW7eQmZkJXV1dkTqWlpZITU1FSkoKAGDz5s1Yt24dt3/KlCno168ftzp8db6v+Ph4rF+/HvHx8fjw4QP4fD6cnZ2xbNky5OTkoKysDMrKypCQkMCtW7eERjABQFBQEGbOnAkejwc+ny+2/xISEpCQkEBmZiaOHTuGM2fO4OnTpzhz5gxGjRqF4cOHY+/evSAibNmyBbNnz4aEhASmTZuG8vJyAFUrUhoYGICIUFxcjIKCAgDA06dPkZ+fz20FBQX49ddfoa+vDycnJ0ydOhV3797FyJEjkZmZCSkpKaxduxZAVd40Z2dn+Pv7IyYmRmSKY20EAgF8fX3h7+8Pb29v+Pj4iK0nKyvLXUO10tJSAFVBMXH7KyoqQERsNcmfUMOyxDMMwzAMwzAMwzDMP9CJEydgYmICExMTLF26FEBVQn1FRUWuvHnz5iKJ16t9+fIFQUFBmDNnDpycnLBkyZI6z0lEUFRUhKamJrfJyMhASUmJe62urg4iwvTp08Hn8zFixAjMmjULUlJSCA0NxYYNG2BlZYU3b95wI9u+DYQBwK1bt7B8+XJIS0vXuK1YsQIAcO/ePSQkJKBJkybw8fHB3bt3uSmBQFWQyNXVVexCAQEBAVBVVYWKigoMDAzQp08frvzAgQO4fPkynj9/jvz8fC4oFxgYiObNm2PgwIH48uULdHR0oKWlxU1/7NKlCwoLC7F+/XokJiZixIgRdd5boCoQZm9vj23btiEmJga+vr41LlKnra2NzMxMobLq13p6etDW1sb79++FpspmZGQAAPT19evVH+afg40MYxiGYRiGYRiGYX56dnZ2IiPD9u7di9u3b+PixYvQ1NREz549YW9vL9JWIBBg6tSp6NmzJ/r374/OnTvD2NgYu3fvhrOzc43njI+Px7Bhw4TKioqKcOHCBaFgWlRUFB4/fiwUfFq9ejU2bNgAADA0NERpaSkqKyuhqanJ1UlISEDr1q0BQCgxvDj9+/eHjo4OgKppoLa2tli0aJHYgFdtucgWLFgAT09PEBE3CkxPTw9bt27lXufl5SE/Px8xMTF48uQJvLy8YGxsjCVLluDu3buQkpLC4cOHsWfPHgBVo+Pc3Nzg5uaGBQsW1JjX7VuhoaG4dOkSbt++XWfAysLCAt7e3njx4gUMDQ0BAJcuXUKXLl2grq6O3r17o6KiArGxsfjll1+4/Y0bN4axsXG9+sP8c7BgGMMwDMMwDMMwDPPTO3HiBO7evQugapSXnZ0dJCQk4OLiAjc3N3Tv3h3a2tpcMvlqZWVlmDp1Ku7du4d79+4BADQ0NLB//37Y2NggPz8fbm5uYs9pYWGB3NxcoTJxq0lWe/36NZKSkpCUlITRo0dj8eLFAIDbt29jyJAhUFRUxOrVq/Hrr782+PozMzPRqlWretU9c+YMysvLkZCQgB49ekBaWhqSkpKIiorCtGnTUFRUBElJSaioqEBFRQUAMH/+fKiqqgptL168QPv27QFU3fPCwkK4urpi0KBB8PLyQkxMDHfOnJwcABAa2ZWXl4ehQ4di3bp1Yldz/OOPP/DLL79AIBDg5cuXXLmcnBx0dHSwefNm3LlzB/v27YOlpSXMzMzg7OyMjRs3Ij09HcHBwdi3bx+AqoURRo0ahXnz5mHHjh0oLi6Gj48PvLy8uKmszE+EGIZhGIZhGIZhGOYnduLECZo6dSrl5eXRw4cPycvLi3r27Ek+Pj4kEAioU6dOpKioSFlZWULt7ty5QyYmJtS+fXvKyMgQOW5kZCQpKipSnz59KDY2loiISktLqXfv3mRgYECFhYUibUaOHElbt24VKquoqKBmzZpRo0aNaNCgQbR06VLKzs6m0tJS2rRpE2loaNCpU6fo5cuXZG5uTuPGjaPPnz/X+/pLSkpIWlqa0tLShMq9vLxo1apVQmXJycmkra1Nvr6+pKmpSd7e3lReXk5ERGVlZZSfn08CgUCoTU2hBX19fUpMTORef/78mczMzAgAdevWje7du0dERBs2bCAVFRXavXs3KSsrk6+vL1VWVtLbt2+pUaNGdOrUKbHH19PTIwAim5mZGREReXh4ULdu3bj6GRkZNGjQIJKVlaUWLVqIPIcvX77Q+PHjSUFBgZo2bUorVqwQuVbm58Aj+s/SEwzDMAzDMAzDMAzzk9m8eTMCAwNRUFAAfX19GBgYoKCgAAKBAEOGDEF0dDTKy8thamqKw4cPY9q0aZg4cSJ27tyJDRs2wNHRERs3boSamprY4z9+/BgeHh44f/48IiMjcfToUVRWVqJDhw44c+YMNm7cCDMzM0hJVU3MysjIgLKyMtTV1VFZWYmysjJISUnh48ePXE4wAFi4cCH27NmDDh06YOPGjTA1NQVQldTd1dUVp0+fRlJSEho1aiTSp/T0dPB4PCgrK4OIsGHDBhw+fBhpaWkAgPLyckhKSsLZ2RkdOnTAwoUL8fr1a+zbtw+bN2/GmjVrMGXKFLx58wZOTk748uULDh48CCMjI7H3oHnz5jhz5gzXRwB4+fIlTExMkJOTg0ePHuHQoUP4888/MXDgQCxbtgx//vknXr58CSLC6dOnERkZCQsLC8TGxsLOzg5WVlY4efLk9zxyhqkTC4YxDMMwDMMwDMMwP61Pnz4BqJraWO3gwYOIi4tDeno67O3t4eTkBB6Ph5SUFGzduhUjR45EkyZN8OnTJy5BfF1SU1Ohp6eHYcOG4eDBg1BXV0doaCg2b96M9PR0VFZWctMNy8vLUV5eDoFAAB6Ph1u3bqFbt25Cx7ty5Qo0NDTQqVMnsedLTEyEiYmJ2H3z5s3Dli1bIBAIAFTlHNu5cycsLS0BVOUys7S0hKqqKi5duoTz58/Dx8cHw4YNw/Lly4XOKRAIsGTJEty/fx/nz58Xe77du3dj4cKFQqtYCgQCeHl5YenSpTh16hSePn0Ke3t7oamafD4fCxcuxNy5c6Gnpyd0L5OTkzF06NCabzjD/AUsGMYwDMMwDMMwDMMwPwifz+dGgX2rrKwMfD4fkpKSkJSUhJSUVI2rH/4IAoEAAoGgxv5UKy0tBZ/PrzVxfUVFBaSlpX90Fxnmf4IFwxiGYRiGYRiGYRiGYZh/DdE1VBmGYRiGYRiGYRiGYRjmJ8WCYQzDMAzDMAzDMAzDMMy/BguGMQzDMAzDMAzDMEw9mZubY/fu3bXWeffunUjZ8+fPsW7dujrb2draCiWir9a3b1+cPXu2Xn28d+8ejh49Wq+6RISMjAxcuHABISEhiI6OFqljYmKC2NjYGo8xceJESElJITMzs9ZzlZWVwc7ODhUVFfXqG8P8t9SeRY9hGIZhGIZhGIZhfgIWFhbIycmBvLy82P15eXmwsrLC3r17az2OQCCAhETN40o+ffqE9u3bIyYmBj169ODK9+zZg4yMjFqPvWfPHvB4vDoT3lfLzc3FyZMnRcrT09MRFBSEoqIisX21s7PDb7/9hufPnyMlJQUlJSXcvrFjx8LGxqZe5weAtWvXIiIiAm3btsX48eNx6dKlGu+xrKwsSkpKEB4eDnt7ewDAyJEjER8fj8rKSuTn50NdXR0AYGVlhYiIiHr1oaCgAN7e3iguLsaOHTvE1uHz+ejQoQMqKyvx8uVLoX0XL17EsmXLkJiYCAUFBQQHB+PXX38FUBXEdHV1RVxcHFRUVODo6Ah/f3/uGZWVlcHT0xOHDh1CRUUFxo4di5CQECgoKIjtR0REBLy9vZGWlgZjY2OEhYXBzMyM2x8XFwd3d3c8efIEBgYGCAwMhLW1db3uA1N/bGQYwzAMwzAMwzAM869w9OhRJCYmit3qGrVVra5gmIaGBpYtW4YJEyYgNTUVrVq1QqtWrbBp0yacO3eOe129dejQAQBQWVmJsLAwLFq0iDuWgYEBNDU1oampifj4eIwfP557vXr1aggEAhQWFiIqKgoREREoLCxEYWEhNDU14efnh+LiYq7s601FRQWNGjWCjY0NPD09oa6ujlu3biE7Oxt//vknAIDH46G0tLTWe7Fq1SosXboUBw4cwPXr11FWVoYBAwbg48ePQvWio6OhoaGBjh07IiMjA6tWrULHjh0hIyOD48eP4+PHjzh8+DAsLCzw8eNHfPz4sV6BsMLCQixfvhz6+voICwtDbesD7tixAykpKSLlZ8+exbBhw2BjY4Pbt2/jxIkTaN26NYCqZ21jYwNpaWlcv34d27Ztw549e+Dn58e1d3d3R1RUFMLDwxEREYHo6Gh4eHiI7cPNmzcxYcIEzJgxA7du3UKLFi1gY2ODgoICAEBaWhpsbGwwcOBA3LlzB1ZWVrCzs0N6enqd94JpGDYyjGEYhmEYhmEYhvlXcHR0rHHEzufPn9GrV686j0FEtQbDAMDNzQ1ZWVmQkZFBYmIijh07htDQUFy5cgWvXr2CiooKGjduDKAq6AQAhw4dQvv27dG9e3c4OTlh0qRJePXqFXfMvn37YtGiRRg8eLDQuVxcXMDn87Fp0yYuqFITT09PDB06FACwdetWAFWjyLZt2wZzc/M6r73ax48f4ezsjGvXruH06dNcny5cuABbW1uYmZlhx44dGDRoEABAQkICY8eOhaqqKuzt7WFiYgIA0NLSQmFhIUpKSnDv3j3o6+sjOzsbAKCurg5ZWdla+/H69WvExMRg27ZtCA4OrrHehw8f4OPjA1tbWzx69Igr5/P5mDlzJhYvXoxly5aJtPv06RNSU1Nx8uRJGBsbo0uXLrh69Sru3LkDoOpnZseOHfjzzz/Rr18/AICPjw/c3d2xYcMGkRFy69atg42NDdzc3AAAu3btgpaWFsLDwzFlyhSEhISgdevWWLNmDQAgODgYkZGR2LNnD1asWFHrvWAahgXDGIZhGIZhGIZhmH+F7t27o2nTpmL3PXv2TOj1hAkTasy7NWnSJEyaNEmkfM+ePXBycoKEhAQCAwMBVAXPNm3ahKVLl0JNTQ2rVq3C4MGDMXPmTK5daWkpVqxYgSNHjiApKQk3b96EjY0NJkyYwNV5+vQpAgIChKZxbtq0CU2bNsWIESOgo6NT5/V369YNAHDr1i0cOHAAQNUUw4KCAri4uAAAJCUlsWnTJrHtKysrsXPnTixbtgxt27blAljVGjVqhMuXL8Pb2xtDhgyBnZ0dli1bhsGDB0NXVxedOnXCqVOnuCmGe/fuRUhICLZv347mzZsDAGxtbfHq1Svs2LED3bt3R8eOHbF9+3aMHj1apD/t27fnAlO1BcPmzp2LiRMnQk1NTSgYdvnyZbx79w5z584V265x48bo168ftm/fjnXr1iErKwunT5/mAmfXrl2DQCDggn4A0L9/f5SWliIxMRE9e/YUOt6VK1ewevVq7rWamhq6dOmChIQETJkyBVeuXBEKdkpJScHS0hIJCQk1XhvzfVgwjGEYhmEYhmEYhvnpLVq0CDo6OlBSUhK7PycnRyix++7duxEWFiZSr1WrVvDw8OCCR19TUFDA+PHjkZeXh169emH58uUIDw9HUVERxo0bV2PfysrKkJubi7FjxyIzMxMXLlxA79690b59e4SGhsLPzw+vX7+Gm5sbwsLCEBsbiwULFnD5tc6fP4+rV6+KDRhVCw4OhpqaGgYOHAhVVVUYGRkBAE6fPg1JSUnudW2j3sLCwuDj4wM/Pz+sXLkSBgYGNdbduHEjTp8+jSVLluD48eNwcnLCkSNHMHbsWDx69AiDBw+GmZkZ7t69i9mzZ2P+/Pk4efIkxo0bBwcHBwCAvLw8jIyM0KxZM7HnqB5VV5uDBw/ixo0bePLkCRegrJaQkIBWrVohMjISq1atQmlpKYYOHYrAwEDu52Tfvn3o2rUrtmzZAiKCg4MDHB0dAQCpqalo0qQJFBUVuWO2aNECAJCVlSV0ri9fviA3Nxd6enpC5S1atODqpqamit2flJRU53UyDcOCYQzDMAzDMAzDMMxPa8yYMbhz5w4KCgq40Ud5eXkoKSmBlpaW2Da2trbw8/MTmVKZmpqKvLw83LhxA97e3mLbzpkzB1FRUbh//z4AIDIyEp8+fULbtm0BANnZ2bhx4wbWr18PANDR0UFsbCw+ffqE/fv3Iz4+Hv379wcAtGnTBiUlJXB2doa3tzfy8vKgoqICb29vBAUFQUZGBgAgLS2N+/fvi13Fstrr16+5EVlGRkYwMjJCWVkZ1qxZAwUFBYwYMYIL5NTkt99+g729PRo1aiQ0su3gwYM4ePCgyGqXbm5uKCsrw6lTp2BlZYU//vgDe/bswatXr7Bx40ZuqigAFBcXw83NTShoqKamhvj4+Fr7VJsXL15g7ty5CA8Ph4qKisj+d+/e4fPnzzh+/DgOHjyIt2/fYs6cOSgvL8fevXtRXFwMGxsb9OjRA4sWLUJGRgbmzZuHNWvWwMvLC4WFhSI/I3JycuDxeCgrKxMqLywsBACR+goKClyONXHHU1BQEDkW89exYBjDMAzDMAzDMAzz0woPD8fJkycRFRWFnTt3AqgK3ty9exdBQUENOtbRo0cxatQo3Lp1C5mZmdDV1RWpY2lpidTUVC5Z++bNm4WS80+ZMgX9+vXjRhdJSkoCAOLj47F+/XrEx8fjw4cP4PP5cHZ2xrJly5CTk4OysjIoKytDQkICt27d4kYjJSUl4dq1a+jZsyd69eoFJSUlxMXFAagKpEyZMgUbNmxAq1at8PDhQ/Tt25fry7Zt29CxY0ckJCTAwsICXl5emDNnTo3XLy0tjUaNGjXonsnKymLcuHEYO3YsoqOjMXnyZKioqGD//v1CI8uIqN6raNZHQUEBRo4cCRcXFwwYMEBsHT6fj7KyMhw+fJi7n9nZ2XB3d8euXbuwb98+ZGdn4/bt25CXl0fPnj1RUVGBGTNmYNasWZCVlUV5ebnQMSsqKkBEIkGt6vxn39YvLS3l6oo73tf7mR+HrSbJMAzDMAzDMAzD/PROnDgBExMTmJiYYOnSpQCqEuorKipy5c2bN8eMGTPEtv/y5QuCgoIwZ84cODk5YcmSJXWek4igqKjIrQCpqakJGRkZKCkpca/V1dVBRJg+fTr4fD5GjBiBWbNmQUpKCqGhodiwYQOsrKzw5s0bbmTb19PyMjMzYWRkBF9fX8TFxSEuLg7Dhw+HpaUlHjx4gDdv3qBVq1ZYs2YNLl++zLXLzs7G2rVrsXjxYsjKyuLixYvw9/eHr69vrdeUkJAAHo8ntE2aNAnnzp0TKa9eVfH8+fPo06cPVq9ejYsXL+L58+cYNGgQ5s6dy01NLSwsFLquv+r48eNITk7GunXrICcnBzk5Ofj5+SE1NRVycnI4cOAAmjRpAi0tLaHztmnTBhUVFfj8+TMePnyINm3aCCXC79KlC4qLi/Hy5Utoa2vj/fv3QtNrMzIyAEAolxoAaGpqQlZWFpmZmULlmZmZXF1tbe1a9zM/DguGMQzDMAzDMAzDMD89Ozs7JCYmIjExEb///juAqgTuurq6uHjxIhITE9GyZUvY29uLtBUIBJg6dSp69uyJ/v37w93dHZcuXcLu3btrPWd8fLxQIExTUxPR0dHw8PAQKouPj8fjx4/x9OlTXLlyBREREdi1axfMzc0RFxcHQ0NDODs7IzIyUqjdy5cvIRAIsHv3bi7PFgCUlJTgwYMH3Kiz8PBwxMbGCl2Pg4MDRo8ezU2NbNOmDc6fPw+BQFDrNfXo0QOxsbHw8fEBEYGIcODAAVhbW3Ovw8PDsWrVKm4qqLq6OoyMjPD+/XtMmDABxsbGCAoKwuHDhyEtLQ0AePPmTY3TVr+Hra0tkpOTuWeemJiImTNnQldXF4mJiRgxYgR69eqF9PR0bgVLAHjy5AnU1NTQuHFjaGtr4+XLl0KjtR49egQej4dmzZqhd+/eqKioELq3ly5dQuPGjWFsbCzUHwkJCfTs2RMXLlzgyvLy8nD37l1u5JqFhYXQ/srKSsTGxtY4so35fiwYxjAMwzAMwzAMw/z0xI0Mk5CQgIuLC9zc3BASEgJtbW2haYRAVXJ7R0dH3Lt3j5tmqaGhgf3792PmzJm1TrW0sLBAbm6u0FadoP3rMgsLC0hISOD169dcMveRI0fi48eP+PjxI6KjoyErK4vGjRtj06ZNXHnr1q1RWFiIuXPn4uDBg9x58/LyoKysDKAqMNa/f38uAAgA0dHRSE9Ph7+/v1B/O3TogJUrVwKoyn1W07RFY2Nj/Pnnn3B3dxfZd/nyZbi4uMDa2por69atG0xMTDB16lQ8e/YMz549Q4sWLVBRUQEDAwPo6+vj8uXLKC8vR3FxMTp37oymTZsiLy8PFhYWuHnzZo33uCbViwR8vWlqakJaWhpGRkZQVVWFtbU12rZti4kTJyIhIQERERHw8/ODp6cnAGDy5MkoLS2Fk5MT7t69i5MnT2L+/PkYPXo0mjVrhlatWmHUqFGYN28erl+/jvPnz8PHxwdeXl6QlJQU6f/8+fNx9OhRbNu2DUlJSZgyZQratm0LGxsbAMC8efNw+/ZtrFy5Eo8fP8bcuXMhEAjg5OTU4OtnaseCYQzDMAzDMAzDMMxPz87ODnFxcThw4ADs7e1x+/Zt+Pr6Ys6cOXj06BEWL16MTZs2CbW5e/cuevTogQcPHuD69evQ1NTk9g0cOBARERHw9vaGpaUlrl69CqAqeLZz5048fvwYRUVF9eobn89H8+bN0aVLF2zZsgVlZWXQ0NBAWVkZgoODYWNjgz179uDKlSsIDg7G+PHj8eXLFwDAy5cvsWfPHm5kmKWlJZ4/f44bN27AxsYG6enp6Nu3L06ePAmgKqG/qakpDh06VOu0xM2bN3PBMCIS2teoUSOcP38eGhoaqKysFNr38uVLREREoFu3bvW6dnt7e3Tp0gWbNm1C48aNYW5ujqFDh6Jnz54oLi5GcnIycnJy6nWshpKSksKZM2cgLy+Pfv36Yc6cOZg1axa8vLwAALq6urh06RLevn0LS0tL/Pbbbxg1ahT27t3LHWPXrl3o3LkzrK2t4ejoiFmzZmH+/PkAINL/4cOHY9OmTVi5ciV69uyJ8vJyREVFcSP4TE1NcfjwYezfvx9du3bF48ePcf78eS6wyfw4PPr2p5phGIZhGIZhGIZhfhKbN29GYGAgCgoKoK+vDwMDAxQUFEAgEGDIkCGIjo5GeXk5F4iYNm0aJk6ciJ07d2LDhg1wdHTExo0boaamJvb4jx8/hoeHB86fP4/IyEgcPXoUlZWV6NChA86cOYONGzfCzMyMCyxlZGRAWVkZ6urqqKysRFlZGaSkpPDx40cuJxgALFy4EHv27EGHDh2wceNGmJqaAqhK0O7q6orTp08jKSkJNjY2iIiIQHFxMVatWgV/f3/Y2dnB0dERr169wuPHj3Ho0CHIyclh8uTJUFJSwqpVq6CnpwcASE9PR48ePYSmClb38+PHjygoKMDQoUNx7do1NG7cWOyiAbXx8fGBr68vNm/ejJUrV3IJ+DMyMvDw4UM8f/4cU6dOxcyZM+Hr6wsvLy+EhoZi9+7dGDt2bIPOxTD1xYJhzP9j766jssrax/+/CSlBUFCxwURFRbE7UTHGwBwEjBmxsWPs7lEHu8UWC7u7RlBMbFFRGUWluYmb8/uDH+frPYDxPDPPzEev11r3Wp6999lnn6P/eK1rX1sIIYQQQgghvlnv3r0D0rY2ptu4cSNnz54lNDSULl264OXlhZ6eHg8ePGDp0qX88MMP5MmTh3fv3lGnTp0ves6TJ0+wt7enZcuWbNy4kZw5c7JkyRJ8fX0JDQ1Fq9WSLVs2DAwMSEpKIikpidTUVPT09Lhy5UqGTKpTp05hbW1N+fLlM31ecHAwuXPnZujQoZQuXRo/Pz9GjBjB7t27mTRpElWrVuXAgQNs27aNDRs2oNVqqVChAqamply5cgV9/bSNYlkFw3bv3k3Pnj3R19fHxcWFTZs2oaen98Xf/c98fX2JjY1l1KhRANSvX59atWoxY8YMfvnlFyZPnqzOP3bsWE6dOsWFCxf+4+cJ8SkSDBNCCCGEEEIIIf4iKSkpWdbaSkxMJCUlBQMDAwwMDDA0NPyvAkyQVgw/PbAFaUXX07fdASQlJWFkZPRfPeOvEBcXR2pqqs6Wv/j4eIKDg6lZs2aG8a9evdLJlBPiryTBMCGEEEIIIYQQQgjx3ZAC+kIIIYQQQgghhBDiuyHBMCGEEEIIIYQQQgjx3ZBgmBBCCCGEEEIIIYT4bkgwTAghhBBCCCGE+EJVq1ZlzZo1nxzz+vXrDG33799nzpw5n72vTZs2pKSkZOirX78+hw8f/rrFfsaoUaOYOnXqfz1P165dMTQ05MWLF58cl5iYSNu2bUlOTv6vnynEf0OCYUIIIYQQQgghvnm1a9emVKlSODk5Zfqzt7fHy8vrs/P8+fTGP3v37h1lypTh8uXLOu1r167l+vXrn5x77dq16OnpZXkaZVasrKzQ09P77K948eKfnMfJyQkbGxtsbW3VX+7cuT953+zZs9m5cyelSpWiU6dOJCQkZDnW2NiYhIQE/P391bYffvgBGxsbcubMiYGBATY2NtjY2NC+ffuv+gaZiYmJYdCgQfz000+Z9m/dupUKFSpgYmKCra0tp06dUvuWLVtGyZIlMTExwcHBgeXLl+vc+8svv2Bvb4+ZmRmOjo7s2rUry3VMnjwZPT09zp8/n+WYnTt3Urp0aUxMTKhSpQpBQUE6/WfPnsXZ2RkTExPKli3LkSNHvuQTiCxIMEwIIYQQQgghxHdh27ZtBAcHZ/r7XNZWus8Fw6ytrRk3bhydO3fmyZMn2NnZYWdnx8KFCzly5Ih6nf4rW7YsAFqtlmXLljFq1Ch1rmLFiqnBoQsXLtCpUyf1esaMGeq4d+/ekZyc/Nnf/fv3P/t+x48fJzw8XP1dvXo1y7FTpkzhl19+wc/Pj/Pnz5OYmEijRo2IiIjQGXfw4EGsra1xdHTk+fPnTJkyBUdHR4yMjNi1axcRERFs2bKF2rVrExERQUREBDt37vzsWrMSGxvL+PHjKVq0KMuWLUNRlAxjVq1aRa9evfjpp58IDAxkw4YN5M2bF4Bbt26xZMkSZs6cye+//0737t3p06cPO3bsUO9//fo1y5cv5+LFi9SvX59OnTpx69atDM958+YNc+fO/eR6L126ROfOnenduzdXrlyhcOHCuLq6EhMTA8DTp09xdXWlcePGXL16lXr16tG2bVtCQ0P/42/0vfu6cLMQQgghhBBCCPF/lIeHB2ZmZpn2vX//npo1a352DkVRPhkMA/Dx8SEsLAwjIyOCg4PZsWMHS5Ys4dSpUzx+/JgcOXKQO3duAPT09ADYtGkTZcqUoVq1anh5edGtWzceP36szlm/fn1GjRpFs2bNMjzPwMDgs+v+2JkzZxg6dCgvX75EX1+fPXv2ZMh8+pSIiAh69OjBuXPn2Ldvn7qmY8eO0aZNG5ydnVm5ciUuLi4A6Ovr06FDBywtLenSpQtOTk4A2NraEhsbS0JCAkFBQRQtWpTw8HAAcubMibGx8Ve9V7pnz55x6NAhli9fzqJFizL0v3//niFDhrB48WI8PT0BcHR0VPsLFCjApUuXyJ49OwDly5fnxIkT7Nq1iw4dOgDobJVdtGgRGzZs4NSpU5QrV07nWYMHD6Zp06Y62XB/NmfOHFxdXfHx8QFg9erV2Nra4u/vT/fu3fntt98oXrw4s2bNUp8XEBDA2rVrmTRp0n/whYQEw4QQQgghhBBCfBeqVaumZv/82b1793SuO3fuzLZt2zId261bN7p165ahfe3atXh5eaGvr8/8+fOBtODZwoUL+eWXX7CysmLKlCk0a9YMb29v9T6NRsOkSZPYunUrN27c4NKlS7i6utK5c2d1zN27d5k5cybr1q1T2xYuXIilpeUXv3+6MmXKMHfuXFasWIGpqSndunWjWLFiADRu3Fhnm6ZWq1WfodVqWbVqFePGjaNUqVJqACtdrly5OHnyJGPHjqV58+a0bduWcePG0axZMwoVKkT58uXZu3evOv+6dev47bffWLFiBfnz5wegTZs2PH78mJUrV1KtWjUcHR1ZsWLFV22bLFOmjJrRllkwzN/fH3Nzc3788cdM78+VK1eGNhMTE7RababjFUVBq9VibW2t037o0CHOnDnD4cOHPxkMO3XqlE6mn5WVFZUqVeLy5ct0796dU6dO6QRBDQ0NqVu3boatuOLLSTBMCCGEEEIIIcQ3b9SoURQsWBBzc/NM+9++fatT2H3NmjUsW7Yswzg7OzuGDRtG//79M/SZmZnRqVMnoqKiqFmzJuPHj8ff35+4uDg6duyY5doSExOJjIykQ4cOvHjxgmPHjlGrVi3KlCnDkiVLmDp1Ks+ePcPHx4dly5Zx+vRphg4dyh9//IGtre1Xf4unT5+qBfnNzc2pX78+AEePHiVnzpxky5ZNHZuSksKrV6+AtDpaEyZMYOrUqUyePFkNoGXm119/Zd++fYwZM4Zdu3bh5eXF1q1b6dChA7du3aJZs2Y4OzsTGBhI3759GTJkCHv27KFjx464u7sDYGpqioODA/ny5fuq90vPtsvK5cuXKVeuHPPmzeO3337D0NCQLl26MHnyZJ13TxcaGsrx48fVAOfH3rx5w+TJkylcuDBt27ZV29Oz5xYvXpzlvzmADx8+EBkZib29vU574cKFCQsLA+DJkyeZ9t+4ceOT7ymyJsEwIYQQQgghhBDfLDc3N65evUpMTIyafRQVFUVCQkKWgaQ2bdowderUDFsqnzx5QlRUFBcvXmTs2LGZ3tuvXz/279/PtWvXAAgICODdu3eUKlUKgPDwcC5evKjWkSpYsCCnT5/m3bt3bNiwgQsXLtCwYUMASpYsSUJCAj169GDs2LFERUWRI0cOxo4dy4IFCyhfvrxaD+vUqVO4u7sTGhqaaUAnK8nJyZw5c4YHDx4watSoTO9NSUlh0aJF/Pzzz3Tp0oVcuXLpZLZt3LiRjRs3Zjjt0sfHh8TERPbu3Uu9evXYvHkza9eu5fHjx/z666/qVlGA+Ph4fHx8dIKGVlZWXLhw4Yvf5Uu9fv2a4OBgChQowK5du7hz5w4DBgzA2NiYiRMn6ox98OABLVu2pFKlSvTo0UNtP3v2LI0bNyY5OZkSJUqwdetW9d+Loih4eXnh4uJCu3btPlnbKzY2FiDDvzUzMzO19lpsbGym/YmJif/pJ/juSTBMCCGEEEIIIcQ3y9/fnz179rB//35WrVoFpAVvAgMDWbBgwVfNtW3bNtq1a8eVK1d48eIFhQoVyjCmbt26PHnyhAcPHgDg6+urU5y/e/fuNGjQAA8PD+D/1fu6cOECc+fO5cKFC7x584aUlBR69OjBuHHjePv2LYmJiVhYWKCvr8+VK1fUelbpFixYgLe3N3p6eqSkpGS6fn19ffT19Xnx4gU7duzgwIED3L17lwMHDtCuXTtatWrFunXrUBSFxYsX07dvX/T19enVqxdJSUlky5Yt0y2En2JsbEzHjh3p0KEDBw8exNPTkxw5crBhwwadzDJFUb76FM2ePXuyfv169XrNmjXqd/2UlJQULCwsWLVqFfr6+lStWpW7d++yYcMGnWDY7t276d69O3Xr1mXTpk0YGRmpfZUrV+bGjRtERERw4MABatWqxfbt22nVqhVjx47l8ePHbN269bNrSa+LlpSUpNOu0WjUAJixsfEn+8XXk9MkhRBCCCGEEEJ883bv3o2TkxNOTk788ssvQFpB/ezZs6vt+fPnp3fv3pne/+HDBxYsWEC/fv3w8vJizJgxn32moihkz55dPQHSxsYGIyMjzM3N1eucOXOiKAo//fQTKSkptG7dmj59+mBoaMiSJUuYN28e9erV4+XLl2pm258DYQBXrlxh/PjxZMuWLctferH1oKAgLl++TJ48eZgwYQKBgYEULlxYnUuj0TBo0KBMDwq4fPkyenp6Or9u3bpx5MiRDO3Dhg0D0rZf1qlThxkzZnD8+HHu37+Pi4sLAwYMULemxsbGZvpenzJ16lRu376t/n744Ycvui9PnjwULVpU5/1KlizJH3/8oV77+vrSuXNnxo8fz969e7GwsNCZw8zMjNKlS1OnTh1mzpxJx44d1bpf06dP59GjR9jY2GBiYqJmBTZq1Eg9VCCdjY0NxsbGvHjxQqf9xYsXaj22AgUKfLJffD3JDBNCCCGEEEII8c1r27ZthsywdevW8fvvv3P8+HFsbGyoUaMGXbp0yXBvamoqPXv2pEaNGjRs2JAKFSpQrlw51qxZo7N17s8uXLhAy5Ytddri4uI4duyYTjBt//793L59Wyc4M2PGDObNmwdAiRIl0Gg0aLVabGxs1DGXL1+mePHiAOopjFlp2LAhBQsWBNK2gbZp04ZRo0ZlGvD6VC2y6tWrc/r0aU6dOqVmUf15m+TOnTsJCQlRt5LmzJkTBwcHzpw5ox4KkJSUxKlTp/Dz8wPg5cuXX13/LF++fF9dTwygZs2aTJo0CY1Gg4mJCQB37tyhZMmSANy4cYPBgwezY8cO2rRp80VzGhoaqgX2Q0JCdPpevnxJ48aNWbt2LbVr19bp09fXp0aNGhw7dgwvLy8gbRtvYGAgI0aMAKB27docO3ZM/d5arZbTp0+r/eLrSTBMCCGEEEIIIcQ3b/fu3QQGBgJpWV5t27ZFX1+f/v374+PjQ7Vq1ShQoIBaTD5dYmIiPXv2JCgoiKCgIACsra3ZsGEDrq6uREdH4+Pjk+kza9euTWRkpE5bmzZtMpwmme7Zs2fcuHGDGzdu0L59e0aPHg3A77//TvPmzcmePTszZszI8hTET3nx4gV2dnZfNPbAgQMkJSVx+fJlqlevTrZs2dTtnADlypWjT58+REVF8euvv+rce/LkSfr3709AQIDaVqVKFa5cuULx4sUZNWoUAPXr1yc5OZlixYphYGDAyZMnSUpKIj4+ngoVKpA3b16ioqJo0aIFc+bMoUaNGl/9zllxd3dn6tSpeHp6Mnz4cG7cuMHy5ctZvXo1kLYdtlChQjg6OvLo0SOde4sVK8bJkyc5ffo0TZs2xdzcnCNHjrB+/Xr1wAUHBwede9IDboULF6Zw4cIZ3mvIkCG0bduWunXrUr16dSZNmkSpUqVwdXUFYODAgVStWpXJkyfTrl07lixZQmpqqho8E19PgmFCCCGEEEIIIb55bdu2Zf78+Tx79oxNmzZx9uxZJk6cyIQJE1i5ciV79uzh/v37OvcEBgby008/kZSUxPnz53Wysho3bszOnTvp0qULu3btYsqUKdSrV4/ExERWrVpFeHg4cXFxX7T1LyUlhcKFC5OYmEjlypWpUqUK1tbWJCYmsnz5ciZPnszatWspW7YsXbt2JSAggGXLlpEzZ84veneNRsOzZ88oUaJElmPc3d1xd3fn3r17zJgxg/79+9OqVSu8vb1ZtGiRTmH9XLlycfToUdatW6dmQ6V79OgRO3fupEqVKl+0ti5duvDo0SMGDBhAjRo1qFq1Ktu3b6dMmTK8fv2akJAQ3r59+0VzfSlLS0uOHj1K3759qVWrFra2tsyaNYuuXbsCaVl2T58+zfR7xcTEUKhQIS5cuMBvv/1GamoqpUuXZvPmzZ88MfRj8fHxOu/VqlUrFi5cyOTJk/nw4QMNGzZk//79agCyYsWKbNmyhVGjRjF9+nSqVq3K0aNHM2zdFF9OT0k/ekIIIYQQQgghhPjG+Pr6Mn/+fGJiYihatCjFihUjJiaG1NRUmjdvzsGDB0lKSlIDDr169aJr166sWrWKefPm4eHhwa+//oqVlVWm89++fZthw4Zx9OhRAgIC2LZtG1qtlrJly3LgwAF+/fVXnJ2d1eLwz58/x8LCgpw5c6LVaklMTMTQ0JCIiAi1JhjAiBEj1ADYr7/+SsWKFYG00x8HDRrEvn37uHHjRqYF7UNDQ9HT08PCwgJFUZg3bx5btmzh6dOnQNoWRQMDA3r06EHZsmUZMWIEz549Y/369fj6+jJr1iy6d+/Oy5cv8fLy4sOHD2zcuBFzc/NMDw34lAkTJjBx4kR8fX2ZPHmyut7nz59z8+ZN7t+/T8+ePfH29mbixImMHDmSJUuWsGbNGjp06PBVzxLiS0kwTAghhBBCCCHEN+vdu3dA2tbGdBs3buTs2bOEhobSpUsXvLy80NPT48GDByxdupQffviBPHny8O7dO+rUqfNFz3ny5An29va0bNmSjRs3kjNnTpYsWYKvry+hoaFotVp1u2FSUhJJSUmkpqaip6fHlStXMmRSnTp1Cmtra8qXL5/p84KDg3Fycsq0b+DAgSxevJjU1FQgrebYqlWrqFu3LpBWy6xu3bpYWlpy4sQJjh49yoQJE2jZsiXjx4/XeWZqaipjxozh2rVrHD169Iu+RWZ8fX2JjY3V2SZZq1YtZsyYwS+//MLkyZPR09MDYOzYsZw6dYoLFy78x88T4lMkGCaEEEIIIYQQQvxFUlJS1CywP0tMTCQlJQUDAwMMDAwwNDRUA0B/h9TUVFJTU7NcTzqNRkNKSgrm5uZZjklOTtbZKvm14uLiSE1N1dnaFx8fT3BwMDVr1sww/tWrVzqZckL8lSQYJoQQQgghhBBCCCG+GxnPUBVCCCGEEEIIIYQQ4hslwTAhhBBCCCGEEOJvEhMT80XjYmNjM5zMKIT4e0gwTAghhBBCCCGE+IykpCQ+fPhAaGgo4eHhavuNGzeAtKL87u7uADx+/BitVkt8fDw5c+bkzZs3Gebz9vZm2bJl6nXjxo25dOnSF63l7NmznDt37qvfYdq0aezfv1+nzd/fn5YtW371XEL8XybBMCGEEEIIIYQQ37T00yL19PQwNDTE3t6e8ePHk5ycrI6ZMGECjo6OODg4ULx4cQoVKkTevHnJnTs3+fLlo3jx4tSoUYP27duzcuVKIK2ofNOmTXWCWLGxsTRs2JBLly4RHBxM/vz5yZMnz1et19zcHGtra2xsbMiVKxd2dnY6/SkpKXh4eJCUlATAqlWrsLKywsHBQf3lyJGDPXv26Ny3fv16rKysvmotn5KYmMjAgQOxtrYmR44c9OzZk/j4+CzH79y5k9KlS2NiYkKVKlUICgrS6T979izOzs6YmJhQtmxZjhw58petVYiPffpICSGEEEIIIYQQ4htQs2ZN1q9fT1JSEpcuXWLAgAHo6ekxadIkAIYOHUrfvn0xMjLCyMgIY2NjFixYQHh4OHPnzs10zmzZsjFq1CimTJlC165dgbTsq7p161K7dm0mTJhAlSpVMtzXqVMnAgIC2LhxI2PHjgUgKiqKli1b4ufnB6Rll1lZWREaGkr9+vUB8PT05MyZM+r4kiVLYmxszLBhw+jUqRPLly9Xn+Hm5qbzzNu3bxMbG0tKSgo2NjZqe1JSEhqNRqfNxMSEsLCwz37TwYMHc/jwYfz9/dUAnbGxMUuWLMkw9tKlS3Tu3Jk5c+bQoEEDJk+ejKurK48ePcLCwoKnT5/i6upKv379WLduHUuXLqVt27bcvXs3QzBQiP+WnCYphBBCCCGEEOKb5uXlRVhYGMePH1fbBgwYwMWLFwkKCmLPnj0ZsqgA7ty5Q0JCApUrV87QV61aNfr06UNSUhLh4eGcPXuWw4cPM27cOHLlykXu3LkpXbo0Go2GUqVKqffVqVOHX375BW9vb5ycnPD29gagevXqzJ07l9q1a2Nubk5YWJhOMCw0NJTo6GhGjx5N/vz56d27NwB6enps3ryZSZMmYWtrqz7nxYsXrF+/njZt2gAwcuRI4uLi8PX11XkPf39/1q1bl2H75Oe8f/+evHnzsn37dtq2bQvAsmXLGDx4MO/fv8fU1FRnfLt27dBqtezduxeAyMhIbG1tWbp0Kd27d2fIkCGcPHmS4OBgIC37zc7Ojp49e6oBSyH+KpIZJoQQQgghhBDiu5OSkoKRkREAdnZ2avbVxzQaDVFRUZn22dvbs2jRImbPng3AmzdvyJYtG6dPnwagT58+3Lt3jwULFmBpacn169c5dOgQo0aN+o/XnCNHDkxNTcmePTs2NjZotVpevXrF69evGTBgABMmTFDHfpwZFh8fz+rVq9UMtylTprBkyRIMDAzUzLCCBQuq3yW9JtqIESM4fvw4165dy7CWc+fOkZqaiouLi9rWsGFDNBoNwcHB1KhRQ2f8qVOnmDFjhnptZWVFpUqVuHz5Mt27d+fUqVM0a9ZM7Tc0NKRu3bpcvnz5P/5eQmRFgmFCCCGEEEIIIb4bWq2Wo0ePsmHDBnU7n5OTE46OjgDs2rWL7du34+/vT0REBOHh4Xh5eeHj44OdnR2tWrVi48aNuLi4UK9ePerWrcuMGTMICwsjISGBcuXKMXLkSLp3746RkRG1atWicuXKJCQk8ObNG53A2rRp09Qi+g8fPtRZZ9myZdHT00Or1WJsbKy2v3nzhvfv39OvXz927dpFz549OXjwILGxsezYsUMd9+LFC7Wg/4oVK3j37p3aFxcXx6BBgzIE5jQaDebm5up1wYIF1e/yZ0+ePCFPnjxkz55dbStcuDBAhi2WHz58IDIyEnt7e532woULq2OfPHmSaX/6AQVC/JUkGCaEEEIIIYQQ4pt36tQpTExMSElJIV++fGzevJkffvgBSKunlb6dMC4ujlq1amU6x/Tp0/H19aVdu3bo6ekxduxYNePpyZMnxMXF4eLiwurVq1m2bBlr167l2rVrVK5cmWvXrlG+fHmd+YYNG0bPnj2BtKyqdIqicPfuXSwtLXVqhgEEBwdToEABfvjhB0aPHs3GjRvRarXcv38fAwMDFi5cCMCsWbPInz8/Hz58YMaMGRlql82aNUvnNMv0535s4MCBWX7P2NhYzMzMdNpMTEzQ09MjMTExw1ggw3gzMzMiIiKynM/MzCzDXEL8FeQ0SSGEEEIIIYQQ37xq1apx/fp1li9fTkREBK9evVL7HB0defToEY8ePeK3335T21+9esWtW7fU6wEDBvDo0SNcXFwICAhg7NixXLhwgfDwcBYvXky/fv0oUaIEM2fOpGLFitSpU4eAgAAUReHIkSPUq1dPZ03Tpk3DyckJJycnbt68qbanpKSQLVu2TN8jMDCQQ4cO4e3tTcGCBXn58iUBAQEYGBgAYGlpiVarZcWKFVStWpWkpCR69OiRIRA3cuRIQkNDdX7379//4u9pbGysnmaZLjk5GUVRMgS10jPb/jxeo9GoYzOb7+N+If5KkhkmhBBCCCGEEOKbZ2ZmRunSpSldujQfPnxg6NChtGjRgsKFC3Pnzh0OHjzIjRs3uHTpEhUrVuT69ev4+vqSkpLCtm3bsLCw4O7du1y7do3Vq1dTrlw5hg0bhr+/PzExMQBqAfvChQvz+++/4+LiQq9evZg3bx76+voZ6mhNnjxZp4A+pG3jTE1NpW/fvsD/y6pKV6ZMGVJSUnTali9fjrW1tc52SkgLJrm5uTFt2jR+/vlnnb5FixaxdetWnbavOV+vQIEC/PHHHyQnJ6uBu+fPnwNQtGhRnbE2NjYYGxvz4sULnfYXL17g7OyszpdZ/5/nEuKvIMEwIYQQQgghhBDflUGDBrF8+XJGjhzJli1bePr0KU+fPqVp06Y0bNiQU6dO0aVLF5o1a0b27NkZM2YM8+fPJygoiH79+lGkSBF69+6NtbU1pUuX5uDBgxw4cACAGTNm8PTpUyAtS6tHjx4MHz6c3377DT09PQAqVapESEgIGzduZOzYsQBERUXRsmVLFi1aRKFChVi3bh1Ahm2Sjx49yvA+Tk5OLFu2TA2o/Zm+fsZNYQMHDvxszbBPqVWrFsnJyZw+fZomTZoAcOLECXLnzk25cuUyPL9GjRocO3YMLy8v9X0DAwMZMWIEALVr1+bYsWNMnDgRSAsKnj59Wu0X4q8kwTAhhBBCCCGEEN+VbNmyMWPGDDp06MCAAQNo2bIlLVu2BNIKuc+cOZMePXpgaGhIeHg406dPx9vbm71791K1alWduRITE7l79y6jR4/G0dGRlStXcvHiRSAt0yq9JlZ6IAxQT2e8d+8eDg4OnDx5EgcHB/Lnz8/vv/9OsWLFslz79evXadu2rU7bq1evaNu2rU5mWOfOnZk5c2aW83xJzTBfX1+uXr3K+vXrM9xvZ2dHu3btGDhwICtXriQ+Pp4JEyYwcuRIDAwMiIqKokWLFsyZM4caNWowZMgQ2rZtS926dalevTqTJk2iVKlSuLq6AmnBuapVqzJ58mTatWvHkiVLSE1NVYNnQvyVpGaYEEIIIYQQQojvjpubGzVq1GDw4MEoioJGo2H+/PlUqlSJvn376mRNderUiblz59KoUSO1bli6IUOGcPr0abZt24a7uzulSpUiJiaG1NRUBg8ezKlTp1i+fDlDhw5l1apV6n07d+6kXr16REdHc/DgQZo1a0ZkZCQHDhygTp06Wa67YsWKGWp9lSlTht27d+u0fSoQBl9WM+zZs2eEhIRkOcfq1aupUKECTZs2xcPDgz59+jBkyBAA4uPjCQkJ4e3btwC0atWKhQsXMnnyZGrUqEFSUhL79+9Xa51VrFiRLVu2sGHDBipXrszt27c5evQoFhYWn3wPIf4TkhkmhBBCCCGEEOKblr7l8M/SM7j8/PwYNGgQJUuW5OTJk1SqVCnD2B9//JGqVauq40aMGEHjxo3Zvn07u3fv5scff2TAgAEsXrwYX19f7t+/T0hICMePH8fBwYEcOXLQvXt3bt++Tffu3enZsye7d+8mR44czJw5kxs3brBy5UrWrl3LiRMnSE5ORk9Pj4cPH2JiYgJAhw4duHr1aoa1ZZYZlu7EiROfzDT7lDlz5nyy38rKKkPdsXT58uXj3bt3Om39+vWjX79+Wc7n5uaGm5vb1y9UiK8kwTAhhBBCCCGEEN+1li1bYmtrq9a+ykqJEiU4ePAgISEh2NrasmvXLhwdHZkyZQp58+YFYP78+cTExPDLL7+wceNGbGxsgLRtiyVLliQhIYEBAwYwd+5cGjRoAIChoSG7du1iwYIF1KpVixIlSnD58mXq1KmDgYEBEyZMAGDHjh3/9buWLVuWp0+foqenx9y5czP0a7VabGxs2LVrF3Xr1v2vnyfEv5Ge8jXHRQghhBBCCCGEEOK/EhUVhaWlZYZ2jUZDXFwc1tbWapuiKDr1xoQQ/z0JhgkhhBBCCCGEEEKI74YU0BdCCCGEEEIIIYQQ3w0JhgkhhBBCCCGE+O5otdp/eglCiH+IBMOEEEIIIYQQQnxXEhISKF68OMHBwX/bM+zs7AgLC/uv5ti1axd6enpMnz79s2M1Gg1WVlb/1fMePXrEpUuX/qs5Fi9ezP379/+rOYT4u0kwTAghhBBCCCHEd8XU1JSOHTuycuXKT44LDQ1FT08Pc3PzLH9mZmbY2dl9cp6IiAj09PQy/D7l1q1beHp60qxZMyZPnszFixczHff+/Xtevnyp0/bixQtsbGywsbHBysoKfX199drGxobXr19nOteNGzfo1KkTcXFxn1xbusePH2NoaMizZ88ACA4OxsfHByMjoy+6/3MSEhIYPHgw+fLlw9zcnJo1a3LmzBm1PzIyks6dO5M3b14sLCxo2LAht27dUvu1Wi0//fQTBQsWJHv27FStWlXnfkgLOFaoUAETExOKFi3K1KlTSU1NzXJNS5cuxd7eHlNTUxo2bMiTJ090+nfu3Enp0qUxMTGhSpUqBAUF/SXfQvy1JBgmhBBCCCGEEOKb5eXllWkgavbs2SxZsiTTvlWrVqn3W1tbExsbm+UvqyBVZhISEkhISPhsxtitW7do1KgRHTt25NChQwwbNgxXV1fOnj2bYWxAQAAzZswAIDExkalTp3LgwAEiIiKIiIhgzZo1VKhQQb2OiIhAUZRMA3s//vgj79+/J2/evBn6GjZsmOHZa9asoWHDhhQpUgSACRMmkJqaSokSJTA0NMTQ0BB9fX0MDAzU6/TflStXPvu9Vq1aRWhoKFu2bOHMmTPY2dnRokULQkNDgbQgo62tLXv27OHIkSOkpqbSokULkpKSAEhJSSExMZGNGzdy5swZChcuTKtWrfjjjz8AiI6OZujQoQwbNoyrV68yevRopk6dyrx58zJdz44dOxg8eDCTJ0/m/PnzJCcn07p1azV4dunSJTp37kzv3r25cuUKhQsXxtXVlZiYmM++q/gfU4QQQgghhBBCiO9ESEiIznVycrISGBiY6dinT58qgJI9e/Ysf6ampkqRIkXUe/LmzatYW1sr+vr6Ss6cORVra2vl7du3ysf//f7z9ccOHjyo5MqVS/n5558VrVartg8bNkwxMTFRFi1apKSmpqrta9euVfr166ckJCQoRkZGSr9+/ZQpU6ao/ZMmTVJ69OiR6Xf48OGDeh0XF6dUqVJF2b17t5KUlKTzjGvXrmW4PyUlRSlQoICydetWRVEUZdWqVUr+/PmVP/74Q2fcjz/+qMyZMyfTd/2c58+f61wnJSUpZmZmyrJlyzId//vvvyuAcuPGjUz737x5owDK3r17FUVRFI1Gk2G9P/30k1K9evVM769YsaIycOBA9TokJEQBlJMnTyqKoiht27ZVWrdurfZ/+PBBMTY2VtasWfOZNxX/a5IZJoQQQgghhBDiuzBp0iQaNGhAZGSk2nbr1i1cXFyYP39+pvd8bWZYeHg4ly9fJjU1lZs3bxIREfFFa0tISGDUqFG0bt2awYMHs3z5cvT1/99/2efMmcP8+fMZMWIEderU4eTJkxnmMDU1xdfXl7Fjx+Ll5YWFhQWLFi1i79692NjYYGZmxtixYwE4ePAg1apV4+HDh7x//57WrVvj7e2Ni4sL7dq1Y/bs2aSmpjJjxgzatm2rZmOlO3z4MBqNhjZt2hAUFISPjw/btm0jT548X/S+AK9fv8ba2pqdO3dm2l+oUCGd6/SssqwOP0hvz5UrV6b96Rlc1tbWABgbG2dYr4mJSabzR0ZGcv36dZo3b662OTg4kC9fPi5fvgzAqVOndPqtrKyoVKmS2i/+PQz/6QUIIYQQQgghhBB/p9TUVEaNGoWfnx9HjhzRKTRfsWJF9u7di7u7O7dv32b58uVky5ZN7Y+MjMTBwSHLuRMTEzO0bdmyBUir3VWwYMHPri86OhonJyc0Gg0zZ85k2LBhjBs3LtOxU6dO5cSJEzRq1Ijz589z9OhRXrx4ga+vL4mJiSxYsEAd++uvv9KrVy8iIyOxsrJi4sSJpKSkADBkyBAsLCzYuXMnWq2W58+fM2/ePLy9vZk3bx4DBgzgwoULBAYGcu3atQwBptWrV+Pu7o6xsTGFChVi5cqV3Lx5k2bNmmX4Pjt27GDixIlqm6GhIZGRkZiamqoBpS+xfft2oqOjadCggU67oig8ePCAkSNH0q1bt0y/+fPnzxkyZAj16tWjZs2amc4fGRmJv78/Hh4eGfqePn0KgL29vU574cKFCQsL48OHD0RGRmbZL/5dJDNMCCGEEEIIIcQ368OHD7Ro0YKAgAC2b99OmzZtiI6OVvsDAgLw9PTk/PnzXLlyhRYtWqgBo0KFChEaGkrv3r0ZOXIkx48fz/Dbtm0brVq1UufTarVs374dKysr+vTpw/Dhw1EU5ZNrzJEjBzNnziQkJIShQ4eSnJyc5W/MmDGcPHmSwMBATE1NMTIyolixYty+fZtOnToRHBysc0pmSkoKOXPmzPS5derU4cqVK+TPn5958+ZRtGhRrly5gpmZGXv27CEkJISUlBTevHmjc9+bN2/Yv38/PXv2BCBPnjx07tyZpKQk3NzcdDLnOnXqxLRp03Ta0jPzrKysuHDhQpbBqY+lP2/48OGULl1abZ86dSrZsmXDwcEBMzOzDBl+fn5+ZMuWjSJFivD8+XPWrl2b6eEF4eHhNGvWDHNzc0aNGpWhPzY2FgAzMzOddjMzMxITEz/bL/5dJDNMCCGEEEIIIcQ3Kz0r6vLly1hZWVGuXDmmT5/OzJkzSU1NZcKECQwePJiCBQty5swZ/P39MTRM+6+ygYEBN2/eZMSIERgaGmJgYJDlcxITE1mxYgWrV6/G0dGRmJgYtmzZwsSJE3n+/Dnm5uafXGfHjh3VP6c//1OcnZ2BtCLziYmJvH79mjZt2nD79m0g7eCArERERNC3b192797NgAEDOHjwIDt37qRatWpqJtn79+/Zu3cvuXLloly5cowYMYJp06YBsGHDBipWrEi5cuU+u87/VmpqKhMnTmT69OmMHTuWCRMm6PR7e3vTpk0bwsLCWLFiBeXKlVOL1wO0bt2a4OBg/vjjD7Zs2YKTkxMnTpygcuXK6hznzp2jc+fOFC5cmLNnz+pkDqYzNjYGUIvzp9NoNJiZmX22X/y7SGaYEEIIIYQQQohvlr29PVu2bFEDHLNnz2bp0qUcOnRIrZ/1888/A2BjY4O3t7d67/Lly5k0aRJv3rzh6dOn6Ovrc+fOHWJjYylXrhzbt28nNjaWMWPGYGBggEajYcKECQwaNAgACwsLtm3bhqIomQZYPpbVqZdZ/RwdHQHYvXs37dq1U+fRarVMnToVjUYDpNUiSw/UpLO0tCQ0NJQjR44wf/586tSpw4oVK5g+fTpTp07lwYMHrFmzht9//521a9eycOFCnWy6NWvWqFlhf6fU1FS6dOnC8uXLOXToEBMnTsyQ1WVjY4OjoyPNmjXD398fExMTli1bpvOuZcuWpWHDhqxcuRInJyed7LHdu3fTuHFjfvzxR86ePYutrW2maylQoACQtvX1Yy9evKBo0aLY2NhgbGycZb/4d5HMMCGEEEIIIYQQ341SpUqxYsUKOnTogImJCVevXsXIyCjDuP79+3Pp0iUOHDiApaUlbm5utGrViiJFivDkyRNCQkJo1KgRkBZwsrCwwMTEBB8fH6pXr64zV2BgIEWKFMl0PQsWLMDW1pZ169axbt06tX3q1Kns2LGDixcvkj17djQaDSYmJhnuX7JkCX379lWvFy1axOnTp9XgzaNHj8idO7fOPdmyZePKlStqYGngwIEAhIaG4ujoSGxsLG3atFH7+/btq271vHjxIs+ePaNLly7qfD179mT9+vUoioKiKGzcuFHtS01NZcuWLRm2Hjo6Oups58zMkiVLOHHiBL///vsXBZT09PQwMDDIssA+oFOAPzw8nG7dujFnzhz1G2SlQIEC2NnZcezYMerXrw/AgwcPCAsLo1GjRujr61OjRg2OHTumZuVFRUURGBjIiBEjPrt28b8lmWFCCCGEEEIIIb4bd+/exc/PDwsLC1JSUlixYgXv37/PMG7QoEGcO3cOCwsLunXrxo0bN/D19QVgwIABDBkyRM24ev/+PZaWlgCMHDkyw1z79u3LECBLd+/ePV6+fKnTduLECX777Td2795N9uzZURSFmjVrsnbtWp1xly5d4vXr17Rr1w5DQ0Pev3/PwoULdepibdq0iVKlSuncd/jwYSwsLDA3N9f5lS1blri4OMzNzTP0W1hYAGmF8zt27Khep7elpKSg1WpJTU0lJSVF/XXt2pVZs2bptKWkpBAcHExUVBS1a9fm0qVLmX6bzZs306RJE1JTU3n06JH6Sy9Iv3DhQpYuXUpQUBCXL1/G09OTly9f0q1bN/X+OXPmcOXKFYKCghg2bBinT59Ws9oCAgLQarU0b95cZ/5Hjx6RlJTE48ePqVGjBo8fPwbSDh1YuHAhO3bsIDAwkJ49e9KyZUt1u+iQIUPYtm0by5cv58aNG3Tv3p1SpUrh6uqa6fuJf5AihBBCCCGEEEJ8w6Kjo5XNmzcrTZs2VUxMTJQBAwYoHz58UO7fv6+4uroqZmZmSrdu3ZSdO3cqb968Ue87dOiQ4uDgoJQrV055+vSpoiiKMnr0aKV06dJKQkKCOq5t27bKihUrdJ5ZpEgR5cWLF8rvv/+uAMqpU6fUvg8fPiiAEh4ertSqVUvZuXOn2hcUFKTkzZtXOX/+vM58ly9fVszNzZVFixbptIeHhyuKoihLly5VChcurNy+fVtRFEVJSkpSNm7cqFhZWSlubm6Ki4uLEhwcrCQnJ2f5nZ4+fapkz549y/6YmBjF3NxcOXfuXJZj/uzHH39U5syZk2nfq1evlFy5cil79+7NtN/e3l4BMvycnZ0VRVGUgIAApXz58oqZmZmSO3duxdXVVQkMDFTvv3jxolKtWjXF3NxcyZkzp1KvXj3l2LFjav+kSZMynR9Qbt26pQQFBSmWlpZKUFCQoiiKkpqaqowbN06xsbFRzM3NlR9//FH58OGDzpp9fX2V/PnzK6ampkqLFi2UFy9efPG3Ev87eorymWMthBBCCCGEEEKI/6OWLFmCj48PdnZ2eHh40KtXrwx1oa5fv86KFSvw9/cnMjKSa9eucenSJQYOHEi/fv2YPHky8fHx+Pj4cPnyZQ4cOICFhQUWFhY8efKE+vXrc+rUKSpWrKjOaWdnx8GDB2nevDnW1tZcu3ZN55nVq1dXC73fuHEDKysrjh49SsuWLbGwsCB//vw6JzAaGRmRM2dO3r59y9y5c+nTpw8AJ0+eVGuEbdu2jUKFCnH9+nWmT5/OmTNn2LFjB/Xq1WPLli0MGDAALy8vJk2aRPbs2TN8q4+3SWZm9erVzJkzh3v37n3x93d3d8fJyYlhw4Z98T1C/N2kZpgQQgghhBBCiG/Wjz/+SK1atahQoUKWYypWrMjSpUtZvHgxoaGhFC1alNKlS9OsWTMKFy5MSkoKzs7OODg4cPHiRR4+fEjp0qUByJEjB3369NEJhKWzsLDA3t6ehQsXZui7fPky0dHRmJubo6+fVsGoZs2aTJgwgUqVKmFtbU3OnDnVX/pJlidOnGDr1q0APH/+nH79+uHj40PPnj0xNDREo9HQv39/atasyfLly8mVKxcAXbp0oXr16vzyyy//8bdcvXr1/6RwvhB/N8kME0IIIYQQQgghPuOPP/4gb968X31fYmJihtMchRD/LAmGCSGEEEIIIYQQQojvhpwmKYQQQgghhBBCCCG+GxIME0IIIYQQQgghhBDfDQmGCSGEEEIIIYQQf6Po6Gi+xQpFCQkJpKam/tPLEOKrSTBMCCGEEEIIIcR3YdasWbx69eq/muP69esEBAR8ckyzZs04fPiwel2yZElevnz5RfPv3r2bu3fv/ldr/FqKohAXF0d4eDj3799XA1xRUVE8e/YMgOLFi/Po0SNSU1N59OgRAJMnT2bAgAH/07UK8VeQYJgQQgghhBBCiG/e3r17mTFjBhYWFv/VPBEREfTo0YMPHz78R/fq6+tjY2ODjY0NlpaW1K9fX2dMZGQkvXr1Uq/Hjh1L7ty5cXBwUH+mpqYEBwcDEBoaip6envqzsrKiUaNGBAYGqnO8fv2a8uXLU6ZMGUqWLIm9vT0FChTA2tqafPnyUaBAAcqXL0+TJk3w9PQkPDwcgHXr1tG7d2+d9fn6+tKnTx8AgoKCcHZ2/urvEBkZibu7Ozly5MDGxoYRI0Z8MsNs6dKl2NvbY2pqSsOGDXny5IlO/86dOyldujQmJiZUqVKFoKCgr16T+L7IaZJCCCGEEEIIIb5pqampVK5cmZs3b+q0a7Va9PX10dPT02l/+fIlefPmxcfHhyVLlmBkZPRFz9mzZw/jxo0jKCgIU1NTsmXLBsD79++xsrJix44dtG/fnsjISABOnz7NxIkTOX36NI0aNeLx48dA2rbKHDlyUKxYMapXr465uTmjR49Wn1O5cmVWrVqFk5MToaGh2Nvbs3XrVpydnXnz5g1Tp07l999/5969e9jY2ABpAbFs2bJhZGSEkZERxsbGWFhYEB4ejrm5eabvEx8fT4kSJdi9ezddu3Zl48aNtGzZknPnzmFnZ0euXLn4/fffKVeu3Bd9n3Surq68efMGX19fXr58iaenJ+PHj2fEiBEZxu7YsYNu3bqxcuVKypQpg4+PDx8+fODmzZvo6+tz6dIl6taty5w5c2jQoAGTJ0/m/PnzPHr06L8OfIpvl+E/vQAhhBBCCCGEEOLvNGHCBLRaLTExMZiamqrttWvXxsfHBzc3tyzvHTNmDBMnTiQ4OJg5c+awadMmtW///v2cPHmS+fPnq22NGzemWbNm+Pj40KxZMwBsbW0JDAzExMQky+fs3LkTDw8POnToQPPmzQEwNDRkypQprF27Vue56UGzjxUoUIDixYtTvHhxNmzYQO7cubl06RKtWrXCy8sr02cmJibi7e2NoWHG0MCMGTPIly8fp0+fpkSJEgDY2Niwe/duSpcuzdatW9FoNAwZMgQDAwP1vuXLl1OkSJEs3/PmzZscOnSIa9euUbFiRQCuXr3K4sWLMw2GzZgxg969e9OtWzcAVq5cSenSpTlz5gwNGjRgzpw5uLq64uPjA8Dq1auxtbXF39+f7t27Z7kO8X2TYJgQQgghhBBCiG/W/v37+e233/j99991AmFf6tKlSxQsWJCkpCQiIyM5c+aM2hcfH49Go2H79u0AnD9/Hjs7u/9onVZWVhgZGWFhYYGNjQ2JiYm8f/+e169fM2PGDDw9PdWxlStX/uRcWq0WQM1o+/NWzHRbt26lTp06GBsbZ+gzMzPDzs6OlJQUNBoN7969o06dOhgYGFCnTh2eP39O/fr1+fHHH4G0gGOnTp2wtLSkY8eOJCcns3v37gzznjp1irx586qBMICGDRsya9YsXr9+Tb58+dT2yMhIrl+/zvTp09U2BwcH8uXLx+XLl2nQoAGnTp1ixowZOt+xUqVKXL58WYJhIksSDBNCCCGEEEII8c1ycHBg8+bNrF69msWLF+v0JSQkEBQUpJM5VaxYMW7cuKFe16hRgyNHjnD+/Hl8fHx0anFt3LiRPXv24O/vn+G5/fr1U7fpvXv3Tm2Pjo6mYMGCQFpmVtmyZdW+N2/ecP78efbu3UtAQAATJkzg5MmTalZauswywz6eo1+/fpQqVYoGDRoA4OHhodbkcnJyYuvWrTg6OtK/f39+/PFHUlJSsLOz482bNxw8eJC3b99iaWnJ3bt3Wbt2Lbt37+b69evUqlWLxo0bky9fPjp27EiTJk3Ubzdy5Eh69uyJlZUVRYsWzXJ9T548wd7eXqetcOHCAISFhekEw54+fQqQ6fiwsDA+fPhAZGRklv1CZEUK6AshhBBCCCGE+GYVL14cV1dXEhMTGTZsGLGxseqvRo0a+Pn56bR9HAgDdOqJhYSEULlyZfU3fvz4LJ87b948zp8/z/nz58mVKxeQdmqjpaUlYWFhhIWFsWPHDnV8fHw8jx8/JjQ0lAYNGnDv3j1CQkKoWLEid+/e5fLly/Ts2ZPu3btjZGRE3rx5dZ7XqFEjTExMyJs3L5aWlpw7d07NDPP29laL7z98+DDT9cbGxmJvb8+KFSuwtrYmPDycsmXLEh4ezoIFC+jcuTMLFy5Ug2rnzp1Ti/i/fPmS2NhYihUrBsDMmTOZOXNmls8xMzPTaUu/TkxMzDD24/6PxycmJn62X4isSGaYEEIIIYQQQgiRifj4eDVTqXDhwsyfP19nu2JISEiGkw3T9e3bVw3SpGeGpaSkqEX1/8zMzIynT5+qAazExERSUlLUWmHZs2fH0NCQlJQU9u3bp5NBBbB27VqKFy/OL7/8wsWLF3Wes2LFCvXPjo6OQNrJlklJSdy6dYvSpUtjZmbGw4cPMTMzo0+fPtStW5eQkBBMTExwdHRk1qxZFChQgL59+6pzWVpaEhgYSHBwMLVq1cq09tifGRsbk5SUpNOm0WjUb/DnsUCm483MzD7bL0RWJBgmhBBCCCGEEEJkIioqihw5clC/fn0ePXoEwJQpUzKMO3nyZIYtmGvWrNEpoA9pwbXk5GR1a2F4eLg6Pjw8nOrVq2eYe/Xq1RQoUECnSD2k1fv6eHzhwoWpWrUq/v7+lClThrFjx+Lr60tqaiqbNm3ixo0b3LhxQ80M8/b2Jjk5GW9vby5evIhGo+Hy5cskJiayfft2hgwZgqOjI4qi8ObNG3r06KFmyS1atIiOHTvSvn175s+fz4MHD3SCZJ9SoEABDh48qNP24sULION2yAIFCqj96Vln6dcdO3bExsYGY2Nj9f6P+52dnb9oPeL7pKcoivJPL0IIIYQQQgghhPg7NGrUiDNnzqjb+/T1/1+1IK1Wi76+vs5WSIAWLVqwd+9enJycmDFjBs2bNycgIIBffvmFQ4cOqTW/1q9fz9ixYwkICFALwtvY2BAdHY2pqamanfX+/XusrKyYN28efn5+nDx5EoDTp08zceJETp8+neX6raysuHfvnhpQ+7PQ0FDs7e05d+4ctWvXBtKyxH766Sdu3LhB2bJl6d69OyVKlKB69er4+vqSJ08enjx5woULF3B3d0dPTw97e3sCAgIwMDDgp59+olu3bsTFxVG+fHk2b95MtWrViIyMpHz58pw9e1atMVakSBFsbGx48ODBFx1QcObMGerXr8+DBw/UUyrHjBnDkSNHCAoKyjDe3t6erl27Mm3aNAAePHhAqVKluHnzJuXKlaNBgwbkz59fzaCLiorC1taW7du306pVq8+uR3yfJDNMCCGEEEIIIcQ368SJE1n21a5dGx8fH9zc3DL0RUZGqjW7AFq3bk1MTAw1a9Zk3LhxHD16lBcvXnD69GmdrKWIiAgA7t27h4ODA3v37qVhw4ZYWFiwfft2nbF/tm/fPgYMGKDTFh0dTZUqVXQyw4YNG0b//v2znMfT05MFCxYwZMgQjhw5wtq1a4G0mmWXLl1izZo1nD9/nhIlSjBlyhRatGgBpGW4fXyyZHh4OEWKFGHEiBEMGTIEX19ffvrpJ/XEzFevXmFoaIiZmRkJCQlqMGz06NEAOqc8pqtbty7Ozs706NGDX3/9ldDQUBYtWsT69euBtMMB3N3d2bhxI8WKFWPIkCGMHj0aJycn7O3tGTx4MC1btqRcuXIADBkyhLZt21K3bl2qV6/OpEmTKFWqFK6urll+HyGkgL4QQgghhBBCCPEnmzdvpmLFimpGlkajwdTUFFtbW3x8fNi7dy9NmjQhOjqaP2+4mjdvHu3bt0er1eLn50fbtm1JSkriwIED1KlTJ8tntmrVitDQUJ1fjhw5uHr1qk7bpwJhkJb9Nnv2bI4ePcqBAwcAuHXrFvXq1cPf35+zZ8+qNcfMzMw4duwYISEhlCtXjl27dqk1vIoVK8bJkydp3Lgxbdq04dy5cxQpUoSUlBTu3r1Ls2bN6NevH4UKFcLV1ZXXr18DaQGtrE681NPTY/fu3ZiZmVG7dm2GDh3K3Llzad++PZCW2RUSEkJUVBQA/fv3Z8iQIfTt25cGDRpQpEgR/Pz8dL7ZwoULmTx5MjVq1CApKYn9+/dn2FYqxMdkm6QQQgghhBBCiO9SVplhqamp2NnZMXv2bO7du8exY8e4desWlSpV4qeffsLNzY179+6xatUqAgICePPmDSVKlGDbtm08f/6czp07c+HCBcqUKUNMTAy1atWif//+jBs3jgcPHmBiYkK2bNlYvXo1O3fu5PDhw9SoUUMNJn3s+fPnmdYMA7h//75OJldm4uLiaNGiBUFBQQwZMoSxY8eq2zfNzc0JDw/H3Nyc1NRUFi1axIwZM0hOTubWrVts2bIFf39/YmJi+PXXX0lNTWXmzJm0b9+eMWPG0KdPH2bPnk10dDQtW7YkJCSES5cuUbx48f/ib0WIv58Ew4QQQgghhBBCfJc+tU3y6tWrVKlShcOHDwNQq1YtLCwsMp3njz/+IDg4mMaNG1O6dGkWLVqkFs+HtC2XI0aMwNramhkzZrB161a6deuGkZERy5Yto1u3bn/PC/7/Tpw4gZOTE9bW1jrtHwfD0iUmJnL79m2KFy/OmDFjcHNzo169ejq11rZs2QJAly5d1LaUlBRWr17Nzz//nKEGmxD/NhIME0IIIYQQQggh/iJRUVFYWlpmaH///j0mJiaYmZn9A6sSQnxMgmFCCCGEEEIIIYQQ4rshBfSFEEIIIYQQQgghxHdDgmFCCCGEEEIIIcTfTKvV/tNL+CJJSUn/9BKyFBsb+08vQXwjJBgmhBBCCCGEEOK7M3nyZOLi4r5obGxsLNeuXWPLli1MnDiR5ORknf49e/ZQv379LO8PCgqidOnSxMfH/zdL/sstWLCA/v3767QNGTKE4cOH/63P1Wq1REdHExYWxpMnT9T2kJAQkpKSCAsLo2DBggC8efOGyMhIAJo0acKOHTv+1rWJ74PhP70AIYQQQgghhBDi73L69GlCQ0MztO/atYuHDx/SqFGjDH0VKlTg9u3brF+/nvv37xMWFqbT36FDB8qWLfvFa6hUqRI2Njbs27ePTp06ZTlu3bp19OrVCxMTkyzHJCcn06VLF9atWwfA2LFjmTZt2hevZcaMGYwaNSrTPkVR2L17N0uWLPnkGrt3765e582blyZNmjBnzhxsbW0B2LdvH2PGjCElJYWUlBSSkpLUn5GREQYGBpiampI9e3bs7e3ZvXs3AGPGjKFatWq4u7ur83t7e+Ps7MyIESO4efMmzs7OX/yu6W7fvk3fvn25evUq+fLlY9KkSVme4JmYmMjw4cPZtGkTycnJdOjQgd9++w0zMzNOnz5NgwYNMr1vw4YNf/upoOKvI8EwIYQQQgghhBDfLI1GQ2xsLKNGjWLmzJlqe69evYDMt94lJSWRM2dOypQpQ8OGDdm3bx81a9akd+/eFC5cGBMTEyZOnIhGo9GZE8DOzo5nz55lupZLly7RuXPnDO0PHz6kePHiALRp0wZ/f/8s32fBggUEBwer1xMnTmTs2LE6Y16+fEnx4sWJjIzE2NgYgNTUVPT19cmWLVuWc1+4cIFXr16xdu1atmzZotPn4eGBq6srAAYGBty7d4/U1FQePnzI4MGD6dSpE2fOnAHAxcWFKlWqkC1bNoyMjDA2NubmzZt4e3sTGBiY5fPHjRuHi4sLbm5uABw5coRbt26xefNmzp8/j6mpKUWLFs3y/sxER0fTpEkTXFxc8PX1JSAgAC8vL0qUKEH16tUzjB88eDCHDx/G39+flJQUPDw8MDY2ZsmSJVSrVo2HDx/qjD99+jRDhgyhRYsWX7Uu8c+SYJgQQgghhBBCiG9Ws2bNgLRAy6eCTAC5c+fW2YbXsmVLAB48eECJEiUoWbLkZ5/3cRZaYmIir169wt7eXm0LDw9Ho9FgZ2eX6f179uzB3Nw8y/nTM8PSGRoaMm7cOPLkycPgwYMB1ACYsbGxmmXm6elJ2bJlGTFiRJZzr1mzhjp16uDk5KTTvnPnTp4+farTlh68K1myJBqNBjc3N6Kjo3n16lWGACHAu3fvCA0NxcvLK0OfpaUlCxcupFKlSly+fFldc61atdi+fTsmJiZs27aN5ORk9e8z/b5t27Zl+T6Qlsmm1WpZuXIlRkZGlC9fnv3797N06dIMwbD379+zcuVKtm/frmaATZgwgcGDBzNv3jxMTU3V907Xo0cPBgwYQK5cuT65DvHvIsEwIYQQQgghhBDfPD8/PzQazSfH2NjYqH+eMGEC7969A+DKlSs8e/aM27dvA9CqVavPPk+j0dC2bVsADh06pLYfOHCA0aNHs3XrVho2bJjhvq/NDANwc3Ojbt26VKlShdq1a2e4Z9++fezcuZPRo0cDcPbsWVxcXNBqtSiKwqpVq5g9ezZbtmwhMDAwwxbQO3fuqAG2zKSkpKhZZ1ZWVpnWTwsNDeX27duZ9pmamnL16lX1e0VFRZGQkICDgwOQlsW3adMm3N3dqVatGgkJCfTt2xc/Pz8SExMpWbIkgwcPxsfHJ8Pcp06domHDhhgZGaltDRs2ZOfOnRnGnjt3jtTUVFxcXHTGajQagoODqVGjhs74EydOcP36dfbs2ZPltxH/TlJAXwghhBBCCCHEN693796f7A8PD9fZblisWDEcHBywt7fn4cOH5M2bFwcHBxwcHLC2tv7kXJGRkbRs2ZLY2Fg2b96s09e5c2cWLFhAmzZtWL58eYZ7jx49qj4ns9+MGTMy3OPs7Mz48eNxd3cnOjpap+/hw4d4enqydOlSNbhUt25dNBoNc+bMwdvbG41Gw+vXr2nWrBn79u1T65GlS0pKyrKO2Z07d5g0aRKenp6Ymppia2tLt27dcHd3p3nz5gwbNgwvLy9atmyJtbU1Xl5eWFlZsWfPHlq3bo1Go8HGxoYqVapw9epVunTpQo0aNbCysqJu3bqsWrWKhIQEkpKSKFasGF5eXtSvXx9bW1vc3d0xNDTEwcGBQoUKZbq+J0+e6GTmARQuXDhDHbj0sXny5CF79uw6Y4FMx8+ZM4du3bpJVtj/QZIZJoQQQgghhBDim2dqaoqvr2+W/YmJiVhYWKjXHh4eAPj6+qLVanF2dsbd3R0rKysA9u/fn+k8t2/fpk2bNpQtW5aePXvSpUsXDh8+rPaPGzeO9+/fc+DAAVq2bEl4eDgTJkwAoGPHjjRu3JhZs2bRoUOHTOtjnTt3Tj1d8WPDhg1j27Zt+Pn58cMPP6jtI0eOpHv37p8t7u7p6YmiKMTExNC4cWP09PTw9PQE0oJhH2eGabVaTExMSE1NBWD69Ok6WVmVKlUiLi5O7c/M6dOncXR0xMXFhapVq3Lo0CEGDhzI+PHjadOmDZs3b2batGnMmTOHli1bUqZMGY4cOQLAtWvXKF++PJBWvyy9PTOxsbGYmZnptJmZmZGYmPhFY01MTNDT08sw/vHjxxw9ejRDlp74v0GCYUIIIYQQQgghvmmTJk1Stw+OHTuWuXPnqn0dO3bkwoULPH78GIDnz5+r2UAajYb58+fToEEDNmzYwMaNGzly5Ih6amJmnj59Svv27ZkxYwYajYahQ4dy+PBhmjVrxsuXL1mxYgUXL16kfPnyHD9+XN2KCWlBmilTpuDr68vatWs/+U7m5ua4u7sTFxentu3cuZPcuXPz6tUrAOLi4vD19SV79uw6BwVkVpMsPWsMYOvWrfTr14/27dtjbm5OYmKiTmaYgYEBwcHBPHv2jN69exMUFISh4f8LL9y4cQOAiIgIdd5Xr17x/PlzoqKiAKhatSqHDx8mKiqKkSNHsnz5cu7cuUNERASlSpUiNDQUKysr9aTMUqVKMXz4cDQaDYcOHaJevXqf/D7pjI2NSUpK0mnTaDQZgl5ZjU1OTkZRlAzjly9fTtWqVdWgnPi/RYJhQgghhBBCCCG+aceOHeP8+fN4eXkRHh7Ohw8fqF27NlZWVty8eZMjR45w/Phxhg8fzvv379Vg2LRp02jatCkJCQl07NiRx48fU6tWLY4ePZrls1q1aqXWFDMzM2P27Nl4enpy6dIlunfvjqenpxpAqVKlinpfQkICw4YNIyYmhtTUVAICAhg+fDj3798nJiaGAgUK8OLFC6ysrHBxccHIyIhnz55l2AL4sY9roH3s7du3WfYBuLq6cuTIESZPnqxmUf25Zlj6ts1t27ZRs2ZN2rdvj5ubG2/fvsXf358bN25w7do1AGJiYhg0aBARERGMGjUKNzc3wsLCuHbtGidPniQ0NJQtW7YwZMgQEhMT0Wg0OnXLAgMDKViwIA4ODsyZM4c9e/YwadKkLNf/sfTv9rEXL15kmnVXoEAB/vjjD5KTk9VTN58/fw6QYby/vz/e3t5ftAbx7yM1w4QQQgghhBBCfNOSkpJo3LixGsTKli0bJ06c0Cmq7u3tjYGBgXp96tQpFi1axJgxY9S2OXPm0KlTJ7Ra7Rc/293dnXbt2lGhQgUSExP59ddfM4xJSUmhZs2axMbGsmbNGiIiIhg2bBjDhw9HT0+PnTt3UrNmTXWLZkJCAhYWFtjZ2aEoSoZfevAnISEh0/4/B8KePXtGUFAQAJcvX6ZTp044Ojpy7949WrRoQUJCAqamppm+X7Vq1XB3d2f48OEkJiaSkJDAhQsXKFeuHLNmzaJs2bJ4eHjg5OREhQoVuH79Os+fP6dq1aoMGDCAEydOMH36dLp27cqRI0fIkSMHERERhIeHs2vXLnLlykW+fPkAGDp0KOPHj6dZs2YUK1bsi75/7dq1OXnypM7f2YkTJ2jUqFGGsbVq1SI5OZnTp0/rjM2dOzflypVT24KDg3n69Clt2rT5ojWIfx8JhgkhhBBCCCGE+KYZGBhw/PhxnVMC4+Li1K1vhoaG6Onp6WR8DR8+nNmzZ2cozD59+nRKlixJ1apVqVmz5mefffjwYc6cOUPu3Ll58uQJfn5+JCcn64wxNDRk3bp1rF+/nrCwMFxcXChXrhw//fQTUVFRjB07Vico9/79eywtLf+jb5FOURRevXrFjh07KFWqFOfPn6dGjRrUr1+fHDlycPv2bQICAqhSpQrx8fFZBsMApk6dSnh4OPPnz6dw4cJs3LiRfv36UaVKFXLmzElycjKjRo3CwMCAtWvXMnr0aOrUqcOlS5c4cuQIlStXBtIOMciePTv9+vXj0KFDuLu74+vrqwYp3759C6T9fSqKAkBqairNmjVj165dma6tV69eREZG0r9/f27fvs2UKVO4desWAwcOBGDXrl00a9aM1NRU7OzsaNeuHQMHDuT8+fMcPXqUCRMmMHLkSJ1A6enTp8mTJw8lS5b8r/4OxD9HgmFCCCGEEEIIIb5ZKSkpPHnyRM0MMzMzo06dOmqGUrly5VAUhV9//ZUmTZoQGxvLjh07mDNnzidPoHR1daV169YAamAm3cuXL1mwYAEVKlTA09OT3r178/DhQ/z8/FiwYAFFihRh6NChnDp1Sq35VaZMGZYsWYKTkxOlSpVi06ZNxMXF4ebmRtOmTXVqZL1+/Zo8efL8x9/kypUrFC9enHXr1tGzZ08ePnzIoEGD6NixI0+fPmXlypU6gZ7o6GidExb/rFChQvj4+DBjxgzCw8NRFIV9+/bh6OhI9uzZ2bVrlxpMKl26NEeOHGHSpEm4uLhw6dIl9fs1bdqU69ev8/79e1xdXdHX10dfPy1ssWPHDgYOHMjy5cu5cOECvXv3JikpieTkZEJCQtQ6aX9WoEAB9u3bx/nz53F2dmb37t0cPHiQIkWKAGm1zEJCQtQA5erVq6lQoQJNmzbFw8ODPn36MGTIEJ05g4KCqFix4n/49cW/giKEEEIIIYQQQnyjDh48qPz000+KoihK165dlcePHyujRo1Spk2bptStW1dZvXq10r9/f0VRFMXHx0fZsWOH4uHhoTOHp6ensnTp0gxzX7hwQbl165bSo0cPpXXr1uoz9PX1lVq1aimrV69W4uPjde7RarXKvn37lDZt2iimpqaKvb29Eh8fr3Tp0kXJkyePsmzZMkVRFCU4OFhxdnZWWrVqpbx8+VJ5/fq1Eh0drWzYsEHJmTOnkpKSkuU7v3jxQgGUhISETPvDwsKU9evXKxqNJtP+0NBQJSwsTImOjlYOHDigmJiYZDn2z4KDg5WiRYsqhQoVUrZs2aK2X716VXF2dlavo6KilL59+yomJiZKjRo1lLt37yqjRo1S7O3tlRYtWii3bt1SfH19lR9++EEZMWKEYmpqqvj7+yuKoii3bt1SChYsqDg7OyvJyclftC4hPiYF9IUQQgghhBBCfLMOHTpEu3btqFixIsnJyVy/fp3w8HCmTZtGr1698Pb2Zvr06QCULVuWQYMGMXPmzC+ae8iQIdy/fx8bGxtWrlwJwJQpU5g9ezYFChTI9B59fX1atmxJy5YtSUhI4P3795iamrJgwQLMzMwwNzfn8ePH1KxZEx8fHyZMmMDs2bMZN24cAPnz52fFihU62/a+VoECBfDw8Miyf/To0WzZsgUAa2trFi9enKGAflbKly/P/Pnzad68uU5Ntj/LkSMHixcvZuLEiURGRhIREYGBgQH79++nTJkyADg6OtKvXz8mTJjAxYsXcXJyUtuvXbvGiRMndE6xFOJL6SnKn/I5hRBCCCGEEEKIb0Rqair6+vooioKenh5arVYnkJSUlPTJoM0/5Y8//iBv3rz/2PNTU1NJSUn5V34bIf5bEgwTQgghhBBCCCGEEN8NKaAvhBBCCCGEEEIIIb4bEgwTQgghhBBCCCH+Zlqt9p9ewhdJSkr6p5eQpdjY2H96CeIbIcEwIYQQQgghhBAiExqN5i8p0B4UFETp0qWJj4//C1b111mwYAH9+/fXaRsyZAjDhw//W5+r1WqJjo4mLCyMJ0+eqO0hISEkJSURFhZGwYIFAXjz5g2RkZEANGnShB07dvytaxPfBwmGCSGEEEIIIYT4pi1YsAA9Pb0v+m3cuDHLefbs2YOJiQm2trY6P1NT00/eV6lSJWxsbNi3b98n17lu3ToMDQ0xNzfP8mdsbIyXl5d6z9ixY7/43fT09D55UqaiKOzevZvatWt/co0fz2dra0u3bt0IDw9Xx+zbt49y5cpRunRpSpQoQZEiRciXLx/W1tbky5ePIkWK4OzsTMuWLRk6dKh635gxY5g/f77O87y9vVm8eDHJycncvHkTZ2fnT37Dr3H79m3q1q2LqakpRYsWxc/PL8uxiYmJDBw4EGtra3LkyEHPnj11gpvbtm2jfPnymJmZUbhwYSZPnoyUaP/3kjNIhRBCCCGEEEJ80wYNGpQhAyorH580mZlmzZqxZ88enbaPg1N2dnY8e/Ys03svXbpE586dM7Q/fPiQ4sWLA9CmTRv8/f2zfP6CBQsIDg5WrydOnMjYsWN1xrx8+ZLixYsTGRmJsbEx8P9O1cyWLVuWc1+4cIFXr16xdu1atmzZotPn4eGBq6srkPaN7t27R2pqKg8fPmTw4MF06tSJM2fOAODi4kKVKlXIli0bRkZGGBsbc/PmTby9vQkMDMzy+ePGjcPFxQU3NzcAjhw5wq1bt9i8eTPnz59Xg1Z/hejoaJo0aYKLiwu+vr4EBATg5eVFiRIlqF69eobxgwcP5vDhw/j7+5OSkoKHhwfGxsYsWbIEgHv37jFmzBjKlCnDpUuX6NevH7lz56ZPnz5/yXrFX0uCYUIIIYQQQgghvml6enpfvd3xxx9/5N69e2i1WipXroyzszPNmzf/7H2hoaHqnxMTE3n16hX29vZqW3h4OBqNBjs7u0zv37NnD+bm5lnOn5ycTJcuXdRrQ0NDxo0bR548eRg8eDCAGgAzNjbGxMQEAE9PT8qWLcuIESOynHvNmjXUqVMHJycnnfadO3fy9OlTnbb04F3JkiXRaDS4ubkRHR3Nq1evMs0+e/fuHaGhoTqBw3SWlpYsXLiQSpUqcfnyZXXNtWrVYvv27ZiYmLBt2zaSk5Np1qyZzn3btm3L8n0+Zd26dWi1WlauXImRkRHly5dn//79LF26NEMw7P3796xcuZLt27fToEEDACZMmMDgwYOZN28epqamTJgwQR1fvnx5AgICOHbsmATD/qUkGCaEEEIIIYQQ4puVnJz81cXrDQwMGDx4MO/fv6d58+bMnTsXKysrQkNDOXz4MLa2tjrjo6KiaNy4sU6bRqOhbdu2ABw6dEhtP3DgAKNHj2br1q00bNgww7O/NjMMwM3Njbp161KlSpVMtzju27ePnTt3Mnr0aADOnj2Li4sLWq0WRVFYtWoVs2fPZsuWLQQGBlK2bFmd++/cuaMG2DKTkpKiZp1ZWVlRv379DGNCQ0O5fft2pn2mpqZcvXpV/V5RUVEkJCTg4OAAQK9evdi0aRPu7u5Uq1aNhIQE+vbti5+fH4mJiZQsWZLBgwfj4+OT5Rr/7NSpUzRs2BAjIyO1rWHDhuzcuTPD2HPnzpGamoqLi4vOWI1GQ3BwMDVq1Mhwj1arxdra+ovXI/63JBgmhBBCCCGEEOKb1aVLl0wDHJ/i6enJunXr0Gg06OnpqQEcBwcHXr58mSHI8ebNG8zMzNTryMhI3NzcSExMJCAgQGds586dMTU1pU2bNsyZM4fevXvr9B89elQNAmXmw4cPGTLUnJ2dGT9+PO7u7ty8eVOn7+HDh3h6erJ06VJ13rp166LRaFiwYAGPHj3C19eX0aNH06xZM/bt28fVq1d1MriSkpLUbK0/u3PnDpMmTcLT0xNTU1NMTU3p1q0biqLw7t07ypYtS0REBIGBgezfvx8vLy/27NnDunXrWLNmDdu3b6dQoUJUqVKFq1evMn/+fG7cuMG1a9eoW7cuHh4enDp1iqSkJIoVK4aXlxchISHY2tri7u6OVqvFwcGBQoUKZfnNMvPkyRN122e6woULExYWlunYPHnykD17dp2xQIbxcXFxbN26lStXrjBr1qyvWpP435FgmBBCCCGEEEKIb9bHWVb16tWjY8eO9OvX76vmCAsL4+XLl/Tp04eXL1+ip6eXYUyePHm4efMmt2/fpk2bNpQtW5aePXvSpUsXDh8+rI4bN24c79+/58CBA7Rs2ZLw8HB1i13Hjh1p3Lgxs2bNokOHDpnWxzp37px6uuLHhg0bxrZt2/Dz8+OHH35Q20eOHEn37t3p1q3bJ9/R09MTRVGIiYmhcePG6Onp4enpCaQFwz7ODNNqtZiYmJCamgrA9OnTdbKyKlWqRFxcnNqfmdOnT+Po6IiLiwtVq1bl0KFDDBw4kPHjx9OmTRs2b97MtGnTmDNnDi1btqRMmTIcOXIEgGvXrlG+fHkgLYsvvf1rxMbG6gQwAczMzEhMTPyisSYmJujp6emMNzExITExkRw5crB06VIqVKjw1esS/xsSDBNCCCGEEEII8c27fv06N2/eJCAggJSUlCzHpdcW27t3L8eOHUOr1eLk5MTgwYOJjo7mypUr2NnZceXKFRRFoXr16oSFhanbE58+fUr79u2ZMWMGGo2GoUOHcvjwYZo1a8bLly9ZsWIFFy9epHz58hw/fpx3796pzzYzM2PKlCn4+vqydu3aT76Pubk57u7uxMXFqW07d+4kd+7cvHr1CkjLUvL19SV79uzExsbq3PtnH2ejbd26lX79+tG+fXvMzc1JTEzUyQwzMDAgODiYZ8+e0bt3b4KCgnRqst24cQOAiIgIdd5Xr17x/PlzoqKiAKhatSqHDx8mKiqKkSNHsnz5cu7cuUNERASlSpUiNDQUKysrpk2bBkCpUqUYPnw4Go2GQ4cOUa9evU9+n3STJ09m8uTJ6vX48eMZP348xsbGJCUl6YzVaDQZgl5ApmOTk5NRFEVnfHBwMFFRUQQGBjJw4EBu377N9OnTv2id4n9LgmFCCCGEEEIIIb55V65cITIyEisrqyzH6Ovrk5iYiKGhIevWraNUqVIYGBjw5s0b9PX1dQJUu3btwtzcPEOx9VatWtGqVSsgLbg1e/ZsPD09uXTpEt27d8fT01PNaqpSpYp6X0JCAsOGDSMmJobU1FQCAgIYPnw49+/fJyYmhgIFCvDixQusrKxwcXHByMiIZ8+e6RTn/zMbG5tM29++fZtlH4CrqytHjhxh8uTJarbUn2uGOTg44ODgwLZt26hZsybt27fHzc2Nt2/f4u/vr251BIiJiWHQoEFEREQwatQo3NzcCAsL49q1a5w8eZLQ0FC2bNnCkCFDSExMRKPR6NQtCwwMpGDBgjg4ODBnzhz27NnDpEmTslz/x/r27UvHjh0zfJP07/mxFy9eZJqNV6BAAf744w+Sk5PV0zifP38OoDM+PfBXrVo1zMzM+Omnnxg3bhympqZftFbxv6P/Ty9ACCGEEEIIIYT4u3l7e6MoSpa/M2fOYGtrq2Y47d69m4kTJwJpQbI/++OPPzIU0s+Mu7s77dq1o0KFCiQmJvLrr79mGJOSkkLNmjWJjY1lzZo1REREMGzYMIYPH46enh47d+6kZs2aaiAvISEBCwsL7OzsMn2X9CBPQkJCpv1/DoQ9e/aMoKAgAC5fvkynTp1wdHTk3r17tGjRgoSEhCwDOtWqVcPd3Z3hw4eTmJhIQkICFy5coFy5csyaNYuyZcvi4eGBk5MTFSpU4Pr16zx//pyqVasyYMAATpw4wfTp0+natStHjhwhR44cREREEB4ezq5du8iVKxf58uUDYOjQoYwfP55mzZpRrFixz357SAt+pQfuHBwc1HevXbs2J0+e1Dlc4cSJEzRq1CjDHLVq1SI5OZnTp0/rjM2dOzflypXL9LmGhoYoivLVhzeI/w0JhgkhhBBCCCGE+O69ePECOzu7LxqblJTE8ePHuXLlComJiejp6WV52uLhw4c5c+YMuXPn5smTJ/j5+ZGcnKwzJj0Tbf369YSFheHi4kK5cuX46aefiIqKYuzYsYwZM0Yd//79eywtLf/jdwVQFIVXr16xY8cOSpUqxfnz56lRowb169cnR44c3L59m4CAAKpUqUJ8fPwns5umTp1KeHg48+fPp3DhwmzcuJF+/fpRpUoVcubMSXJyMqNGjcLAwIC1a9cyevRo6tSpw6VLlzhy5AiVK1cGIDw8nOzZs9OvXz8OHTqEu7s7vr6+GBgYAGkZbZC2TVNRFABSU1Np1qwZu3bt+qr379WrF5GRkfTv35/bt28zZcoUbt26xcCBA4G0zL9mzZqRmpqKnZ0d7dq1Y+DAgZw/f56jR48yYcIERo4ciYGBAdHR0Xh4eHD06FFu377Npk2bGDFiBF26dMl0S6r450kwTAghhBBCCCHEd+/evXuUKFHik2MePXqEnZ0dI0aMwNHRkbdv3+Ls7Mzbt2+5f/++Ou7ly5csWLCAChUq4OnpSe/evXn48CF+fn4sWLCAIkWKMHToUE6dOqXW/CpTpgxLlizBycmJUqVKsWnTJuLi4nBzc6Np06Y6NbJev35Nnjx5/uN3vXLlCsWLF2fdunX07NmThw8fMmjQIDp27MjTp09ZuXIlJUuWVMdHR0frnKT4Z4UKFcLHx4cZM2YQHh6Ooijs27cPR0dHsmfPzq5du9SAVunSpTly5AiTJk3CxcWFS5cuqYGtpk2bcv36dd6/f4+rqyv6+vpqVt6OHTsYOHAgy5cv58KFC/Tu3ZukpCSSk5MJCQlR66R9qQIFCrBv3z7Onz+Ps7Mzu3fv5uDBgxQpUgRIq3EWEhKiBi5Xr15NhQoVaNq0KR4eHvTp04chQ4YAaYXzk5OT8fDwoEqVKkyZMoUBAwawevXqr1qT+B9ShBBCCCGEEEKI70h8fLxy9+5dJTw8XImKilKuX7+uFCpUSFm3bp3OmCdPniimpqaKoihKQkKCcuTIEaVBgwZKtWrVlLdv3yqKoihr1qxRcubMqcyYMUPRarVK165dFX19faVWrVrK6tWrlfj4eJ1na7VaZd++fUqbNm0UU1NTxd7eXomPj1e6dOmi5MmTR1m2bJmiKIoSHBysODs7K61atVJevnypvH79WomOjlY2bNig5MyZU0lJScny/V68eKEASkJCQqb9YWFhyvr16xWNRpNpf2hoqBIWFqZER0crBw4cUExMTLIc+2fBwcFK0aJFlUKFCilbtmxR269evao4Ozur11FRUUrfvn0VExMTpUaNGsrdu3eVUaNGKfb29kqLFi2UW7duKb6+vsoPP/ygjBgxQjE1NVX8/f0VRVGUW7duKQULFlScnZ2V5OTkL1qXEB/TU5T/PwQrhBBCCCGEEEJ8B96+fUv+/PnVUyVNTU3p0qULK1asUDOYXFxcOHHihLpVr3jx4lhYWNC/f3/69Omjsy3y1q1btG/fntWrV1OgQAGMjY0pUKDAZ9eRkJDA+/fvKVCgAG/evMHMzAxzc3MeP35M+fLl8fHxYcKECcyePZtx48YBkD9/fhYuXIibm1uW84aFhVGoUCESEhJ0ToH8Ul27dmXLli0AWFtbM3v2bHr06PFF9yqKQkBAAM2bN8fIyEhtDwwMxNvbm8DAQJ3xb9++JTIykoiICA4cOEDXrl0pU6aMzpgJEybQtm1bnJycdO47ceIEnTt3/ur3E0KCYUIIIYQQQgghvlvJyckYGhqip6f3yXERERGfPIHx45MG/wp//PEHefPm/cvm+1qpqamkpKToBLSE+FZIMEwIIYQQQgghhBBCfDekgL4QQgghhBBCCCGE+G5IMEwIIYQQQgghhBBCfDckGCaEEEIIIYQQQvzLabVavvcqR7Gxsf/0EsQ3QoJhQgghhBBCCCHEF6patSpr1qz5orHx8fGZticmJmbartFoyJs3L4cPH9Zpf/XqFSYmJjx79uyLnhsUFMS2bdu+aKyiKDx//pxjx47x22+/cfDgwQxjnJycOH369BfN9yW0Wi3R0dGEhYXx5MkTtT0kJISkpCTCwsIoWLAgAG/evCEyMhKAJk2asGPHjr9sHeL7ZfhPL0AIIYQQQgghhPi71a5dm7dv32Jqapppf1RUFPXq1WPdunWfnCc1NRV9/c/nlcTHx1O2bFl+++03WrZsqbZHR0dTsWJFfv31V1q3bq1zz/r164mPj6dBgwY67atWraJkyZLY2dnptEdGRrJnz54Mzw4NDWXBggXExcVluta2bdvy888/c//+fR48eEBCQoLa16FDB1xdXbN8r3Xr1tG9e3f1Om/evDRp0oQ5c+Zga2sLwL59+xgzZgwpKSmkpKSQlJSk/oyMjDAwMMDU1JTs2bNjb2/P7t27ARgzZgzVqlXD3d1dnd/b2xtnZ2dGjBjBzZs3cXZ2znJtmUlNTWX69OmsXLmSt2/fUrJkScaPH0+7du0+e+/kyZOZMGEC586do3bt2pw+fTrD3026DRs20K1bt69am/jnSDBMCCGEEEIIIcR3Ydu2bTg5OWXa5+/vz/79+z87x5cGw8zMzFixYgUdOnQgJCSEfPnyAdC/f3/Kly+vEyCDtC2As2bNQl9fnwIFCqjtxYsXJzQ0lMTERKytrdHT01P7bty4QWxsLKdPnyYxMZGmTZsCYGNjw9SpU7PMTMuRIwe5cuXC1dWVH374gd9++43Dhw9TpEgR8ubNC4Cenh4JCQmYmJhkuN/AwIB79+6RmprKw4cPGTx4MJ06deLMmTMAuLi4UKVKFbJly4aRkRHGxsbcvHkTb29vAgMDs/xm48aNw8XFBTc3NwCOHDnCrVu32Lx5M+fPn8fU1JSiRYt+9tt/7MCBAxw9epTly5dja2vLhg0b6NChAxcuXKB69epZ3vfmzRvmzp2r01atWjUePnyo03b69GmGDBlCixYtvmpd4p8lwTAhhBBCCCGEEN8FDw8PzMzMMu17//49NWvW/OwciqJ8NhjWq1cv/Pz8yJYtGwAlSpQgISEBY2Nj9d4cOXIQFxenBpy8vb3JnTs3d+7c4eeff6Zr1640b96cPn36oCgKFy5cwN3dHVdXVzw8PNRn9e/fn5SUFBYuXEhMTMwn1zV8+HA1aLN06VIgLYts+fLlVK1a9bPv/rHixYsDULJkSTQaDW5ubkRHR/Pq1StmzpyZYfy7d+8IDQ3Fy8srQ5+lpSULFy6kUqVKXL58WQ3A1apVi+3bt2NiYsK2bdtITk6mWbNmOvd9bjto5cqVOXnyJIaGaeEPJycn9u/fz969ez8ZDBs8eDBNmzbF399fbTM1NVXfO12PHj0YMGAAuXLl+uQ6xL+LBMOEEEIIIYQQQnwXqlWrpmY+/dm9e/d0rjt37pxloKVbt26Zbolbu3atGuyZN28e/fv3V/scHR1ZtmwZtWvXVtvSs7yioqJ4+fIlAQEBmJqaMnz4cObMmUPz5s3Jly8ffn5+xMbGMmjQIObOnYuzszNly5ZV52ndurVaY+tTqlSpAsCVK1fw8/MDICYmhpiYGHWtBgYGLFy48LNzfSwlJQV9fX2yZcuGlZUV9evXzzAmNDSU27dvZ9pnamrK1atXadu2rfo9EhIScHBwANKCi5s2bcLd3Z1q1aqRkJBA37598fPzIzExkZIlSzJ48GB8fHwyzJ2ekfcxExMTtFptlu9z6NAhzpw5w+HDh3WCYX924sQJrl+/nulWVfHvJsEwIYQQQgghhBDfvFGjRlGwYEHMzc0z7X/79i3Jycnq9Zo1a1i2bFmGcXZ2dgwbNkwn0JXu46yzUaNGMXHiRPU6MjKSli1bqhlKH7O0tGTXrl106tSJuLg44uPjiYuLo2DBgsTGxqrzmJubY2VlxaxZs9iwYYN6/9GjRzlz5gzt27fP8v0XLVqElZUVjRs3xtLSUg007du3DwMDA/X6S7aAfuzOnTtMmjQJT09PTE1NMTU1pVu3biiKwrt37yhbtiwREREEBgayf/9+vLy82LNnD+vWrWPNmjVs376dQoUKUaVKFa5evcr8+fO5ceMG165do27dunh4eHDq1CmSkpIoVqwYXl5ehISEYGtri7u7O1qtFgcHBwoVKvRF671y5Qq3bt1i9uzZmfZHRETQo0cPFi9enOW/lXRz5syhW7dukhX2f5AEw4QQQgghhBBCfLPc3Ny4evUqMTEx5M+fH/h/mUfpBd//rE2bNkydOjXDlsonT54QFRXFxYsXGTt27CefO3PmzC/ODAPImTMnHh4emJqaYm5ujrm5ObNmzaJevXp0794dS0tLDAwMMn1WtmzZuHbtGq9fv85yPc+ePVMDcQ4ODjg4OJCYmMisWbMwMzOjdevWFC5c+JPvlE6r1WJiYkJqaioA06dP18nKqlSpEnFxcWp/Zk6fPo2joyMuLi5UrVqVQ4cOMXDgQMaPH0+bNm3YvHkz06ZNY86cObRs2ZIyZcpw5MgRAK5du0b58uWBtEy29PbPuXLlCq1bt6ZDhw462y3TKYqCl5cXLi4utGvXjtDQ0Cznevz4MUePHiU4OPiLni3+XSQYJoQQQgghhBDim+Xv78+ePXvYv38/q1atAmDjxo0EBgayYMGCr5pr27ZttGvXjitXrvDixYsvzkb6UumnKCYlJREdHY2xsTF//PEH586dIzo6mpiYGKKjoylZsqR6GuKNGzc4d+4cNWrUoGbNmpibm3P27FkgLVOte/fuzJs3Dzs7O27evKmzTXH58uU4Ojpy+fJlateuzciRI+nXr99n12lgYEBwcDDPnj2jd+/eBAUF6WS83bhxA0jLskrPOHv16hXPnz8nKioKgKpVq3L48GGioqIYOXIky5cv586dO0RERFCqVClCQ0OxsrJi2rRpAJQqVYrhw4ej0Wg4dOgQ9erV+6pvu3TpUgYPHoyHhweLFy/OdMzYsWN5/PgxW7du/ex86XXW0oNy4v8WCYYJIYQQQgghhPjm7d69Wz3J8MOHD7Rt2xYPDw927txJiRIlgLQTBFu1asXy5csz3P/hwwcWLFjAli1bKF26NGPGjFHrbmVm6NChjBo1Sr1OSEjAxcUly22IT58+pUqVKmqh/Rw5chAZGYmtrS23b9/GwsJC/dnZ2an3vXjxAgcHBzp37szYsWMxNzenVatWREVFsXLlSlxcXLCzs6NPnz707t2bgQMHAhAeHs7s2bPZvHkzHTt25Pjx4zRo0IC3b9/qbO/MSnp22bZt26hZsybt27fHzc2Nt2/f4u/vr251hLS6ZIMGDSIiIoJRo0bh5uZGWFgY165d4+TJk4SGhrJlyxaGDBlCYmIiGo1GpyZaYGAgBQsWxMHBgTlz5rBnzx4mTZr02TWmGzZsGMuXL2fNmjV07do1y3HTp0/H0NAQGxsbIC1TDKBRo0bUq1ePo0ePqmP9/f3x9vb+4jWIfxcJhgkhhBBCCCGE+Oa1bds2Q2bYunXr+P333zl+/Dg2NjbUqFGDLl26ZLg3NTWVnj17UqNGDRo2bEiFChUoV64ca9asoUePHhnGN2zYEA8PD+rWrau2ZbZNsn///mpGlZ2dHWFhYRgbGxMbG0t0dDQDBw6kWrVquLi4EB0dTXR0NFFRUXz48IGZM2cybNgwUlNTWbNmDfv371eDZAkJCVy/fl3dVunv70/p0qV13sfd3Z327durWyNLlizJ0aNHP3s6459Vq1YNd3d3hg8fTqtWrUhISODChQvUqFGDTp06MXHiRDw8PHBycsLKyorr169TtWpVqlatyoABA8iRIwfTp0+ncuXKlC1bltatWxMaGoqlpSUXL16kV69eahH8oUOH0qZNG9q3b0+xYsW+aH379+/H19eXs2fPfvbEzJCQEJ3rly9f0rhxY9auXavz9xYcHMzTp09p06bNV30r8e8hwTAhhBBCCCGEEN+8zDLD9PX16d+/Pz4+PlSrVo0CBQpkOO0wMTGRnj17EhQURFBQEADW1tZs2LABV1dXoqOjdeplWVlZodFoMjw/KSmJRo0a6dQJA1i1ahUrV65k8uTJvH37Fq1Wi7m5OTly5CAiIoKHDx9y9epVLC0tyZEjB5aWlrx7946CBQuSmppKbGwsAwYMoFWrVmods6ioKCwsLIC0wFjDhg2ZNm0aRYsWBeDgwYOEhoayd+9e3r59q66lbNmyTJ48GYB+/fplWuw/M1OnTqVkyZLMnz+f0aNHs3HjRgBiY2PJmTMnycnJjBs3jn79+rF27VoaNGjAtGnTWLdunc484eHhZM+enX79+vHjjz/Sr18/Vq1apQb10tdqYGCAoijo6emRmpqKq6srP//8s7p19GObN2+matWq5MqVi0ePHqntBgYG2Nvbs2vXLlasWMHBgwfVLZ3pTExMAChcuLBOPbXTp0+TJ08eSpYs+UXfR/z7fN0xEUIIIYQQQgghxP9Bbdu25ezZs/j5+dGlSxd+//13Jk6cSL9+/bh16xajR49m4cKFOvcEBgZSvXp1rl+/zvnz59XtcwCNGzdm586djB07lrp163LmzBkg7dRIjUaT4VemTBlOnDiRaV+3bt24evUqERERxMTE8Pr1a+7fv0/r1q0ZMmQIO3bsYNWqVcyfP58JEyaQO3duXr9+jZGREY8ePWLt2rVqvbG6dety//59Ll68iKurK6GhodSvX589e/YAEBAQQMWKFdm0aRPZs2fP8nv5+vqqwbD07YJZKVSoED4+PsyYMYPw8HAURWHfvn04OjqSPXt2du3apQa0SpcuzZEjR5g0aRIuLi5cunRJnb9p06Zcv36d9+/f4+rqir6+vrqtdMeOHQwcOJDly5dz4cIFevfuTVJSEsnJyYSEhPDq1atM1xYeHs65c+coUaKEzq9KlSpAWi2zkJAQnZNEPycoKIiKFSt+8Xjx7yPBMCGEEEIIIYQQ3yxfX1+GDBnC3r17adKkCTNmzODOnTvkzJkTGxsbWrRoQe7cuenTpw/VqlVj4sSJPHjwgBEjRlCtWjWcnJy4cOFCpsXyW7VqxeXLlzEzM6NBgwbs37//P16nlZVVhkysfPnycf36dZ22lJQUzp07R7ly5YC0LK99+/axdu1a9PX1qVu3LqdOnaJFixY8f/6cAwcOUKtWLaytrQHYvn07SUlJVKtW7ZPref78OdeuXePMmTM8fvwYS0tLALy8vEhJSckwfsaMGURHR/PHH39QvHhx+vXrx8yZM9m0aRNGRkY6YytUqMDt27cpUaIEDRs2pFatWoSEhDB69GhKly6Nvr4+t27dYvDgwSxYsICRI0fi6emJn58fP//8M4cPH+bQoUPUrFkTAwMDnj17pnNy58dOnjyJoigZfhEREUDaVtVnz55hbGyc4V47OzsURdHZIgng5+fH4cOHP/n9xL+bnvK5EK8QQgghhBBCCPF/1Lt37wDUYBCk1Qw7e/YsoaGhdOnSBS8vL/T09Hjw4AFLly7lhx9+IE+ePLx79446dep80XOePHmibkPMTGY1wz4nNDRUraGVniGVkpJChQoVOHToEDExMQwdOpTSpUvj5+fHiBEj2L17N5MmTaJq1aocOHCAbdu2sWHDBrRaLRUqVMDU1JQrV66o84WGhlK9enXCw8N1nr1792569uyJvr4+Li4ubNq0KcMWz8woikJAQADNmzfXCYIFBgbi7e2tblVN9/btWyIjI4mIiODAgQN07dqVMmXK6IyZMGECbdu2xcnJSee+EydO0Llz5y/+nkKkk2CYEEIIIYQQQgjxf1RqaqrOCZVarVbdkghptcr+nJklxPdOgmFCCCGEEEIIIYQQ4rshNcOEEEIIIYQQQgghxHdDgmFCCCGEEEIIIcS/nFar/eypjt+62NjYf3oJ4hshwTAhhBBCCCGEEOILVa1alTVr1nzR2Pj4+EzbExMTM23XaDTkzZs3w0mFr169wsTEhGfPnn3dYj9j1KhRTJ069S+d80totVqio6MJCwvjyZMnantISAhJSUmEhYVRsGBBAN68eUNkZCQATZo0YceOHf/z9YpvjwTDhBBCCCGEEEJ882rXrk2pUqVwcnLK9Gdvb4+Xl9dn5/lzwfqsxMfHU7ZsWfbv36/THh0dTZkyZQgICMhwz/r164mPj6dBgwY67atWraJkyZLY2dll+iwrKyv09PQ++ytevPgn1+zk5ISNjQ22trbqL3fu3Dr3rVu3TmdOW1tbunXrpnMa5b59+yhXrhylS5emRIkSFClShHz58mFtbU2+fPkoUqQIzs7OtGzZkqFDh6r3jRkzhvnz5+usydvbm8WLF5OcnMzNmzdxdnb+5Dt8jdu3b1O3bl1MTU0pWrQofn5+WY59/fo1HTt2xMLCgly5cvHTTz8RExOj9kdFReHl5YWVlRU5cuSgV69eWQZDxT/P8J9egBBCCCGEEEII8b+wbds2nJycMu3z9/fPELjKzJcGw8zMzFixYgUdOnQgJCSEfPnyAdC/f3/Kly9Py5YtdcbHxsYya9Ys9PX1KVCggNpevHhxQkNDSUxMxNraGj09PbXv2bNnZM+enXfv3n3RFsqP783K8ePHdb5RaGgojRs31hljYGDAvXv3SE1N5eHDhwwePJhOnTpx5swZAFxcXKhSpQrZsmXDyMgIY2Njbt68ibe3N4GBgVk+e9y4cbi4uODm5gbAkSNHuHXrFps3b+b8+fNq0OqvEB0dTZMmTXBxccHX15eAgAC8vLwoUaIE1atXzzC+S5cuJCYmcvToUeLi4vD29mbAgAGsW7cOAA8PD548eUJAQABRUVH8/PPPKIrC6tWr/5L1ir+WBMOEEEIIIYQQQnwXPDw8MDMzy7Tv/fv31KxZ87NzKIry2WBYr1698PPzI1u2bACUKFGChIQEjI2N1Xtz5MhBXFwcCQkJmJiY4O3tTe7cublz5w4///wzXbt2pXnz5vTp0wdFUbhw4QLu7u64urri4eGh8zwDA4MveX3VmTNnGDp0KC9fvkRfX589e/awfPnyr5ojPVusZMmSaDQa3NzciI6O5tWrV8ycOTPD+Hfv3hEaGppp9p2lpSULFy6kUqVKXL58GRMTEwBq1arF9u3bMTExYdu2bSQnJ9OsWTOd+7Zt2/ZV6063bt06tFotK1euxMjIiPLly7N//36WLl2aaTDs6tWrbN26lRo1agAwcOBA9ZvdvXuXgIAAfv/9d6pUqQLAvHnz8PT0ZM6cOeTKles/WqP4+0gwTAghhBBCCCHEd6FatWrkzZs307579+7pXHfu3DnLQEu3bt3o1q1bhva1a9eqwZ558+bRv39/tc/R0ZFly5ZRu3ZttS09UysqKoqXL18SEBCAqakpw4cPZ86cOTRv3px8+fLh5+dHbGwsgwYNYu7cuTg7O1O2bFkURcmy/tinlClThrlz57JixQpMTU3p1q0bxYoVA6Bx48YYGv6/UIFWq8XS0vKT86WkpKCvr0+2bNmwsrKifv36GcaEhoZy+/btTPtMTU25evUqbdu2Vb9HQkICDg4OQFpwcdOmTbi7u1OtWjUSEhLo27cvfn5+JCYmUrJkSQYPHoyPj88Xf4NTp07RsGFDjIyM1LaGDRuyc+fOTMd36tSJdevWUbduXeLj49m6dSudOnUC0oJhgE5GXf369UlJSeHatWsZMuvEP0+CYUIIIYQQQgghvnmjRo2iYMGCmJubZ9r/9u1bkpOT1es1a9awbNmyDOPs7OwYNmyYTqAr3cdZZ6NGjWLixInqdWRkJC1bttQJNKWztLRk165ddOrUibi4OOLj44mLi6NgwYLExsaq85ibm2NlZcWsWbPYsGEDd+7coVy5cl/6CVRPnz6lfv36HD58GHNzczVAdfToUXLmzKlmtEFaoOvVq1dZznXnzh0mTZqEp6cnpqamanBNURTevXtH2bJliYiIIDAwkP379+Pl5cWePXtYt24da9asYfv27RQqVIgqVapw9epV5s+fz40bN7h27Rp169bFw8ODU6dOkZSURLFixfDy8iIkJARbW1vc3d3RarU4ODhQqFChr/oGT548wdXVVaetcOHChIWFZTp+wYIFVK1alZw5c6IoCvXr1+eXX34BwNraGoDnz5+rQcXo6Ggg7QAA8e8jwTAhhBBCCCGEEN8sNzc3rl69SkxMDPnz5wf+X+aRra1tpve0adOGqVOnZthS+eTJE6Kiov6/9u48ruoy////AwgDQcEkJbcsRyUwQ03QMvcwGTXAGpeQwaVkxHXMXFJccitz37IIHNdQUj/uSyZamAugKGZZ2lHRMYEU0WE/7+8f/DifOQGC8+sz08jzfrudP871vt7X+zpv8Z/n7XVdF0ePHmXy5Mn3fe7cuXMrXBkGUKNGDUJCQnB0dMTZ2RlnZ2fef/99OnTowMCBA3FxcSmxHLJZs2aWvcIOHTpEcHAwJpPJKswqT35+PocPH+bChQtMmDCh1HsLCgpYsmQJ/fv3B4qqxRwcHDCbzQDMnj3bqiqrZcuW3Lt3z3K9NHFxcTRr1gw/Pz98fHzYs2cPI0eOJCIigoCAADZs2MCsWbOYN28ePXr0wNPTk3379gGQlJRE8+bNgaIlosXtD+Lu3bsl/n2rVq1aaqWdYRgEBQXx2GOPcejQIe7evcvo0aMZPnw4K1euxNfXl3r16vHXv/6VqKgoCgsLGTNmjGV+8vujMExEREREREQeWrGxsWzbto2dO3cSGRkJwLp160hISGDRokUPNFZMTAxBQUEcP36cq1evPnA1UnmCg4MByMvL486dOzz66KP8/PPPfPXVV9y5c4esrCzu3LlDkyZNCAoKsrp30aJFhIWFYWNjQ0FBQanj29raYmtry9WrV9m8eTO7du3i22+/ZdeuXQQFBdGzZ09Wr16NYRgsX76cYcOGYWtry5AhQ8jLy7OMY2dnx+nTp7l8+TJDhw4lMTHRquItOTkZgPT0dMtSx+vXr3PlyhUyMzMB8PHxYe/evWRmZjJ+/HhWrVrFuXPnSE9Pp2nTpphMJlxdXZk1axYATZs2Zdy4ceTk5LBnzx46dOhQoXc6Y8YMZsyYYfkeERFBREQEjz76qNVvAsjJySl1T7m9e/cSFxdHamqqJUB1cXGhffv2/PWvf6Vx48Z8/vnn9OvXDzc3Nx599FHGjx/P3r17qVWrVoXmKf9e5R+BISIiIiIiIvJfbuvWrXh7e+Pt7W1Z3hYSEoKTk5OlvU6dOgwdOrTU+2/dusWiRYsIDw8nNDSUSZMm3fd5Y8eOtVR4OTs7c/78efz8/Kza/tlPP/2Em5sbTk5OuLu78/zzz3PgwAG2b9/OqlWr2LlzJ0lJSfz8888lQhyA48ePExERgb29fZmf6dOnA5CYmMixY8eoVasWU6dOJSEhgQYNGljGysnJYdSoUfc9KMDDw4Nu3boRExPDpk2biI2NBYqWm65cuZKwsDDLMsSsrCxGjRpFeno6EyZMoFq1aqSmppKUlMQnn3yCyWRi48aNNGjQAC8vL3JycvDy8sLd3R13d3dSU1OpVasWHh4ezJs3j23btln26yrPsGHDSElJsXyGDRsGQN26dbl69apV36tXr5Z6WuWZM2eoXbu2VSVhy5YtMQyDs2fPAkXh3sWLF0lNTSU9PZ0ePXpga2trqWCT3xeFYSIiIiIiIvLQCwwM5PTp05w+fdpSbbR69Wrq16/PF198wenTp3nyySfp169fiXvNZjODBw+mbdu2dO7cmTFjxnDw4EGioqJKfVbnzp05cOAAd+/etXyeeeYZ9u/fb9UWHh5uqahq2LAhqamp3L17l8uXLxMfH0+XLl0YNGgQs2fPJjw8nICAAJ5//nlu3brF3LlzrSrAbty4gWEYZX46depEvXr1gKJloJs2baJ169alBl4///xzmUtIf83X15fg4GDGjRtHbm4u2dnZxMfH8+yzz/L+++/j5eVFSEgI3t7ePPfcc5w6dYorV67g4+PDiBEjOHjwILNnz6Z///7s27eP6tWrk56ezo0bN9iyZQuPPfYYTzzxBFAUMEZERPDKK69Y9uYqj5ubGx4eHpaPm5sbAO3atePLL7+ksLDQ0vfgwYN06dKlxBh169bl5s2bpKenW9qKQ7C6deuW6Ovs7MyKFSt4+eWXLfuJye+LlkmKiIiIiIjIQ2/r1q0kJCQARVVegYGB2NraMnz4cEaPHo2vry9169Ytcdphbm4ugwcPJjExkcTERKBow/Q1a9bg7+/PnTt3rPbLcnV1JScnp8Tz8/Ly6NKli9U+YQCRkZF88sknzJgxg7S0NAoLC3F2draEQj/88AMnT57ExcWF6tWr4+LiQkZGBvXq1bvvnly/dvXqVRo2bFihvrt27SIvL49jx47Rpk0b7O3t77v31cyZM2nSpAkLFixg4sSJrFu3Dijal6tGjRrk5+czZcoUwsPDiY6OplOnTsyaNYvVq1dbjXPjxg2cnJwIDw/njTfeIDw8nMjISMuz09LSgKJlmoZhYGNjg9lsxt/fn7feeqvE0tH7GTJkiOXEz/DwcLZu3crZs2fZuHEjAFu2bOHjjz9m9+7dBAQEMGnSJPr27ct7773HvXv3GDlyJK1bt8bHxweADRs20LhxY+zt7dmwYQOxsbEcPXq0wvORfzNDRERERERE5CG2detWY/DgwUZmZqZx5swZY/z48Ubbtm2NqVOnGmaz2WjevLnh5ORkpKamWt138uRJw9vb2/D09DSuXLlSYtzt27cbTk5OxksvvWTExcXddw5eXl7GV199Veb1W7duGfn5+VZtoaGhRnR0dIm+M2bMMEaPHn3f5/2z7Oxsw97e3vjpp5+s2sePH2+89957Vm3nz5836tata0ybNs1wc3MzJk+ebOTl5VmuR0dHG3Z2diWeMWHCBKNatWrG3//+d8NsNhvbt283nnzySaN///5Gbm6ucfLkSaNVq1aGYRjG6dOnjfr16xsvv/yycfToUcNsNlvGycnJMbp3724ARqNGjYxDhw4ZhmEYmzZtMhwdHY1Vq1YZdevWNd58800jNzfXyMnJMRo0aGAsXbq0wu+jWFxcnNGsWTOjSpUqRosWLYwjR45Yri1dutRo0KCBkZOTYxiGYfz444/GH//4R8PZ2dlwdXU1goODjZs3b1r6v/nmm0a1atUMJycno0uXLkZCQsIDz0f+fWwM4/87ekJERERERETkIbNs2TIWLFhAVlYWTz/9NI0aNSIrKwuz2Uz37t3ZvXs3eXl5tGjRgo0bNzJkyBD69+9PZGQk8+fPJyQkhIULF+Lq6lrq+CkpKbz99tvs37+f7du306NHj1L7lXaaZHkmTZrEvXv3WLx4saWtoKAAf39/+vbty6BBg0q9z2QyYWNjQ7Vq1TAMg/nz57Nx40Z++uknoKhKzc7OjkGDBuHl5cU777zD5cuX+dvf/sayZct4//33GThwINeuXSM0NJRbt26xbt06y2b495OcnExQUBD5+fl88MEH9O3bF4CEhATCwsIs1Xl37txh4sSJREVF0aJFCz799FPWrFlDTEwMnp6ezJ07l8OHD3PgwAGaNm3K0qVLWbt2Lb179yYlJYXu3btTu3Ztjh07ZrV5v0hFKAwTERERERGRh1ZGRgaA1d5N69at48iRI5hMJvr160doaCg2NjZcuHCBlStX8uqrr1KrVi0yMjJ46aWXKvScS5culbr5erF/JQwzmUz06tULk8lk2duroKCA5557jj179lC9evVS7xs5ciTLly+3LKNs3LgxkZGRtG/fHoD4+Hjat2+Pi4sLBw8eZP/+/UydOpUePXoQERFhtem72Wxm0qRJJCUlsX///nLnbBgG27dvp3v37lSpUsXS/uswrFhaWhq3b98mPT2dXbt20b9/fzw9Pa36TJ06lcDAQLy9va3uO3jwoCVsE3kQCsNEREREREREHkJmsxmz2Vxu5VROTg4FBQUlTrj8Z/n5+djb2//WUxT5j1AYJiIiIiIiIiIilUbJM1RFREREREREREQeUgrDRERERERERESk0lAYJiIiIiIiIiK/e3fv3v1PT0EeEgrDRERERERERCrIx8eHqKio+/b5+9//XqLt+++/Z968eeXeFxAQQEFBQYlrHTt2ZO/evRWaY2JiIjExMRXqaxgGV65c4cCBAyxdupTdu3eX6OPt7U1cXFyFxquIwsJC7ty5Q2pqKpcuXbK0nz9/nry8PFJTU6lXrx4AN2/e5Pbt2wC8/PLLbN68+Tebh1Re9z9SQkREREREROQh0K5dO9LS0nB0dCz1emZmJh06dGD16tX3HcdsNmNrW3ZdSUZGBp6enuzZs4c2bdpY2qOjo7ly5cp9x46OjsbGxqbc0x+L3b59m23btpVoN5lMLFq0iHv37pU618DAQN566y2+//57Lly4QHZ2tuXa66+/jr+/f5nPXL16NQMHDrR8r127Ni+//DLz5s3D3d0dgB07djBp0iQKCgooKCggLy/P8qlSpQp2dnY4Ojri5OTEU089xdatWwGYNGkSvr6+BAcHW8YPCwujVatWvPPOO5w5c4ZWrVpV6N2UpqCgAC8vLwoLC/nxxx/L7T9jxgymTp3KV199Rbt27YiLi6NTp06l9l2zZg0DBgz4l+cm/14Kw0RERERERKRSiImJwdvbu9RrsbGx7Ny5s9wxygvDatasyZQpU+jbty9ffvklnTt3BuDnn3+matWqNGzY0Kq/k5MT586do7CwkI8++siq8qlRo0ZkZmYCRWFdnz59sLe3B2Ds2LEMHTqUu3fvEhcXR25uLt26dQPAzc2NmTNn8o9//KPUOVavXp3HHnsMf39/Xn31VZYuXcrevXt58sknqV27NgA2NjZkZ2fj4OBQ4n47Ozu+++47zGYzP/zwA2PGjKFPnz4cPnwYAD8/P1q3bo29vT1VqlTh0Ucf5cyZM4SFhZGQkFDmu5syZQp+fn689tprAOzbt4+zZ8+yYcMGvv76axwdHXn66afLvL88n3zyCRcuXKBRo0bl9r158yYffvihVZuvry8//PCDVVtcXBx//etf+eMf//gvz0v+/RSGiYiIiIiISKUQEhJC1apVS732yy+/8MILL5Q7hmEY9w3DAEaPHk1qaipVqlTh9OnTbN68mRUrVnDo0CEuXrxI9erVefzxx4Gi0Alg/fr1eHp64uvrS2hoKAMGDODixYuWMTt27MiECRN45ZVXrJ41fPhwCgoKWLx4MVlZWfed17hx4yyhzcqVK4GiKrJVq1bh4+NT7m//Z3/4wx8AaNKkCTk5Obz22mvcuXOH69evM3fu3BL9MzIyMJlMhIaGlrjm4uLC4sWLadmyJceOHbMEcC+++CKbNm3CwcGBmJgY8vPzrX6/i4tLhZeD3rx5k6lTpxIQEMDZs2fL7T9mzBi6detGbGyspc3R0dHyu4sNGjSIESNG8Nhjj1VoHvL7oDBMREREREREKgVfX19L5dOvfffdd1bf+/btW2bQMmDAgFKXxEVHRxMaGoqtrS0LFiwAisKzxYsX8+677+Lq6sp7773HK6+8QlhYmOW+nJwcpk+fzmeffUZycjLffPMN/v7+9O3b19Ln22+/Ze7cuVbLOBcvXkzt2rXp1auXZY+t+2ndujUAx48fZ+3atQBkZWWRlZXF8OHDgaKqr8WLF5c71j8rKCjA1tYWe3t7XF1d6dixY4k+JpOJlJSUUq85Ojpy8uRJAgMDgaIquOzsbDw8PAAYMmQI69evJzg4GF9fX7Kzsxk2bBhr164lNzeXJk2aMGbMGEaPHl3mHEeMGEH//v1xdXUtNwzbs2cPhw8fZu/evVZh2K8dPHiQU6dOlbpUVX7fFIaJiIiIiIjIQ2/ChAnUq1cPZ2fnUq+npaWRn59v+R4VFcVHH31Uol/Dhg15++23LeHRP6tatSp9+vQhMzOTF154gYiICGJjY7l37x5/+tOfypxbbm4ut2/f5vXXX+fq1ascOHCAF198EU9PT1asWMHMmTO5fPkyo0eP5qOPPiIuLo6xY8dSo0YNAPbv38/hw4fp3bt3mc9YsmQJrq6udO3aFRcXF0vQtGPHDuzs7Czfy6t6+7Vz584xffp0/vznP+Po6IijoyMDBgzAMAwyMjLw8vIiPT2dhIQEdu7cSWhoKNu2bWP16tVERUWxadMm6tevT+vWrTl58iQLFiwgOTmZpKQk2rdvT0hICIcOHSIvL49GjRoRGhrK+fPncXd3Jzg4mMLCQjw8PKhfv36Zc1y3bh1Hjx7l3LlzlpCyLOnp6QwaNIjly5eX+bdSbN68eQwYMEBVYf+FFIaJiIiIiIjIQ+u1117j5MmTZGVlUadOHeB/K4+KN3z/tYCAAGbOnFliSeWlS5fIzMzk6NGjTJ48udR7w8PD2blzJ0lJSQBs376djIwMmjZtCsCNGzc4evSoZT+qevXqERcXR0ZGBmvWrCE+Pt6yz1iTJk3Izs5m0KBBTJ48mczMTKpXr87kyZNZtGgRVapUAcDe3p6kpKRST7EsdvnyZcvG/B4eHnh4eJCbm8v7779P1apV6dWrFw0aNKjQOy0sLMTBwQGz2QzA7NmzraqyWrZsyb179yzXSxMXF0ezZs3w8/PDx8eHPXv2MHLkSCIiIggICGDDhg3MmjWLefPm0aNHDzw9Pdm3bx8ASUlJNG/eHCiqZCtuL80PP/zAiBEjiI2NpXr16vf9XYZhEBoaip+fH0FBQZhMpjL7Xrx4kf3793P69On7jim/TwrDRERERERE5KEVGxvLtm3b2LlzJ5GRkUBRpVBCQgKLFi16oLFiYmIICgri+PHjXL16tdRqpPbt23Pp0iUuXLgAwLJly5g3b57l+sCBA+nUqRMhISFAUZgDEB8fz4cffkh8fDw3b96koKCAQYMGMWXKFNLS0sjNzaVatWrY2tpy/PhxnJycAEhOTuarr76ibdu2vPDCCzg7O3PkyBGgqFJt4MCBzJ8/n4YNG3LmzBmrZYqrVq2iWbNmHDt2jHbt2jF+/HjCw8PLfQ92dnacPn2ay5cvM3ToUBITE61OwExOTgaKqqyKK86uX7/OlStXLAcC+Pj4sHfvXjIzMxk/fjyrVq3i3LlzpKen07RpU0wmE66ursyaNQuApk2bMm7cOHJyctizZw8dOnQod55ZWVm8+uqrDB8+nC5dupTbf/LkyVy8eJHPPvus3L7F+6wVh3Ly3+XB6h9FRERERERE/gtt3boVb29vvL29effdd4GiDfWdnJws7XXq1GHo0KGl3n/r1i0WLVpEeHg4oaGhTJo0qdxnGoaBk5MTbm5ulk+VKlVwdna2fK9RowaGYfDmm29SUFBAr169+Mtf/sIjjzzCihUrmD9/Ph06dODatWuWyrbiIAzg6tWreHh4MG3aNI4cOcKRI0fo2bMn7du359SpU1y7do2GDRvy/vvv8+WXX1ruu3HjBh988AETJ07k0Ucf5YsvvmD27NlMmzatQu/Tw8ODbt26ERMTw6ZNmyx7a6WlpbFy5UrCwsLw9/cHikKpUaNGkZ6ezoQJE6hWrRqpqakkJSXxySefYDKZ2LhxIw0aNMDLy4ucnBy8vLxwd3fH3d2d1NRUatWqhYeHB/PmzWPbtm306dOn3Dlu2bKF8+fPM2/ePBwcHHBwcGDmzJlcunQJBwcHy75pxWbPns2PP/6Im5sbDg4Olmq+Ll264OfnZ9U3NjaWoKCgCr0r+f1RZZiIiIiIiIg89AIDA0tUhq1evZoTJ07wxRdf4ObmRtu2benXr1+Je81mM4MHD6Zt27Z07tyZ5557jmeffZaoqCgGDRpU5jPj4+Pp0aOHVdu9e/c4cOCAVZi2c+dOUlJSrPbrmjNnDvPnzwegcePG5OTkUFhYiJubm6XPsWPHMJvNREVFsXPnTho2bAhAdnY2p06dslSdxcbG8swzz1j9nuDgYHr37m1ZGtmkSRP2799f4dMZi/n6+hIcHMy4cePo2bMn2dnZxMfH07ZtW/r06cO0adMICQnB29sbV1dXTp06hY+PDz4+PowYMYLq1asze/Zsnn/+eby8vOjVqxcmkwkXFxeOHj3KkCFDeOKJJwAYO3YsAQEB9O7dm0aNGpU7t4CAAM6fP2/VtmzZMnbs2MG+ffss4xb7dd9r167RtWtXoqOjadeunaX99OnT/PTTTwQEBDzQu5LfD4VhIiIiIiIi8tDbunUrCQkJQFGVV2BgILa2tgwfPpzRo0fj6+tL3bp1S5x2mJuby+DBg0lMTCQxMRGAmjVrsmbNGvz9/blz506Zpxi2a9eO27dvW7UFBASUOE2y2OXLl0lOTiY5OZnevXszceJEAE6cOEH37t1xcnJizpw5vPHGG5Z7Tpw4wYgRI+jZs6dlH7PMzEyqVasGFAVjnTt3ZtasWTz99NMA7N69G5PJxP/8z/+QlpZmGcvLy4sZM2YARXuf/fPSx/uZOXMmTZo0YcGCBUycOJF169YBcPfuXWrUqEF+fj5TpkwhPDyc6OhoOnXqxKxZs6xOxoSiajUnJyfCw8N54403CA8PJzIy0hLqFc/Vzs4OwzCwsbHBbDbj7+/PW2+9VaJSy8XFBRcXF6s2Nzc37O3tLcs3t2zZwscff8zu3bstbcUcHBwAaNCggdV+anFxcdSqVYsmTZpU6P3I74+WSYqIiIiIiMhDLzAwkCNHjrB27Vr69evHiRMnmDZtGuHh4Zw9e5aJEyeyePFiq3sSEhJo06YNp06d4uuvv7aqyuratSuff/45kydPpn379hw+fBgoCs8iIyNJSUnh3r17FZpbQUEBderUoWXLlixfvpzc3Fxq1qxJbm4uS5Yswd/fn+joaA4dOsSSJUvo06cPt27dAuDHH38kOjqa4OBgoGjPsu+//56jR4/i7++PyWSiY8eObNu2DSja0L9FixasX7/earnlry1btswShhmGcd/5169fn9GjRzNnzhxu3LiBYRjs2LGDZs2a4eTkxJYtWyyB1jPPPMO+ffuYPn06fn5+fPPNN5bxu3XrxqlTp/jll1/w9/fH1tbWUi23efNmRo4cyapVq4iPj2fo0KHk5eWRn5/P+fPnuX79eoXe9a9dv36d8+fPW50kWp7ExERatGjxLz1Pfh9sjPL+qkVERERERET+Sy1btowFCxaQlZXF008/TaNGjcjKysJsNtO9e3d2795NXl4eLVq0YOPGjQwZMoT+/fsTGRnJ/PnzCQkJYeHChbi6upY6fkpKCm+//Tb79+9n+/btxMTEUFhYiJeXF7t27WLhwoW0atXKEixduXKFatWqUaNGDQoLC8nNzeWRRx4hPT3dsicYwDvvvEN0dDReXl4sXLjQEr7k5+czatQoduzYQXJyMv7+/nz++ef84x//4L333mP27NkEBgYSEhLCxYsXSUlJYf369Tg4OPDnP/8ZZ2dn3nvvPZ566ikATCYTbdq04caNG1a/68qVK6Snp5OVlcUf//hHvvrqqwoFQMnJyQQFBZGfn88HH3xA3759gaJgMSwszFKdd+fOHSZOnEhUVBQtWrTg008/Zc2aNcTExODp6cncuXM5fPgwBw4coGnTpixdupS1a9fSu3dvUlJS6N69O7Vr1+bYsWMVrmATKaYwTERERERERB5aGRkZQNHSxmLr1q3jyJEjmEwm+vXrR2hoKDY2Nly4cIGVK1fy6quvUqtWLTIyMnjppZcq9JxLly7x1FNP0aNHD9atW0eNGjVYsWIFy5Ytw2QyUVhYiL29PXZ2duTl5ZGXl4fZbMbGxobjx4/TunVrq/EOHTpEzZo1yzyt8PTp0zz++OOMHTuWZ555hrVr1/LOO++wdetWpk+fjo+PD7t27SImJoY1a9ZQWFjIc889h6OjI8ePH7dUXJUVhm3dupXBgwdja2uLn58f69evx8bGptz3YBgG27dvp3v37lSpUsXS/uswrFhaWhq3b98mPT2dXbt20b9/fzw9Pa36TJ06lcDAQLy9va3uO3jwoCVsE3kQCsNEREREREREfiMFBQVlVirl5uZSUFCAnZ0ddnZ2PPLIIxUKmO7HbDZbbbxfWFhoWZIIkJeXZxVKiYjCMBERERERERERqUS0gb6IiIiIiIiIiFQaCsNERERERERE5Hfv7t27/+kpyENCYZiIiIiIiIhIBfn4+BAVFXXfPn//+99LtH3//ffMmzev3PsCAgIoKCgoca1jx47s3bv3wSZbjgkTJjBz5szfdMyKKCws5M6dO6SmpnLp0iVL+/nz58nLyyM1NZV69eoBcPPmTW7fvg3Ayy+/zObNm//t85WHj8IwEREREREReei1a9eOpk2b4u3tXernqaeeIjQ0tNxxfr1h/a9lZGTg6enJsWPHrNqjo6M5derUfceOjo7GxsamzA34y+Lq6oqNjU25nz/84Q/3Hcfb2xs3Nzfc3d0tn8cff9zqvtWrV1uN6e7uzoABA6xOo9yxYwfPPvsszzzzDI0bN+bJJ5/kiSeeoGbNmjzxxBM8+eSTtGrVih49ejB27FjLfZMmTWLBggVWcwoLC2P58uXk5+dz5swZWrVq9UDvpjx5eXnMnDmT7t27V/ieGTNmYGNjw9dff21pO3PmDH5+flStWhV3d3cGDhxoOclUfn8e7H+YiIiIiIiIyH+pmJgYvL29S70WGxvLzp07yx2jvDCsZs2aTJkyhb59+/Lll1/SuXNnAH7++WeqVq1Kw4YNrfo7OTlx7tw5CgsL+eijj6wqnxo1akRmZiYAmZmZ9OnTB3t7ewDGjh3LxIkTgaIAriJn41Xk5MovvvjC6h2ZTCa6du1q1cfOzo7vvvsOs9nMDz/8wJgxY+jTpw+HDx8GwM/Pj9atW2Nvb0+VKlV49NFHOXPmDGFhYSQkJJT57ClTpuDn58drr70GwL59+zh79iwbNmzg66+/xtHRkaeffrrc31ARhYWFzJ8/n+XLl3Pjxg1eeumlCt138+ZNPvzwwxLtw4cP55VXXuHDDz/k6tWrjBgxgpCQEHbt2vWbzFd+WwrDREREREREpFIICQmhatWqpV775ZdfeOGFF8odwzCM+4ZhAKNHjyY1NZUqVapw+vRpNm/ezIoVKzh06BAXL16kevXqPP7448D/BlTr16/H09MTX19fQkNDGTBgABcvXrSM2bFjRyZMmMArr7xS4nl2dnblzvufHT58mLFjx3Lt2jVsbW3Ztm0bq1ateqAxiqvFmjRpQk5ODq+99hp37tzh+vXrzJ07t0T/jIwMTCZTqdV3Li4uLF68mJYtW3Ls2DEcHBwAePHFF9m0aRMODg7ExMSQn59v9ftdXFyIiYl5oHkXy87OJjo6milTpvDVV19x7dq1Ct03ZswYunXrRmxsrFX7+vXrqV+/PgDNmzcnMzOTAQMG8I9//KPMvzn5z1EYJiIiIiIiIpWCr68vtWvXLvXad999Z/W9b9++ZQYtAwYMYMCAASXao6OjCQ0NxdbW1rLczzAMFi9ezLvvvourqyvvvfcer7zyCmFhYZb7cnJymD59Op999hnJycl88803+Pv707dvX0ufb7/9lrlz57J69WpL2+LFi3Fxcanw7y/m6enJhx9+yMcff4yjoyMDBgygUaNGAHTt2tVqmWZhYWG5zygoKMDW1hZ7e3tcXV3p2LFjiT4mk4mUlJRSrzk6OnLy5EkCAwOBoiq47OxsPDw8ABgyZAjr168nODgYX19fsrOzGTZsGGvXriU3N5cmTZowZswYRo8eXeF34OzszPnz5wGsljvez549ezh8+DB79+4tEYYVB2HFHBwcMJvNFZ6P/HspDBMREREREZGH3oQJE6hXrx7Ozs6lXk9LSyM/P9/yPSoqio8++qhEv4YNG/L2228zfPjwEteqVq1Knz59yMzM5IUXXiAiIoLY2Fju3bvHn/70pzLnlpuby+3bt3n99de5evUqBw4c4MUXX8TT05MVK1Ywc+ZMLl++zOjRo/noo4+Ii4tj7Nix/Pzzz7i7uz/wu/jpp58sG/I7OztbAqr9+/dTo0YNy1JMKAq6rl+/XuZY586dY/r06fz5z3/G0dHREq4ZhkFGRgZeXl6kp6eTkJDAzp07CQ0NZdu2baxevZqoqCg2bdpE/fr1ad26NSdPnmTBggUkJyeTlJRE+/btCQkJ4dChQ+Tl5dGoUSNCQ0M5f/487u7uBAcHU1hYiIeHR4kw6reWnp7OoEGDWL58eZl/Q8UMw+DTTz/F19dXVWG/UwrDRERERERE5KH12muvcfLkSbKysqhTpw7wv5VHZQVJAQEBzJw5s0SQcenSJTIzMzl69CiTJ08u9d7w8HB27txJUlISANu3bycjI4OmTZsCcOPGDY4ePWrZd6pevXrExcWRkZHBmjVriI+Pt+wz1qRJE7Kzsxk0aBCTJ08mMzOT6tWrM3nyZBYtWkTz5s0te4UdOnSI4OBgTCaTVZhVnvz8fA4fPsyFCxeYMGFCqfcWFBSwZMkS+vfvDxRVi/1z5dPs2bOtqrJatmzJvXv37lsZFRcXR7NmzfDz88PHx4c9e/YwcuRIIiIiCAgIYMOGDcyaNYt58+bRo0cPPD092bdvHwBJSUk0b94cKFoiWtz+f8UwDEJDQ/Hz8yMoKAiTyVRm3/z8fIYNG8ahQ4c4cuTI/+m85F+nMExEREREREQeWrGxsWzbto2dO3cSGRkJwLp160hISGDRokUPNFZMTAxBQUEcP36cq1evllqN1L59ey5dusSFCxcAWLZsGfPmzbNcHzhwIJ06dSIkJAT43/2+4uPj+fDDD4mPj+fmzZsUFBQwaNAgpkyZQlpaGrm5uVSrVg1bW1uOHz+Ok5OT1XMXLVpEWFgYNjY2FBQUlDp/W1tbbG1tuXr1Kps3b2bXrl18++237Nq1i6CgIHr27Mnq1asxDIPly5czbNgwbG1tGTJkCHl5eZZx7OzsOH36NJcvX2bo0KEkJiZaLa1MTk4Giqqpipc6Xr9+nStXrlgOBPDx8WHv3r1kZmYyfvx4Vq1axblz50hPT6dp06aYTCZcXV2ZNWsWAE2bNmXcuHHk5OSwZ88eOnToUKF/sxkzZjBjxgzL94iICCIiIip0b7HJkydz8eJFPvvss/v2S01NpU+fPly6dIkvv/yS559//oGeI/8+99/1T0REREREROQhsHXrVry9vfH29ubdd98FijbUd3JysrTXqVOHoUOHlnr/rVu3WLRoEeHh4YSGhjJp0qRyn2kYBk5OTri5uVk+VapUwdnZ2fK9Ro0aGIbBm2++SUFBAb169eIvf/kLjzzyCCtWrGD+/Pl06NCBa9euWSrbfh2EARw/fpyIiAjs7e3L/EyfPh2AxMREjh07Rq1atZg6dSoJCQk0aNDAMlZOTg6jRo2670EBHh4edOvWjZiYGDZt2mTZQystLY2VK1cSFhaGv78/AFlZWYwaNYr09HQmTJhAtWrVSE1NJSkpiU8++QSTycTGjRtp0KABXl5e5OTk4OXlhbu7O+7u7qSmplKrVi08PDyYN28e27Zto0+fPuW+f4Bhw4aRkpJi+QwbNqxC9/2z2bNn8+OPP+Lm5oaDg4Olyq9Lly74+fkBcOHCBXx9falWrRrJycm0adPmgZ8j/z6qDBMREREREZGHXmBgYInKsNWrV3PixAm++OIL3NzcaNu2Lf369Stxr9lsZvDgwbRt25bOnTvz3HPP8eyzzxIVFcWgQYPKfGZ8fDw9evSwart37x4HDhywCtN27txJSkqKVfg0Z84c5s+fD0Djxo3JycmhsLAQNzc3S59jx45ZTnW8cePGfX9/586dqVevHlC0DDQgIIAJEyaUGng9yF5kvr6+BAcHM27cOHr27El2djbx8fG0bduWPn36MG3aNEJCQvD29sbV1ZVTp07h4+ODj48PI0aMoHr16syePZvnn38eLy8vevXqhclkwsXFhaNHjzJkyBCeeOIJAMaOHUtAQAC9e/e2bPhfnuLQ8f+P4o32i127do2uXbsSHR1Nu3btAOjfvz9t27Zl06ZN5Z42Kv95CsNERERERETkobd161YSEhKAoiqvwMBAbG1tGT58OKNHj8bX15e6deuWOO0wNzeXwYMHk5iYSGJiIgA1a9ZkzZo1+Pv7c+fOnTJPMWzXrh23b9+2agsICChxmmSxy5cvk5ycTHJyMr1792bixIkAnDhxgu7du+Pk5MScOXN44403Hvj3X716lYYNG1ao765du8jLy+PYsWO0adMGe3t7y3LO0sycOZMmTZqwYMECJk6cyLp16wC4e/cuNWrUID8/nylTphAeHk50dDSdOnVi1qxZVidjQlGg5+TkRHh4OG+88Qbh4eFERkZanp2WlgYULdM0DAMbGxvMZjP+/v689dZbBAUFPfB7KcuWLVv4+OOP2b17t2WpZzEHBwcAGjRoQIMGDbhw4QKJiYlMmDCBS5cuWfV9/PHH/6UTP+X/luJKEREREREReegFBgZy5MgR1q5dS79+/Thx4gTTpk0jPDycs2fPMnHiRBYvXmx1T0JCAm3atOHUqVN8/fXXVhVGXbt25fPPP2fy5Mm0b9+ew4cPA0XhWWRkJCkpKdy7d69CcysoKKBOnTq0bNmS5cuXk5ubS82aNcnNzWXJkiX4+/sTHR3NoUOHWLJkCX369OHWrVsV/u05OTlcvnyZxo0bl9knODiY1atX89133zFnzhyGDx9Oz549mTJlCkuWLGHAgAFl3lu/fn1Gjx7NnDlzuHHjBoZhsGPHDpo1a4aTkxNbtmyxBFrPPPMM+/btY/r06fj5+fHNN99YDgHo1q0bp06d4pdffsHf39+yxxnA5s2bGTlyJKtWrSI+Pp6hQ4eSl5dHfn4+58+fv++Jl/+K69evc/78easTRstSXJX3+uuv07hxY6vP2rVrf9N5yW/Dxij+qxMRERERERF5yCxbtowFCxaQlZXF008/TaNGjcjKysJsNtO9e3d2795NXl4eLVq0YOPGjQwZMoT+/fsTGRnJ/PnzCQkJYeHChbi6upY6fkpKCm+//Tb79+9n+/btxMTEUFhYiJeXF7t27WLhwoW0atXKssH8lStXqFatGjVq1KCwsJDc3FweeeQR0tPTLXuCAbzzzjtER0fj5eXFwoULadGiBVB0WuGoUaPYsWMHycnJPPbYYyXmZDKZsLGxoVq1ahiGwfz589m4cSM//fQTAHl5edjZ2TFo0CC8vLx45513uHz5Mn/7299YtmwZ77//PgMHDuTatWuEhoZy69Yt1q1bV6JCqjTJyckEBQWRn5/PBx98QN++fYGiYDEsLMxSnXfnzh0mTpxIVFQULVq04NNPP2XNmjXExMTg6enJ3LlzOXz4MAcOHKBp06YsXbqUtWvX0rt3b1JSUujevTu1a9fm2LFjVpv3i1SEwjARERERERF5aGVkZABFSxuLrVu3jiNHjmAymejXrx+hoaHY2Nhw4cIFVq5cyauvvkqtWrXIyMjgpZdeqtBzLl26xFNPPUWPHj1Yt24dNWrUYMWKFSxbtgyTyURhYaFluWFeXh55eXmYzWZsbGw4fvw4rVu3thrv0KFD1KxZk+bNm5f6vNOnT+Pt7V3qtZEjR7J8+XLMZjNQtOdYZGQk7du3B4r2Mmvfvj0uLi4cPHiQ/fv3M3XqVHr06EFERITVM81mM5MmTSIpKYn9+/eX+x4Mw2D79u10796dKlWqWNp/HYYVS0tL4/bt26Snp7Nr1y769++Pp6enVZ+pU6cSGBho9XvT0tI4ePCgJWwTeRAKw0RERERERER+IwUFBWVWKuXm5lJQUICdnR12dnY88sgj2NjY/J/NxWw2Yzaby62cysnJoaCgAGdn5zL75OfnY29v/1tPUeQ/QmGYiIiIiIiIiIhUGtpAX0REREREREREKg2FYSIiIiIiIiIiUmkoDBMRERERERERkUpDYZiIiIiIiIiIiFQaCsNERERERERERKTSUBgmIiIiIiIiIiKVhsIwERERERERERGpNBSGiYiIiIiIiIhIpaEwTEREREREREREKg2FYSIiIiIiIiIiUmkoDBMRERERERERkUpDYZiIiIiIiIiIiFQaCsNERERERERERKTSUBgmIiIiIiIiIiKVhsIwERERERERERGpNBSGiYiIiIiIiIhIpaEwTEREREREREREKg2FYSIiIiIiIiIiUmkoDBMRERERERERkUpDYZiIiIiIiIiIiFQaCsNERERERERERKTSUBgmIiIiIiIiIiKVhsIwERERERERERGpNBSGiYiIiIiIiIhIpaEwTEREREREREREKg2FYSIiIiIiIiIiUmkoDBMRERERERERkUpDYZiIiIiIiIiIiFQa/w+i6TQeuDMxRgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "my_pf.performance()" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "45463e2b-f005-4417-9347-452b00d455ad", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "TradeManager {\n", + " params: params[precision(int): 2, save_action(bool): 1, support_borrow_cash(bool): 0, support_borrow_stock(bool): 0, ],\n", + " name: SYS,\n", + " init_date: 2001-01-01 00:00:00,\n", + " init_cash: 2000000.00,\n", + " firstDatetime: 2022-03-16 00:00:00,\n", + " lastDatetime: 2024-04-01 00:00:00,\n", + " TradeCostFunc: TradeCostFunc(TC_Zero, params[]),\n", + " current total funds: 2084553.25,\n", + " current cash: 1507660.25,\n", + " current market_value: 576893.00,\n", + " current short_market_value: 0.00,\n", + " current base_cash: 2000000.00,\n", + " current base_asset: 0.00,\n", + " current borrow_cash: 0.00,\n", + " current borrow_asset: 0.00,\n", + " Position: \n", + " SH600028 中国石化 2024-04-01 00:00:00 1 1000.00 6390.00 6360.00 -30.00 -0.47% -0.00%\n", + " SH600011 华能国际 2024-03-27 00:00:00 4 600.00 5556.00 5520.00 -36.00 -0.65% -0.00%\n", + " SH600031 三一重工 2024-03-20 00:00:00 9 400.00 5660.00 5992.00 332.00 5.87% 0.02%\n", + " SH600015 华夏银行 2024-03-25 00:00:00 6 1100.00 6952.00 7216.00 264.00 3.80% 0.01%\n", + " SH600016 民生银行 2024-03-28 00:00:00 3 1700.00 6919.00 6834.00 -85.00 -1.23% -0.00%\n", + " SH600023 浙能电力 2024-03-06 00:00:00 19 1200.00 6732.00 7896.00 1164.00 17.29% 0.06%\n", + " SH600000 浦发银行 2024-03-27 00:00:00 4 900.00 6426.00 6507.00 81.00 1.26% 0.00%\n", + " SH600025 华能水电 2024-04-01 00:00:00 1 700.00 6692.00 6615.00 -77.00 -1.15% -0.00%\n", + " SH600010 包钢股份 2024-02-07 00:00:00 33 4200.00 6105.00 6846.00 741.00 12.14% 0.04%\n", + " SH600018 上港集团 2024-04-01 00:00:00 1 1200.00 6468.00 6480.00 12.00 0.19% 0.00%\n", + " SH600036 招商银行 2024-03-27 00:00:00 4 200.00 6458.00 6528.00 70.00 1.08% 0.00%\n", + " SH600176 中国巨石 2024-03-26 00:00:00 5 500.00 5125.00 5600.00 475.00 9.27% 0.02%\n", + " SH600219 南山铝业 2024-02-19 00:00:00 31 2100.00 6195.00 7245.00 1050.00 16.95% 0.05%\n", + " SH600150 中国船舶 2024-04-01 00:00:00 1 100.00 3681.00 3745.00 64.00 1.74% 0.00%\n", + " SH600104 上汽集团 2024-03-27 00:00:00 4 400.00 6008.00 6108.00 100.00 1.66% 0.01%\n", + " SH600362 江西铜业 2024-01-18 00:00:00 47 300.00 5298.00 7065.00 1767.00 33.35% 0.09%\n", + " SH600346 恒力石化 2024-02-19 00:00:00 31 500.00 6245.00 7135.00 890.00 14.25% 0.04%\n", + " SH600233 圆通速递 2024-02-08 00:00:00 32 500.00 6140.00 7850.00 1710.00 27.85% 0.09%\n", + " SH600276 恒瑞医药 2024-02-08 00:00:00 32 100.00 4184.00 4632.00 448.00 10.71% 0.02%\n", + " SH600426 华鲁恒升 2024-04-01 00:00:00 1 200.00 5198.00 5380.00 182.00 3.50% 0.01%\n", + " SH600489 中金黄金 2024-01-26 00:00:00 41 600.00 5337.00 8040.00 2703.00 50.65% 0.14%\n", + " SH600547 山东黄金 2024-02-06 00:00:00 34 300.00 6270.00 8847.00 2577.00 41.10% 0.13%\n", + " SH600584 长电科技 2024-02-20 00:00:00 30 200.00 4846.00 5624.00 778.00 16.05% 0.04%\n", + " SH600690 海尔智家 2024-03-29 00:00:00 2 200.00 5000.00 5124.00 124.00 2.48% 0.01%\n", + " SH600674 川投能源 2024-03-19 00:00:00 10 400.00 6524.00 6580.00 56.00 0.86% 0.00%\n", + " SH600795 国电电力 2024-01-26 00:00:00 41 1400.00 5888.00 6930.00 1042.00 17.70% 0.05%\n", + " SH600803 新奥股份 2024-02-08 00:00:00 32 200.00 3488.00 3764.00 276.00 7.91% 0.01%\n", + " SH600886 国投电力 2024-03-27 00:00:00 4 300.00 4431.00 4443.00 12.00 0.27% 0.00%\n", + " SH601009 南京银行 2024-01-18 00:00:00 47 800.00 6224.00 7272.00 1048.00 16.84% 0.05%\n", + " SH600989 宝丰能源 2024-02-08 00:00:00 32 400.00 5524.00 6736.00 1212.00 21.94% 0.06%\n", + " SH600938 中国海油 2024-01-18 00:00:00 47 300.00 6180.00 8694.00 2514.00 40.68% 0.13%\n", + " SH600926 杭州银行 2024-03-27 00:00:00 4 600.00 6648.00 6774.00 126.00 1.90% 0.01%\n", + " SH600919 江苏银行 2024-03-22 00:00:00 7 900.00 6957.00 7164.00 207.00 2.98% 0.01%\n", + " SH601169 北京银行 2024-02-21 00:00:00 29 1300.00 6682.00 7423.00 741.00 11.09% 0.04%\n", + " SH601088 中国神华 2024-03-26 00:00:00 5 100.00 3899.00 3838.00 -61.00 -1.56% -0.00%\n", + " SH601186 中国铁建 2024-03-21 00:00:00 8 800.00 6784.00 6960.00 176.00 2.59% 0.01%\n", + " SH601288 农业银行 2024-03-26 00:00:00 5 1600.00 6768.00 6768.00 0.00 0.00% 0.00%\n", + " SH601319 中国人保 2024-04-01 00:00:00 1 1300.00 6773.00 6890.00 117.00 1.73% 0.01%\n", + " SH601328 交通银行 2024-03-26 00:00:00 5 1100.00 6941.00 7128.00 187.00 2.69% 0.01%\n", + " SH601229 上海银行 2024-03-18 00:00:00 11 900.00 5922.00 6102.00 180.00 3.04% 0.01%\n", + " SH601766 中国中车 2024-03-28 00:00:00 3 1000.00 6530.00 6870.00 340.00 5.21% 0.02%\n", + " SH601800 中国交建 2024-04-01 00:00:00 1 800.00 6944.00 7072.00 128.00 1.84% 0.01%\n", + " SH601390 中国中铁 2024-03-06 00:00:00 19 1000.00 6510.00 7190.00 680.00 10.45% 0.03%\n", + " SH601398 工商银行 2024-03-26 00:00:00 5 1300.00 6877.00 6877.00 0.00 0.00% 0.00%\n", + " SH601600 中国铝业 2024-01-26 00:00:00 41 1100.00 6160.00 7942.00 1782.00 28.93% 0.09%\n", + " SH601618 中国中冶 2024-03-18 00:00:00 11 1900.00 6517.00 6403.00 -114.00 -1.75% -0.01%\n", + " SH601668 中国建筑 2024-04-01 00:00:00 1 1300.00 6838.00 6851.00 13.00 0.19% 0.00%\n", + " SH601728 中国电信 2024-03-25 00:00:00 6 1200.00 6972.00 7224.00 252.00 3.61% 0.01%\n", + " SH601872 招商轮船 2024-02-20 00:00:00 30 900.00 6417.00 7164.00 747.00 11.64% 0.04%\n", + " SH601901 方正证券 2024-04-01 00:00:00 1 700.00 5852.00 5761.00 -91.00 -1.56% -0.00%\n", + " SH601988 中国银行 2024-03-27 00:00:00 4 1500.00 6660.00 6690.00 30.00 0.45% 0.00%\n", + " SH601878 浙商证券 2024-02-21 00:00:00 29 600.00 5934.00 6636.00 702.00 11.83% 0.04%\n", + " SH601899 紫金矿业 2024-01-29 00:00:00 40 500.00 6115.00 8435.00 2320.00 37.94% 0.12%\n", + " SH601838 成都银行 2024-03-27 00:00:00 4 500.00 6665.00 6860.00 195.00 2.93% 0.01%\n", + " SH601865 福莱特 2024-02-26 00:00:00 26 300.00 6270.00 8997.00 2727.00 43.49% 0.14%\n", + " SH601916 浙商银行 2024-02-07 00:00:00 33 2400.00 6528.00 7152.00 624.00 9.56% 0.03%\n", + " SH601808 中海油服 2024-03-18 00:00:00 11 400.00 6140.00 7720.00 1580.00 25.73% 0.08%\n", + " SH601857 中国石油 2024-03-21 00:00:00 8 700.00 6356.00 6818.00 462.00 7.27% 0.02%\n", + " SH601939 建设银行 2024-03-28 00:00:00 3 1000.00 6920.00 6910.00 -10.00 -0.14% -0.00%\n", + " SH601985 中国核电 2024-03-26 00:00:00 5 700.00 6167.00 6216.00 49.00 0.79% 0.00%\n", + " SH603369 今世缘 2024-02-29 00:00:00 23 100.00 5044.00 5963.00 919.00 18.22% 0.05%\n", + " SH603993 洛阳钼业 2024-03-08 00:00:00 17 1000.00 6750.00 8250.00 1500.00 22.22% 0.07%\n", + " SZ000001 平安银行 2024-03-27 00:00:00 4 500.00 5280.00 5320.00 40.00 0.76% 0.00%\n", + " SZ000408 藏格矿业 2024-04-01 00:00:00 1 100.00 3120.00 3169.00 49.00 1.57% 0.00%\n", + " SZ000100 TCL科技 2024-04-01 00:00:00 1 1400.00 6622.00 7196.00 574.00 8.67% 0.03%\n", + " SZ000157 中联重科 2024-03-27 00:00:00 4 800.00 6448.00 6808.00 360.00 5.58% 0.02%\n", + " SZ000338 潍柴动力 2024-04-01 00:00:00 1 400.00 6744.00 6868.00 124.00 1.84% 0.01%\n", + " SZ000333 美的集团 2024-03-28 00:00:00 3 100.00 6423.00 6622.00 199.00 3.10% 0.01%\n", + " SZ000651 格力电器 2024-03-29 00:00:00 2 100.00 3910.00 4012.00 102.00 2.61% 0.01%\n", + " SZ000625 长安汽车 2024-02-22 00:00:00 28 500.00 6795.00 8870.00 2075.00 30.54% 0.10%\n", + " SZ000725 京东方A 2024-04-01 00:00:00 1 1700.00 6936.00 7378.00 442.00 6.37% 0.02%\n", + " SZ000786 北新建材 2024-03-26 00:00:00 5 100.00 2792.00 2938.00 146.00 5.23% 0.01%\n", + " SZ000877 天山股份 2024-03-27 00:00:00 4 900.00 6489.00 6273.00 -216.00 -3.33% -0.01%\n", + " SZ000876 新 希 望 2024-02-19 00:00:00 31 700.00 5992.00 6741.00 749.00 12.50% 0.04%\n", + " SZ000977 浪潮信息 2024-03-29 00:00:00 2 100.00 4449.00 4403.00 -46.00 -1.03% -0.00%\n", + " SZ002027 分众传媒 2024-03-12 00:00:00 15 1000.00 6260.00 6710.00 450.00 7.19% 0.02%\n", + " SZ001979 招商蛇口 2024-03-14 00:00:00 13 700.00 6391.00 6636.00 245.00 3.83% 0.01%\n", + " SZ002648 卫星化学 2024-04-01 00:00:00 1 300.00 5544.00 5556.00 12.00 0.22% 0.00%\n", + " SZ002475 立讯精密 2024-03-12 00:00:00 15 200.00 5648.00 5934.00 286.00 5.06% 0.01%\n", + " SZ002493 荣盛石化 2024-02-07 00:00:00 33 600.00 6000.00 6732.00 732.00 12.20% 0.04%\n", + " SZ002601 龙佰集团 2024-04-01 00:00:00 1 200.00 3804.00 3890.00 86.00 2.26% 0.00%\n", + " SZ002714 牧原股份 2024-03-20 00:00:00 9 100.00 4019.00 4464.00 445.00 11.07% 0.02%\n", + " SZ002709 天赐材料 2024-03-28 00:00:00 3 200.00 4320.00 4640.00 320.00 7.41% 0.02%\n", + " SZ002916 深南电路 2024-02-20 00:00:00 30 100.00 5600.00 9590.00 3990.00 71.25% 0.20%\n", + " SZ002938 鹏鼎控股 2024-02-19 00:00:00 31 300.00 5727.00 6807.00 1080.00 18.86% 0.05%\n", + " SZ003816 中国广核 2024-03-27 00:00:00 4 1700.00 6715.00 6766.00 51.00 0.76% 0.00%\n", + " SZ300433 蓝思科技 2024-02-20 00:00:00 30 500.00 5785.00 7090.00 1305.00 22.56% 0.07%\n", + " SZ300450 先导智能 2024-04-01 00:00:00 1 200.00 4856.00 4992.00 136.00 2.80% 0.01%\n", + " SZ300498 温氏股份 2024-04-01 00:00:00 1 300.00 5685.00 5832.00 147.00 2.59% 0.01%\n", + " Short Position: \n", + " Borrow Stock: \n", + "}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "my_tm" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c38355f-0e77-41db-95b2-3da8e11fd003", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2d672e28-f582-41ff-941e-60e6f408d046", + "metadata": {}, "outputs": [], "source": [] } @@ -152,7 +325,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.7" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/hikyuu/examples/notebook/Demo/Demo1.ipynb b/hikyuu/examples/notebook/Demo/Demo1.ipynb index da34338f..128a7055 100644 --- a/hikyuu/examples/notebook/Demo/Demo1.ipynb +++ b/hikyuu/examples/notebook/Demo/Demo1.ipynb @@ -5,24 +5,29 @@ "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-04-02 16:53:23,119 [INFO] hikyuu version: 1.3.5_202404021336_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:91) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2024-02-29 15:52:37.298 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2024-02-29 15:52:37.316 [HKU-I] - Loading market information... (StockManager.cpp:506)\n", - "2024-02-29 15:52:37.322 [HKU-I] - Loading stock type information... (StockManager.cpp:519)\n", - "2024-02-29 15:52:37.326 [HKU-I] - Loading stock information... (StockManager.cpp:435)\n", - "2024-02-29 15:52:37.460 [HKU-I] - Loading stock weight... (StockManager.cpp:536)\n", - "2024-02-29 15:52:38.615 [HKU-I] - Loading KData... (StockManager.cpp:132)\n", - "2024-02-29 15:52:39.371 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:170)\n", - "2024-02-29 15:52:39.372 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:173)\n", - "2024-02-29 15:52:39.372 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:176)\n", - "2024-02-29 15:52:39.377 [HKU-I] - 0.76s Loaded Data. (StockManager.cpp:149)\n", - "CPU times: total: 500 ms\n", - "Wall time: 3.16 s\n" + "2024-04-02 16:53:23.511 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-04-02 16:53:23.537 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", + "2024-04-02 16:53:23.554 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", + "2024-04-02 16:53:23.561 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", + "2024-04-02 16:53:23.777 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", + "2024-04-02 16:53:24.908 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", + "2024-04-02 16:53:25.588 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", + "2024-04-02 16:53:25.588 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", + "2024-04-02 16:53:25.589 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", + "2024-04-02 16:53:25.593 [HKU-I] - 0.68s Loaded Data. (StockManager.cpp:150)\n", + "CPU times: total: 469 ms\n", + "Wall time: 2.91 s\n" ] } ], @@ -95,7 +100,7 @@ "metadata": {}, "outputs": [], "source": [ - "def DEMO_SG(self):\n", + "def DEMO_SG(self, k):\n", " \"\"\"\n", " 买入信号:周线MACD零轴下方底部金叉,即周线的DIF>DEA金叉时买入\n", " 卖出信号:日线级别 跌破 20日均线\n", @@ -106,7 +111,6 @@ " week_macd_n3: 周线macd平滑窗口\n", " day_n: 日均线窗口\n", " \"\"\"\n", - " k = self.to\n", " if (len(k) == 0):\n", " return\n", " \n", @@ -152,7 +156,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -175,8 +179,8 @@ " cash = tm.current_cash\n", " \n", " #可以不用考虑最小交易单位的问题,已经自动处理\n", - " #num = int((cash * 0.3 // price // stk.atom) * stk.atom)\n", - " return int(cash*0.3/price) #返回类型必须是int\n", + " # return int(cash*0.3/price) #返回类型必须是int\n", + " return cash / price\n", " \n", " def _get_sell_num(self, datetime, stk, price, risk, part_from):\n", " tm = self.tm\n", @@ -197,7 +201,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -226,7 +230,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -262,7 +266,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -285,12 +289,12 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 21, "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA8IAAAL9CAYAAADgsN9hAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAADu9ElEQVR4nOzdd5wcdfkH8M+223I1d5eeu1RCCklIJ5QEE5qhCQYpAoKKIE0BC4goBASx/JQiKAgqKCBVpYdAQgikE1JJb5d2ud637++P3e/szOzM9t3bvfu8X6+82N2Z3Z0r3M4zz/N9HkMgEAiAiIiIiIiIqJcwdvcBEBEREREREWUTA2EiIiIiIiLqVRgIExERERERUa/CQJiIiIiIiIh6FQbCRERERERE1KswECYiIiIiIqJehYEwERERERER9Srm7j6AdPL7/Th8+DCKi4thMBi6+3CIiIiIiIgoSwKBANra2jBo0CAYjdFzvj0qED58+DCqqqq6+zCIiIiIiIiom9TU1GDIkCFR9+lRgXBxcTGA4BdeUlLSzUdDRERERERE2dLa2oqqqiopLoymRwXCohy6pKSEgTAREREREVEvFM8yWTbLIiIiIiIiol6FgTARERERERH1KgyEiYiIiIiIqFdhIExERERERES9CgNhIiIiIiIi6lUYCBMREREREVGvwkCYiIiIiIiIehUGwkRERERERNSrMBAmIiIiIiKiXoWBMBEREREREfUqDISJiIiIiIioV2EgTERERERERL0KA2EiIiIiIiLqVRgIExERERERUa/CQJiIiIiIiIh6FQbCRERERERE1KskHAj/+9//xqRJk+BwOFBdXY0HHngAgUAAALBkyRIYDAbFv/POO0/x/Ndeew1jx46FzWbD9OnTsW7dOsX2ZcuWYerUqbDZbBg/fjzef//9FL48IiIiIiIiIqWEA+Ft27bhrrvuwsqVK3H33Xfj3nvvxV/+8hcAQENDAwYPHoydO3dK/55++mnpuStWrMBll12G66+/HqtWrUJVVRXmz5+P9vZ2AMDevXsxf/58nHHGGVizZg3mzJmDiy66CPv370/Tl0tERERERES9nSEg0rlJOvfcc2G1WvH666/jqaeewl/+8peILK9w8cUXw+/34z//+Q8AoLm5GQMGDMCTTz6Ja6+9Frfffjs++ugjfPHFFwAAr9eLYcOG4Tvf+Q7uu+++mMfS2tqK0tJStLS0oKSkJJUvi4iIiIiIiPJIIvFgymuE/X4/KioqAACNjY2orKzU3XfJkiU455xzpPtlZWWYMmUKVq5cqbndbDZj9uzZ0nYiIiIiIiKiVJmTfWJHRwdeeuklrFy5Eh9//DGAYGn04sWLUVhYiOrqalx44YX4+c9/jqKiIjQ1NaG5uRnDhw9XvE51dTUOHToEANizZ4/m9o0bN2oeg8vlgsvlku63trYm++UQERERERFRL5FUIGyz2eByuVBcXIwnn3wSEydOBADccMMNuOKKK+D3+7FixQr84he/wK5du/Dqq69K64AdDofitRwOB+rr6wEA7e3tmtudTqfmcTz00ENxlUwTERERERERCUkFwl988QVaWlqwdu1a/OAHP8CWLVvw4IMPYuTIkdI+U6dORWlpKa6++mrU1dXBarUCANxut+K1nE6nFPxardao29Xuuusu3H777dL91tZWVFVVJfMlERERERERUS+RVCA8ZswYAMDMmTPhcDhw3XXX4Z577oHdblfsN2XKFADA/v37MWXKFFitVtTU1Cj2qampwdSpUwEAgwcP1tw+YsQIzeOwWq1SgE1EREREREQUj5SbZZnNZgQCAfh8vohtq1evhtFoxPDhw2E0GjFr1ix88MEH0vaWlhasW7cO8+bNAwCceuqpiu0+nw9Lly6VthMRERERERGlKqGMcGtrK26++WZceeWVGDhwIDZs2ICf/OQnuPzyy1FUVIS7774b06dPx4gRI7Bq1Sr89Kc/xfXXXy91lb7ttttw8cUXY/bs2TjppJOwcOFCjB49GvPnzwcA3HrrrZgxYwYWLlyIiy++GE888QT8fj+uueaatH/hRERERERE1DslFAjbbDZ4PB5cffXVaGlpwdChQ3HLLbfgjjvuAAAYDAZcf/31aG1txYgRI3DPPffg1ltvlZ5/wQUX4JFHHsHChQvR1NSEuXPn4q233oLJZAIATJ48GS+++CLuvPNOPPjgg5gxYwYWLVqE4uLiNH7JRERERERE1JsZAoFAoLsPIl0SGaBMREREREREPUci8WDKa4SJiIiIKLpAIIAelHsgIsp7DISJiIiIMuy659bhnD9+ArfX392HQkREYCBMRERElFGBQACLv6zF9to2rN3f2N2HQ0REYCBMRERElFEeX7gk2uVhRpiIKBcwECYiIiLK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v1RycrK+/e1vh1xnfrpfa3PT1NSk++67T0OGDJHL5dLIkSNVUVFh3WduusjoJX7+858bOTk5xquvvmq8//77xlFHHWXMmzfP7mHFjenTpxs//elPjXXr1hmLFi0yiouLrd//888/b7hcLuPZZ581Pv74Y+Okk04yxo0bZ3g8HsMwDOODDz4wEhMTjV/96lfG2rVrjXPPPdfIz883qqur7fyRep36+npjyJAhhiTjueeeMwyDuYkFVVVVxlFHHWWcfvrpxtKlS421a9caL7zwgmEYzI/drrjiCmPUqFHGO++8Y7z//vvG1KlTjZkzZxqGwdzYYfXq1caFF15opKamGpKM9957z7rX3u9727ZtRnp6uvH973/fWL9+vXHDDTcYqampxo4dOwzDMIyKigqjoKDA+Na3vmWsW7fOuP/++w2n02l8+OGHtvysR5q25ua73/2ucc011xgffvih8cEHHxinnnqqMWDAAKOystIwDOamJ7Q1P8EuvPBCQ5JxzTXXWNeYn+7V3txceumlxvDhw41//etfxoYNG4x//OMf/LsWRb0iCHs8HiMvL8/41a9+ZV177bXXDEnGtm3b7BtYHNm5c2fI47/+9a+G0+k0amtrjYkTJxrf/e53rXufffaZIcl49913DcMwjHPPPdc455xzrPvl5eWGy+UynnrqqZ4Yety46667jAsuuCAkCDM39rvrrruM0aNHGw0NDS3uMT/2Gjt2rPHoo49aj1955RUjLS3NMAzmxg533323cdlllxnvvPNOixeM7f2+b7vtNmP8+PHW/aamJqOoqMi49957DcMwjEceecTIz88P+e9w2rRpxre+9a3u/aF6ibbmJvz1wZ49ewxJxuuvv24YBnPTE9qaH9Obb75pDBkyxJg6dWpIEGZ+uldbc/Pmm28aiYmJxtatWyN+LnPTdb1iafSGDRtUWlqquXPnWtdmzZolp9OpFStW2Diy+DF48OCQxykpKfJ6vaqoqNAnn3wSMjdjxozRgAEDrLlZvHhxyP0+ffpo0qRJzF0UrV+/Xr///e/18MMPW9eYm9jwzDPP6JZbblFycnLIdebHfhdddJGef/557d+/XxUVFXrqqad00UUXMTc2uf/++/Xcc89p2LBhLe619/sOv5+YmKgZM2aE3D/llFNC/jucPXs289VBbc1NpNcHkuTxeCQxNz2hrfmRpNra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", + "image/png": "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H//4R/P888+bKVOmmEWLFtkeVtY79dRTzQ9+8AOzfv1689hjj5mJEyeaxYsXG2OMefjhh43f7zcPPPCAefXVV838+fPN1KlTTWdnpzHGmBdeeMH4fD7zk5/8xLz++uvm3HPPNdXV1SYYDNr8krJKW1ubGT9+vJFkHnzwQWMM82JbMBg0kydPNmeeeaZZvXq1WbdunXn44YeNMcyNTRdffLE55phjzNNPP22ef/55M3fuXLNw4UJjDPOSTmvXrjXnnXeeCQQCRpJZs2aNe2+w87B582ZTXFxs/vEf/9Fs2LDBXHbZZSYQCJgtW7bY+FIPK33Ny7Jly8x3vvMds3btWrNy5Uozc+ZMM23aNOYlDfqaF0c0GjUnn3yykWRuvPFG9zrzMnz6mpdIJGIWLFhgZs+ebZ566imzYcMG9/czY5iXdMuKINzZ2WmqqqrMbbfd5l574oknjCSzefNmiyPLfh9++GHKx7/+9a+N1+s1LS0tZubMmebKK690773zzjtGklm5cqUxxpilS5eas88+271/4MAB4/f7zX333ZeeweeA6667znz+859PCcLMi13XXXedOeaYY0w4HO52j7mx57jjjjN33HGH+/Ef/vAHU1RUZIxhXtLpn/7pn8xFF11knn766W6/QA52Hq6++mozffp0935HR4epra01119//TB/VYe/vual6+8Bzz//vJFkNm7caIxhXoZTX/PiuOeee8yJJ55oxo0blxKEmZfh09e83HPPPWbEiBHmwIEDPX4u85JeWbE0+o033tDevXu1aNEi99rChQvl9Xr14osvWhxZ9qurq0v5uLCwUNFoVA0NDVq3bl3KnEyePFljxoxx52TVqlUp9ysqKjRr1izmbIhs2LBBd999t37yk5+415gX+/7jP/5DV155pQoKClKuMzd2LVu2TL/73e+0e/duNTQ06L777tOyZcuYlzS78cYb9eCDD2rSpEkp14diHlatWqWzzjrLve/z+bRgwQLmaQB6mxep598DJKmzs1MS8zKc+poXSdq5c6euvfZa3XXXXfJ4PCn3mJfh09e83H///frqV7+qioqKHj+XeUmvrAjCzh6hiRMnutcCgYBGjRql7du32xpWzjHG6Je//KVOOukk7d69W1LqnEjSuHHjtH37dh04cEANDQ293sfghEIhXXjhhfrud7+rsWPHute3bNkiiXmx5cMPP9SOHTtUVlam0047TSNHjtSpp56qV199lbmx7LrrrpPX69Xo0aNVWVmpTZs26fbbb2de0qzrL+uOoZiHzZs3M0+HqLd56cm9996rsWPH6phjjmFehllf82KM0cUXX6wLLrhAs2fPTrnHvAyv3uYlEolo7dq1Gjt2rM4++2xVVlZqzpw57h5f5iX9siIINzc3y+v1yu/3p1wvKipSOBy2NKrc0tHRoa9//etatWqVfvrTn6q5uVlSbA6SOXPS330MztVXX62qqipdddVVKdeZF7uc5ks//vGPdfnll+uxxx5TRUWFPvWpTykYDEpibmz5+te/rj179uiJJ57Q008/reLiYi1btoy/MxliKOahubmZeRpGJt609J577tFdd90ln8/HvFj0ox/9SNu2bdMtt9zS7R7zYse+ffvU3t6uO++8U+ecc46efPJJzZgxQ0uWLNGmTZuYFwt8tgcwFPx+v6LRqCKRiHy+xJcUCoW6/bBg6G3fvl3Lli3T5s2btXLlSs2ZM0cvv/yyJKm9vT3luc6cOP9o0dt9HLpf/OIXevTRR/X666/L6039t67+vu/My/CKRCKSpG9/+9s677zzJMW6eldXV2v16tWSmBsb3n77bd1///16+eWXNXfuXEnSo48+qvHjx+u0006TxLzYNhT/3+X3+5mnYdLQ0KCLL75Yq1at0v/8z/9oyZIlkvqfN+c5zMvQeuqpp/T9739fL7zwggKBQLf7zIsdzu8AX/rSl/TVr35VkjRr1iz97//+r37729/qG9/4hiTmJZ2yoiJcW1srSSnLAsLhsOrr63vdN4GhsWnTJp100kkqLS3V+vXrdfLJJ0tKzMm2bdtSnr9t2zZNmjRJVVVV8vv9vd7HofvBD36gffv2aeLEiSosLHT3a33lK1/RhRdeKIl5saW6ulqSdNRRR7nXRowYoerqahljJDE3NrzxxhuSpBkzZrjX6urqVFVVpY6ODknMi21D8d+U2tpa5mkY1NfX65RTTtGOHTu0bt06NwRLYl4sufnmm9Xa2qq5c+e6vwds3bpVy5cvV2FhIfNiyciRI+X1elN+B/D5fJo0aZJ2797NvFiQFUF41qxZCgQCWrFihXtt9erV8ng8WrBggcWRZb8LLrhA8+bN0+OPP+7+ki/F/qJOmDAhZU42bdqk7du364wzzpDX69W8efNS7jc2NurVV1/VGWeckdavIdv85S9/0ZtvvqnXX3/d/SPFAvKTTz7JvFh05JFHqrq6OqWpxd69e7Vnzx7NmDGDubHECVlvv/22e23nzp3au3evjjrqKOYlAwzFf1Pmz5+fcr+zs1PPPPMM8zRIl112mcrLy7VmzRodeeSRKfeYFzt+9atf6a233kr5PaCmpkaXX365u1qMeUk/58zg5N8B2tvb9f777+uYY45hXmyw27R66FxzzTVm9OjR5sknnzTPPfecmTx5srniiitsDyurvfvuu0aSeeSRR8x7772X8qehocHccccdpri42Dz88MPmlVdeMfPnzzef+cxn3M//4x//aPLy8szPfvYz8/rrr5ulS5ea6dOnm0gkYvGryk5KOj6JebHr5ptvNhUVFeahhx4yL730kvnkJz9pjjvuONPe3s7cWBKNRs2sWbPMjBkzzMqVK81zzz1nTjnlFDN+/HjT3NzMvFiwZcuWbseODHYeXnvtNePz+czy5cvNG2+8YS677DJTU1PDec8Hoeu8tLS0uGeedv09YO/evcYY5iUdevr70tX48eNTjk9iXoZfT/Py29/+1vj9fnP33XebV1991Vx00UVm1KhRZv/+/cYY5iXdsiYIh0Ihc9lll5mysjIzYsQI861vfcuEQiHbw8pqq1evNpJ6/HPnnXeaaDRqvve975mqqipTUlJiLrzwwm7npv30pz81NTU1JhAImE9/+tNm27Ztdr6YLJcchJkXu6LRqLn++utNdXW1KSwsNIsWLXLPO2du7Nm9e7e54IILTEVFhSkpKTFLliwxH3zwgTGGebGhp18gh2IeHnnkEXPkkUcav99vTj31VPPmm2+m48vJGl3nZevWrb3+HvDtb3/b/TzmZXgdShA2hnkZbr3Ny7//+7+bcePGmYKCAjN//nzz2muvpdxnXtLHY0x8YxoAAAAAADkgK/YIAwAAAAAwUARhAAAAAEBOIQgDAAAAAHIKQRgAAAAAkFMIwgAAAACAnEIQBgAAAADkFIIwAAAAACCnEIQBAAAAADmFIAwAAAAAyCkEYQAAAABATiEIAwAAAAByCkEYAAAAAJBT/j/AtyWqA8tFrQAAAABJRU5ErkJggg==", "text/plain": [ "
" ] @@ -309,65 +313,65 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 22, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "R乘数期望值: 0.00\n", - "亏损交易亏损总额: 0.00\n", - "亏损交易平均R乘数: 0.00\n", - "亏损交易平均亏损: 0.00\n", - "亏损交易平均持仓时间: 0.00\n", - "亏损交易数: 0.00\n", - "亏损交易最大持仓时间: 0.00\n", - "交易平均占用现金比例%: 29.34\n", - "交易机会频率/年: 0.00\n", - "净赢利/亏损比例: 0.00\n", - "单笔交易最大占用现金比例%: 30.01\n", - "已平仓交易总成本: 0.00\n", - "已平仓交易总数: 0.00\n", - "已平仓净利润总额: 0.00\n", - "已平仓帐户收益率%: 0.00\n", "帐户初始金额: 100000.00\n", - "帐户平均年收益率%: 0.20\n", - "帐户年复合收益率%: 0.19\n", - "平均空仓时间: 2488.00\n", - "平均赢利/平均亏损比例: 0.00\n", - "年度期望R乘数: 0.00\n", - "当前总资产: 101331.66\n", - "最大单笔亏损: 0.00\n", - "最大单笔亏损R乘数: 0.00\n", - "最大单笔亏损百分比%: 0.00\n", - "最大单笔盈利百分比%: 0.00\n", - "最大单笔赢利: 0.00\n", - "最大单笔赢利R乘数: 0.00\n", - "最大连续亏损R乘数: 0.00\n", - "最大连续亏损笔数: 0.00\n", - "最大连续亏损金额: 0.00\n", - "最大连续赢利R乘数: 0.00\n", - "最大连续赢利笔数: 0.00\n", - "最大连续赢利金额: 0.00\n", - "最长空仓时间: 2487.00\n", - "未平仓头寸净值: 1646.93\n", - "现金余额: 99684.73\n", - "空仓总时间: 2488.00\n", - "空仓时间/总时间%: 100.00\n", - "累计借入现金: 0.00\n", - "累计借入资产: 0.00\n", "累计投入本金: 100000.00\n", "累计投入资产: 0.00\n", - "累计红利: 206.90\n", - "赢利交易平均R乘数: 0.00\n", - "赢利交易平均持仓时间: 0.00\n", - "赢利交易平均赢利: 0.00\n", - "赢利交易数: 0.00\n", - "赢利交易最大持仓时间: 0.00\n", - "赢利交易比例%: 0.00\n", + "累计借入现金: 0.00\n", + "累计借入资产: 0.00\n", + "累计红利: 206.23\n", + "现金余额: 101306.49\n", + "未平仓头寸净值: 1393.84\n", + "当前总资产: 102700.33\n", + "已平仓交易总成本: 0.00\n", + "已平仓净利润总额: 0.00\n", + "单笔交易最大占用现金比例%: 99.55\n", + "交易平均占用现金比例%: 99.22\n", + "已平仓帐户收益率%: 0.00\n", + "帐户年复合收益率%: 0.39\n", + "帐户平均年收益率%: 0.39\n", "赢利交易赢利总额: 0.00\n", + "亏损交易亏损总额: 0.00\n", + "已平仓交易总数: 0.00\n", + "赢利交易数: 0.00\n", + "亏损交易数: 0.00\n", + "赢利交易比例%: 0.00\n", "赢利期望值: 0.00\n", + "赢利交易平均赢利: 0.00\n", + "亏损交易平均亏损: 0.00\n", + "平均赢利/平均亏损比例: 0.00\n", + "净赢利/亏损比例: 0.00\n", + "最大单笔赢利: 0.00\n", + "最大单笔盈利百分比%: 0.00\n", + "最大单笔亏损: 0.00\n", + "最大单笔亏损百分比%: 0.00\n", + "赢利交易平均持仓时间: 0.00\n", + "赢利交易最大持仓时间: 0.00\n", + "亏损交易平均持仓时间: 0.00\n", + "亏损交易最大持仓时间: 0.00\n", + "空仓总时间: 2521.00\n", + "空仓时间/总时间%: 100.00\n", + "平均空仓时间: 2521.00\n", + "最长空仓时间: 2520.00\n", + "最大连续赢利笔数: 0.00\n", + "最大连续亏损笔数: 0.00\n", + "最大连续赢利金额: 0.00\n", + "最大连续亏损金额: 0.00\n", + "R乘数期望值: 0.00\n", + "交易机会频率/年: 0.00\n", + "年度期望R乘数: 0.00\n", + "赢利交易平均R乘数: 0.00\n", + "亏损交易平均R乘数: 0.00\n", + "最大单笔赢利R乘数: 0.00\n", + "最大单笔亏损R乘数: 0.00\n", + "最大连续赢利R乘数: 0.00\n", + "最大连续亏损R乘数: 0.00\n", "\n" ] } @@ -378,6 +382,26 @@ "print(per.report(my_tm, Datetime.now()))" ] }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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WhyjRo2yeY3AwEBq1qxJcHAw+/bt448//mDIkCFMmDAh0XtHR0fz2WefcenSJfbu3UuHDh1o164du3fvthpbvnx58uXLR5cuXQAoXLgwmTNnZufOnQQGBhIYGEiJEiXYvHmz8rpcuXJJvv99+/ZRr1496tWrx+3bt636p02bxi+//ML8+fPZtWsXV69epUOHDkr/jRs36NKlC8WLF2fPnj1W51+/fp0mTZqwb98+lixZgq+vL927d1f6b9++TYMGDahVqxYnTpygatWqNGvWzGLHyYULF9KlSxe+/vprTp48yfLly8mQIUOi7+vp06dMnz7dqr19+/acOnWKzZs3s2rVKrZu3cp3332X5HMSqcsbBbtOnz5N69atqVChAmfOnEl07IgRIxg3bhzDhw/n9OnTzJo1CxcXl3earBBCCCFSVowuPtjlqLEd7PJycVR+Ts4yRr1Bgl1CCCH+WYsWLUKn01GjRg3WrVuHTqejatWqSv/x48cTTdTYu3evknF04MABMmbMSN26ddm8eTPTp0+nWbNmPH782Oq8p0+fUqdOHY4ePcrGjRupUqUKixYtomnTpjRu3JgVK1ZYjHdwcGD27NlUr16dK1eucPHiRQYPHoxGo+HJkyc8fvyYq1ev4u3tzePHj3n+/Hmy3v+KFSvInTs3y5cvt+ozGAxMmzaN4cOH07hxYypWrMiPP/7I9u3blcDY7t27efbsGX5+fjav37p1a/r27UuJEiVo3LgxvXr1YteuXUr/zJkzyZMnD1OmTKFIkSL88ssvpEuXjiVLlgAQFBREv379mD17Nj179qRw4cLUqVOHggULJvq++vbta7Wc9PLly2zevJnFixdTpUoVGjduzA8//MDy5cutss1E6vZGwa6NGzfi6OjIli1bEh135coVJk+ezG+//cb//vc/ChUqRJMmTfDx8XmnyQohhBAiZcXp44NTDhrbHwncnF4tb0xOgXq9ZHYJIYT4wGzbto3169dbtQcEBNC5c2fq1atHx44d2bNnD+nTp1f6a9euzZkzZ3j48CEFChRg3LhxhISEALB//35Kly7N/fv3OXDgALVq1UKn02E0Glm2bBlDhgyhQ4cO/O9//1MCZX5+fixYsIBVq1ZRuHBhChcuzO7duxkxYgRZs2aladOm5MuXj86dO1O/fn1q1qwJwMCBAylZsmSC72/RokXMnj0b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1q5dS0REzs7O5ObmRkRE06ZNo1q1atGbN2/+0jFkMhk5OjqStbU1ZWZm0oABAyg8PJyIiC5fvkx169alDx8+0PDhwykoKIi3r5ubG+3du5ecnZ3p2rVrNHr0aPL39ycTExNq3749SaVSIiKKj48nU1NTSk5O5s7Dy8uLiIhMTEzo+fPntH37dgKgcNPU1KTc3FzS1NQsdxMKhSQQCHhpXbt2rdI1aNGiBf3666+0bNkysrS0LLecVCqlatWqkaenJ5d26dIlAkCJiYkK95k2bRrZ2NgQEdH79++pevXqXN7FixepT58+RER09OhRatu2Lclksiq1+Uv4+PhQrVq16N27d7z00aNH06ZNmyrct02bNrRw4UJemrOzM82fP5+IiI4cOUIikYhu375NREQhISHUpk0bIiJavnw5LV++nN69e0cikajc9/f06dM0ZsyYct/b0n3V1dV56c+ePaPMzEx6+vQpr32urq40Z84cIiIqKiri0uPj4yknJ6fS65WRkUFCoZDOnj3LpXl5eZFIJKL8/PxK9//1119JX1+fe92qVSuaNm0a9zo3N5f09fVp+/btRETUqFEjevLkCRER5eXlkYaGBhERvXjxgmrWrElv376t9JhfY+/evWRkZERt2rQpdxMIBJXWs23bNurevXuFZczNzenatWuV1vX8+XMq/Yj39vamdu3aUfPmzen8+fP05MkTql69Ov355580fvx4EovFFBsbS6qqqmRiYkI6Ojo0fPjwqp28AqWfT6Vev35NAOjy5csKy1f0ufHbb7+RoaEh736ZPHky91nw66+/8v6uhg4dSqdOnSIiop49e9Lu3bu/+jwYhmEYhvm+TZw4kRYvXlxhmbS0NNLU1CRjY2OysLCgffv2kY6ODjVq1IgOHjxIxcXFRER04sQJ6tu3b4V1WVpaUkBAQKXtmj59OvcsSkQkFospLy+Pe+YjIrKzs6OLFy8SEVHnzp3J19eXxGIxvXjxgjQ1NRXWa21tTZcuXeJed+7cmfz9/Ymo5LlDTU1N7vng1atXREQ0ePBg2rt3LxERjRs3jnbu3KnwGEuXLqU6derQ8+fP5TaZTEapqak0e/ZsCg8Pp8jISJo8eTIJhUKKiIggIqIrV64QALp9+7bc/nl5ebxjJSUlEQC6desWlyaVSql379505swZIiJ69OgRCYVCWr16NcXFxZGr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YUydOpVTp05x9OhRFi1axJw5c7CxsUGj0TB58mQWLlxIfHw8ycnJTJs2jUmTJlGzZs3/q7+H+H9Lwi4hhBBCCCGEEM+8H374gZYtWz5yzC+//IK7uzvvv/8+bdq0ITc3l06dOpGbm8uPP/6ojrt58yarVq2iffv2jB07lkmTJvHzzz8TExPDqlWraNq0KTNnziQxMVHdGufp6cm6devw9vamdevWbNu2jXv37jFixAj69etndkbVb7/9Rt26df/j33r27Fmef/55oqOjmTBhAj///DPTpk3jrbfe4vr162zatIlWrVqp4wsLC83eUPhHjRs3Zvr06fzjH/8gJycHRVHYv38/bdq0wcnJiT179qiB1QsvvEB8fDxhYWH4+vry3XffqcFVv379uHDhAnfu3GHAgAFYW1urVXU7d+5k6tSpbNy4keTkZCZNmkRpaSkGg4GrV6+q55T9kZubG/v37+fUqVN06tSJvXv3cujQIZo2bQqUnyV29epVNYD88ssvad++Pf369SMwMJDJkyfz97//XZ1v2bJlDB06lLfeeotBgwbRt29fPvnkk//4byH+JIoQQgghhBBCCPEMuX//vvL9998rOTk5SkFBgXLhwgWlcePGSnR0tNmYa9euKRqNRlEURSkpKVHi4+OV3r17K126dFFyc3MVRVGUyMhIpUaNGso//vEPpaysTPH391esra2Vbt26KV9++aVy//59s3uXlZUp+/fvV4YMGaJoNBqlWbNmyv3795XRo0crdevWVTZs2KAoiqKkpaUpnTp1UgYNGqTcvHlT+e2335TCwkJly5YtSo0aNRSj0Vjp78vKylIApaSkxGJ/dna28tVXXyk6nc5if0ZGhpKdna0UFhYqBw8eVBwcHCod+0dpaWlK8+bNlcaNGyvbt29X21NSUpROnTqp3wsKCpR3331XcXBwUF5++WXl+++/V0JDQ5VmzZopr7/+unL58mVl7dq1yuDBg5X3339f0Wg0yq5duxRFUZTLly8rjRo1Ujp16qQYDIYqrUuIh1kpyr8jViGEEEIIIYQQ4hmQm5tLw4YN1bcyajQaRo8ezRdffKFWIPn6+pKQkKBupXv++eepVq0aU6ZMYfLkyWbbFi9fvszw4cP58ssvcXNzw97eHjc3t8euo6SkhDt37uDm5sbvv/+Oo6Mjzs7O/Prrr7Rr147p06ezaNEiPv74YxYsWABAw4YNWb16NSNGjKh03uzsbBo3bkxJSYnZWxSryt/fn+3btwNQq1YtPv74Y8aPH1+laxVFIS4uDj8/P+zs7NT21NRUgoODSU1NNRufm5tLfn4+Wq2WgwcP4u/vj6enp9mYRYsWMXToULy9vc2uS0hIYNSoUU/8+4SQsEsIIYQQQgghxDPLYDDw3HPPYWVl9chxWq32kW8wfPgNfv8Nt2/fpl69ev+1+Z6UyWTCaDSaBVZCPCsk7BJCCCGEEEIIIYQQzww5oF4IIYQQQgghhBBCPDMk7BJCCCGEEEIIIf5kZWVl/G/feFVcXPxnL0E8IyTsEkIIIYQQQggh/s3Hx4fIyMgqjb1//77Fdr1eb7Fdp9NRr149jhw5YtZ+69YtHBwcuHHjRpXue+7cOWJjY6s0VlEUMjMzOXbsGGvWrOHQoUMVxnh7e5OUlFSl+aqirKyMwsJCsrOzuXbtmtp+9epVSktLyc7OplGjRgD8/vvv5OfnA9C3b1927tz5X1uH+N/ruT97AUIIIYQQQgghxP+t7t27k5ubi0ajsdhfUFBAz549iY6OfuQ8JpMJa+vH14Xcv38fLy8v1qxZw8CBA9X2wsJCOnTowKeffsobb7xhds1XX33F/fv36d27t1n75s2badWqFe7u7mbt+fn57Nu3r8K9MzIyWLVqFffu3bO41qFDh/LOO+/w448/8tNPP1FSUqL2vfnmmwwYMKDS3xUdHc24cePU7/Xq1aNv376Eh4dTv359APbv38+8efMwGo0YjUZKS0vVj52dHTY2Nmg0GpycnGjWrBl79+4FYN68eXTp0oWAgAB1/uDgYDp16sT777/PpUuX6NSpU6Vrq0x6ejrvvvsuKSkpNGjQgLCwMMaMGWNxrF6vZ/bs2Wzbtg2DwcCbb77JmjVrcHR0BMrDwY8++oiNGzdSUFCAr68vX3zxBXXq1HnidYk/j4RdQgghhBBCCCGeCbGxsXh7e1vs27VrFwcOHHjsHFUNuxwdHfniiy948803uXr1Kg0aNABgypQptGvXziwAg/IteitWrMDa2ho3Nze1/fnnnycjIwO9Xk+tWrXM3hp58eJFiouLSUpKQq/X069fPwBq167NkiVLKq0sc3FxoWbNmgwYMIDBgwezZs0ajhw5QtOmTdU3QFpZWVFSUoKDg0OF621sbPjhhx8wmUz8/PPPzJgxg5EjR3L8+HEAfH196dy5M7a2ttjZ2WFvb8+lS5cIDg4mNTW10me2YMECfH19GTFiBADx8fFcvnyZr7/+mlOnTqHRaGjevPljn/3DCgsL6du3L76+vqxdu5a4uDiCgoJo2bIlL730UoXxM2bM4MiRI+zatQuj0UhgYCD29vasW7cOgPDwcD777DOio6OpVasWEydOZOzYsRYr4sRfl4RdQgghhBBCCCGeCYGBgWqFzh/duXOHrl27PnYORVEeG3ZNnDiRmJgYbG1tAWjZsiUlJSXY29ur17q4uHDv3j01UAoODqZOnTpcuXKFd955B39/f/z8/Jg8eTKKopCcnExAQAADBgwgMDBQvdeUKVMwGo2sXr2aoqKiR65r9uzZvP766wCsX78eKK8C27hxIz4+Po/97Q97/vnnAWjVqhU6nY4RI0ZQWFjIrVu3WL58eYXxeXl5ZGRkEBQUVKGvevXqrF69mo4dO3LmzBk1YOvWrRs7duzAwcGB2NhYDAYD/fv3N7vucds1o6OjKSsrY9OmTdjZ2dGuXTsOHDjA+vXrK4Rdd+7cYdOmTezYsUOtrlu0aBEzZsxg5cqV2NvbEx4ezvz58xk0aBAAERER+Pn5cf36dZo1a1b1Byj+VBJ2CSGEEEIIIYR4JnTp0kWtXPqjH374wez7qFGjKg1SxowZY3EbXFRUlBrmrFy5kilTpqh9bdq0YcOGDXTv3l1te1ClVVBQwM2bN4mLi0Oj0TB79mzCw8Px8/OjQYMGxMTEUFxczLRp0/jkk0/o1KkTXl5e6jxvvPGGesbVo3Tu3BmAs2fPEhMTA0BRURFFRUXqWm1sbFi9evVj53qY0WjE2toaW1tbXF1d6dWrV4UxGRkZpKenW+zTaDSkpKQwdOhQ9XmUlJTg4eEBlIeH27ZtIyAggC5dulBSUsK7775LTEwMer2eVq1aMWPGDKZPn15h7sTERPr06YOdnZ3a1qdPH3bv3l1h7MmTJzGZTPj6+pqN1el0pKWl4ejoiFarxc/PT+3v2bMn1tbWnDlzRsKup4iEXUIIIYQQQgghnnqhoaE0atQIZ2dni/25ubkYDAb1e2RkJBs2bKgwzt3dnVmzZpkFWQ88XDUWGhrKhx9+qH7Pz89n4MCBPPdcxX9mV69enT179jBy5Eju3bvH/fv3uXfvHo0aNaK4uFidx9nZGVdXV1asWMGWLVvU648ePcrx48cZPnx4pb//s88+w9XVlddee43q1aurQdL+/fuxsbFRv1dli+bDrly5QlhYGGPHjkWj0aDRaBgzZgyKopCXl4eXlxdarZbU1FQOHDhAUFAQ+/btIzo6msjISHbs2EHjxo3p3LkzKSkpREREcPHiRc6fP0+PHj0IDAwkMTGR0tJSWrRoQVBQEFevXqV+/foEBARQVlaGh4cHjRs3tri+a9euVTiDrEmTJmRnZ1scW7duXZycnMzGAmRnZ6t/u4dDLY1GQ506dSzOJ/66JOwSQgghhBBCCPHUGjFiBCkpKRQVFdGwYUPgfyqHHhyo/kdDhgxhyZIlFbY8Xrt2jYKCAk6fPs38+fMfed/ly5dXubILoEaNGgQGBqLRaHB2dsbZ2ZkVK1bQs2dPxo0bR/Xq1bGxsbF4L1tbW86fP89vv/1W6Xpu3LihhjUeHh54eHig1+tZsWIFjo6OvPHGG2qw8zhlZWU4ODhgMpkAWLZsmVlVVceOHbl3757ab0lSUhJt2rTB19cXHx8fDh8+zNSpU1m4cCFDhgzh66+/ZunSpYSHhzNw4EA8PT2Jj48H4Pz587Rr1w4or0R70G5JcXFxhb+jo6OjxTdiWhrr4OCAlZUVer0enU6HtbU19vb2VZpP/HVJ2CWEEEIIIYQQ4qm1a9cu9u3bx4EDB9i8eTMAW7duJTU1lVWrVj3RXLGxsQwbNoyzZ8+SlZVVaTXRf+rBWwhLS0spLCzE3t6e27dvc/LkSQoLCykqKqKwsJBWrVoxbNgwoPyQ+pMnT/Lyyy/TtWtXnJ2dOXHiBFAewowbN46VK1fi7u7OpUuXzLYRbty4kTZt2nDmzBm6d+/OnDlzCAkJeew6bWxsSEtL48aNG0yaNIlz586ZVaxdvHgRAK1Wq1aM3bp1i8zMTAoKCgDw8fHhyJEjFBQUMGfOHDZu3MiVK1fQarW0bt2ajIwMXF1dWbp0KQCtW7dm9uzZ6HQ6Dh8+TM+ePav0TO3t7SktLTVr0+l0Fs9uszTWYDCgKAqOjo4YjUZMJhNGo9Hs91Y2n/jrkrBLCCGEEEIIIcRTb+/eveqbAO/evcvQoUMJDAxk9+7dtGzZEoDff/+dQYMGsXHjxgrX3717l1WrVrF9+3ZeeOEF5s2bp557ZcnMmTMJDQ1Vv5eUlODr61vpNsHr16/TuXNn9SB7FxcX8vPzqV+/Punp6VSrVk39uLu7q9dlZWXh4eHBqFGjmD9/Ps7OzgwaNIiCggI2bdqEr68v7u7uTJ48mUmTJjF16lQAcnJy+Pjjj/n666956623+Oabb+jduze5ublm2y8r86A6LDY2lq5duzJ8+HBGjBhBbm4uu3btUrciQvm5YNOmTUOr1RIaGsqIESPIzs7m/PnzfPvtt2RkZLB9+3b+/ve/qxVUD59JlpqaSqNGjfDw8CA8PJx9+/YRFhb22DUCuLm5kZWVZdaWlZVl8a2Obm5u3L59G4PBoL5cIDMzE4DmzZtz7949oHxL44O/gV6vJzc394nfEin+XE+2WVcIIYQQQgghhPgLGjp0KGlpaaSlpanVQtHR0TRu3JhvvvmGtLQ0mjZtyujRoytcazKZmDBhAi+//DJ9+vRhxowZJCQkEBkZafFeffr04dixYxQXF6ufF154gaNHj5q1hYSEqBVC7u7uZGdnU1xczI0bN0hOTubVV19l/PjxLFu2jJCQEIYMGcKLL77I3bt3Wb58uVppFBkZqVaFQXmwduHCBXXb465du0hKSjL7PQEBAQwfPlzdutiqVSuOHj36yK2HlnTp0oWAgABmz56NXq+npKSE5ORk2rZty4oVK/Dy8iIwMBBvb2/at2/PhQsXyMzMxMfHh/fee4+EhASWLVuGv78/8fHxuLi4oNVqycnJYc+ePdSsWZMGDRoA5QHiwoUL6d+/Py1atKjS+rp37863335LWVmZ2paQkMCrr75aYWy3bt0wGAxmzyohIYE6derQtm1bOnbsiEaj4dixY2r/8ePHsbKyokePHk/03MSfSyq7hBBCCCGEEEI89SxVdllbWzNlyhSmT59Oly5dcHNzq/C2QL1ez4QJEzh37hznzp0DoFatWmzZsoUBAwZQWFhodl6Vq6srOp2uwv1LS0t59dVXzc7pAti8eTObNm3io48+Ijc3l7KyMpydndXQ5+effyYlJYXq1avj4uJC9erVycvLo1GjRphMJoqLi3nvvfcYNGiQeo5YQUEB1apVA8qDrz59+rB06VK1+ujQoUNkZGTwr3/9i9zcXHUtXl5efPTRRwBmQdzjLFmyhFatWhEREcHcuXPZunUrUH4GVo0aNTAYDCxYsICQkBCioqLo3bs3S5cuJTo62myenJwcnJycCAkJ4W9/+xshISFs3rxZDe0erNXGxgZFUbCyssJkMjFgwADeeecddWvnwyZOnKi+GTMkJIS9e/dy+fJltm/fDsCePXv44osvOHToEO7u7gwbNoypU6eyadMm7t+/z6JFi5gzZw42NjZoNBomT57MwoULadKkCc7OzkybNo1JkyZRs2bNKj0r8RehCCGEEEIIIYQQT7G9e/cqEyZMUAoKCpRLly4pc+bMUV5++WVl0aJFislkUtq1a6c4OTkp2dnZZtelpKQo3t7eiqenp5KZmVlh3ri4OMXJyUl55ZVXlKSkpEeuwcvLSzl58mSl/Xfv3lUMBoNZW1BQkBIVFVVh7EcffaRMnz5dURRFCQsLUzp06KB069ZN+dvf/qZs27ZNmT17ttKvXz9l5cqVSkREhLJ7927l119/VQYPHqz861//UrKzs5UzZ84oiqIo169fV+rVq/fItbdr105JTExUFEVRoqKiFBsbmwpjQkNDlWrVqim//fabYjKZlLi4OKVp06aKv7+/otfrlZSUFKVTp06KoihKWlqa0rhxY6Vv377K6dOnFZPJpM6j0+kUPz8/BVBatGih3nfHjh2KRqNRNm7cqLi5uSlvv/22otfrFZ1OpzRp0kRZs2ZNpetPSkpS2rRpo9jZ2SkdOnRQTpw4ofatWbNGadKkiaLT6dS/w8iRIxVHR0elXr16SlhYWIX1TZ48WXFxcVFq1KihvPfee+q14ulhpSiK8mcHbkIIIYQQQgghxH9i7dq1REREUFRURPPmzWnRogVFRUWYTCb8/Pw4dOgQpaWldOjQge3btzNx4kT8/f3ZvHkzK1euJDAwkE8//RRXV1eL86enpzNr1iyOHj1KXFwcAwcOtDjO0tsYH2fevHncu3eP1atXq21Go5EBAwYwatQoxo8fz0svvcTu3bu5f/8+ixcvZtmyZep5ZL/++ivp6els27YNBwcHxo4di7OzM4sXL6ZZs2YAZGRk8NJLL5GTk2N278zMTLRaLUVFRbz++uucPHmSDh06PHbNFy9eZNiwYRgMBj7++GNGjRoFlJ+7FRwcrFbXFRYWMnfuXCIjI+nQoQNffvklW7ZsITY2Fk9PT5YvX87x48c5duwYrVu3Zs2aNcTExDB8+HDS09Px8/OjXr16nDlzpsoVaEI8IGGXEEIIIYQQQoinVl5eHlC+9fCBrVu3cuLECTIyMhg9ejRBQUFYWVnx008/sX79egYPHkzdunXJy8vjlVdeqdJ9rl279shDyv+TsCsjI4M33niDjIwM9WB7o9FI+/btOXz4MEVFRcycOZMXXniBmJgY3n//ffbu3UtYWBg+Pj4cPHiQ2NhYtmzZQllZGe3bt0ej0XD27Fl1vsrCrr179zJhwgSsra3x9fVl27ZtFbZgWqIoCnFxcfj5+WFnZ6e2/zHseiA3N5f8/Hy0Wi0HDx7E398fT09PszGLFi1i6NCheHt7m12XkJCghmlCPAkJu4QQQgghhBBCiL8ok8lk9obHsrIy9YwrKD8r7OHQSQghYZcQQgghhBBCCCGEeIZYP36IEEIIIYQQQgghhBBPBwm7hBBCCCGEEEKIP1lZWRn/2zdeFRcX/9lLEM8ICbuEEEIIIYQQQoh/8/HxITIyskpj79+/b7Fdr9dbbNfpdNSrV48jR46Ytd+6dQsHBwdu3LjxZIt9jNDQUJYsWfJfnbMqysrKKCwsJDs7m2vXrqntV69epbS0lOzsbBo1agTA77//Tn5+PgB9+/Zl586d/8/XK549EnYJIYQQQgghhHjqde/endatW+Pt7W3x06xZM4KCgh47zx8PhK/M/fv38fLy4sCBA2bthYWFeHp6EhcXV+Gar776ivv379O7d2+z9s2bN9OqVSvc3d0t3svV1RUrK6vHfp5//vlHrtnb25vatWtTv3599VOnTh2z66Kjo83mrF+/PmPGjDF7m+P+/ftp27YtL7zwAi1btqRp06Y0aNCAWrVq0aBBA5o2bUqnTp0YOHAgM2fOVK+bN28eERERZmsKDg7m888/x2AwcOnSJTp16vTI32BJeno6PXr0QKPR0Lx5c2JiYiodq9frmTp1KrVq1cLFxYUJEyaYhZaKohAWFkbDhg1xcnJi6NCh5ObmPvGaxJ/ruT97AUIIIYQQQgghxH9DbGws3t7eFvt27dpVIZiypKphl6OjI1988QVvvvkmV69epUGDBgBMmTKFdu3aMXDgQLPxxcXFrFixAmtra9zc3NT2559/noyMDPR6PbVq1cLKykrtu3HjBk5OTuTl5VVpi+PD11bmm2++MXtGGRkZvPbaa2ZjbGxs+OGHHzCZTPz888/MmDGDkSNHcvz4cQB8fX3p3Lkztra22NnZYW9vz6VLlwgODiY1NbXSey9YsABfX19GjBgBQHx8PJcvX+brr7/m1KlTalj1JAoLC+nbty++vr6sXbuWuLg4goKCaNmyJS+99FKF8TNmzODIkSPs2rULo9FIYGAg9vb2rFu3DoDw8HA+++wzoqOjqVWrFhMnTmTs2LEcOnToidYl/lwSdgkhhBBCCCGEeCYEBgbi6Ohose/OnTt07dr1sXMoivLYsGvixInExMRga2sLQMuWLSkpKcHe3l691sXFhXv37lFSUoKDgwPBwcHUqVOHK1eu8M477+Dv74+fnx+TJ09GURSSk5MJCAhgwIABBAYGmt3PxsamKj9fdfz4cWbOnMnNmzextrZm3759bNy48YnmeFDt1apVK3Q6HSNGjKCwsJBbt26xfPnyCuPz8vLIyMiwWD1XvXp1Vq9eTceOHTlz5gwODg4AdOvWjR07duDg4EBsbCwGg4H+/fubXRcbG/vIdUZHR1NWVsamTZuws7OjXbt2HDhwgPXr11cIu+7cucOmTZvYsWOHWl23aNEiZsyYwcqVK7G3tyc8PJz58+czaNAgACIiIvDz8+P69es0a9as6g9Q/Kkk7BJCCCGEEEII8Uzo0qUL9erVs9j3ww8/mH0fNWpUpUHKmDFjGDNmTIX2qKgoNcxZuXIlU6ZMUfvatGnDhg0b6N69u9r2oNKqoKCAmzdvEhcXh0ajYfbs2YSHh+Pn50eDBg2IiYmhuLiYadOm8cknn9CpUye8vLxQFKXS878exdPTk08++YQvvvgCjUbDmDFjaNGiBQCvvfYazz33P1FAWVkZ1atXf+R8RqMRa2trbG1tcXV1pVevXhXGZGRkkJ6ebrFPo9GQkpLC0KFD1edRUlKCh4cHUB4ebtu2jYCAALp06UJJSQnvvvsuMTEx6PV6WrVqxYwZM5g+fXqFuRMTE+nTpw92dnZqW58+fdi9e3eFsSdPnsRkMuHr62s2VqfTkZaWhqOjI1qtFj8/P7W/Z8+eWFtbc+bMGQm7niISdgkhhBBCCCGEeOqFhobSqFEjnJ2dLfbn5uZiMBjU75GRkWzYsKHCOHd3d2bNmmUWZD3wcNVYaGgoH374ofo9Pz+fgQMHmgVJD1SvXp09e/YwcuRI7t27x/3797l37x6NGjWiuLhYncfZ2RlXV1dWrFjBli1buHLlCm3btq3qI1Bdv36dXr16ceTIEZydndUA6ujRo9SoUUOtSIPyIOvWrVuVznXlyhXCwsIYO3YsGo1GDc8URSEvLw8vLy+0Wi2pqakcOHCAoKAg9u3bR3R0NJGRkezYsYPGjRvTuXNnUlJSiIiI4OLFi5w/f54ePXoQGBhIYmIipaWltGjRgqCgIK5evUr9+vUJCAigrKwMDw8PGjdubHF9165dY8CAAWZtTZo0ITs72+LYunXr4uTkZDYWIDs7W/3bPRxqaTQa6tSpY3E+8dclYZcQQgghhBBCiKfWiBEjSElJoaioiIYNGwL/UzlUv359i9cMGTKEJUuWVNjyeO3aNQoKCjh9+jTz589/5H2XL19e5cougBo1ahAYGIhGo8HZ2RlnZ2dWrFhBz549GTduHNWrV6+wXbFNmzbqWV2JiYkEBASQkZFhFlY9jsFg4Pjx4/z000+EhoZavNZoNPLZZ5/h7+8PlFd7OTg4YDKZAFi2bJlZVVXHjh25d++e2m9JUlISbdq0wdfXFx8fHw4fPszUqVNZuHAhQ4YM4euvv2bp0qWEh4czcOBAPD09iY+PB+D8+fO0a9cOKN/C+aDdkuLi4gp/R0dHR4sVcZbGOjg4YGVlhV6vR6fTYW1tjb29fZXmE39dEnYJIYQQQgghhHhq7dq1i3379nHgwAE2b94MwNatW0lNTWXVqlVPNFdsbCzDhg3j7NmzZGVlVVpN9J8KCAgAoLS0lMLCQuzt7bl9+zYnT56ksLCQoqIiCgsLadWqFcOGDTO7dtWqVQQHB2NlZYXRaLQ4v7W1NdbW1mRlZbFz504OHjzI999/z8GDBxk2bBiDBg0iOjoaRVH4/PPPeffdd7G2tmbixImUlpaq89jY2JCWlsaNGzeYNGkS586dM6tYu3jxIgBarVbdinjr1i0yMzMpKCgAwMfHhyNHjlBQUMCcOXPYuHEjV65cQavV0rp1azIyMnB1dWXp0qUAtG7dmtmzZ6PT6Th8+DA9e/as0jO1t7c3WzuATqezeHabpbEGgwFFUXB0dMRoNGIymTAajWa/t7L5xF/X418xIYQQQgghhBBC/MXt3bsXb29vvL29+eCDD4DyA+udnJzU9oYNGzJp0iSL19+9e5dVq1YREhJCUFAQ8+bNe+T9Zs6cqVZoOTs7c/XqVXx9fc3aHnb9+nVq166Nk5MT9evX58UXX+TYsWPExcWxceNGDhw4wPnz57l9+3aFQAbg7NmzLFy4EFtb20o/YWFhAJw7d44zZ85Qt25dFi1aRGpqqrpdD8rDm2nTpj3yIH4PDw/69etHbGwsO3bsYNeuXUD5dtD169cTHBysbh8sKipi2rRpaLVaQkNDqVatGtnZ2Zw/f55NmzaRkZHB9u3badKkCV5eXuh0Ory8vKhfvz7169cnOzubunXr4uHhQXh4OPv27WPkyJGPfP4PuLm5kZWVZdaWlZVl8a2Obm5u3L5922w7a2ZmJgDNmzdX35L58JZFvV5Pbm7uE78lUvy5JOwSQgghhBBCCPHUGzp0KGlpaaSlpanVQtHR0TRu3JhvvvmGtLQ0mjZtyujRoytcazKZmDBhAi+//DJ9+vRhxowZJCQkEBkZafFeffr04dixYxQXF6ufF154gaNHj5q1hYSEqBVC7u7uZGdnU1xczI0bN0hOTubVV19l/PjxLFu2jJCQEIYMGcKLL77I3bt3Wb58uVkFV05ODoqiVPrp3bs3jRo1Asq3ae7YsYPOnTtbDLRu375d6RbPP+rSpQsBAQHMnj0bvV5PSUkJycnJtG3blhUrVuDl5UVgYCDe3t60b9+eCxcukJmZiY+PD++99x4JCQksW7YMf39/4uPjcXFxQavVkpOTw549e6hZsyYNGjQAygPEhQsX0r9/f/VA/cfp3r073377LWVlZWpbQkICr776aoWx3bp1w2AwkJSUZDa2Tp06tG3blo4dO6LRaDh27Jjaf/z4caysrOjRo0eV1iP+GmQboxBCCCGEEEKIp97evXtJTU0Fyqu0hg4dirW1NVOmTGH69Ol06dIFNze3Cm8L1Ov1TJgwgXPnznHu3DkAatWqxZYtWxgwYACFhYVm51W5urqi0+kq3L+0tJRXX33V7JwugM2bN7Np0yY++ugjcnNzKSsrw9nZWQ19fv75Z1JSUqhevTouLi5Ur16dvLw8GjVq9Mgzsf4oKysLd3f3Ko09ePAgpaWlnDlzhpdeeglbW9sK54U9bMmSJbRq1YqIiAjmzp3L1q1bgfIzsGrUqIHBYGDBggWEhIQQFRVF7969Wbp0KdHR0Wbz5OTk4OTkREhICH/7298ICQlh8+bN6r1zc3OB8m2UiqJgZWWFyWRiwIABvPPOOxW2dkL5mxwfvBkzJCSEvXv3cvnyZbZv3w7Anj17+OKLLzh06BDu7u4MGzaMqVOnsmnTJu7fv8+iRYuYM2cONjY2aDQaJk+ezMKFC2nSpAnOzs5MmzaNSZMmUbNmzSo9W/EXoQghhBBCCCGEEE+xvXv3KhMmTFAKCgqUS5cuKXPmzFFefvllZdGiRYrJZFLatWunODk5KdnZ2WbXpaSkKN7e3oqnp6eSmZlZYd64uDjFyclJeeWVV5SkpKRHrsHLy0s5efJkpf13795VDAaDWVtQUJASFRVVYexHH32kTJ8+/ZH3e1hJSYlia2urXL9+3ax9zpw5yuLFi83arl69qri5uSkffvihUrt2bWX+/PlKaWmp2h8VFaXY2NhUuEdoaKhSrVo15bffflNMJpMSFxenNG3aVPH391f0er2SkpKidOrUSVEURUlLS1MaN26s9O3bVzl9+rRiMpnUeXQ6neLn56cASosWLZTExERFURRlx44dikajUTZu3Ki4ubkpb7/9tqLX6xWdTqc0adJEWbNmTaW/PykpSWnTpo1iZ2endOjQQTlx4oTat2bNGqVJkyaKTqdTFKX87zBy5EjF0dFRqVevnhIWFlZhfZMnT1ZcXFyUGjVqKO+99556rXh6WCnKv1/tIIQQQgghhBBCPGXWrl1LREQERUVFNG/enBYtWlBUVITJZMLPz49Dhw5RWlpKhw4d2L59OxMnTsTf35/NmzezcuVKAgMD+fTTT3F1dbU4f3p6OrNmzeLo0aPExcUxcOBAi+MsvY3xcebNm8e9e/dYvXq12mY0GhkwYACjRo1i/PjxFq/LyMjAysqKatWqoSgKK1euZPv27Vy/fh0orzKzsbFh/PjxeHl58f7773Pjxg2++uor1q5dy4oVKxg3bhw3b94kKCiIu3fvsnXrVvWw+Ue5ePEiw4YNw2Aw8PHHHzNq1CgAUlNTCQ4OVqvrCgsLmTt3LpGRkXTo0IEvv/ySLVu2EBsbi6enJ8uXL+f48eMcO3aM1q1bs2bNGmJiYhg+fDjp6en4+flRr149zpw5Y3ZYvBBVIWGXEEIIIYQQQoinVl5eHlC+9fCBrVu3cuLECTIyMhg9ejRBQUFYWVnx008/sX79egYPHkzdunXJy8vjlVdeqdJ9rl279shDyv+TsCsjI4M33niDjIwM9Wwto9FI+/btOXz4MC4uLhavmzp1Kp9//rm6zbFly5Zs3rxZPVcqOTmZHj16UL16dRISEjh69CiLFi1i4MCBLFy4kHbt2qlzmUwm5s2bx/nz5zl69Ohj16woCnFxcfj5+WFnZ6e2/zHseiA3N5f8/Hy0Wi0HDx7E398fT09PszGLFi1i6NCheHt7m12XkJCghmlCPAkJu4QQQgghhBBCiKeQyWTCZDI9tvJJp9NhNBorvCHyYQaDAVtb2//2EoX4U0jYJYQQQgghhBBCCCGeGRXfQSqEEEIIIYQQQgghxFNKwi4hhBBCCCGEEEII8cyQsEsIIYQQQgghhBB/uuLi4j97CeIZIWGXEEIIIYQQQgjxbz4+PkRGRj5yzG+//Vah7ccffyQ8PPyx1w0ZMgSj0Vihr1evXhw5cqRKazx37hyxsbFVGqsoCpmZmRw7dow1a9Zw6NChCmO8vb1JSkqq0nxVUVZWRmFhIdnZ2Vy7dk1tv3r1KqWlpWRnZ9OoUSMAfv/9d/Lz8wHo27cvO3fu/K+tQ/zv9ehXNgghhBBCCCGEEE+B7t27k5ubi0ajsdhfUFBAz549iY6OfuQ8JpMJa+vK60Ly8vLw9PTk8OHDvPTSS2p7VFQUmZmZj5w7KioKKyurx7498YH8/Hz27dtXoT0jI4NVq1Zx7949i2sdOnQo77zzDj/++CM//fQTJSUlat+bb77JgAEDKr1ndHQ048aNU7/Xq1ePvn37Eh4eTv369QHYv38/8+bNw2g0YjQaKS0tVT92dnbY2Nig0WhwcnKiWbNm7N27F4B58+bRpUsXAgIC1PmDg4Pp1KkT77//PpcuXaJTp05VejYPS09P59133yUlJYUGDRoQFhbGmDFjLI7V6/XMnj2bbdu2YTAYePPNN1mzZg2Ojo5AeTj40UcfsXHjRgoKCvD19eWLL76gTp06T7wu8eeRsEsIIYQQQgghxDMhNjYWb29vi327du3iwIEDj53jcWFXrVq1WLBgAaNGjeLbb7+lT58+ANy+fRtHR0fc3d3Nxjs5OXHlyhXKysrYsGGDWeVSixYtKCgoAMrDuJEjR2JrawvAzJkzmTRpEsXFxSQlJaHX6+nXrx8AtWvXZsmSJdy/f9/iGl1cXKhZsyYDBgxg8ODBrFmzhiNHjtC0aVPq1asHgJWVFSUlJTg4OFS43sbGhh9++AGTycTPP//MjBkzGDlyJMePHwfA19eXzp07Y2tri52dHfb29ly6dIng4GBSU1MrfXYLFizA19eXESNGABAfH8/ly5f5+uuvOXXqFBqNhubNm1d6vSWFhYX07dsXX19f1q5dS1xcHEFBQbRs2dIsjHxgxowZHDlyhF27dmE0GgkMDMTe3p5169YBEB4ezmeffUZ0dDS1atVi4sSJjB071mJFnPjrkrBLCCGEEEIIIcQzITAwUK3Q+aM7d+7QtWvXx86hKMojwy6A6dOnk52djZ2dHWlpaezcuZN169aRmJjIr7/+iouLi1oJZGVlBcC2bdvw9PSkS5cuBAUFMWbMGH799Vd1zl69ehEaGkr//v3N7jVlyhSMRiOrV6+mqKjokeuaPXs2r7/+OgDr168HyqvANm7ciI+Pz2N/+8Oef/55AFq1aoVOp2PEiBEUFhZy69Ytli9fXmF8Xl4eGRkZBAUFVeirXr06q1evpmPHjpw5c0YN2Lp168aOHTtwcHAgNjYWg8Fg9vurV6/+2O2a0dHRlJWVsWnTJuzs7GjXrh0HDhxg/fr1FcKuO3fusGnTJnbs2EHv3r0BWLRoETNmzGDlypXY29sTHh7O/PnzGTRoEAARERH4+flx/fp1mjVrVvUHKP5UEnYJIYQQQgghhHgmdOnSRa1c+qMffvjB7PuoUaMqDVLGjBljcRtcVFQUQUFBWFtbExERAZSHY6tXr+aDDz7A1dWVxYsX079/f4KDg9XrdDodYWFh/POf/+TixYt89913DBgwgFGjRqljvv/+e5YvX262zXL16tXUq1ePN954Qz3j6lE6d+4MwNmzZ4mJiQGgqKiIoqIipkyZApRXba1evfqxcz3MaDRibW2Nra0trq6u9OrVq8KYjIwM0tPTLfZpNBpSUlIYOnQoUF7FVlJSgoeHBwATJ05k27ZtBAQE0KVLF0pKSnj33XeJiYlBr9fTqlUrZsyYwfTp0yvMnZiYSJ8+fbCzs1Pb+vTpw+7duyuMPXnyJCaTCV9fX7OxOp2OtLQ0HB0d0Wq1+Pn5qf09e/bE2tqaM2fOSNj1FJGwSwghhBBCCCHEUy80NJRGjRrh7OxssT83NxeDwaB+j4yMZMOGDRXGubu7M2vWLDUcepijoyMjR46koKCArl27snDhQnbt2sW9e/d46623Kl2bXq8nPz+fN998k6ysLI4dO0a3bt3w9PRk3bp1LFmyhBs3bjB9+nQ2bNhAUlISM2fOpEaNGgAcPXqU48ePM3z48Erv8dlnn+Hq6sprr71G9erV1SBp//792NjYqN8fV7X2R1euXCEsLIyxY8ei0WjQaDSMGTMGRVHIy8vDy8sLrVZLamoqBw4cICgoiH379hEdHU1kZCQ7duygcePGdO7cmZSUFCIiIrh48SLnz5+nR48eBAYGkpiYSGlpKS1atCAoKIirV69Sv359AgICKCsrw8PDg8aNG1tc37Vr1yqcQdakSROys7Mtjq1bty5OTk5mYwGys7PVs9QeDrU0Gg116tSxOJ/465KwSwghhBBCCCHEU2vEiBGkpKRQVFREw4YNgf+pHHpwoPofDRkyhCVLllTY8njt2jUKCgo4ffo08+fPt3htSEgIBw4c4Pz58wDExcWRl5dH69atAcjJyeH06dN88sknADRq1IikpCTy8vLYsmULycnJ6jlfrVq1oqSkhPHjxzN//nwKCgpwcXFh/vz5rFq1Sq1WsrW15fz58xbfAvnAjRs31LDGw8MDDw8P9Ho9K1aswNHRkTfeeEMNdh6nrKwMBwcHTCYTAMuWLTOrqurYsSP37t1T+y1JSkqiTZs2+Pr64uPjw+HDh5k6dSoLFy5kyJAhfP311yxdupTw8HAGDhyIp6cn8fHxAJw/f5527doB5ZVoD9otKS4urvB3dHR0RK/XV2msg4MDVlZW6PV6dDod1tbW2NvbV2k+8dclYZcQQgghhBBCiKfWrl272LdvHwcOHGDz5s0AbN26ldTUVFatWvVEc8XGxjJs2DDOnj1LVlaWxWqiHj16cO3aNX766ScA1q5dS3h4uNo/btw4evfuTWBgIFAe1gAkJyfzySefkJyczO+//47RaGT8+PEsWLCA3Nxc9Ho91apVw9ramrNnz6rVRxcvXuTkyZO8/PLLdO3aFWdnZ06cOAGUhzDjxo1j5cqVuLu7c+nSJbNthBs3bqRNmzacOXOG7t27M2fOHEJCQh77HGxsbEhLS+PGjRtMmjSJc+fOmb1B8uLFiwBotVq1YuzWrVtkZmaqB+77+Phw5MgRCgoKmDNnDhs3buTKlStotVpat25NRkYGrq6uLF26FIDWrVsze/ZsdDodhw8fpmfPnlX4i4G9vT2lpaVmbTqdzuLZbZbGGgwGFEXB0dERo9GIyWTCaDSa/d7K5hN/XU9WvyiEEEIIIYQQQvwF7d27F29vb7y9vfnggw+A8gPrnZyc1PaGDRsyadIki9ffvXuXVatWERISQlBQEPPmzXvsPRVFwcnJidq1a6sfOzs7nJ2d1e81atRAURTefvttjEYjb7zxBpMnT+a5555j3bp1rFy5kp49e3Lz5k21Mu3hbXZZWVl4eHjw4YcfcuLECU6cOMGgQYPo0aMHFy5c4ObNm7i7u7NixQq+/fZb9bqcnBw+/vhj5s6di729Pd988w3Lli3jww8/rNLz9PDwoF+/fsTGxrJjxw527doFlG8HXb9+PcHBwer2waKiIqZNm4ZWqyU0NJRq1aqRnZ3N+fPn2bRpExkZGWzfvp0mTZrg5eWFTqfDy8uL+vXrU79+fbKzs6lbty4eHh6Eh4ezb98+Ro4cWaV1urm5kZWVZdaWlZVl8a2Obm5u3L5922w7a2ZmJgDNmzfHzc0NwGzLol6vJzc394nfEin+XBJ2CSGEEEIIIYR46g0dOpS0tDTS0tLUaqHo6GgaN27MN998Q1paGk2bNmX06NEVrjWZTEyYMIGXX36ZPn36MGPGDBISEoiMjHzkPZOTk82Crtq1a3Po0CFmzZpl1pacnEx6ejrff/89iYmJ7N69my+//BIfHx9OnDhBy5YtGT9+PHFxcWbX/fLLL5hMJiIjIwkICFDvW1JSwoULF9SqsV27dpGUlGT2ewICAhg+fLi6dbFVq1YcPXr0kVsPLenSpQsBAQHMnj0bvV5PSUkJycnJtG3blhUrVuDl5UVgYCDe3t60b9+eCxcukJmZiY+PD++99x4JCQksW7YMf39/4uPjcXFxQavVkpOTw549e6hZsyYNGjQAYObMmSxcuJD+/fvTokWLKq2ve/fufPvtt5SVlaltCQkJvPrqqxXGduvWDYPBYPasEhISqFOnDm3btqVjx45oNBqOHTum9h8/fhwrKyt69OjxRM9N/Lkk7BJCCCGEEEII8dSzVNllbW3NlClTmD59OmvWrMHNza3C2wL1ej2BgYGcO3dO3QZZq1YttmzZQnBw8CO3Qnbv3p38/Hyzz+uvv05ERIRZW/fu3bG2tubGjRvExcWxePFiBg8ejFarRavVcujQIezt7alTpw6rV69W259//nmKi4t577332Lp1q3rfgoICqlWrBpQHX3369FEDPoBDhw6RkZHBsmXLzNbr5eXFRx99BJSfPfbwVr1HWbJkCTk5OURERNCkSRO2bt1KSEgInTt3pkaNGhgMBkJDQ7GxsSEqKoq5c+fyyiuv8N133xEfH8+LL74IlFebOTk5ERISwuHDhwkICGDt2rVqaJebmwuUb6NUFAUoD+769+/Pnj17LK5t4sSJ5OfnM2XKFNLT01m8eDGXL19m6tSpAOzZs4f+/ftjMplwd3dn2LBhTJ06lVOnTnH06FEWLVrEnDlzsLGxQaPRMHnyZBYuXEh8fDzJyclMmzaNSZMmUbNmzSo9K/HXIGGXEEIIIYQQQoin3tChQzlx4gQxMTGMHj2a//N//g8ffvghISEhXL58mblz57J69Wqza1JTU3nppZe4cOECp06donbt2mrfa6+9xu7du5k/fz49evTg+PHjQHk4tnnzZtLT07l3716V1mY0GmnYsCEdO3bk888/R6/XU6tWLfR6PZ999hkDBgwgKiqKxMREPvvsM0aOHMndu3cB+OWXX4iKilIru3r06MGPP/7I6dOnGTBgABkZGfTq1Yt9+/YB5Qfmd+jQgW3btplth/yjtWvXqmHXg2CpMo0bN2b69On84x//ICcnB0VR2L9/P23atMHJyYk9e/aogdULL7xAfHw8YWFh+Pr68t1336nz9+vXjwsXLnDnzh0GDBiAtbW1+nbInTt3MnXqVDZu3EhycjKTJk2itLQUg8HA1atXuXXrlsW1ubm5sX//fk6dOkWnTp3Yu3cvhw4domnTpkD5WWJXr15Vty5++eWXtG/fnn79+hEYGMjkyZP5+9//rs63bNkyhg4dyltvvcWgQYPo27ev+rIB8fSwUh73X7UQQgghhBBCCPEXtXbtWiIiIigqKqJ58+a0aNGCoqIiTCYTfn5+HDp0iNLSUjp06MD27duZOHEi/v7+bN68mZUrVxIYGMinn36Kq6urxfnT09OZNWsWR48eJS4ujtjYWMrKyvDy8uLgwYN8+umndOrUSQ2OMjMzqVatGjVq1KCsrAy9Xs9zzz2HVqtVz+QCeP/994mKisLLy4tPP/2UDh06AOUHpk+bNo39+/dz8eJFBgwYwO7du7l//z6LFy9Ww5jAwEB+/fVX0tPT2bZtGw4ODowdOxZnZ2cWL15Ms2bNAMjIyOCll14iJyfH7HdlZmai1WopKiri9ddf5+TJk+oaHuXixYsMGzYMg8HAxx9/zKhRo4Dy4DA4OJjU1FQACgsLmTt3LpGRkXTo0IEvv/ySLVu2EBsbi6enJ8uXL+f48eMcO3aM1q1bs2bNGmJiYhg+fDjp6en4+flRr149zpw5U+UKNCEekLBLCCGEEEIIIcRTKy8vDyjfevjA1q1bOXHiBBkZGYwePZqgoCCsrKz46aefWL9+PYMHD6Zu3brk5eXxyiuvVOk+165do1mzZgwcOJCtW7dSo0YN1q1bx9q1a8nIyKCsrAxbW1tsbGwoLS2ltLQUk8mElZUVZ8+epXPnzmbzJSYmUqtWLdq1a2fxfmlpadSpU4eZM2fywgsvEBMTw/vvv8/evXsJCwvDx8eHgwcPEhsby5YtWygrK6N9+/ZoNBrOnj2rVkxVFnbt3buXCRMmYG1tja+vL9u2bcPKyuqxz0FRFOLi4vDz88POzk5t/2PY9UBubi75+flotVoOHjyIv78/np6eZmMWLVrE0KFD8fb2NrsuISFBDdOEeBISdgkhhBBCCCGEEFVkNBorrTTS6/UYjUZsbGywsbHhueeeq1KA9Cgmk0kNrgDKysrULYMApaWlZqGTEELCLiGEEEIIIYQQQgjxDJED6oUQQgghhBBCCCHEM0PCLiGEEEIIIYQQQvzpiouL/+wliGeEhF1CCCGEEEIIIcS/+fj4EBkZ+cgxv/32W4W2H3/8kfDw8MdeN2TIEIxGY4W+Xr16ceTIkSdb7GOEhoayZMmS/+qcVVFWVkZhYSHZ2dlcu3ZNbb969SqlpaVkZ2fTqFEjAH7//Xfy8/MB6Nu3Lzt37vx/vl7x7JGwSwghhBBCCCHEU6979+60bt0ab29vi59mzZoRFBT02Hn+eCD8H+Xl5eHp6cmZM2fM2qOiorhw4cIj546KisLKyqrSA+4r4+rqipWV1WM/zz///CPn8fb2pnbt2tSvX1/91KlTx+y66Ohosznr16/PmDFjzN7muH//ftq2bcsLL7xAy5Ytadq0KQ0aNKBWrVo0aNCApk2b0qlTJwYOHMjMmTPV6+bNm0dERITZmoKDg/n8888xGAxcunSJTp06PdGzAUhPT6dHjx5oNBqaN29OTExMpWP1ej1Tp06lVq1auLi4MGHCBO7fv6/2K4pCWFgYDRs2xMnJiaFDh5Kbm/vEaxJ/rif7P0wIIYQQQgghhPiLio2Nxdvb22Lfrl27OHDgwGPneFzYVatWLRYsWMCoUaP49ttv6dOnDwC3b9/G0dERd3d3s/FOTk5cuXKFsrIyNmzYYFa51KJFCwoKCgAoKChg5MiR2NraAjBz5kzmzp0LlAdsVXm3XFXe/PjNN9+YPaOMjAxee+01szE2Njb88MMPmEwmfv75Z2bMmMHIkSM5fvw4AL6+vnTu3BlbW1vs7Oywt7fn0qVLBAcHk5qaWum9FyxYgK+vLyNGjAAgPj6ey5cv8/XXX3Pq1Ck1rHoShYWF9O3bF19fX9auXUtcXBxBQUG0bNmSl156qcL4GTNmcOTIEXbt2oXRaCQwMBB7e3vWrVsHQHh4OJ999hnR0dHUqlWLiRMnMnbsWA4dOvRE6xJ/Lgm7hBBCCCGEEEI8EwIDA3F0dLTYd+fOHbp27frYORRFeWTYBTB9+nSys7Oxs7MjLS2NnTt3sm7dOhITE/n1119xcXGhTp06wP8EUNu2bcPT05MuXboQFBTEmDFj+PXXX9U5e/XqRWhoKP37969wPxsbm8eu+2HHjx9n5syZ3Lx5E2tra/bt28fGjRufaI4H1V6tWrVCp9MxYsQICgsLuXXrFsuXL68wPi8vj4yMDIvVc9WrV2f16tV07NiRM2fO4ODgAEC3bt3YsWMHDg4OxMbGYjAYzH5/9erViY2NfeQ6o6OjKSsrY9OmTdjZ2dGuXTsOHDjA+vXrK4Rdd+7cYdOmTezYsYPevXsDsGjRImbMmMHKlSuxt7cnPDyc+fPnM2jQIAAiIiLw8/Pj+vXrNGvWrOoPUPypJOwSQgghhBBCCPFM6NKlC/Xq1bPY98MPP5h9HzVqVKVBypgxYxgzZkyF9qioKIKCgrC2tla34ymKwurVq/nggw9wdXVl8eLF9O/fn+DgYPU6nU5HWFgY//znP7l48SLfffcdAwYMYNSoUeqY77//nuXLlxMdHa22rV69murVq1f59z/g6enJJ598whdffIFGo2HMmDG0aNECgNdee81sG2VZWdlj72E0GrG2tsbW1hZXV1d69epVYUxGRgbp6ekW+zQaDSkpKQwdOhQor2IrKSnBw8MDgIkTJ7Jt2zYCAgLo0qULJSUlvPvuu8TExKDX62nVqhUzZsxg+vTpFeZOTEykT58+2NnZqW19+vRh9+7dFcaePHkSk8mEr6+v2VidTkdaWhqOjo5otVr8/PzU/p49e2Jtbc2ZM2ck7HqKSNglhBBCCCGEEOKpFxoaSqNGjXB2drbYn5ubi8FgUL9HRkayYcOGCuPc3d2ZNWsWU6ZMqdDn6OjIyJEjKSgooGvXrixcuJBdu3Zx79493nrrrUrXptfryc/P58033yQrK4tjx47RrVs3PD09WbduHUuWLOHGjRtMnz6dDRs2kJSUxMyZM7l9+zb169d/4mdx/fp19cB7Z2dnNYA6evQoNWrUULdKQnmQdevWrUrnunLlCmFhYYwdOxaNRqOGZ4qikJeXh5eXF1qtltTUVA4cOEBQUBD79u0jOjqayMhIduzYQePGjencuTMpKSlERERw8eJFzp8/T48ePQgMDCQxMZHS0lJatGhBUFAQV69epX79+gQEBFBWVoaHhweNGze2uL5r164xYMAAs7YmTZqQnZ1tcWzdunVxcnIyGwuQnZ2thoAPh1oajYY6depYnE/8dUnYJYQQQgghhBDiqTVixAhSUlIoKiqiYcOGwP9UDlUWFA0ZMoQlS5ZU2PJ47do1CgoKOH36NPPnz7d4bUhICAcOHOD8+fMAxMXFkZeXR+vWrQHIycnh9OnTfPLJJwA0atSIpKQk8vLy2LJlC8nJyeo5X61ataKkpITx48czf/58CgoKcHFxYf78+axatYp27dqpZ3UlJiYSEBBARkaGWVj1OAaDgePHj/PTTz8RGhpq8Vqj0chnn32Gv78/UF7t5eDggMlkAmDZsmVmVVUdO3bk3r17ar8lSUlJtGnTBl9fX3x8fDh8+DBTp05l4cKFDBkyhK+//pqlS5cSHh7OwIED8fT0JD4+HoDz58/Trl07oHwL54N2S4qLiyv8HR0dHdHr9VUa6+DggJWVFXq9Hp1Oh7W1Nfb29lWaT/x1SdglhBBCCCGEEOKptWvXLvbt28eBAwfYvHkzAFu3biU1NZVVq1Y90VyxsbEMGzaMs2fPkpWVZbGaqEePHly7do2ffvoJgLVr1xIeHq72jxs3jt69exMYGAj8z3lbycnJfPLJJyQnJ/P7779jNBoZP348CxYsIDc3F71eT7Vq1bC2tubs2bNm1UcAq1atIjg4GCsrK4xGo8X1W1tbY21tTVZWFjt37uTgwYN8//33HDx4kGHDhjFo0CCio6NRFIXPP/+cd999F2trayZOnEhpaak6j42NDWlpady4cYNJkyZx7tw5s62PFy9eBECr1apbEW/dukVmZqZ64L6Pjw9HjhyhoKCAOXPmsHHjRq5cuYJWq6V169ZkZGTg6urK0qVLAWjdujWzZ89Gp9Nx+PBhevbsWaW/mb29vdnaoXzbqKWz2yyNNRgMKIqCo6MjRqMRk8mE0Wg0+72VzSf+uh596p4QQgghhBBCCPEU2Lt3L97e3nh7e/PBBx8A5QfWOzk5qe0NGzZk0qRJFq+/e/cuq1atIiQkhKCgIObNm/fYeyqKgpOTE7Vr11Y/dnZ2ODs7q99r1KiBoii8/fbbGI1G3njjDSZPnsxzzz3HunXrWLlyJT179uTmzZtqZdofgy6As2fPsnDhQmxtbSv9hIWFAXDu3DnOnDlD3bp1WbRoEampqep2PSgPb6ZNm/bIg/g9PDzo168fsbGx7Nixg127dgHl20HXr19PcHCwun2wqKiIadOmodVqCQ0NpVq1amRnZ3P+/Hk2bdpERkYG27dvp0mTJnh5eaHT6fDy8qJ+/frUr1+f7Oxs6tati4eHB+Hh4ezbt4+RI0c+9vkDuLm5kZWVZdaWlZVl8a2Obm5u3L5922w7a2ZmJgDNmzfHzc0NwGzLol6vJzc394nfEin+XBJ2CSGEEEIIIYR46g0dOpS0tDTS0tLUaqHo6GgaN27MN998Q1paGk2bNmX06NEVrjWZTEyYMIGXX36ZPn36MGPGDBISEoiMjHzkPZOTk82Crtq1a3Po0CFmzZpl1pacnEx6ejrff/89iYmJ7N69my+//BIfHx9OnDhBy5YtGT9+PHFxcWbX/fLLL+q9cnJyUBSl0k/v3r1p1KgRUL5Nc8eOHXTu3NlioPUkZ4F16dKFgIAAZs+ejV6vp6SkhOTkZNq2bcuKFSvw8vIiMDAQb29v2rdvz4ULF8jMzMTHx4f33nuPhIQEli1bhr+/P/Hx8bi4uKDVasnJyWHPnj3UrFmTBg0aADBz5kwWLlxI//791QP1H6d79+58++23lJWVqW0JCQm8+uqrFcZ269YNg8FAUlKS2dg6derQtm1bOnbsiEaj4dixY2r/8ePHsbKyokePHlVaj/hrkLBLCCGEEEIIIcRTz1Jll7W1NVOmTGH69OmsWbMGNze3Cm8L1Ov1BAYGcu7cOXUbZK1atdiyZQvBwcGP3ArZvXt38vPzzT6vv/46ERERZm3du3fH2tqaGzduEBcXx+LFixk8eDBarRatVsuhQ4ewt7enTp06rF69Wm1//vnnq/z7s7KycHd3r9LYgwcPUlpaypkzZwCwtbVVt1tasmTJEnJycoiIiKBJkyZs3bqVkJAQOnfuTI0aNTAYDISGhmJjY0NUVBRz587llVde4bvvviM+Pp4XX3wRKA/snJycCAkJ4fDhwwQEBLB27Vr13rm5uUD5NsoHZ5WZTCb69+/Pnj17LK5t4sSJ5OfnM2XKFNLT01m8eDGXL19m6tSpAOzZs4f+/ftjMplwd3dn2LBhTJ06lVOnTnH06FEWLVrEnDlzsLGxQaPRMHnyZBYuXEh8fDzJyclMmzaNSZMmUbNmzSo9W/EXoQghhBBCCCGEEE+xvXv3KhMmTFAKCgqUS5cuKXPmzFFefvllZdGiRYrJZFLatWunODk5KdnZ2WbXpaSkKN7e3oqnp6eSmZlZYd64uDjFyclJeeWVV5SkpCRFURRFp9Mp3bp1U1q0aKEUFxdXuGbw4MHK+vXrzdoMBoPSoEEDpWbNmoqvr6/ywQcfKDk5OYpOp1NWr16t1KpVS/nXv/6l/PLLL4qPj4/y1ltvKXfu3Kny7y8pKVFsbW2V69evm7XPmTNHWbx4sVnb1atXFTc3N+XDDz9UateurcyfP18pLS1V+6OiohQbG5sK9wgNDVWqVaum/Pbbb4rJZFLi4uKUpk2bKv7+/oper1dSUlKUTp06KYqiKGlpaUrjxo2Vvn37KqdPn1ZMJpM6j06nU/z8/BRAadGihZKYmKgoiqLs2LFD0Wg0ysaNGxU3Nzfl7bffVvR6vaLT6ZQmTZooa9asqfT3JyUlKW3atFHs7OyUDh06KCdOnFD71qxZozRp0kTR6XSKoijK3bt3lZEjRyqOjo5KvXr1lLCwsArrmzx5suLi4qLUqFFDee+999RrxdPDSlH+HZcKIYQQQgghhBBPmbVr1xIREUFRURHNmzenRYsWFBUVYTKZ8PPz49ChQ5SWltKhQwe2b9/OxIkT8ff3Z/PmzaxcuZLAwEA+/fRTXF1dLc6fnp7OrFmzOHr0KHFxccTGxlJWVoaXlxcHDx7k008/pVOnTuqB5pmZmVSrVo0aNWpQVlaGXq/nueeeQ6vVqmdyAbz//vtERUXh5eXFp59+SocOHYDyA9OnTZvG/v37uXjxosWKooyMDKysrKhWrRqKorBy5Uq2b9/O9evXASgtLcXGxobx48fj5eXF+++/z40bN/jqq69Yu3YtK1asYNy4cdy8eZOgoCDu3r3L1q1b1cPmH+XixYsMGzYMg8HAxx9/zKhRowBITU0lODiY1NRUAAoLC5k7dy6RkZF06NCBL7/8ki1bthAbG4unpyfLly/n+PHjHDt2jNatW7NmzRpiYmIYPnw46enp+Pn5Ua9ePc6cOWN2WLwQVSFhlxBCCCGEEEKIp1ZeXh5QvvXwga1bt3LixAkyMjIYPXo0QUFBWFlZ8dNPP7F+/XoGDx5M3bp1ycvL45VXXqnSfa5du0azZs0YOHAgW7dupUaNGqxbt461a9eSkZFBWVmZuh2wtLSU0tJSTCYTVlZWnD17ls6dO5vNl5iYSK1atWjXrp3F+6WlpeHt7W2xb+rUqXz++eeYTCYAWrZsyebNm9VzpZKTk+nRowfVq1cnISFB3a43cOBAFi5caHZPk8nEvHnzOH/+PEePHn3sc1AUhbi4OPz8/LCzs1Pb/xh2PZCbm0t+fj5arZaDBw/i7++Pp6en2ZhFixYxdOhQs9+bm5tLQkKCGqYJ8SQk7BJCCCGEEEIIIarIaDRWWmmk1+sxGo3Y2NhgY2PDc889h5WV1f9vazGZTJhMpsdWPul0OoxGI87OzpWOMRgM2Nra/reXKMSfQsIuIYQQQgghhBBCCPHMkLcxCiGEEEIIIYQQQohnhoRdQgghhBBCCCGEEOKZIWGXEEIIIYQQQgghhHhmSNglhBBCCCGEEEIIIZ4ZEnYJIYQQQgghhBBCiGeGhF1CCCGEEEIIIYQQ4pkhYZcQQgghhBBCCCGEeGZI2CWEEEIIIYQQQgghnhkSdgkhhBBCCCGEEEKIZ4aEXUIIIYQQQgghhBDimSFhlxBCCCGEEEIIIYR4ZkjYJYQQQgghhBBCCCGeGRJ2CSGEEEIIIYQQQohnhoRdQgghhBBCCCGEEOKZIWGXEEIIIYQQQgghhHhmSNglhBBCCCGEEEIIIZ4ZEnYJIYQQQgghhBBCiGeGhF1CCCGEEEIIIYQQ4pkhYZcQQgghhBBCCCGEeGZI2CWEEEIIIYQQQgghnhkSdgkhhBBCCCGEEEKIZ4aEXUIIIYQQQgghhBDimSFhlxBCCCGEEEIIIYR4ZkjYJYQQQgghhBBCCCGeGRJ2CSGEEEIIIYQQQohnhoRdQgghhBBCCCGEEOKZIWGXEEIIIYQQQgghhHhmSNglhBBCCCGEEEIIIZ4Z/x8FiOeRs2RUmQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "my_sys.performance()" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -392,7 +416,7 @@ "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA94AAAMdCAYAAABtEa4xAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3hUVeLG8e+UJJMeUoAhEjoCKhZUdEVBXTvq2hYr2NdgX/e3KjZsuJa1olFZ26prQVdRUFwLdlFAqiJFSkIYIL1PMuX+/rjJJMNMQhISwoT38zx5mDn33nPPJAPkndMshmEYiIiIiIiIiEinsHZ1A0RERERERES6MwVvERERERERkU6k4C0iIiIiIiLSiRS8RURERERERDqRgreIiIiIiIhIJ1LwFhEREREREelECt4iIiIiIiIinUjBW0RERERERKQTKXiLiIhEqC+++ILFixfv8vsWFRXh8/l2+X1FREQilYK3iMge7Pfff2fSpEkMGjSI6OhokpOTOfzww8nNzQVg6tSpWCyWFr+efvrpsHX/7W9/o2fPnng8HgC2bNnCeeedR48ePUhOTmbChAls27Yt5Loff/yRsWPHEh8fj9PpZMqUKYE6mvr3v//NPvvsQ2xsLMOHD+fVV18NOaeuro7bbruNvfbai7i4OMaMGcOCBQtCzmtt2xq89957JCcnN3u8qZKSEn777bc2f61du3aHdd9///2ceuqpLba1gdfr5aijjuK7774LlBUWFrJ27dpmv8rKysLWM378eC655JJWvf5Ict555/HJJ58ElRUXFzNmzBiWLl1KeXk599xzD9XV1V3UQhERiViGiIjskb7++mvD4XAYGRkZxvXXX29MmzbN+Mtf/mL07t3bWLBggWEYhvHDDz8Yjz32WMjXfffdZ1itVmPgwIGG2+0Oqdvj8Ri9evUybrjhBsMwDKOystLYe++9jR49ehi33nqrce211xpRUVHGQQcdZHg8nsB1y5YtM2JjY41hw4YZ9913n/GnP/3JAIxrr702qP5//etfBmAcf/zxxv33328ceOCBBmC8++67QeddeOGFhtVqNa644grjrrvuMnr27GkkJSUZ69evD5zT2rYZhmEsXrzYOO200wzAaO1/oY899ljg/LZ89erVa4d1u1wuo1evXsZjjz22w3PfeOMNw2azGS6XK1B29dVXt9iGcPVOmTLFiI+PN3799VfDMAzj9NNPb7GOsWPHtur71Bp+v9+4/PLLjYceeiiofP369W363t51110hdX/99deG1WoNvK4GJSUlBmB88cUXRn5+vnHIIYcYw4YNM3755Zeg8/75z38al19+ueH3+zvs9YqISPeh4C0isoc69NBDjYSEBCMvLy+ovLq62qioqGjx2ptvvtkAjA8//DDs8Q8++MAAjKVLlxqGYRgPPvigARhfffVV4JxHH33UAIy33347UHbSSScZ6enpRlFRUaDstNNOM+x2u5Gfn28YhmFUVVUZaWlpxrHHHhsIORUVFUZmZqYxYsSIwHU//vijARh33313oGzhwoWGxWIxJk+eHChrbdv++c9/GoDRp08f45BDDml18G6JzWYzZsyYscPzkpOT2xXeL7vsMsMwzMB6yCGHGH/4wx+MNWvWGGvWrDEKCgqMq6++2pg0aVLYe44dOzYkeL/xxhuG1Wo1/v3vfwfKNm3aZKxcubLZr40bN7b7+7O9Rx991MjMzAx6fxhGY/B+5ZVXWmzLypUrjT59+oQN3ieeeKJxwQUXhJTX1dUZgDF37lzDMAzD7XYbF1xwgfH0008HnVdcXGz07dvXePjhhzvs9YqISPeh4C0isoeKiYkxDj744DZf99tvvxnR0dHGKaec0uw5Z5xxhnHggQcGnu+///7GQQcdFHROeXm5ER0dHQg7RUVFhtVqNf76178GnTd37lwDCATU9957zwCMDz74IOi8W265xQCMNWvWGIZhGNdff71ht9uN4uLioPMOO+wwIzMzs01tMwzDyMnJMW6//XajtLTUmDRpUpuC93fffWdcfPHFhs/nCyoPF7wfeeQR46abbgrqOU1OTjYeeuihoAA5d+5c48UXX2w2YI4bNy4QvJ977rmQUH799de3KXg///zzhtVqNf7+978bhmH2BO9K27ZtM+Li4oyXX3455FhD8J43b94O6+nXr19I8J4/f74RFRVlrF271ti2bZuxdevWoOPR0dHG+++/bxQWFho///yz8f777xtPPvmk8eSTTwad9+qrrxqxsbEh14uIiNjbNT5dREQiXu/evfnll19Yv349AwYMaPV1V199NRaLhSeeeCLs8cLCQmbPns0///lPAGpqali2bBnXXHNN0HmJiYmMGDGCJUuWALBgwQL8fj9HHHFE0HmHHXYYQOC8H3/8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nTsXgB9++IGJEyfyv//9L6Q+m80WqFdERKSB5niLiEjAaaedBpiLdzV44403mDdvHjfccENgGG04s2fPprCwkEsuuSSovGfPnmRmZjJ//vyg8qqqKpYvX87hhx8OmAt4ASHnNTzf/ryG1c0b/PDDD1it1kCP50EHHcTy5cuprq4OOW/kyJHExcW1um3tNXz4cAoKCpr9stlsPPbYY80enz59elB9zz77LDabjVdeeYVffvklUF5RUcFFF13EkUceGTIioV+/flRUVFBRUUGfPn348MMPqaio4Lnnngs6b+LEibz11lsUFBSwYsUKpk6dys8//0xeXh6xsbGccMIJzJgxI2S19V0hPT0dIGg0QEd57733+PDDD1m4cCGpqamccMIJfPbZZ0yePJkTTzyRwYMHM2bMmEDv908//cSpp57K2LFjQ6YFQGNPd0ObRUREQMFbRGSPFW6o8pdffgk0zqWtqKjgb3/7G5mZmdx5550t1vfSSy9xyCGHhJ2He9ZZZ/Htt9+yePHiQNm//vUvamtrueCCCwAYPHgwI0eO5KWXXgr0RIM5LzojI4Pjjz8egPHjxxMdHR3U+15ZWckrr7zCSSedFFjU7ayzzqKmpibQgwuwePFivv/+ey688MI2ta29bDYb6enpzX6BOU+7uePbz+H+8MMP+e9//8vKlSvZd999OfHEE5k1axYnnHACxcXFvPbaayFtsFgsJCQkkJCQgMViweFwkJCQELRYG8Do0aM58MADOeOMMzj88MM56aSTWLx4MYMGDWKfffbhrbfeCvTm7moNc6XDzTvfWYceeihPPvkks2bNYtOmTZSXl/PQQw8FnbP//vuTmZnJNddcw9ixY/nzn//M888/z4IFCwLrBDRYv349ELo6v4iI7Nk01FxEZA913nnncd9993HSSSeRnp7O2rVreeONN+jVq1dg6PNdd93F5s2bOeWUU4LmDTd1/PHHk5aWxscffxyyMFWDm2++mddff50TTjiBq666ipKSEp577jkmTZrEIYccEjjvwQcf5OSTT2bMmDFMmDCBH3/8kVmzZvHSSy8FhpX37t2bm266iQceeIDTTz+dww47jLfffpvi4mIefPDBQF2nn346RxxxBH/7299Ys2YNaWlpPPvsswwbNozJkye3uW27A4vFwmmnncZpp53GN998w4UXXsif/vQnAKZOnUrv3r1bVY/f7+f7778PKX/wwQc55phjmDBhQmAf9gYdvdBcW+y3336kpaUxb968kP3bd1bfvn259tprAfP7snTpUr777jsuv/zywOryeXl5pKen89///penn3468P455ZRTeOihh4JGecybNw+n09ni6BAREdnzKHiLiOyh7r77bj788EOee+45fD4f/fr144orruCWW26hV69eFBYWBnqV58yZw5w5c8LW89JLL1FUVITNZgvMH95enz59mDdvHtdddx2PPPIIKSkp/N///V/IatonnngiM2fO5M477+Tuu+9m4MCB/Pvf/+aiiy4KOu++++4jNjaWZ599lk8//ZSDDz6YefPmsc8++wTOsVqtfPjhh9xwww2BnuCTTjqJxx9/PGhueGvbtruorKzk3XffZfr06eTm5nLBBRdQXV3N3XffzSuvvMLf/vY3Lr30UhwOR8i1fr+f5cuXc8cdd5CYmBjYUuzbb79lypQpjB07lpdffpkLL7wQj8fDk08+GbJfemt4PJ4OHZJusVgYP348s2bN4vbbb2/2vNzc3MAK6C21rakFCxbw0Ucf8d133zF//nxqamo46KCDuPLKK9m2bRuPPPIITz31FAceeCDx8fGUl5cDUFJSQmFhIQcccEBQfR988EHYfdpFRGQPZ4iIiOykfffd15gwYUJXNyPi2Gw2Y8aMGS2eU1dXZ8yePdu45557jGOPPdaIiYkxrFarcfrppxvff/994Lxly5YZZ599tmGxWIyePXsa999/v+F2uwPHV61aZVgsFsNisRhXX321UVpaahxzzDFGXFycYbPZjCuuuMLIy8szDMMw/ve//xk9evQwYmNjjcmTJxt+vz9QT79+/YwHHnggqI3z5s0zHnroIePZZ581pkyZYgDGs88+2xHfooDly5cbFovFmDdvXsix9evXG0Crv+66667Atffee69x+OGHG7feeqsxd+5co6Kiwvj666+NCRMmGNHR0cagQYOMt99+2zAMw3jxxRcNq9VqTJ061bjjjjuMtLQ0w+fzBer69NNPDavVavz2228d+tpFRCTyqcdbRER2ysKFC1mxYgUPP/xwVzelW7Lb7dx6662sX7+eww47jAcffJCzzjqLvfbaK+i8/fbbj5kzZ7J48WJuu+02PvjgA2699dbA8djYWIYNG0ZOTg5jx47F6/VSVFTEmDFjePzxxxk+fHjg3OOOO45169bx0EMPkZSUtMPe6/Xr1/P3v/8dMOe1H3nkkZx77rkd+F2Afffdl4svvpgpU6bwzTffhJ1vPm/ePMaNG9diPdsviHb77beH9KIvXbqUgoIC3nrrLU477bTAMPtLLrmE5ORk7rzzTnJzc3nyySeD9o2fMmUKl112mYaZi4hICIthGEZXN0JERESaV1xcTHJycpsWN6upqSE2NjaozO/3B83VLiwsJC0trUOGhRuGgd/vx2KxdNp88KqqKg4++GAmTJiw200FuP3223n33XdZtGhR0FQGERERUPAWERERERER6VTaTkxERERERESkEyl4i4iIiIiIiHQiBW8RERERERGRThSxq5r7/X42b95MYmJih+4VKiIiIiIiIrs3wzCoqKigT58+nbaoZ0eK2OC9efNm+vbt29XNEBERERERkS6Sl5cXssXm7ihig3diYiJgfqOTkpK6uDUiIiIiIiKyq5SXl9O3b99ALtzdRWzwbhhenpSUpOAtIiIiIiKyB4qUace7/2B4ERERERERkQim4C0iIiIiIiLSiRS8RURERERERDqRgreIiIiIiIhIJ1LwFhEREREREelECt4iIiIiIiIinUjBW0RERERERKQTKXiLiIiIiIiIdCIFbxEREREREZFOpOAtIiIiIiIi0okUvEVEREREREQ6kYK3iIiIiIiISCdS8BYRERERERHpRAreIiIiIiIiIp1IwVtERERERESkEyl4i4iIiIiIiHQiBW8RERERERGRTqTgLSIiIiIiItKJFLxFRERERGS35PF5uroJIh1CwVtEREREuo1aby3f5n7b1c3ocB6fh6NfOZqi6qKQY4ZhUFBVwJItS/hozUcYhtEFLewYzy58lovfvzjwfMQzI/h83edd16AONH/T/LDlG0o3MGz6sKCyWm9t0PPbv7idl5e83FlNk11AwVtERER2S0u3LOX4V48n7v44ej/Sm0tmXRISOnIW5DDgiQHE3h/LMa8cw7qSdYFjRdVFXDLrEtIeSiP5H8kc9+pxLNu6LOy9St2lpD2Uxh///ceg8hXbVnDUS0cRe38sA58YyGvLXgs6/uzCZxn61FAc9zkYNn0Yzy18Luj4u7++y/7P7o/jPgcDnxjIfV/fh9/wB467Klz8eeafSXwgkdQHU7nigyuoqK1o9nvy7q/vMvzp4Tjuc3DIjENY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FLx9zO1xY8LE6P8bjXuHUeTKHRlB+OFMbtxafpG2dZJz8G3WZuMLrxdHLoRbQyk1w1UHwN3kzDUC3hq2ALp21c+ciIiIiEgNUvD2MYvZghcvjTKLMWdkArD3psFsaAJ9kmq5c1IvFXqKK7525MGloa0IDAimiS2cZm26n7mxwwFNmyp4i4iIiIjUIAXvGtI2A0zlX0cGhDNlqA0P4DWZIFJFrqTqij2lYLEYf4D2IbFE2CMI8Q+p5Z6JiIiIiMjpKHjXEEdu+RdmM85OfVnxzz0U97sC08CBWt8t58TP4kdJmzh4w1jrnRg/matbXU184/ha7pmIiIiIiJyOgrePRdojCSs1Y/OU158eNAicTpxhTgJDGkKrVrXdRalnSgpy8d+xi/SiYxXHEoclVv0G0SqsJiIiIiJSkxS8fcwZ5qRzo8uweMqnmj/11MmT0dGQeA6BSS5qjmAHwf7BWDCzMdpMadvWlQulVTVQ62dORERERKRGaR/vGtDc3hTYdOoJBSA5B44QB0F+QXRs04u2HcF+5dXQ/aOTwVs/TyIiIiIidZJGvGvAm4NeAqsV7HYVUpPfJcAaQKuGrbD7BRoHFLZFREREROo8Be+a4HSS+OJ+2LlThdTkd4lvHH9u67lFRERERKTWaap5TVHgFhERERERuShpxFtERERERETEhxS8ReqR6JDyyuXaEkxEREREpN4web1eb2134nzk5OQQFhZGdnY2oaGhtd0dERERERERqSH1LQ9qxFtERERERETEhxS8RURERERERHxIwVtERERERETEhxS8RURERERERHxIwbuGpKam1nYXREREREREpBYoeNeA5557jujoaFatWlXbXREREREREZEapuDtYx6PhxdffBGAGTNm1HJvREREREREpKYpePvY+++/z6FDhwD44osv2LRpU+12SERERERERGqUgrcPeTwennzySbp16wZAbGwsTz75ZC33SkRERERERGqSgrcPLV26lO3bt3PLLbcAcO+99/Lhhx9q1FtEREREROQiouDtQ9OmTWPw4MG0b98egOHDhxMXF8f06dNruWciIiIiIiJSUxS8fahXr17Mnj274rXVaiUxMZF27drVYq9ERERERESkJllruwMXsldffRWAI0eOVBwbOHAgAwcOrK0uiYiIiIiISA3TiLeIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiIDyl4i4iIiIiIiPiQgreIiIiIiIiID1lruwPny+v1ApCTk1PLPTm7/Px8AHJzc+tFf0VEREREROqyE7nqRC6s6+pt8M7NzQUgJiamlntSdR07dqztLoiIiIiIiFwwcnNzCQsLq+1unJXJW18eEfyKx+MhNTWVkJAQTCZTbXfnjFauXMnIkSPZunUrTqeztrsjIiIiIiJSb+Xk5BATE8OOHTto06YNZnPdX0Fdb0e8zWYzzZo1q+1uVElQUBAAISEhhIaG1nJvRERERERE6r/o6Oh6EbpBxdVEREREREREfErBW0RERERERMSHFLxFRERERESk3rDZbDz++OPYbLba7kqV1ds13iIiIiIiInLxsdlsPPHEE7XdjXOiEW8RERERERERH1LwFhEREREREfEhBW8RERERERERH1LwFhEREREREfEhBW8RERERERE5J5s3b2bQoEHY7XaaNGnCHXfcQWZmZsX5xMREYmNjCQwMZMCAARw4cKDiXGZmJnfccQcRERGEhYXxxz/+kS1btpz2fbKysoiIiOCqq646a5/cbjfTpk2jefPm2O12OnXqxLJly6r0eV5++WVat25NQEAAbdu2Zf78+ZXO7969myFDhlR83r///e+UlZVV6d6g4C0iIiIiIiLnKCEhgX79+rF27VoWLFjA119/za233grAkiVLeOCBB5g6dSqrV6+mtLSUESNG4PF4AJg2bRoWi4VPP/2U5cuXAzBkyBBycnJOeZ+ZM2dy7NixKvXpo48+YsWKFcyfP5/vvvuOAQMGMGbMGNauXXvGdps2beKll15i1qxZrFu3jjvuuIOEhATee+89ADweD9dccw1+fn6sXr2a+fPn88YbbzB9+vQqf79MXq/XW+Wr5bxs2bKFnj17kpKSQnh4eG13R0RERERE5HdJSkrC6XRWvP73v//NLbfcQm5uLldccQV9+vTh+eefB2Dnzp20b9+er776iv79+5/SNjU1lejoaJYvX87gwYMrjm/fvp2+ffvStWtX3G43K1asOGOfUlNTady4MRaLpeJY69atGT16NDNnzvzNdpmZmQQGBmK32yuODRo0iIiICBYtWkR6ejqNGjViy5YtdOzYEYAHH3yQ3bt38+mnn1bp+6UR7xoQHx/PsWPHFLpFREREROSC8MvgDBAQEIDH4yErK4tNmzYxZMiQinPt2rXD4XBUjDyfri0YU8VP8Hg8/PWvf2Xy5Mk4HI4q9alp06aVQveJe//yvqcTERFRKXT/ul1UVBT9+/fnlVdeoaioiL179/Lxxx9z/fXXV6lfoOBdYwIDA2u7CyIiIiIiItXO6/WyYMECevbsSVpaGgCxsbGVrnE6naSkpJy2/auvvkpgYCC9evWqODZjxgwKCgq47777zrtfa9euZevWrQwYMOCc2h06dIgVK1ZUavf222+zZMkS7HY7rVu3plevXhVT66tCwVtERERERETOS2lpKePGjWPlypXMmzePvLw8gFNGkO12O0VFRae0f+2115gyZQqzZ8+umCG8Zs0annnmGd59912sVut59euHH35gxIgRjB07ttLo+9ns2bOHQYMG0aVLF+68804ACgsLGTp0KL169WLNmjUsWrSI5cuX8/TTT1f5vgreIiIiIiIics4OHz5Mv379+PTTT1m1ahXdunXDZrMBUFJSUunaoqKiSmG8qKiIcePGMWHCBObPn8/48eMr7jl69Giee+45OnTocNr3nTp1KlarteLP1KlTK51PTEykb9++XHvttbzzzjsVx99+++1K7e66665K7T744AN69OhB+/btWb58Of7+/gC89dZbpKamsnjxYnr37s0NN9zAnDlzmDp16mkLwp3O+T0+EBERERERkYvWnj176N+/P/Hx8WzevJmoqCgAoqOjAUhOTqZly5YV1ycnJzN27FjAGEEePHgwLpeLtWvX0rlz54rrFixYQFpaGhMmTGDChAmAMaoOxrrrzz//nPHjx1fcCyAyMrLi64cffrii6viNN95Yqc8jRoxg27ZtFa/DwsIqvp43bx4PPfQQs2bNYuLEiZhMpopzmzdvpnXr1pWWD3fr1o2CggL27dtHly5dzvr9UvAWERERERGRc3LTTTfRt29f3nnnHczmkxOpo6OjadGiBV988QX9+vUDjJB++PBhBg4cCMCjjz5Keno669ato2HDhpXuO2HChFOKlk2aNIm0tDRef/11nE4ndru9Utg+4ZNPPuGll15i9erVpw3DYWFhlcL2CZs3b+aBBx7gww8/5JprrjnlfHR0NMuWLaOkpKRiFHzbtm2YTKYqF35T8BYREREREZEq27t3Lxs2bOCRRx7hwIEDlc5FRUXx4IMPMmnSJDp16kRsbCwPPPAAw4YNq9iKa9GiRdx0001kZmaSmZlZ0TY4OJgmTZqcEqrDwsLIzc2lbdu2Z+zXokWL6NatG6Ghoezbt6/iuMViOaXY2y8tWbKEmJgYWrduXakdQMuWLbntttv417/+xe23386DDz5ISkoKDz30EKNHj1bwrhNuvx3eesv42s8P2raFJ56AUaNqs1ciIiIiIiLnzeVyATBmzJhTzs2dO5d77rmH9PR0xo8fT1FRESNGjGDevHmV2s+ePZvZs2dXajt69GiWLl36u/r17bffEhcXV+l4REQEGRkZZ2x38ODBU9oB5ObmEhMTw5dffsnDDz9Mnz59CAkJYezYscyaNavKfTN5vV5v1T+KnJPbb4e9e43wnZcHCxbAyy/Dhg0QH1/bvRMREREREZEaoKrmvhYYCK1aQadO8OKLEBQEq1bVdq9ERERERESkhih41yS3G0pKoFGj2u6JiIiIiIiI1BAF75qSkwMTJ0JsLIwcWdu9ERERERERkRqi4mq+tnIlBARAcbExzfzVV43XIiIiIiIiclFQ8Pa1nj3h9dehoAB+/BHGj4dDh2DSpNrumYiIiIiIiNQABW9fs9uNbcQAunSB9HSjyJqCt4iIiIiIyEVBa7xrmsdj7OktIiIiIiIiFwWNePtaYSHs22dUM1+3DubMgfvuq+1eiYiIiIiISA1R8Pa1776DuDiwWo2K5pMmwcMP13avREREREREpIaYvF6vt7Y7ISIiIiIiInKh0hpvERERERERER9S8BYRERERERHxIQVvERERERERER9S8BYRERERERHxIQVvERERERERER9S8BYRERERERHxIQVvERERERERER9S8BYRERERERHxIQVvERERERERER9S8BYRERERERHxIQVvERERERERER9S8BYRERERERHxof8PX/SXj/HbPTEAAAAASUVORK5CYII=", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9oAAAMcCAYAAACvpt3uAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOzdd3yb1dn/8Y+GLXnIdmw5ieLEISGDlVAIkFJ2KPTXEgpPW8bTEsJqIYxS6KCkpWUV2tJFCYSW0bIKFAq0ZfRpIYFCywgBEkbIIMOOoyQe8bYkS7p/fxxLsix5K7Edf9+vl16W7nluyXF03dd1zrFZlmUhIiIiIiIiIhlhH+oGiIiIiIiIiOxNFGiLiIiIiIiIZJACbREREREREZEMUqAtIiIiIiIikkEKtEVEREREREQySIG2iIiIiIiISAYp0BYRERERERHJIAXaIiIi0i/19fVEIpGhboaIiMiwpUBbRERkN2pvb6e5ubnfj9bW1l6P/dBDD/HSSy/1uS2//vWv2bhx42Auh0gkwmmnncYZZ5wxqOMMRz/60Y94/fXXk5a1trZy3nnnsWbNGsLhMA8//DDRaHSIWigiIiOFAm0REZHd6Gc/+xkej6ffj6lTp/Z67GeffZYvf/nLbNiwoddtP/zwQ7797W8TDAbjy26++Wb222+/bh9LlixJOc5NN93Em2++yQ9+8AMALrroImw2W7eP/fbbrx/vVu/++te/8qc//Slp2ebNm3tsQ9fH9ddfn3Lc999/n5tuugnLspKW22w2HnjgASoqKti2bRs333wzxx13HFu3bk3a7sknn+Spp57K6LWKiMjIpUBbRGQU+/e//83//M//UF5eTlZWFh6Phzlz5rB582YAzjvvvF6Dlvvuuy/tsa+++mrGjx9Pe3s7AGvWrOHUU0+lsLAQj8fDKaecwrp161L2+/vf/84RRxxBTk4OY8eOZdGiRTQ2NiZtE41Gue2225gxYwZut5t9992XX/7ylylBUn19PYsWLWL8+PG43W4OO+wwnn/++ZRz9rVtAOFwmDvvvBOPx9Pr+wtw5ZVXUllZmfYB8Nvf/jbtunfffTd+jHXr1vHxxx+nPL773e/idru599570673+/3xY/zud7/jsMMOIycnh82bN1NXV8f27duZPHkyS5YsSXmUlJRQU1OTdC1PPPEEN998M7/5zW+YM2cOYIL1NWvWdPt44YUX+vQ+9cV///tfzj77bPbZZ5+06++///4e27JmzRomTJiQdt9bb72VU045hc985jNJy91uNzabjWAwSHl5OStWrKC4uDjl937fffdlwYIFLFu2LCPXKiIiI5wlIiKj0t13320B1tFHH23de++91tNPP23deeed1v/7f//PWrlypWVZlrV69WrrhRdeSHncddddls1ms+bNm2dFo9GUY7e2tlpjxoyxfvjDH1qWZVmbNm2yiouLrVmzZlkPP/ywdc8991g+n8/y+XxWbW1tfL9nnnnGstls1umnn2795S9/sW688UYrKyvL+tznPpd0/Kuuusqy2+3W9773Pevpp5+2zj33XAuwbr311vg27e3t1hFHHGEVFxdbv/3tb63HH3/c+vSnP205nU7r1VdfjW/X17ZVVlZat912mzV16lQLsPrzX+g777xj/fjHP05ZDlgPPfRQ0rL777/fuu2225Le17y8vPg5+/NYuHChZVmW5ff7LY/Hk7Tusssusy677LL4Nl2ddtppSW3+wx/+YDkcDuvKK6+0LMuy6urq+nz9mdDe3m5NnTrV+va3v52ybtOmTRZg/etf/+r1OJMnT075LN5//30rKyvLWr16tVVfX2/V19cnrc/Ly7Mef/xxKxgMWhs3brRefvll66GHHrLuv//+pO2uv/56q6yszAoEAv2/QBER2aso0BYRGaUmTJhgTZ482Wpvb+/XfuFw2DrqqKOs4uJia+vWrWm3uffeey2Hw2FVVlZalmVZCxcutPLy8iy/3x/fZtmyZRZg/eQnP7Esy7Ki0ag1ZcoU6/DDD7cikUh8ux/96EcWYP3nP/+xLMuyNmzYYDkcDuvqq69OOudxxx1neTweq62tzbIsExgC1t/+9rf4NnV1dVZhYaF10kknxZf1pW2WZVlXXnmlVVBQYP3whz+0zjrrrH4F2q+99prldDqtu+66K2l510B7w4YNVn5+vnXOOeckbZeXl2c9+uijSctef/1169577+328zvttNPiQfTChQut448/PmWbyy67zDrmmGOshx56KOUxZ86ceED61ltvWTabzbrmmmssy7Ksp59+2iopKbF27tzZ5/dgsO6//37L7XZb27dvT1k32ED7pJNOsr7+9a9blmU+5wMPPND69a9/bV111VXWV77yFSsrK8vKz8+3bDabBVgFBQXWQQcdZP3v//5v0nF27dplFRQUWEuWLBn4hYqIyF5BgbaIyChVWFg4oED7xhtvtADrySef7HabQw891PrSl75kWZZlRSIRq7Cw0DrzzDNTtps4caJ19NFHW5ZlWW+//bYFpASj69evt4B4dvwXv/iFBVgfffRR0nb33HOPBVgvvviiZVmWNX/+fGvcuHEpGfevfe1rltPptNra2vrcNsuyrHXr1lnNzc2WZZnAtb9FYT/5yU+srKws680334wv6xxoBwIBa+7cudYBBxwQP0/MjBkzrL/+9a/x16FQyDrwwAOtk08+udvzXXjhhdZVV11lbd++3bLb7dZf/vIXq7KyMv4IhULWZZddZhUUFFgzZ85MeeTn58cD0nA4bN19992WZZlqhalTp1qXXHJJv65/sI488kjrrLPOSrtuMIH2gw8+aOXn51sffPCBtXLlSis7O9t67LHHrHPOOcdasGCB9YMf/MAaN26cdemll1rvv/++1dDQ0OPxL7zwQuuQQw7p17WJiMjex7k7y9JFRGT4Ouuss/j973/P6aefzm9/+9s+Db61YsUKbrzxRi644AK+/OUvp93m9ddf55133uHnP/85YAaqamho4FOf+lTKtgcddBArV64EYNWqVQAp202bNo2cnBzWrl0b387lcqUMsjVr1iwA1q5dy4knnsiqVas4+OCDsdlsKds98sgjbNy4Ebfb3ae2AUyfPr2bd6Vvvv/97/PCCy9wzjnn8O6775KXl5e0/oILLmDt2rW8/vrrKeti1x7z61//moqKirT9zWPuvffe+PO//vWvnHrqqUnrY33A/+d//oc//vGPKfuffvrp8ecOh4OLL74YgO9+97tEIhF+/vOf853vfIdf/vKX3V90h+OOO46XX3651+2609DQwFtvvcVFF1004GN05/XXX6e5uZnZs2cTjUY56aSTOOusszjrrLPi27zyyiuUlpZy0EEHJe0bjUaxLAuHwxFfdtJJJ3H//fdTXV1NaWlpxtsrIiIjgwZDExEZpX7zm99w0UUX8fzzzzNz5kzOP//8Hkevbmlp4Wtf+xr77LMPt99+e7fb3XXXXey3336ceOKJAOzcuRMAr9ebsm1xcTH19fW9bjdmzJik7UpKSlIC6OLiYoCk7bo7Z2y7vrYtE+x2O/fddx+1tbVJATzApk2bePHFF3nyySdTbiAcf/zxKQPQXXPNNTQ1NTF58uReB6u79957mT9/PpZlUVZWxgsvvIBlWfGbC5ZlEQ6HUx5Wl4HlAJ566inuuusuvvGNb+DxeLjmmmvSDjh24IEHctZZZ8VfP/jgg4N671avXk0kEuGwww7rcbuTTjqp1/djy5YtSfv87Gc/o6amhr///e/k5ORw5513Jq0PBALk5+fz1ltvcfvtt3PllVcyf/589t9/f3Jzc/m///u/pO0PPfRQLMvi/fffH9Q1i4jIyKaMtojIKJWTk8M999zD5Zdfzk9+8hMefPBB/vSnP3HzzTfz3e9+N2X7q666ik2bNvHaa6+Rn5+f9pjV1dU88cQT3HbbbfFl4XAYMIFmVzabLb68P9t1t03n/fuyXV/PmSkzZsxg06ZNFBQUJC2fMmUKmzZtIjc3N2WfBx98MGlO7YULFxIKhXj00UdTtr3kkksIhULcf//98WU+n6/HNj3++OM899xz1NfXk5OTg8vlAqCpqYlDDjkkvt1bb73FggULsCwr/r6Ulpamzdq63W6KiooyNrVX7IbI2LFje9zu/vvv58gjj+xxm9gNoBiPx0N9fT2XXHIJt956K9OnT6euro7TTz+djRs3sm3bNizLIi8vj3A4zLRp05g3bx4XXnghM2bMSKl0iL0fsTaLiMjopEBbRGSUO/jgg/nzn//M+++/z1lnncX3vvc9Jk+ezJlnnhnf5m9/+xv33HMPN910E3Pnzu32WPfeey9Op5OFCxfGlxUWFgKkzQ7v2rUrnk3uabv6+vqk7bo7FtCv7dra2vrUtoF6++23Ofzww3vcZsGCBSxYsCDtuoULFyaVdT/xxBOsWLGC1157LSWIfeedd3j11Vf585//nLRu8+bNFBUVxV9//vOfB+BrX/saRUVFnH322fzxj3/kvPPOo7CwMF6t8Oabb/LUU0/R3t7O6tWrOemkkzjjjDNYvnx5f96CjGhpaQFIKanvatKkSb0G91lZWUmvLcvi/PPPZ+rUqSxYsIDXX3+dxsZGzj77bKZOncq0adNYtmwZN954I//85z8BWLlyJWeddRbPPvss2dnZScfLyckBTCZcRERGLwXaIiICmL7L//jHP5g8eTKPPvpoPNDesWMHF110EUcffTTXXnttt/tHo1F+97vfcc455yRlbKdNm4bT6eTDDz9M2efDDz/k4IMPBogHSB9++GF8jmYwgWJLS0vSdk899RQVFRWUl5cnHQtI2q67c3o8HqZOnUowGOxT2wbqoIMOYs2aNd2u33///fn5z3+e0n86JnbzIeaRRx7BZrNx5513kpWVFQ/i6+vr+drXvsa8efNS+s5PmjQpPmf34Ycfzm233cbxxx9PXl4e1113XXy7888/n9NPP50f//jHtLS08N3vfpd169Zx1lln4XQ6OeGEE7jvvvvYd999B/ReDEZJSQkANTU1fZ6/vK/+9re/8cwzz5CTk0NJSQn5+flccsklSVUZoVCIqqqq+PzjX/jCFzjppJOYMWNGyvGqq6uB9N0RRERk9FAfbRGRUcrv96csC4VCQCIrByYACwaDPPTQQ0mDPnX17LPPsmXLFi699NKk5Tk5OcybN4+//vWvSZnjV155hS1btnDGGWcAcMwxx1BYWMgDDzyQtP8DDzyAw+HgS1/6EgDz588HSBnA64EHHmDKlCnxfrzz589n06ZN/Pvf/45vU19fz9/+9je+9KUvYbfb+9y2gXK73ey3337dPsCUdne3vmvZ9zPPPMOyZctobGxk7ty5HHXUUTz44IOccMIJtLe388gjj6S0weFwMHHiRCZOnIjD4cDr9TJx4kTGjBmTtN2xxx7L9OnTOfPMM/nUpz5FeXk5H330EYceeigHH3wwTz75ZI+f/+40ceJEgPgNg0yaM2cOt99+O3/961+prKykqakpKcgGOOCAA5g8eTJXXXUVxxxzDKeeeioPPPAAK1eupL29PWnbiooKwNzgEBGR0UsZbRGRUWrmzJmcddZZHHPMMRQVFVFRUcGSJUvIzs7msssuA2DJkiW88MILXHjhhXz88cd8/PHHKceZNWsWZWVl3HnnnRxzzDHMnj07ZZubbrqJY445hs997nNcffXVNDY28qMf/Yi5c+fGy6bdbjc33HAD3/rWtzjnnHP4yle+wurVq/nJT37Ct771rXgm9cgjj+T000/n5ptvjg/q9eSTT/LKK6/wzDPPxPtgX3bZZdx9992cddZZXH/99YwZM4Zf/epX2O12brjhhn61bTg57rjjOO6441i9ejWnnnpqvEz/u9/9bto+3t158803k17bbDZ+85vfcOKJJ3LKKafw8MMPJ613OofuK8OsWbMoLi5m+fLlHHvssRk99sSJE/nmN78JmKqMVatW8d///pfzzz8ft9sNwNatWykpKeGZZ57hl7/8JVdffTUAX/rSl7j22muTbi4tX76c4uLi+Cj4IiIySg3VvGIiIjK0vvOd71iHHHKIVVxcbLndbmvq1KnWueeea7333nuWZVlWbW2tlZOTYwE9Pv7whz9Y69ats2w2m/XYY491e75ly5ZZc+fOtVwul1VaWmpdfPHF1q5du1K2W7JkiTVt2jQrKyvLmjp1qvXzn//cikQiSdu0tLRYV155pTV27FjL5XJZc+bMsf72t7+lHGvTpk3W6aefbnk8His/P9/6whe+kDL/dn/aFjOQebTTodM82n31ySefWDfffLM1efJkKzs72/rOd75jXXbZZZbb7bbGjBljLV682Nq+fXvafSdMmGD98pe/tI499lhr3rx51mWXXWYtXLjQevnll62jjz7aWrx4sfX4449b2dnZ1hlnnGHt3Lkz5RiTJ0+2br311h7bePDBB2d8nu0FCxZYhx56aNp1sXm077//fmvNmjU9PiZMmJA0j/Zbb71lXX/99dbJJ59sFRQUWE6n0zriiCOsUChk7dy507rmmmsst9ttHXHEEVZBQUF8Pvfm5mbLZrNZr732WlJb5s6da51zzjkZvXYRERl5FGiLiMigfetb37LGjx9vhUKhoW7KiNKXQLu1tdW64447rAsvvNCaNm2aBViFhYXWlVdeaW3cuDG+nd/vt77zne9Yubm5lsvlsr7+9a9bLS0t8fVr1661bDabZbfbrcsuu8yqr6+35s2bZ+Xm5lpZWVnWxRdfbFVUVFiWZVkvv/xy/CbGueeea7W3t8ePky7Qfvvtt63bb7/d+sMf/mDddtttlsPhsK6//vpMvEVxq1evtmw2m/Wvf/0rZV0s0O7ro3OgfdNNN1lHHnmk9f3vf9/6xz/+YTU1NVmvvfaadfbZZ1vZ2dnWvvvuaz366KNWNBq1Hn30Uctms1nf/va3rWuvvdbyeDxWMBiMH2v58uWWzWazVq1aldFrFxGRkUel4yIiMiitra388Y9/5IorrkgZ0VkGLycnhz//+c80NTVx8sknc/vtt/PZz342ZbTr8ePHc9ttt/Gd73yHn/70p2zevDmplDw/P5/Zs2ezZMkSjj76aCKRCC0tLZx44on88pe/TJqm6rjjjmPjxo3ceeedhEKhXsvG/X4/V111FdFoFIfDwZw5c/j617+e0fdh1qxZnH/++Vx77bUcd9xxaX/X/vWvf/HZz362x+Pss88+Sa9/+MMf8sMf/jBp2TvvvMP27dt57LHHOO200+LTmZ199tkUFBSwePFiPvnkE37+85/HP4dwOMy1117Lueeem7b7hIiIjC42y7KsoW6EiIiMXBs2bODhhx/m4osv7nXOZhlalmXF+7ADNDQ0pIxsPpy1trYyd+5cPv/5z/Pzn/98qJuTZPHixTz11FO8/fbb3c4zLyIio4cCbREREREREZEM0vReIiIiIiIiIhmkQFtEREREREQkgxRoi4iIiIiIiGTQiB11PBqNsm3bNjweT9LALiIiIiIiIrJ3syyLpqYmJkyYEJ8dYjgZsYH2tm3bmDRp0lA3Q0RERERERIZIZWUlEydOHOpmpBixgbbH4wHMG1tQUDDErREREREREZE9pbGxkUmTJsXjwuFmxAbasXLxgoICBdoiIiIiIiKj0HDtRjz8itlFRERERERERjAF2iIiIiIiIiIZpEBbREREREREJIMUaIuIiIiIiIhkkAJtERERERERkQxSoC0iIiIiIiKSQQq0RURERERERDJIgbaIiIiIiIhIBinQFhEREREREckgBdoiIiIiIiIiGaRAW0RERERERCSDFGiLiIiIiIiIZJACbREREREREZEMUqAtIiIiIiIikkEKtEVEREREREQySIG2iIiIiIiISAYp0BYRERERERHJIAXaIiIiIiIiIhmkQFtEREREREaWcBgsa6hbIdItBdoiIiIiMjrU1sLKlUPdisx77jn4/vfTrwuFYMsW+O9/4Z139my7Mm3iRNiwwTx/+GE44YShbU+mrFkDjY3p1x1/PLzxRuJ11xsMGzaYbWTYUaAtIiIiI8/q1XDyyZCbC+PHw/nnmyCqs6VLYcoUyMmBefNg48bEuro6uOACKCmBwkI46SRzzHTq6812n/1s8vIPPoBjjzXHnzoVHnqo+/aeeCI4nanHPeccKCgArxe+9z2IRlP3ffllc/ytW7s/PsBf/gL77w9uNxx+eM8B5Xnngc3W98d3vtP9saJR+N3v4FOfSl3X12sEWLYs9bzz5yfW9+cz685bb8Fpp0Eg0L/9MnWNAM3N8K1vmd9btxsOPTSxbuPG1PfgoIN6b9/vfgdjxpjf+Tlz4IADzO9+aak5x6xZcO65cPvtu/8ae/scY3buhLPPhptv7v360nnySTjqqP7ts6c+x84++QSys+Gi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XqGLiIiIiKhM4DnaREREREREVKY4ej/IQ8eJiIiIiIiIrIiNNhEREREREZEVsdEmIiIiIiIisiI22kRERERERERWxEa7FCQkJCAnJ8feZRAREREREVEpYKNtY4mJidBqtejXr5+9SyEiIiIiIqJSwEbbxj777DPo9Xp8//33SE5Otnc5REREREREZGNstG0oMTERS5YsgUwmQ05ODj7//HN7l0REREREREQ2xkbbhnJ/zZbL5WjZsiU+//xz/qpNRERERET0nGOjbSO5v2aPGjUKEokE7dq1Q2ZmJn/VJiIiIiIies6x0baRxYsXQ6/XY8KECQAAd3d3DB8+HIsWLUJaWpqdqyMiIiIiIiJbkdm7gOeVTqfD4sWL4e3tbZz20Ucf4e7duxBC2LEyIiIiIiIisiU22jYyZMiQPNMqVqyI9evX26EaIiIiIiIiKi08dJyIiIiIiIjIithoExEREREREVkRG20iIiIiIiIiK2KjTURERERERGRFbLSJiIiIiIiIrIiNNhEREREREZEVsdEmIiIiIiIisiI22kRERERERERWxEabiIiIiIiIyIrYaBMRERERERFZERttIiIiIiIiIitio01ERERERERkRWy0iYiIiIiIiKyIjTYRERERERGRFbHRJiIiIiIiIrIiNtpEREREREREVsRGm4iIiIiIiMiKZPYuoLiEEACA1NRUO1dimfT09DJTKxERERERkSPL7a1y+0JHIxGOWpkZt27dQtWqVe1dBhEREREREdlJXFwcqlSpYu8y8iizjbZer8eff/4JtVoNiURi73IKVblyZXz88ccYMWKEvUtxKKmpqahatSri4uLg7u5u73KoGJhh2cb8yjbmV/Yxw7KN+ZVtzK9sy83v4sWL8PPzg1TqeGdEl9lDx6VSqUPuuSiIUqnkRlwAd3d3vjZlHDMs25hf2cb8yj5mWLYxv7KN+ZVtWq3WIZtsgIOhEREREREREVkVG20iIiIiIiIiK2KjTXajUCgwY8YMKBQKe5dCxcQMyzbmV7Yxv7KPGZZtzK9sY35lW1nIr8wOhlaWuLm5Yc6cORg7dqy9SyEiIiIiIiIb4y/aRERERERERFbERpuIiIiIiIjIithoExEREREREVkRG20iIiIiIiIiK2Kj/QKJiYnBq6++CpVKhUqVKmHQoEFITEw03h8eHg4fHx+4uLigffv2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", "text/plain": [ "
" ] @@ -422,8 +446,8 @@ "name": "stdout", "output_type": "stream", "text": [ - "CPU times: total: 8.84 s\n", - "Wall time: 30.7 s\n" + "CPU times: total: 3.2 s\n", + "Wall time: 30.1 s\n" ] } ], @@ -481,55 +505,55 @@ " 0\n", " SH600605\n", " 汇通能源\n", - " 102872.58\n", + " 103585.38\n", " \n", " \n", " 1\n", " SH600606\n", " 绿地控股\n", - " 99094.57\n", + " 99064.87\n", " \n", " \n", " 2\n", " SH603272\n", " 联翔股份\n", - " 98478.36\n", + " 98734.04\n", " \n", " \n", " 3\n", " SH600354\n", " 敦煌种业\n", - " 106984.32\n", + " 106967.16\n", " \n", " \n", " 4\n", " SZ300359\n", " 全通教育\n", - " 98394.08\n", + " 98368.75\n", " \n", " \n", " 5\n", " SZ000725\n", " 京东方A\n", - " 97358.10\n", + " 97428.94\n", " \n", " \n", " 6\n", " BJ838163\n", " 方大新材\n", - " 99602.61\n", + " 99732.99\n", " \n", " \n", " 7\n", " SZ300358\n", " 楚天科技\n", - " 101457.57\n", + " 101552.29\n", " \n", " \n", " 8\n", " SH600355\n", " 精伦电子\n", - " 95001.59\n", + " 93048.01\n", " \n", " \n", " 9\n", @@ -543,15 +567,15 @@ ], "text/plain": [ " 代码 股票 当前总资产\n", - "0 SH600605 汇通能源 102872.58\n", - "1 SH600606 绿地控股 99094.57\n", - "2 SH603272 联翔股份 98478.36\n", - "3 SH600354 敦煌种业 106984.32\n", - "4 SZ300359 全通教育 98394.08\n", - "5 SZ000725 京东方A 97358.10\n", - "6 BJ838163 方大新材 99602.61\n", - "7 SZ300358 楚天科技 101457.57\n", - "8 SH600355 精伦电子 95001.59\n", + "0 SH600605 汇通能源 103585.38\n", + "1 SH600606 绿地控股 99064.87\n", + "2 SH603272 联翔股份 98734.04\n", + "3 SH600354 敦煌种业 106967.16\n", + "4 SZ300359 全通教育 98368.75\n", + "5 SZ000725 京东方A 97428.94\n", + "6 BJ838163 方大新材 99732.99\n", + "7 SZ300358 楚天科技 101552.29\n", + "8 SH600355 精伦电子 93048.01\n", "9 SH603273 天元智能 100000.00" ] }, @@ -566,27 +590,6 @@ "data[:10]" ] }, - { - "cell_type": "code", - "execution_count": 13, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Trade(+infinity, , , UNKNOWN, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, --)" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "iodog.close()\n", - "my_tm.buy(Datetime(201906160000), sm['sz000001'], 10, 10000)" - ] - }, { "cell_type": "code", "execution_count": null, @@ -611,7 +614,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/hikyuu/examples/notebook/Demo/Demo2.ipynb b/hikyuu/examples/notebook/Demo/Demo2.ipynb index 2c4db368..79ee6538 100644 --- a/hikyuu/examples/notebook/Demo/Demo2.ipynb +++ b/hikyuu/examples/notebook/Demo/Demo2.ipynb @@ -5,24 +5,29 @@ "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-04-02 16:56:45,242 [INFO] hikyuu version: 1.3.5_202404021336_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:91) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2024-02-29 15:52:52.792 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2024-02-29 15:52:52.859 [HKU-I] - Loading market information... (StockManager.cpp:506)\n", - "2024-02-29 15:52:52.877 [HKU-I] - Loading stock type information... (StockManager.cpp:519)\n", - "2024-02-29 15:52:52.883 [HKU-I] - Loading stock information... (StockManager.cpp:435)\n", - "2024-02-29 15:52:53.165 [HKU-I] - Loading stock weight... (StockManager.cpp:536)\n", - "2024-02-29 15:52:54.838 [HKU-I] - Loading KData... (StockManager.cpp:132)\n", - "2024-02-29 15:52:55.542 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:170)\n", - "2024-02-29 15:52:55.543 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:173)\n", - "2024-02-29 15:52:55.543 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:176)\n", - "2024-02-29 15:52:55.572 [HKU-I] - 0.73s Loaded Data. (StockManager.cpp:149)\n", - "CPU times: total: 1.05 s\n", - "Wall time: 4.01 s\n" + "2024-04-02 16:56:45.638 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-04-02 16:56:45.663 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", + "2024-04-02 16:56:45.678 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", + "2024-04-02 16:56:45.683 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", + "2024-04-02 16:56:45.847 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", + "2024-04-02 16:56:47.006 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", + "2024-04-02 16:56:47.696 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", + "2024-04-02 16:56:47.696 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", + "2024-04-02 16:56:47.696 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", + "2024-04-02 16:56:47.701 [HKU-I] - 0.69s Loaded Data. (StockManager.cpp:150)\n", + "CPU times: total: 656 ms\n", + "Wall time: 2.94 s\n" ] } ], @@ -349,7 +354,7 @@ "outputs": [ { "data": { - "image/png": "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+EPNnj1b1113nY444gjdeOONGjdunG677TZJ0u23365UKqWlS5dqzpw5+sY3vqFjjz1WN998s5e/og37NAAAAADvmSOPaDbW8kIu/+HW+eL14nkgbQTOVl1dXXrllVf0kY98xLzt0EMP1QEHHGBmjJcvX247PmbMGM2dOzfv8XA4rIULF9qOn3jiiYpGo+Z9TjrppKIZ6YGBAfX09Nj+lMr4/+OqGAAAAOAdY+QRc6TREhnpnTt36mc/+5k6Ozs1Z84cffe731U8HtemTZskSTNnzrTdf9q0adq6dat2796trq6uvMclaePGjVUdz+faa6/V6NGjzT9Tp04t+/fmyQwAAAB4x61Ts1HaHSSJ1VJSLjOjrU3o6sXTQPqqq67SSy+9pCeeeEKf+MQn9K1vfUtXXnmluc+5o6PDdv+Ojg719/cXPS5Je/furep4PldeeaW6u7vNP1u2bCn59zUaHvBkBgAAALzjNkeaZmOtacfewZzb/JCRDnv5zd73vveZH8+fP1/JZFLXXXedzj77bEnS4KD9H6G/v18dHR2KxWIFj0tSLBar6ng+sVjM/PnlYp8GAAAA4D3XQHrob5JYrWXH3oGc25qua7fT3Llz1dvbq/3331+ScrK9W7Zs0axZszRhwgTFYrG8xyVpypQpVR2vBXOfBs9lAAAAwDPmHGlLwPTDp16v13JQR26BtB8y0jUNpFetWqWxY8dq6tSpmjFjhh5//HHz2GuvvaY333xTixcvVjAY1AknnGA73t3drdWrV2vx4sWSpAULFtiOJ5NJPf3007bjTz31lJLJpHmfJ5980jxeC2ZGml3SAAAAgGfCxhzpoRPurV195rG0y55ZNK/+eG6H7qbr2n3ZZZfpkUce0Zo1a/S9731P3/3ud/XVr35VwWBQX/ziF/WDH/xAd999t1566SV99rOf1WmnnaYjjjhCkvSFL3xBv/71r3XLLbdozZo1uuCCC3TIIYdoyZIlkqTLL79cq1at0tVXX63169frsssuUyqV0mc+8xlJ0oUXXqiuri5deumlWr9+va655hqtW7dOl19+uZe/ok2a0m4AAADAc86MdH88mywbTNY/iEJ9+SCO9jaQ7u3t1Sc/+Um9//3v15133qlbb71VV1xxhSTp0ksv1Re/+EV97nOf04knnqjp06frjjvuML/29NNP1w9+8ANdffXVOuGE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", + "image/png": "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udK8780339Rf/dVfafjw4Ro9erQ+/elP68033zSPb9iwQT6fz/Zx1FFH2Z73qaee0owZM9Te3q6pU6fqoYcesh1ft26d5s6dq46ODk2ePFnLli2zHd+8ebPOOOMMdXV1ady4cfr+979fyo9XkqSM0m4m1IAXrOXS9fam6BxPvTQbA4BCmftIE0ijgbk1G6u3i+9oXkUF0i+++KLOPfdczZkzRy+99JLt2Ne//nVNnjxZTzzxhO666y5t2rRJZ555phLpjdB37Nghv9+v1157TevXr9f69ev14IMPml+/ceNGLViwQCeffLKee+45zZs3T2eddZY2bdokSert7dUpp5yiSZMmadWqVbrwwgu1cOFCPfvss5KkeDyu008/XbFYTE8//bSuvPJKff3rX9edd95Zzv9Pwczzl/k04IlgFTPSxZ62WYE0pd0AGgxrpNHIjHfhgMtWmcTRqJaitr+65557FA6HtXz5cp100km2Y9dee60mTJhg3v7Rj36kj3zkI1q/fr0OO+ww7dixQyNHjtShhx7q+tw33HCDpkyZomuuuUaSdP311+s3v/mNbr31Vl155ZW67bbbFI/HtXTpUrW1tWnatGlavny5brzxRp1wwgm6//779eqrr+rRRx/V2LFjNX36dD322GP68Y9/rHPPPbfY/5eimaXdFf9OQGuwZnmLXcNcaVml3SySAdBgjNLuSCyhZDLJriNoKMa82620mzXSqJaiAumrrrpKPp/PzBJbWYNoSWpvb5eUyhRLqYz0mDFjcj73ihUrdOqpp2YGFgxq7ty5ZsZ5xYoVmj9/vtra2szHzJ8/X3fffbd5fPr06Ro7dqzt+OLFi3O+QUQiEUUimW1vent7c45vKGx/BXjLVtpdZ4F0wlEJSWk3gEbTls5IJ5PSYDxhBtZAIzCWVFLajVoqKo9STJC4dOlSHXjggfrQhz4kSdq+fbvWr1+vjo4OTZkyRV/84he1detW8/EbNmzQpEmTbM8xceJEbdmypazjkUhE27Ztcx3j1VdfrREjRpgfzosBxTCuflHhCXjDei7FK3x1udg4mDXSABpdm6WUJubcLwioc/ky0sTRqBbPCxKTyaSuvPJK3XTTTfrJT36iYDCV9P7kJz+p5557Tr///e/1zW9+U/fff7/OPPNMM2Pd19enzs5O23N1dnaaGeNSj0uyZZ2trrjiCvX0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OA1btmxRz549lZ2drf79+0dlt2/fPpWVlWn8+PFkG2eKiopUWlqq2267rd37MX/jUzgc7vB8HT16dNTtDQ0NWr9+PVl3Efn5+erRo4c2btwYtYLZli1btkiSLrjgAknkG0/cvg+TbddXWVmpjRs3nvIzWGLuRvH0mOFdzOOPP259+/a1L774wr799lsbNGiQzZgxw+th4RT27t1rkmzZsmUtzoV35MgRe/bZZ2358uVWXFxsCxYssJ49e1p+fr7z+JUrV5rf77d3333XioqKbNKkSXb55ZdbfX29h68KjWbMmGGff/65FRUV2auvvmqpqak2d+5cMzN7/fXXLT093T7++GPbunWrjR492iZMmOA8lmzjx2uvvWZ9+vRpsZ35G59KS0tbnGKlo/N1x44dFggErKCgwHbv3m35+fmWlZXFOcM90DzfY8eOOecRbv45XFFRYWZmhYWFtnTpUisuLrYPP/zQsrOzLS8vz3lO8u0aWpu7HX0fJtuuo7V8zcyWL19uPp/Pjh492uIxzN22UaSbqKmpsfz8fMvIyLCePXvaww8/bDU1NV4PC6ewYcMGk9TqzxtvvGEzZ860Pn36WGpqqg0ZMsTmzZvX4pfsN99807KysiwUCtnNN99sBw8e9OjVoLnp06dbZmampaWl2RVXXGFLlixxbotEIjZr1izr1auXdevWze699147fPhw1OPJNj5MnTrVbrjhhhbbmb/xqbVf1s7EfF22bJkNHDjQgsGgjRkzxn744YfOeDlopnm+Bw4caPNz+IknnjAzs7feesvC4bAFg0G78MILbebMmXb8+PGo5yVf77U2d8/E+zDZdg1tFennnnvOLr744lYfw9xtW4qZWacugQMAAAAAEMf4jjQAAAAAAC5QpAEAAAAAcIEiDQAAAACACxRpAAAAAABcoEgDAAAAAOACRRoAAAAAABco0gAAAAAAuECRBgAAAADABYo0AAAAAAAuUKQBAAAAAHCBIg0AAAAAgAsUaQAAAAAAXPh/EAppcsWzMHEAAAAASUVORK5CYII=", "text/plain": [ "
" ] @@ -374,58 +379,58 @@ "name": "stdout", "output_type": "stream", "text": [ - "R乘数期望值: 0.02\n", - "亏损交易亏损总额: -199118.00\n", - "亏损交易平均R乘数: -0.11\n", - "亏损交易平均亏损: -33186.33\n", - "亏损交易平均持仓时间: 86.00\n", - "亏损交易数: 6.00\n", - "亏损交易最大持仓时间: 161.00\n", - "交易平均占用现金比例%: 49.84\n", - "交易机会频率/年: 1.71\n", - "净赢利/亏损比例: 1.14\n", - "单笔交易最大占用现金比例%: 49.99\n", - "已平仓交易总成本: 0.00\n", - "已平仓交易总数: 13.00\n", - "已平仓净利润总额: 27117.57\n", - "已平仓帐户收益率%: 5.42\n", "帐户初始金额: 500000.00\n", - "帐户平均年收益率%: 0.71\n", - "帐户年复合收益率%: 0.70\n", - "平均空仓时间: 152.00\n", - "平均赢利/平均亏损比例: 0.97\n", - "年度期望R乘数: 0.03\n", - "当前总资产: 527117.57\n", - "最大单笔亏损: -54740.00\n", - "最大单笔亏损R乘数: -0.19\n", - "最大单笔亏损百分比%: -18.85\n", - "最大单笔盈利百分比%: 56.58\n", - "最大单笔赢利: 140710.57\n", - "最大单笔赢利R乘数: 0.57\n", - "最大连续亏损R乘数: -0.26\n", - "最大连续亏损笔数: 2.00\n", - "最大连续亏损金额: -90815.00\n", - "最大连续赢利R乘数: 0.29\n", - "最大连续赢利笔数: 2.00\n", - "最大连续赢利金额: 142426.57\n", - "最长空仓时间: 431.00\n", - "未平仓头寸净值: 0.00\n", - "现金余额: 527117.57\n", - "空仓总时间: 1987.00\n", - "空仓时间/总时间%: 71.00\n", - "累计借入现金: 0.00\n", - "累计借入资产: 0.00\n", "累计投入本金: 500000.00\n", "累计投入资产: 0.00\n", + "累计借入现金: 0.00\n", + "累计借入资产: 0.00\n", "累计红利: 35609.57\n", - "赢利交易平均R乘数: 0.12\n", - "赢利交易平均持仓时间: 39.86\n", - "赢利交易平均赢利: 32319.37\n", - "赢利交易数: 7.00\n", - "赢利交易最大持仓时间: 105.00\n", - "赢利交易比例%: 53.85\n", + "现金余额: 512542.57\n", + "未平仓头寸净值: 0.00\n", + "当前总资产: 512542.57\n", + "已平仓交易总成本: 0.00\n", + "已平仓净利润总额: 12542.57\n", + "单笔交易最大占用现金比例%: 49.99\n", + "交易平均占用现金比例%: 49.85\n", + "已平仓帐户收益率%: 2.51\n", + "帐户年复合收益率%: 0.32\n", + "帐户平均年收益率%: 0.33\n", "赢利交易赢利总额: 226235.57\n", - "赢利期望值: 2085.97\n", + "亏损交易亏损总额: -213693.00\n", + "已平仓交易总数: 14.00\n", + "赢利交易数: 7.00\n", + "亏损交易数: 7.00\n", + "赢利交易比例%: 50.00\n", + "赢利期望值: 895.90\n", + "赢利交易平均赢利: 32319.37\n", + "亏损交易平均亏损: -30527.57\n", + "平均赢利/平均亏损比例: 1.06\n", + "净赢利/亏损比例: 1.06\n", + "最大单笔赢利: 140710.57\n", + "最大单笔盈利百分比%: 56.58\n", + "最大单笔亏损: -54740.00\n", + "最大单笔亏损百分比%: -18.85\n", + "赢利交易平均持仓时间: 39.86\n", + "赢利交易最大持仓时间: 105.00\n", + "亏损交易平均持仓时间: 74.86\n", + "亏损交易最大持仓时间: 161.00\n", + "空仓总时间: 2012.00\n", + "空仓时间/总时间%: 71.00\n", + "平均空仓时间: 143.00\n", + "最长空仓时间: 431.00\n", + "最大连续赢利笔数: 2.00\n", + "最大连续亏损笔数: 3.00\n", + "最大连续赢利金额: 142426.57\n", + "最大连续亏损金额: -105390.00\n", + "R乘数期望值: 0.01\n", + "交易机会频率/年: 1.82\n", + "年度期望R乘数: 0.02\n", + "赢利交易平均R乘数: 0.12\n", + "亏损交易平均R乘数: -0.10\n", + "最大单笔赢利R乘数: 0.57\n", + "最大单笔亏损R乘数: -0.19\n", + "最大连续赢利R乘数: 0.29\n", + "最大连续亏损R乘数: -0.19\n", "\n" ] } @@ -447,9 +452,20 @@ "cell_type": "code", "execution_count": 12, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "image/png": "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tWjXrh07duygWbNmDBs2jAoVKmBubs706dPRaDQ6wSN3d3eioqL0XsuJEycYMGCATnt4eDhVq1YFoGfPnmzZssXg/QQHB3PhwgX59fTp05k6daqiz71796hatSrx8fGYmZkBOcEftVpdYKH5Y8eOcf/+fb7//nt++OEHxT4/Pz/q1KnDjRs3ePDgAdeuXePAgQNotVq6d+8u90tPT1cUvL916xZubm6KscLCwpgyZQoHDhzA3t6eJk2aUKtWLXx9fRX9Hj16xKVLl/j222/le79165bB68+rbt26fP311yxfvpzs7GzS09P1ZnlduXKFrl27FmnMa9eu8f7779OwYUOuXbvGsGHDsLCwICgoSL6+efPmsXz5cipUqMCkSZPo2rUrFy9e1Hnf4+Pj6dixI3369GHp0qVcvHiR999/HxcXFzmYeeHCBVq2bElgYCDz588nPT29yNmIQsFKZLArv/a1yiheizRGQRAEQRAEQRCE10OnTp0A+OyzzwoMIgE4OzuzefNm+bW3tzcAf/31F9WqVaN69eqFni8yMlL+d3p6Ovfv36dSpUpyW0xMDBqNBnd3d73Hb9++HWtra4PjZ2Zm4uPjI782Njbms88+w8XFhXHjxgHIAS4zMzM58OTv74+npycfffSRwbG/++47WrZsSf369RXtW7du5fbt26SmprJkyRKuXbtGbGws586dY9OmTXJ2U3R0NLVr1yY+Pt7gObZt24afnx9r1qyhefPmAGzatIm+ffuSmZnJoEGDFP1tbGzk57BlyxYiIyMNztBSqVR07dqVr776io0bNzJz5kx69OiBsbHh0EZxVnvMO+WzXr16HD16lP3798vBrjlz5vDpp5/KAc0ffviBKlWq8OuvvyoCgpAT4ExISGDRokXY2NhQr149tm3bxpkzZ+Rg16hRo+jbty/BwcFFvkahaEp8sKuUlSl1ytkp2rJFZpcgCIIgCIIgCMJrZd26dYopZ/o4OTnJ/542bRqPHj0CcqabRUVFcfnyZQC6detW6Pk0Gg29evUC4Ndff5Xbd+3axZQpU9i0aRPt2rXTOa64mV2QU0OrVatWNGrUSO8UxB07drB161amTJkCwO+//06HDh3Izs5GkiRWrVrFvHnz+OGHHzh79iyenp6K469cuYKZmRl9+vSRt06dOsnZcUWh1WqZPXs2c+bMYc2aNfTp00fe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c44fP86QIUO4e/cuhw8f1pvx1L17d/744w969+7Nrl27+Prrr3FwcND7HqSmphIVFUXNmjUNvk+5JEmiXLly3Lx5U27btGkTe/bs0enr6OhIjRo1ipTRuHDhQry8vHSe1dixY/H09KRnz54FBrtMTU3ZuHEjzZs3Z8qUKUiSxNSpU2nbti0AJ0+exMzMjMjISPr160dcXBytW7fmm2++oXTp0oVen1CwVzbYJQiCIAiCIAiCIAiFyZsl1bp1a/r168eoUaOKNUZ0dDT37t1jxIgR3Lt3T+80OhcXFy5evMjly5fp2bMnnp6eDBkyBB8fH0Vg5bPPPuPx48fs2rULb29vYmJi5ALt/fr1w8vLi6CgIPr27UvlypV1znP06FG90wQnTpzIjz/+yLp16+jRo4fc/vHHHzNo0CDee++9Au/R398fSZJISkrCy8sLlUqFv78/kBPsyp0amevKlSsEBwfLUxjzunPnDjt37mTkyJEALFiwgIoVK7J27VqqV69u8BpatmzJpUuX+OSTT0hLS8PBwYErV67I0xC1Wi3z5s1jy5YtBAUFcenSJdTqF7euXvPmzTl27Fih/RITE9mwYQMbN25UtP/444/s2LFDJ+CoT0xMDN7e3rz77rsEBgZy8eJFxo8fj6enJwMGDODvv//GxMSEBQsWsHTpUjQaDWPGjGHAgAEcOnTome9RyCGCXYIgCIIgCIIgCEKJd/78eS5evEhYWBhZWVkG++UGVn755Rf2799PdnY29evXZ9y4cSQmJnLq1Cnc3d05deoUkiTRpEkToqOj5emDt2/f5p133mHOnDloNBomTJjAnj176NSpE/fu3WPlypUcP36cunXrcuDAAR49eiSf29LSklmzZrFkyRK+//77Au/H2toaX19fRcHyrVu34uzszP3794GcKXVLlizBysqK5ORkxbH55c1Q2rRpE6NGjeKdd97B2tpaztbat28fS5cu5eDBgxgbGxss7H7nzh2WLl0qB7vGjx+PSqUiLi4O0L/4wbp16/j555+xtrZm8eLFQM50RE9PTy5fvgw8XQhgwIABTJ48mS1bttCvX78C3yeAiIgIxT1nZWXpXSigqNavX4+trS1dunSR2/7880+GDh3K1q1bi5R5tWjRIhwcHFi5ciUqlYrGjRsTGxvLxIkTGTBgAFlZWSQnJxMaGkqFChXkY7y9vblz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", + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], "source": [ - "#自己写吧" + "my_sys.performance()" ] }, { @@ -466,9 +482,18 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 13, "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "CPU times: total: 4.98 s\n", + "Wall time: 29.2 s\n" + ] + } + ], "source": [ "import pandas as pd\n", "def calTotal(blk, q):\n", @@ -489,9 +514,119 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
代码股票当前总资产
0SH600605汇通能源778089.17
1SH600606绿地控股459330.60
2SH603272联翔股份480224.00
3SH600354敦煌种业575339.00
4SZ300359全通教育441039.85
5SZ000725京东方A465180.59
6BJ838163方大新材519824.00
7SZ300358楚天科技496350.30
8SH600355精伦电子390633.00
9SH603273天元智能543317.00
\n", + "
" + ], + "text/plain": [ + " 代码 股票 当前总资产\n", + "0 SH600605 汇通能源 778089.17\n", + "1 SH600606 绿地控股 459330.60\n", + "2 SH603272 联翔股份 480224.00\n", + "3 SH600354 敦煌种业 575339.00\n", + "4 SZ300359 全通教育 441039.85\n", + "5 SZ000725 京东方A 465180.59\n", + "6 BJ838163 方大新材 519824.00\n", + "7 SZ300358 楚天科技 496350.30\n", + "8 SH600355 精伦电子 390633.00\n", + "9 SH603273 天元智能 543317.00" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "#保存到CSV文件\n", "#data.to_csv(sm.tmpdir() + '/统计.csv')\n", @@ -522,7 +657,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.18" + "version": "3.11.7" } }, "nbformat": 4, From dd7b0d62d6b54e80e30109cfdd90e2b5685c0875 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 2 Apr 2024 21:56:25 +0800 Subject: [PATCH 142/601] update example --- docs/examples/Turtle_SG.py | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/examples/Turtle_SG.py b/docs/examples/Turtle_SG.py index ce0c9b08..2f6cc6ee 100644 --- a/docs/examples/Turtle_SG.py +++ b/docs/examples/Turtle_SG.py @@ -2,15 +2,15 @@ # -*- coding: utf8 -*- # cp936 -#=============================================================================== +# =============================================================================== # Aothor: fasiondog # History: 20160407, Added by fasiondog -#=============================================================================== +# =============================================================================== from hikyuu import * -class TurtleSignal(SignalBase): +class TurtleSignal(SignalBase, k): def __init__(self, n=20): super(TurtleSignal, self).__init__("TurtleSignal") self.set_param("n", 20) @@ -20,10 +20,9 @@ class TurtleSignal(SignalBase): def _calculate(self): n = self.get_param("n") - k = self.to c = CLOSE(k) - h = REF(HHV(c, n), 1) #前n日高点 - L = REF(LLV(c, n), 1) #前n日低点 + h = REF(HHV(c, n), 1) # 前n日高点 + L = REF(LLV(c, n), 1) # 前n日低点 for i in range(h.discard, len(k)): if (c[i] >= h[i]): self._add_buy_signal(k[i].datetime) @@ -38,7 +37,7 @@ if __name__ == "__main__": s = get_stock("sh000001") k = s.get_kdata(Query(-500)) - #只有设置交易对象时,才会开始实际计算 + # 只有设置交易对象时,才会开始实际计算 sg.to = k dates = k.get_datetime_list() for d in dates: From 4b340c8f261a5de2b127def2714170b0dc2ba8dd Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 00:50:08 +0800 Subject: [PATCH 143/601] System support trace --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 2 +- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 104 +++++++++++------- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 4 +- .../system/test_Simple_SYS_for_base.cpp | 20 +--- .../system/test_Simple_SYS_for_cn.cpp | 4 - .../system/test_Simple_SYS_for_ev.cpp | 11 -- .../system/test_Simple_SYS_for_pg.cpp | 1 - .../system/test_Simple_SYS_for_st.cpp | 2 - .../system/test_Simple_SYS_for_tp.cpp | 2 - 9 files changed, 71 insertions(+), 79 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index ffb46b1e..ebc72c3f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -148,7 +148,7 @@ void Portfolio::_readyForRun() { sys->getTM()->name(fmt::format("TM_SUB_{}", sys_name)); sys->name(fmt::format("PF_{}", sys_name)); - HKU_CHECK(sys->readyForRun(), "Exist invalid system, it could not ready for run!"); + sys->readyForRun(); KData k = sys->getStock().getKData(m_query); sys->setTO(k); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index d2be7092..d2ec7434 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -82,6 +82,8 @@ System::System(const TradeManagerPtr& tm, const MoneyManagerPtr& mm, const Envir System::~System() {} void System::initParam() { + setParam("trace", false); + // 连续延迟交易请求的限制次数,需大于等于0,0表示只允许延迟1次 setParam("max_delay_count", 3); @@ -92,7 +94,7 @@ void System::initParam() { // 延迟操作的情况下,是使用当前的价格计算新的止损价/止赢价/目标价还是使用上次计算的结果 setParam("delay_use_current_price", true); setParam("tp_monotonic", true); // 止赢单调递增 - setParam("tp_delay_n", 3); // 止赢延迟判断天数 + setParam("tp_delay_n", 1); // 止赢延迟判断天数 setParam("ignore_sell_sg", false); // 忽略卖出信号,只使用止损/止赢等其他方式卖出 // 最高价等于最低价时,是否可进行交易 @@ -318,10 +320,10 @@ void System::_sellNotifyAll(const TradeRecord& record) { m_pg->sellNotify(record); } -bool System::readyForRun() { - HKU_ERROR_IF_RETURN(!m_tm, false, "Not setTradeManager!"); - HKU_ERROR_IF_RETURN(!m_mm, false, "Not setMoneyManager!"); - HKU_ERROR_IF_RETURN(!m_sg, false, "Not setSignal!"); +void System::readyForRun() { + HKU_CHECK(m_tm, "Not setTradeManager! {}", name()); + HKU_CHECK(m_mm, "Not setMoneyManager! {}", name()); + HKU_CHECK(m_sg, "Not setSignal! {}", name()); // 如果存在市场环境判断策略,则需要将默认的前一日市场有效标志置为false // 因为需要由市场环境判断策略全权判定市场是否有效 @@ -344,40 +346,18 @@ bool System::readyForRun() { m_tm->setParam("support_borrow_cash", getParam("support_borrow_cash")); m_tm->setParam("support_borrow_stock", getParam("support_borrow_stock")); - - return true; } void System::run(const KQuery& query, bool reset, bool resetAll) { HKU_CHECK(!m_stock.isNull(), "m_stock is NULL!"); - - // reset必须在readyForRun之前,否则m_pre_cn_valid、m_pre_ev_valid将会被赋为错误的初值 - if (resetAll) { - this->forceResetAll(); - } else if (reset) { - this->reset(); - } - KData kdata = m_stock.getKData(query); - HKU_DEBUG_IF_RETURN(m_calculated && m_kdata == kdata, void(), "Not need calculate."); - - HKU_IF_RETURN(!readyForRun(), void()); - - setTO(kdata); - size_t total = kdata.size(); - auto const* ks = kdata.data(); - auto const* src_ks = m_src_kdata.data(); - for (size_t i = 0; i < total; ++i) { - if (ks[i].datetime >= m_tm->initDatetime()) { - _runMoment(ks[i], src_ks[i]); - } - } - m_calculated = true; + run(kdata, reset, resetAll); } void System::run(const Stock& stock, const KQuery& query, bool reset, bool resetAll) { - m_stock = stock; - run(query, reset, resetAll); + HKU_CHECK(!stock.isNull(), "stock is NULL!"); + KData kdata = stock.getKData(query); + run(kdata, reset, resetAll); } void System::run(const KData& kdata, bool reset, bool resetAll) { @@ -385,21 +365,37 @@ void System::run(const KData& kdata, bool reset, bool resetAll) { if (resetAll) { this->forceResetAll(); } else if (reset) { - // System::run 供单体系统进行回测,需要强制复位所有的组件,忽略组件的共享属性 this->reset(); } HKU_DEBUG_IF_RETURN(m_calculated && m_kdata == kdata, void(), "Not need calculate."); - HKU_IF_RETURN(!readyForRun(), void()); + readyForRun(); + bool trace = getParam("trace"); setTO(kdata); size_t total = kdata.size(); auto const* ks = kdata.data(); auto const* src_ks = m_src_kdata.data(); for (size_t i = 0; i < total; ++i) { if (ks[i].datetime >= m_tm->initDatetime()) { - _runMoment(ks[i], src_ks[i]); + auto tr = _runMoment(ks[i], src_ks[i]); + if (trace) { + HKU_INFO_IF(!tr.isNull(), "{}", tr); + PositionRecord position = m_tm->getPosition(ks[i].datetime, m_stock); + FundsRecord funds = m_tm->getFunds(kdata.getQuery().kType()); + if (position.number > 0.0) { + // clang-format off + HKU_INFO("+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+"); + HKU_INFO("| total | cash | profit | market | position | close price | stoploss | goal price | total cost |"); + HKU_INFO("+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+"); + HKU_INFO("| {:<12.2f}| {:<12.2f}| {:<12.2f}| {:<12.2f}| {:<12.2f}| {:<12.2f}| {:<12.2f}| {:<12.2f}| {:<12.2f}|", + funds.total_assets(), funds.cash, funds.profit(), funds.market_value, position.number, src_ks[i].closePrice, + position.stoploss, position.goalPrice, position.totalCost); + HKU_INFO("+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+-------------+"); + // clang-format on + } + } } } m_calculated = true; @@ -422,12 +418,21 @@ TradeRecord System::runMoment(const Datetime& datetime) { } TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { + bool trace = getParam("trace"); + if (trace) { + HKU_INFO("{} ------------------------------------------------------", today.datetime); + HKU_INFO("[{}] source {} ", name(), src_today); + HKU_INFO_IF(m_kdata.getQuery().recoverType() != KQuery::NO_RECOVER, "[{}] Restitution {} ", + name(), src_today); + } + m_buy_days++; m_sell_short_days++; TradeRecord result; if ((today.highPrice == today.lowPrice || today.closePrice > today.highPrice || today.closePrice < today.lowPrice) && !getParam("can_trade_when_high_eq_low")) { + HKU_INFO_IF(trace, "[{}] ignore current highPrice == lowPrice", name()); return result; } @@ -446,6 +451,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 如果持有多头仓位,则立即清仓卖出 if (m_tm->have(m_stock)) { tr = _sell(today, src_today, PART_ENVIRONMENT); + HKU_INFO_IF(trace, "[{}] EV to sell", name()); } m_pre_ev_valid = current_ev_valid; @@ -456,6 +462,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { if (!m_pre_ev_valid) { // 如果使用环境判定策略进行初始建仓 if (getParam("ev_open_position")) { + HKU_INFO_IF(trace, "[{}] EV to buy", name()); TradeRecord tr = _buy(today, src_today, PART_ENVIRONMENT); m_pre_ev_valid = current_ev_valid; return tr.isNull() ? result : tr; @@ -476,6 +483,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 如果持有多头仓位,则立即清仓卖出 if (m_tm->have(m_stock)) { tr = _sell(today, src_today, PART_CONDITION); + HKU_INFO_IF(trace, "[{}] CN to sell", name()); } m_pre_cn_valid = current_cn_valid; @@ -486,6 +494,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { if (!m_pre_cn_valid) { // 如果使用环境判定策略进行初始建仓 if (getParam("cn_open_position")) { + HKU_INFO_IF(trace, "[{}] CN to buy", name()); TradeRecord tr = _buy(today, src_today, PART_CONDITION); m_pre_cn_valid = current_cn_valid; return tr.isNull() ? result : tr; @@ -501,20 +510,28 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 如果有买入信号 if (m_sg->shouldBuy(today.datetime)) { TradeRecord tr; - if (m_tm->haveShort(m_stock)) + if (m_tm->haveShort(m_stock)) { + HKU_INFO_IF(trace, "[{}] SG to buy short", name()); tr = _buyShort(today, src_today, PART_SIGNAL); - else + + } else { + HKU_INFO_IF(trace, "[{}] SG to buy", name()); tr = _buy(today, src_today, PART_SIGNAL); + } return tr.isNull() ? result : tr; } // 发出卖出信号 if (m_sg->shouldSell(today.datetime)) { TradeRecord tr; - if (m_tm->have(m_stock)) + if (m_tm->have(m_stock)) { + HKU_INFO_IF(trace, "[{}] SG to sell", name()); tr = _sell(today, src_today, PART_SIGNAL); - else + + } else { + HKU_INFO_IF(trace, "[{}] SG to sell short", name()); tr = _sellShort(today, src_today, PART_SIGNAL); + } return tr.isNull() ? result : tr; } @@ -531,10 +548,15 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { if (position.number != 0) { TradeRecord tr; if (src_current_price <= position.stoploss) { + HKU_INFO_IF(trace, "[{}] ST to sell, current price: {}, stoploss: {}", name(), + src_current_price, position.stoploss); tr = _sell(today, src_today, PART_STOPLOSS); - //} else if (current_price >= position.goalPrice) { + } else if (src_current_price >= _getGoalPrice(today.datetime, src_current_price)) { + HKU_INFO_IF(trace, "[{}] PG to sell, current price: {}, goal: {} ", name(), + src_current_price, _getGoalPrice(today.datetime, src_current_price)); tr = _sell(today, src_today, PART_PROFITGOAL); + } else { price_t current_take_profile = _getTakeProfitPrice(today.datetime); if (current_take_profile != 0.0) { @@ -549,6 +571,10 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { size_t position_pos = m_kdata.getPos(position.takeDatetime); // 如果当前价格小于等于止盈价,且满足止盈延迟条件则卖出 if (pos - position_pos >= tp_delay_n && current_price <= current_take_profile) { + HKU_INFO_IF( + trace, + "[{}] TP to sell, current price after restoration: {}, take_profit: {}", + name(), current_price, current_take_profile); tr = _sell(today, src_today, PART_TAKEPROFIT); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index d850443d..7e8a321d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -206,8 +206,8 @@ public: // 当前是否存在延迟的操作请求,供Portfolio bool haveDelayRequest() const; - // 运行前准备工作 - bool readyForRun(); + // 运行前准备工作, 失败将抛出异常 + void readyForRun(); TradeRecord sell(const KRecord& today, const KRecord& src_today, Part from) { return _sell(today, src_today, from); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_base.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_base.cpp index e09d2c47..c023d520 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_base.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_base.cpp @@ -50,30 +50,18 @@ TEST_CASE("test_SYS_Simple_for_base") { /** @arg 未指定账户运行 */ sys = SYS_Simple(); - CHECK_EQ(sys->readyForRun(), false); - sys->run(stk, query); - CHECK_UNARY(!sys->getTM()); + CHECK_THROWS_AS(sys->readyForRun(), std::exception); /** @arg 指定了账户,但未指定其他策略组件 */ sys = SYS_Simple(); sys->setTM(tm->clone()); - CHECK_EQ(sys->readyForRun(), false); - sys->run(stk, query); - CHECK_EQ(sys->getTM()->currentCash(), init_cash); - tr_list = sys->getTM()->getTradeList(); - CHECK_EQ(tr_list.size(), 1); - CHECK_EQ(tr_list[0].business, BUSINESS_INIT); + CHECK_THROWS_AS(sys->readyForRun(), std::exception); /** @arg 指定了TM和SG,但未指定其他策略组件 */ sys = SYS_Simple(); sys->setTM(tm->clone()); sys->setSG(sg->clone()); - CHECK_EQ(sys->readyForRun(), false); - sys->run(stk, query); - CHECK_EQ(sys->getTM()->currentCash(), init_cash); - tr_list = sys->getTM()->getTradeList(); - CHECK_EQ(tr_list.size(), 1); - CHECK_EQ(tr_list[0].business, BUSINESS_INIT); + CHECK_THROWS_AS(sys->readyForRun(), std::exception); /** @arg 指定了TM、SG、MM,但未指定其他策略组件,非延迟操作 */ sys = SYS_Simple(); @@ -82,7 +70,6 @@ TEST_CASE("test_SYS_Simple_for_base") { sys->setTM(tm->clone()); sys->setSG(sg->clone()); sys->setMM(mm->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -135,7 +122,6 @@ TEST_CASE("test_SYS_Simple_for_base") { sys->setTM(tm->clone()); sys->setSG(sg->clone()); sys->setMM(mm->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_cn.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_cn.cpp index 1b9d6a30..a842dd4a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_cn.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_cn.cpp @@ -76,7 +76,6 @@ TEST_CASE("test_SYS_Simple_for_cn") { sys->setEV(ev1); sys->setCN(cn1); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -135,7 +134,6 @@ TEST_CASE("test_SYS_Simple_for_cn") { sys->setEV(ev1); sys->setCN(cn2); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -193,7 +191,6 @@ TEST_CASE("test_SYS_Simple_for_cn") { sys->setEV(ev1); sys->setCN(cn3); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -251,7 +248,6 @@ TEST_CASE("test_SYS_Simple_for_cn") { sys->setEV(ev1); sys->setCN(cn3); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_ev.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_ev.cpp index 508e4e1b..989fde8b 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_ev.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_ev.cpp @@ -71,7 +71,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setTP(tp->clone()); sys->setEV(ev1); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -116,7 +115,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setTP(tp->clone()); sys->setEV(ev1); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -173,8 +171,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setST(st->clone()); sys->setTP(tp->clone()); sys->setEV(ev1); - - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -218,8 +214,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setST(st->clone()); sys->setTP(tp->clone()); sys->setEV(ev1); - - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -276,8 +270,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setSG(sg->clone()); sys->setMM(mm->clone()); sys->setEV(ev2); - - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -321,8 +313,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setSG(sg->clone()); sys->setMM(mm->clone()); sys->setEV(ev2); - - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -365,7 +355,6 @@ TEST_CASE("test_SYS_Simple_for_ev") { sys->setMM(mm->clone()); sys->setEV(ev3); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_pg.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_pg.cpp index 9e09e184..9e3cbaa5 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_pg.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_pg.cpp @@ -62,7 +62,6 @@ TEST_CASE("test_SYS_Simple_for_pg") { sys->setST(st->clone()); sys->setTP(tp->clone()); sys->setPG(pg->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_st.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_st.cpp index 53cd95b4..0ea72046 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_st.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_st.cpp @@ -57,7 +57,6 @@ TEST_CASE("test_SYS_Simple_for_st") { sys->setSG(sg->clone()); sys->setMM(mm->clone()); sys->setST(st->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -111,7 +110,6 @@ TEST_CASE("test_SYS_Simple_for_st") { sys->setSG(sg->clone()); sys->setMM(mm->clone()); sys->setST(st->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_tp.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_tp.cpp index 1ff0dcd9..50f45163 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_tp.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/system/test_Simple_SYS_for_tp.cpp @@ -59,7 +59,6 @@ TEST_CASE("test_SYS_Simple_for_tp") { sys->setMM(mm->clone()); sys->setST(st->clone()); sys->setTP(tp->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); @@ -127,7 +126,6 @@ TEST_CASE("test_SYS_Simple_for_tp") { sys->setMM(mm->clone()); sys->setST(st->clone()); sys->setTP(tp->clone()); - CHECK_EQ(sys->readyForRun(), true); sys->run(stk, query); CHECK_NE(sys->getTM()->currentCash(), init_cash); tr_list = sys->getTM()->getTradeList(); From ddefc3769ec26cfba0847960fb846ba229747612 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 01:06:23 +0800 Subject: [PATCH 144/601] =?UTF-8?q?jupyter=20=E7=8E=AF=E5=A2=83=E4=B8=AD?= =?UTF-8?q?=E7=A6=81=E6=AD=A2=E4=BD=BF=E7=94=A8=20PF/SYS=20trace?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../imp/FixedRatioMoneyManager.cpp | 68 ------------------- .../moneymanager/imp/FixedRatioMoneyManager.h | 46 ------------- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 4 ++ hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 5 ++ 4 files changed, 9 insertions(+), 114 deletions(-) delete mode 100644 hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp delete mode 100644 hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp deleted file mode 100644 index 083aa391..00000000 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * FixedRatioMoneyManager.cpp - * - * Created on: 2016年5月3日 - * Author: Administrator - */ - -#include "FixedRatioMoneyManager.h" - -#if HKU_SUPPORT_SERIALIZATION -BOOST_CLASS_EXPORT(hku::FixedRatioMoneyManager) -#endif - -namespace hku { - -FixedRatioMoneyManager::FixedRatioMoneyManager() -: MoneyManagerBase("MM_FixedRadio"), m_current_num(1), m_pre_cash(0.0) { - setParam("delta", 1000.00); -} - -FixedRatioMoneyManager::~FixedRatioMoneyManager() {} - -void FixedRatioMoneyManager::_checkParam(const string& name) const { - if ("delta" == name) { - double delta = getParam("delta"); - HKU_ASSERT(delta > 0.0); - } -} - -void FixedRatioMoneyManager::_reset() { - m_current_num = 1; - m_pre_cash = 0.0; -} - -MoneyManagerPtr FixedRatioMoneyManager::_clone() { - FixedRatioMoneyManager* p = new FixedRatioMoneyManager(); - p->m_current_num = m_current_num; - p->m_pre_cash = m_pre_cash; - return MoneyManagerPtr(p); -} - -double FixedRatioMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& stock, - price_t price, price_t risk, SystemPart from) { - double delta = getParam("delta"); - if (m_pre_cash == 0.0) { - m_pre_cash = m_tm->initCash(); - m_current_num = (m_pre_cash / delta) / stock.minTradeNumber(); - } - - price_t level = m_pre_cash + m_current_num * getParam("delta"); - price_t current_cash = m_tm->cash(datetime, m_query.kType()); - - if (current_cash > level) { - m_current_num++; - - } else if (current_cash < level) { - m_current_num--; - if (m_current_num < 1) { - m_current_num = 1; - } - } - - m_pre_cash = current_cash; - - return m_current_num * stock.minTradeNumber(); -} - -} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h deleted file mode 100644 index 0db9ad2b..00000000 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRatioMoneyManager.h +++ /dev/null @@ -1,46 +0,0 @@ -/* - * FixedRatioMoneyManager.h - * - * Created on: 2016年5月3日 - * Author: Administrator - */ - -#pragma once -#ifndef TRADE_SYS_MONEYMANAGER_IMP_FIXEDRATIOMONEYMANAGER_H_ -#define TRADE_SYS_MONEYMANAGER_IMP_FIXEDRATIOMONEYMANAGER_H_ - -#include "../MoneyManagerBase.h" - -namespace hku { - -class FixedRatioMoneyManager : public MoneyManagerBase { -public: - FixedRatioMoneyManager(); - virtual ~FixedRatioMoneyManager(); - - virtual void _checkParam(const string& name) const override; - virtual void _reset() override; - virtual MoneyManagerPtr _clone() override; - virtual double _getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, - price_t risk, SystemPart from) override; - -private: - int m_current_num; - price_t m_pre_cash; - -#if HKU_SUPPORT_SERIALIZATION -private: - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { - ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(MoneyManagerBase); - ar& BOOST_SERIALIZATION_NVP(m_current_num); - ar& BOOST_SERIALIZATION_NVP(m_pre_cash); - } - -#endif /* HKU_SUPPORT_SERIALIZATION */ -}; - -} /* namespace hku */ - -#endif /* TRADE_SYS_MONEYMANAGER_IMP_FIXEDRATIOMONEYMANAGER_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index ebc72c3f..dbddba0e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -63,6 +63,10 @@ void Portfolio::baseCheckParam(const string& name) const { if ("adjust_cycle" == name) { int adjust_cycle = getParam("adjust_cycle"); HKU_ASSERT(adjust_cycle >= 1); + } else if ("trace" == name) { + if (getParam("trace") && pythonInJupyter()) { + HKU_THROW("You can't trace in jupyter!"); + } } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index d2ec7434..1f9a10dd 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include "hikyuu/global/sysinfo.h" #include "System.h" namespace hku { @@ -130,6 +131,10 @@ void System::baseCheckParam(const string& name) const { HKU_ASSERT(getParam("max_delay_count") >= 0); } else if ("tp_delay_n" == name) { HKU_ASSERT(getParam("tp_delay_n") >= 0); + } else if ("trace" == name) { + if (getParam("trace") && pythonInJupyter()) { + HKU_THROW("You can't trace in jupyter!"); + } } } From f0b17e1233a417f1ef22b64f46d0ae04932d5aa2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 02:34:38 +0800 Subject: [PATCH 145/601] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20CLOSE/OPEN..=20pyt?= =?UTF-8?q?hon=20=E4=B8=AD=E5=85=AC=E5=BC=8F=E5=8F=AF=E4=BB=A5=E4=B8=8D?= =?UTF-8?q?=E5=8A=A0=E6=8B=AC=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/indicator/indicator.py | 10 ++++++++++ hikyuu_pywrap/indicator/_build_in.cpp | 28 +++++++++++++-------------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/hikyuu/indicator/indicator.py b/hikyuu/indicator/indicator.py index f597cf98..478d0e49 100644 --- a/hikyuu/indicator/indicator.py +++ b/hikyuu/indicator/indicator.py @@ -133,3 +133,13 @@ def concat_to_df(dates, ind_list, head_stock_code=True, head_ind_name=False): df = pd.concat([df, x.to_df()], axis=1) df.set_index('date') return df + + +# 避免 python 中公式原型必须加括号 +KDATA = C_KDATA() +CLOSE = C_CLOSE() +OPEN = C_OPEN() +HIGH = C_HIGH() +LOW = C_LOW() +AMO = C_AMO() +VOL = C_VOL() diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index b3d45763..0ac99eb7 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -492,56 +492,56 @@ Indicator (*ZSCORE_1)(bool, double, bool) = ZSCORE; Indicator (*ZSCORE_2)(const Indicator&, bool, double, bool) = ZSCORE; void export_Indicator_build_in(py::module& m) { - m.def("KDATA", KDATA1); - m.def("KDATA", KDATA3, R"(KDATA([data]) + m.def("C_KDATA", KDATA1); + m.def("C_KDATA", KDATA3, R"(KDATA([data]) 包装KData成Indicator,用于其他指标计算 :param data: KData 或 具有6个返回结果的Indicator(如KDATA生成的Indicator) :rtype: Indicator)"); - m.def("CLOSE", CLOSE1); - m.def("CLOSE", CLOSE3, R"(CLOSE([data]) + m.def("C_CLOSE", CLOSE1); + m.def("C_CLOSE", CLOSE3, R"(CLOSE([data]) 获取收盘价,包装KData的收盘价成Indicator :param data: 输入数据(KData 或 Indicator) :rtype: Indicator)"); - m.def("OPEN", OPEN1); - m.def("OPEN", OPEN3, R"(OPEN([data]) + m.def("C_OPEN", OPEN1); + m.def("C_OPEN", OPEN3, R"(OPEN([data]) 获取开盘价,包装KData的开盘价成Indicator :param data: 输入数据(KData 或 Indicator) :rtype: Indicator)"); - m.def("HIGH", HIGH1); - m.def("HIGH", HIGH3, R"(HIGH([data]) + m.def("C_HIGH", HIGH1); + m.def("C_HIGH", HIGH3, R"(HIGH([data]) 获取最高价,包装KData的最高价成Indicator :param data: 输入数据(KData 或 Indicator) :rtype: Indicator)"); - m.def("LOW", LOW1); - m.def("LOW", LOW3, R"(LOW([data]) + m.def("C_LOW", LOW1); + m.def("C_LOW", LOW3, R"(LOW([data]) 获取最低价,包装KData的最低价成Indicator :param data: 输入数据(KData 或 Indicator) :rtype: Indicator)"); - m.def("AMO", AMO1); - m.def("AMO", AMO3, R"(AMO([data]) + m.def("C_AMO", AMO1); + m.def("C_AMO", AMO3, R"(AMO([data]) 获取成交金额,包装KData的成交金额成Indicator :param data: 输入数据(KData 或 Indicator) :rtype: Indicator)"); - m.def("VOL", VOL1); - m.def("VOL", VOL3, R"(VOL([data]) + m.def("C_VOL", VOL1); + m.def("C_VOL", VOL3, R"(VOL([data]) 获取成交量,包装KData的成交量成Indicator From 7faf0749abfbba558f212b97d2953e8f724afce4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 02:34:56 +0800 Subject: [PATCH 146/601] =?UTF-8?q?HikyuuTDX=20=E5=8A=A0=E5=85=A5=E6=96=B0?= =?UTF-8?q?=E7=89=88=E6=9C=AC=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/HikyuuTDX.py | 6 ++++++ hikyuu_pywrap/main.cpp | 2 ++ 2 files changed, 8 insertions(+) diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index e65449b8..3da39169 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -29,6 +29,7 @@ from hikyuu.gui.data.CollectSpotThread import CollectSpotThread from hikyuu.gui.data.SchedImportThread import SchedImportThread from hikyuu.gui.spot_server import release_nng_senders +from hikyuu import can_upgrade, get_last_version from hikyuu.data import hku_config_template from hikyuu.util import * @@ -624,6 +625,11 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): self.escape_time_thread = None self.start_import_pushButton.setEnabled(True) self.import_detail_textEdit.append("导入完毕!") + if can_upgrade(): + self.import_detail_textEdit.append("========================================================") + self.import_detail_textEdit.append( + "新版本 ({}) 已发布,建议更新".format(get_last_version())) + self.import_detail_textEdit.append("========================================================") self.import_running = False elif msg_task_name == 'IMPORT_KDATA': diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index 7825c27b..57cbe42e 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -121,6 +121,8 @@ PYBIND11_MODULE(core, m) { m.def("get_version_with_build", getVersionWithBuild); m.def("get_version_git", getVersionWithGit); + m.def("get_last_version", getLatestVersion); + m.def("can_upgrade", CanUpgrade); m.def("get_stock", getStock, R"(get_stock(market_code) From 5d421f6980da0d3a8405341cf656f9046b77e974 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 07:20:21 +0800 Subject: [PATCH 147/601] Change MIT LICENSE to Apache V2.0 --- LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 201 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..261eeb9e --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From 6355aa1983fe7b1614c8e870a1e940bb0a53078e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 07:21:16 +0800 Subject: [PATCH 148/601] update --- LICENSE.txt | 21 --------------------- 1 file changed, 21 deletions(-) delete mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index 5f0ea812..00000000 --- a/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2017 fasiondog - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file From 1ae09fc0752d1ef744c40240279453a21bfbfb1f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 07:47:28 +0800 Subject: [PATCH 149/601] update cppcheck info --- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 39 ++++++++++++---------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index cec0b18d..54f27bf7 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -28,6 +28,10 @@ std::atomic g_latest_version{0}; std::atomic_bool g_runningInPython{false}; // 是否是在 python 中运行 std::atomic_bool g_pythonInJupyter{false}; // python 是否为交互模式 +boost::uuids::uuid g_uid; +std::string g_feedback_host; +uint64_t g_feedback_port; + bool HKU_API runningInPython() { return g_runningInPython; } @@ -72,13 +76,13 @@ std::string getVersionWithGit() { } // cppcheck-suppress constParameterReference -static bool readUUID(boost::uuids::uuid& out) { +static bool readUUID() { std::string filename = fmt::format("{}/.hikyuu/uid", getUserDir()); FILE* fp = fopen(filename.c_str(), "rb"); HKU_IF_RETURN(!fp, false); bool ret = true; - if (16 != fread((void*)out.data, 1, 16, fp)) { + if (16 != fread((void*)g_uid.data, 1, 16, fp)) { ret = false; } @@ -86,22 +90,21 @@ static bool readUUID(boost::uuids::uuid& out) { return ret; } -static void saveUUID(const boost::uuids::uuid& uid) { +static void saveUUID() { std::string filename = fmt::format("{}/.hikyuu/uid", getUserDir()); FILE* fp = fopen(filename.c_str(), "wb"); HKU_IF_RETURN(!fp, void()); - fwrite(uid.data, 16, 1, fp); + g_uid = boost::uuids::random_generator()(); + fwrite(g_uid.data, 16, 1, fp); fclose(fp); } void sendFeedback() { std::thread t([] { try { - boost::uuids::uuid uid; - if (!readUUID(uid)) { - uid = boost::uuids::random_generator()(); - saveUUID(uid); + if (!readUUID()) { + saveUUID(); } NodeClient client(FEEDBACK_SERVER_ADDR); @@ -110,15 +113,15 @@ void sendFeedback() { json req, res; req["cmd"] = 2; client.post(req, res); - std::string host = res["host"].get(); - uint64_t port = res["port"].get(); + g_feedback_host = res["host"].get(); + g_feedback_port = res["port"].get(); g_latest_version = res.contains("last_version") ? res["last_version"].get() : 0; client.close(); - client.setServerAddr(fmt::format("tcp://{}:{}", host, port)); + client.setServerAddr(fmt::format("tcp://{}:{}", g_feedback_host, g_feedback_port)); client.dial(); req["cmd"] = 1; - req["uid"] = boost::uuids::to_string(uid); + req["uid"] = boost::uuids::to_string(g_uid); req["part"] = "hikyuu"; req["version"] = HKU_VERSION; req["build"] = fmt::format("{}", HKU_VERSION_BUILD); @@ -137,19 +140,9 @@ void sendFeedback() { void sendPythonVersionFeedBack(int major, int minor, int micro) { std::thread t([=]() { try { - NodeClient client(FEEDBACK_SERVER_ADDR); + NodeClient client(fmt::format("tcp://{}:{}", g_feedback_host, g_feedback_port)); client.dial(); - json req, res; - req["cmd"] = 2; - client.post(req, res); - std::string host = res["host"].get(); - uint64_t port = res["port"].get(); - g_latest_version = res.contains("last_version") ? res["last_version"].get() : 0; - client.close(); - - client.setServerAddr(fmt::format("tcp://{}:{}", host, port)); - client.dial(); req["cmd"] = 3; req["major"] = major; req["minor"] = minor; From 0c70c7a251a847ceb105a5d7319076fc9fc42249 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 11:19:09 +0800 Subject: [PATCH 150/601] update for release --- docs/source/release.rst | 36 ++++++++++++++++++++++++++++++++++++ sub_setup.py | 3 ++- xmake.lua | 2 +- 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 236d0136..4680369f 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,42 @@ 版本发布说明 ======================= +2.0.0 - 2024年4月3日 +------------------------- + +1. 新增特性 + - 新增 MF 多因子组件,用于时间截面对各标的排序评分,重新整理 PF(投资组合)、SE(选股算法)。从投资组合(PF)--截面评分(MF)--选股过滤(SE)--系统策略(SYS)--择时(SG)--资金管理(MM)--止损(ST)/止盈(TP)--盈利目标(PG) 全链条的交易组件化。 + - 新增指标 ZBOND10(10年期国债收益率用于计算夏普比例)、SPEARMAN(秩相关系数)、IC(信息系数)、ICIR(信息比率) + - 新增复权类指标(EQUAL_FORWARD 等), 方便需要复权数据的指标计算 + - python 中 PF、SYS 增加 performance 方法,直接查看系统绩效 + - 新增 concat_to_df 将多个指标数据合并为 pandas DataFrame,方便其他使用 pandas 的工具包进一步处理 + - 所有系统部件及指标支持参数变更时的动态检查 + +2. 其他优化与调整 + - python 中增强系统部件快速创建方法直接支持带有私有属性的 python 继承实例进行 clone,从而在 c++ 中调用 + - ALIGN 指标 增加 “fill_null” 参数,控制对齐填充(填充 nan 值 或使用最近数据进行填充) + - System reset/clone 改为依据部件共享属性进行实际操作 + - 优化 C++ log 输出到 python 环境的交互 + - StockManager、Block、MF 可以直接通过过滤函数进行过滤获取相关证券 + - python 中改进 CLOSE/OPEN/HIGH/LOW/AMO/VOL,使其在公式中不再必须要括号 + - Indicator 增加 equal/isSame 方法,简化一些测试代码 + - Performance 统计结果按顺序输出 + - 获取仓库组件的 get_part 方法,不用必须指定参数名 + - 优化 TradeManager 获取资金曲线相关方法及其他 python 引入调整 + - 清理 C++ serialization 头文件包含及 cppcheck 静态检查信息 + - MYSQL_OPT_RECONNECT 兼容 + - SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 + +SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 + +3. 缺陷修复 + - fixed 建stock.db时候没包括历史退市的股票 + - fixed tdx本地数据导入问题 + - fixed low_precision 下python部分测试用例 + - fixed python 日志目录创建 + - fixed get_trans_list 数据错误 + + 1.3.5 - 2024年2月29日 ------------------------- diff --git a/sub_setup.py b/sub_setup.py index 60827bb2..1f90ec58 100644 --- a/sub_setup.py +++ b/sub_setup.py @@ -91,7 +91,8 @@ hku_data_files = [] packages = ['hikyuu'] for root, dirs, files in os.walk('hikyuu'): for p in dirs: - if p.find('__pycache__') < 0 and p.find('ipynb_checkpoints') < 0 and p.find('.virtual_documents') < 0: + if p.find('__pycache__') < 0 and p.find('ipynb_checkpoints') < 0 \ + and p.find('virtual_documents') < 0 and p.find('idea') < 0 and p.find('venv') < 0: packages.append(f'{root}/{p}') setup( diff --git a/xmake.lua b/xmake.lua index f1064cb8..bbb5dafb 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("1.3.5", {build = "%Y%m%d%H%M"}) +set_version("2.0.0", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 845c240c5521a3183748d6238840c19a746554a1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 3 Apr 2024 11:24:24 +0800 Subject: [PATCH 151/601] hub add mf --- hikyuu/hub.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hikyuu/hub.py b/hikyuu/hub.py index 4fc7f02d..99b773ca 100644 --- a/hikyuu/hub.py +++ b/hikyuu/hub.py @@ -324,6 +324,7 @@ class HubManager(metaclass=SingletonType): 'af': 'part/af', 'cn': 'part/cn', 'ev': 'part/ev', + 'mf': 'part/mf', 'mm': 'part/mm', 'pg': 'part/pg', 'se': 'part/se', @@ -405,7 +406,7 @@ class HubManager(metaclass=SingletonType): name_parts = name.split('.') checkif( len(name_parts) < 2 - or (name_parts[-2] not in ('af', 'cn', 'ev', 'mm', 'pg', 'se', 'sg', 'sp', 'st', 'prtflo', 'sys', 'ind', 'other')), + or (name_parts[-2] not in ('af', 'cn', 'ev', 'mf', 'mm', 'pg', 'se', 'sg', 'sp', 'st', 'prtflo', 'sys', 'ind', 'other')), name, PartNameError ) From 11eb7b2820062e75efaf25242c11d1aea1a2f9a4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 4 Apr 2024 00:42:03 +0800 Subject: [PATCH 152/601] fixed python version feedback --- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 39 ++++++++++++++++------------ 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index 54f27bf7..cec0b18d 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -28,10 +28,6 @@ std::atomic g_latest_version{0}; std::atomic_bool g_runningInPython{false}; // 是否是在 python 中运行 std::atomic_bool g_pythonInJupyter{false}; // python 是否为交互模式 -boost::uuids::uuid g_uid; -std::string g_feedback_host; -uint64_t g_feedback_port; - bool HKU_API runningInPython() { return g_runningInPython; } @@ -76,13 +72,13 @@ std::string getVersionWithGit() { } // cppcheck-suppress constParameterReference -static bool readUUID() { +static bool readUUID(boost::uuids::uuid& out) { std::string filename = fmt::format("{}/.hikyuu/uid", getUserDir()); FILE* fp = fopen(filename.c_str(), "rb"); HKU_IF_RETURN(!fp, false); bool ret = true; - if (16 != fread((void*)g_uid.data, 1, 16, fp)) { + if (16 != fread((void*)out.data, 1, 16, fp)) { ret = false; } @@ -90,21 +86,22 @@ static bool readUUID() { return ret; } -static void saveUUID() { +static void saveUUID(const boost::uuids::uuid& uid) { std::string filename = fmt::format("{}/.hikyuu/uid", getUserDir()); FILE* fp = fopen(filename.c_str(), "wb"); HKU_IF_RETURN(!fp, void()); - g_uid = boost::uuids::random_generator()(); - fwrite(g_uid.data, 16, 1, fp); + fwrite(uid.data, 16, 1, fp); fclose(fp); } void sendFeedback() { std::thread t([] { try { - if (!readUUID()) { - saveUUID(); + boost::uuids::uuid uid; + if (!readUUID(uid)) { + uid = boost::uuids::random_generator()(); + saveUUID(uid); } NodeClient client(FEEDBACK_SERVER_ADDR); @@ -113,15 +110,15 @@ void sendFeedback() { json req, res; req["cmd"] = 2; client.post(req, res); - g_feedback_host = res["host"].get(); - g_feedback_port = res["port"].get(); + std::string host = res["host"].get(); + uint64_t port = res["port"].get(); g_latest_version = res.contains("last_version") ? res["last_version"].get() : 0; client.close(); - client.setServerAddr(fmt::format("tcp://{}:{}", g_feedback_host, g_feedback_port)); + client.setServerAddr(fmt::format("tcp://{}:{}", host, port)); client.dial(); req["cmd"] = 1; - req["uid"] = boost::uuids::to_string(g_uid); + req["uid"] = boost::uuids::to_string(uid); req["part"] = "hikyuu"; req["version"] = HKU_VERSION; req["build"] = fmt::format("{}", HKU_VERSION_BUILD); @@ -140,9 +137,19 @@ void sendFeedback() { void sendPythonVersionFeedBack(int major, int minor, int micro) { std::thread t([=]() { try { - NodeClient client(fmt::format("tcp://{}:{}", g_feedback_host, g_feedback_port)); + NodeClient client(FEEDBACK_SERVER_ADDR); client.dial(); + json req, res; + req["cmd"] = 2; + client.post(req, res); + std::string host = res["host"].get(); + uint64_t port = res["port"].get(); + g_latest_version = res.contains("last_version") ? res["last_version"].get() : 0; + client.close(); + + client.setServerAddr(fmt::format("tcp://{}:{}", host, port)); + client.dial(); req["cmd"] = 3; req["major"] = major; req["minor"] = minor; From 106990dcc3dea3d86bb67a9ad3538327c829df74 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 4 Apr 2024 00:50:43 +0800 Subject: [PATCH 153/601] update --- docs/source/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 4ff87140..0c7e7a53 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -16,7 +16,7 @@ pip 安装 Hikyuu 后,可在命令行终端中执行 hikyuutdx 命令,启动 .. note:: - 如在命令行终端中无法执行 hikyuutdx 命令,请到 python 安装目录下的 Scripts 子目录中选择该执行文件(HikyuuTDX.exe)。 + 如在命令行终端中无法执行 hikyuutdx 命令,请到 python 安装目录下的 Scripts (通常是安装python时没有将该路径放入系统 PATH 路径中)子目录中选择该执行文件(HikyuuTDX.exe)。如果还是不行,可以到 python/Lib/site-packages 下找到 hikyuu 的安装目录,在其下的 gui 目录中有 HikyuuTdx.py, 可以从命令行终端中直接执行 python HikyuuTdx.py 执行观察报错信息。 如不希望使用 GUI 图形界面下载,可在命令行终端中执行 importdata 命令,如下图所示: From a792c98a675ece7dbb6000f8d4c0292f6d30d87c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 4 Apr 2024 01:51:58 +0800 Subject: [PATCH 154/601] =?UTF-8?q?fixed=20tm=20=E7=9A=84=E5=BB=BA?= =?UTF-8?q?=E7=AB=8B=E6=97=A5=E6=9C=9F=E5=B0=8F=E4=BA=8E=E5=8F=82=E8=80=83?= =?UTF-8?q?=E6=97=A5=E6=9C=9F=E6=97=B6=20sys=5Fperformance=20=E6=8A=A5?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 74076824..ca9f3108 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -755,7 +755,7 @@ def sys_performance(sys, ref_stk=None): funds_list = sys.tm.get_funds_list(ref_dates) funds = [f.total_assets for f in funds_list] funds = VALUE(funds) - funds_return = [f.total_assets / f.total_base for f in funds_list] + funds_return = [f.total_assets / f.total_base if f.total_base != 0.0 else None for f in funds_list] funds_return = VALUE(funds_return) funds_return.name = "系统累积收益率" ref_return = ROCR(ref_k.close, 0) From 8c1272796b7add04464776afa3a6bd805329e465 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 4 Apr 2024 03:14:45 +0800 Subject: [PATCH 155/601] =?UTF-8?q?hub=20=E4=B8=AD=E7=9A=84=20prtflo=20?= =?UTF-8?q?=E6=9B=B4=E5=90=8D=E4=B8=BA=20pf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/hub.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu/hub.py b/hikyuu/hub.py index 99b773ca..02b0f1ba 100644 --- a/hikyuu/hub.py +++ b/hikyuu/hub.py @@ -331,7 +331,7 @@ class HubManager(metaclass=SingletonType): 'sg': 'part/sg', 'sp': 'part/sp', 'st': 'part/st', - 'prtflo': 'prtflo', + 'pf': 'pf', 'sys': 'sys', 'ind': 'ind', 'other': 'other', @@ -356,7 +356,7 @@ class HubManager(metaclass=SingletonType): if (not entry.name.startswith('.')) and entry.is_dir() and (entry.name != "__pycache__"): # 计算实际的导入模块名 module_name = '{}.part.{}.{}.part'.format(base_local, part, entry.name) if part not in ( - 'prtflo', 'sys', 'ind', 'other' + 'pf', 'sys', 'ind', 'other' ) else '{}.{}.{}.part'.format(base_local, part, entry.name) # 导入模块 @@ -375,7 +375,7 @@ class HubManager(metaclass=SingletonType): continue name = '{}.{}.{}'.format(hub_model.name, part, entry.name) if part not in ( - 'prtflo', 'sys', 'ind', 'other' + 'pf', 'sys', 'ind', 'other' ) else '{}.{}.{}'.format(hub_model.name, part, entry.name) try: @@ -406,7 +406,7 @@ class HubManager(metaclass=SingletonType): name_parts = name.split('.') checkif( len(name_parts) < 2 - or (name_parts[-2] not in ('af', 'cn', 'ev', 'mf', 'mm', 'pg', 'se', 'sg', 'sp', 'st', 'prtflo', 'sys', 'ind', 'other')), + or (name_parts[-2] not in ('af', 'cn', 'ev', 'mf', 'mm', 'pg', 'se', 'sg', 'sp', 'st', 'pf', 'sys', 'ind', 'other')), name, PartNameError ) @@ -496,7 +496,7 @@ class HubManager(metaclass=SingletonType): """ abs_path = os.path.abspath(filename) # 当前文件的绝对路径 path_parts = pathlib.Path(abs_path).parts - local_base = path_parts[-4] if path_parts[-3] in ('prtflo', 'sys', 'ind', 'other') else path_parts[5] + local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[5] hub_model = self._session.query(HubModel.name).filter_by(local_base=local_base).first() checkif(hub_model is None, local_base, HubNotFoundError) return hub_model.name From 0aa68bdfc7e600afccd879bd2b371d1de695399b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 4 Apr 2024 04:01:18 +0800 Subject: [PATCH 156/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20MF.getScores=20?= =?UTF-8?q?=E5=91=BD=E5=90=8D=E5=8F=8A=E4=B8=8D=E5=AD=98=E5=9C=A8=E6=97=B6?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=A9=BA=E5=88=97=E8=A1=A8=EF=BC=8C=E4=B8=8D?= =?UTF-8?q?=E6=8A=9B=E5=87=BA=E5=BC=82=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/multifactor/MultiFactorBase.cpp | 18 ++++++++++++------ .../trade_sys/multifactor/MultiFactorBase.h | 2 +- .../multifactor/test_MF_EqualWeight.cpp | 6 +++--- 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 3c6f6dc1..19070fca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -183,18 +183,20 @@ const IndicatorList& MultiFactorBase::getAllFactors() { return m_all_factors; } -const ScoreRecordList& MultiFactorBase::getScore(const Datetime& d) { +ScoreRecordList MultiFactorBase::getScores(const Datetime& d) { calculate(); + ScoreRecordList ret; const auto iter = m_date_index.find(d); - HKU_CHECK(iter != m_date_index.cend(), "Could not find this date: {}", d); - return m_stk_factor_by_date[iter->second]; + HKU_IF_RETURN(iter == m_date_index.cend(), ret); + ret = m_stk_factor_by_date[iter->second]; + return ret; } ScoreRecordList MultiFactorBase::getScores(const Datetime& date, size_t start, size_t end) { ScoreRecordList ret; HKU_IF_RETURN(start >= end, ret); - const auto& cross = getScore(date); + const auto& cross = getScores(date); if (end == Null() || end > cross.size()) { end = cross.size(); } @@ -211,7 +213,9 @@ ScoreRecordList MultiFactorBase::getScores(const Datetime& date, size_t start, s ScoreRecordList ret; HKU_IF_RETURN(start >= end, ret); - const auto& cross = getScore(date); + const auto& cross = getScores(date); + HKU_IF_RETURN(cross.empty(), ret); + if (end == Null() || end > cross.size()) { end = cross.size(); } @@ -237,7 +241,9 @@ ScoreRecordList MultiFactorBase::getScores( ScoreRecordList ret; HKU_IF_RETURN(start >= end, ret); - const auto& cross = getScore(date); + const auto& cross = getScores(date); + HKU_IF_RETURN(cross.empty(), ret); + if (end == Null() || end > cross.size()) { end = cross.size(); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h index 04e96dea..199fa881 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h @@ -80,7 +80,7 @@ public: const IndicatorList& getAllFactors(); /** 获取指定日期截面的所有因子值,已经降序排列 */ - const ScoreRecordList& getScore(const Datetime&); + ScoreRecordList getScores(const Datetime&); ScoreRecordList getScores(const Datetime& date, size_t start, size_t end = Null()); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_EqualWeight.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_EqualWeight.cpp index 72bdffbb..9d90a8a9 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_EqualWeight.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/multifactor/test_MF_EqualWeight.cpp @@ -106,15 +106,15 @@ TEST_CASE("test_MF_EqualWeight") { auto ic2 = IC(MA(CLOSE()), stks, query, ref_stk, 1); CHECK_UNARY(ic1.equal(ic2)); - CHECK_THROWS_AS(mf->getScore(Datetime(20111204)), std::exception); - auto cross = mf->getScore(Datetime(20111205)); + CHECK_UNARY(mf->getScores(Datetime(20111204)).empty()); + auto cross = mf->getScores(Datetime(20111205)); CHECK_EQ(cross.size(), 2); CHECK_EQ(cross[0].stock, sm["sh600004"]); CHECK_EQ(cross[0].value, doctest::Approx(6.85)); CHECK_EQ(cross[1].stock, sm["sh600005"]); CHECK_EQ(cross[1].value, doctest::Approx(3.13)); - cross = mf->getScore(Datetime(20111206)); + cross = mf->getScores(Datetime(20111206)); CHECK_EQ(cross.size(), 2); CHECK_EQ(cross[0].stock, sm["sh600004"]); CHECK_EQ(cross[0].value, doctest::Approx(6.855)); From 55d37de9b3c22a96fc17794ce779faf0017d9269 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 03:36:49 +0800 Subject: [PATCH 157/601] Update .gitattributes --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index a95603f2..79e17c8f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,3 +3,4 @@ *.py linguist-language=C++ *.html linguist-language=C++ *.mod linguist-language=C++ +*.ipynb linguist-language=C++ From ba240f0e5f6f5e49355074e92e3fd7dbe2b50c85 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 03:37:34 +0800 Subject: [PATCH 158/601] Update .gitattributes --- .gitattributes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitattributes b/.gitattributes index 79e17c8f..5fafb6cf 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,6 @@ *.h linguist-language=C++ *.cpp linguist-language=C++ -*.py linguist-language=C++ +*.py linguist-language=python *.html linguist-language=C++ *.mod linguist-language=C++ *.ipynb linguist-language=C++ From 9710729dada35700e4103abe915339c9423a744d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 03:39:27 +0800 Subject: [PATCH 159/601] Update .gitattributes --- .gitattributes | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitattributes b/.gitattributes index 5fafb6cf..236ada72 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,8 @@ -*.h linguist-language=C++ -*.cpp linguist-language=C++ *.py linguist-language=python +*.h linguist-language=C++ +*.c linguist-language=C++ +*.hpp linguist-language=C++ +*.cpp linguist-language=C++ *.html linguist-language=C++ *.mod linguist-language=C++ *.ipynb linguist-language=C++ From 5ae5e05ed1bb91d4ea3f08ece05c36a4ee3440d7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 16:39:21 +0800 Subject: [PATCH 160/601] =?UTF-8?q?=E9=87=8D=E6=96=B0=E5=BC=95=E5=87=BA=20?= =?UTF-8?q?TradeRecordList/PositionRecordList=E8=87=B3=20python=EF=BC=8C?= =?UTF-8?q?=E5=90=A6=E5=88=99=20to=5Fdf=20=E6=96=B9=E6=B3=95=E5=A4=B1?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/bind_stl.cpp | 4 ++-- hikyuu_pywrap/bind_stl.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hikyuu_pywrap/bind_stl.cpp b/hikyuu_pywrap/bind_stl.cpp index 8369d396..a6a28781 100644 --- a/hikyuu_pywrap/bind_stl.cpp +++ b/hikyuu_pywrap/bind_stl.cpp @@ -27,9 +27,9 @@ void export_bind_stl(py::module& m) { py::bind_vector(m, "TransList"); // py::bind_vector(m, "BorrowRecordList"); // py::bind_vector(m, "LoanRecordList"); - // py::bind_vector(m, "PositionRecordList"); + py::bind_vector(m, "PositionRecordList"); // py::bind_vector(m, "FundsList"); - // py::bind_vector(m, "TradeRecordList"); + py::bind_vector(m, "TradeRecordList"); py::bind_vector(m, "SystemWeightList"); // py::bind_vector(m, "SystemList"); py::bind_vector(m, "ScoreRecordList"); diff --git a/hikyuu_pywrap/bind_stl.h b/hikyuu_pywrap/bind_stl.h index cce88f7e..45a34a4e 100644 --- a/hikyuu_pywrap/bind_stl.h +++ b/hikyuu_pywrap/bind_stl.h @@ -28,9 +28,9 @@ PYBIND11_MAKE_OPAQUE(TimeLineList); PYBIND11_MAKE_OPAQUE(TransList); // PYBIND11_MAKE_OPAQUE(BorrowRecordList); // PYBIND11_MAKE_OPAQUE(LoanRecordList); -// PYBIND11_MAKE_OPAQUE(PositionRecordList); +PYBIND11_MAKE_OPAQUE(PositionRecordList); // PYBIND11_MAKE_OPAQUE(FundsList); -// PYBIND11_MAKE_OPAQUE(TradeRecordList); +PYBIND11_MAKE_OPAQUE(TradeRecordList); PYBIND11_MAKE_OPAQUE(SystemWeightList); // PYBIND11_MAKE_OPAQUE(SystemList); PYBIND11_MAKE_OPAQUE(ScoreRecordList); \ No newline at end of file From 8524ff37e654d01d7ddabd374f353f5437de37bc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 16:52:26 +0800 Subject: [PATCH 161/601] =?UTF-8?q?hku=5Fcatch=20=E5=BF=BD=E7=95=A5?= =?UTF-8?q?=E5=AF=B9=20KeyboardInterrupt=20=E7=9A=84=E6=8D=95=E8=8E=B7?= =?UTF-8?q?=EF=BC=8C=E4=BB=A5=E5=85=8D=20Ctrl-C=20=E6=97=A0=E6=B3=95?= =?UTF-8?q?=E7=BB=88=E6=AD=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/util/check.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu/util/check.py b/hikyuu/util/check.py index 55ab1d1d..1fd9f6f6 100644 --- a/hikyuu/util/check.py +++ b/hikyuu/util/check.py @@ -124,6 +124,8 @@ def hku_catch(ret=None, trace=False, callback=None, retry=1, with_msg=False, re_ callback(*args, **kargs) if re_raise: raise Exception(errmsg) + except KeyboardInterrupt: + raise KeyboardInterrupt() except: errmsg = "Unknown error! {} [{}.{}]".format(get_exception_info(), func.__module__, func.__name__) hku_logger.error(errmsg) From 7d4a8c9edaf70e91004eba32038eb2f71c0dcc16 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 22:59:33 +0800 Subject: [PATCH 162/601] =?UTF-8?q?crtSL=20->=20=E6=9B=B4=E5=90=8D=20crtSP?= =?UTF-8?q?,=20crtMM=E5=92=8CcrtSP=E5=AE=9E=E9=99=85=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=9C=AA=E7=BA=B3=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_sys/trade_sys.py | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index 43047353..17f2b5d3 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -82,17 +82,25 @@ def crtEV(func, params={}, name='crtEV'): # ------------------------------------------------------------------ # moneymanager # ------------------------------------------------------------------ -def crtMM(func, params={}, name='crtMM'): +def crtMM(get_buy_num, get_sell_num, params={}, name='crtMM', buy_notify=None, sell_notify=None): """ 快速创建资金管理策略 - :param func: 资金管理策略计算函数 + :param get_buy_num: 买入数量接口 + :param sell_buy_num: 卖出数量接口 :param {} params: 参数字典 :param str name: 自定义名称 + :param buy_notify: 接收买入交易记录通知 + :param sell_notify: 接收卖出交易记录通知 :return: 自定义资金管理策略实例 """ meta_x = type(name, (MoneyManagerBase, ), {'__init__': part_init, '_clone': part_clone}) - meta_x._calculate = func + meta_x._get_buy_num = get_buy_num + meta_x._get_sell_num = get_sell_num + if buy_notify is not None: + meta_x._buy_notify = buy_notify + if sell_notify is not None: + meta_x._sell_notify = sell_notify return meta_x(name, params) @@ -188,17 +196,22 @@ def crtMF(calculate_func, params={}, name='crtMF'): # ------------------------------------------------------------------ # slippage # ------------------------------------------------------------------ -def crtSL(func, params={}, name='crtSL'): +def crtSP(get_real_buy_price, get_real_sell_price, params={}, name='crtSP', calculate=None): """ 快速创建移滑价差算法 - :param func: 移滑价差算法函数 + :param get_real_buy_price: 移滑价差算法接口计算实际买入价格 + :param get_real_sell_price: 移滑价差算法接口计算实际买入价格 :param {} params: 参数字典 :param str name: 自定义名称 + :param calculate: 预处理函数 :return: 移滑价差算法实例 """ meta_x = type(name, (SlippageBase, ), {'__init__': part_init, '_clone': part_clone}) - meta_x._calculate = func + meta_x.get_real_buy_price = get_real_buy_price + meta_x.get_real_sell_price = get_real_sell_price + if calculate is not None: + meta_x._calculate = calculate return meta_x(name, params) From 56aa21bcf855efdfcec9cc1e89d4f6341535c55b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 5 Apr 2024 23:23:23 +0800 Subject: [PATCH 163/601] =?UTF-8?q?fixed=20=E7=BC=BA=E5=A4=B1=E7=9A=84=20h?= =?UTF-8?q?ku=5Fsave=20/=20hku=5Fload=20=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/base/other_utils.rst | 4 ++-- hikyuu/__init__.py | 25 +++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/source/base/other_utils.rst b/docs/source/base/other_utils.rst index 56229b60..0bca6c78 100644 --- a/docs/source/base/other_utils.rst +++ b/docs/source/base/other_utils.rst @@ -40,12 +40,12 @@ :param var: hikyuu内建类型的变量 :param str filename: 指定的文件名 -.. py:function:: hku_load(var, filename) +.. py:function:: hku_load(filename) 将通过 hku_save 保存的变量,读取到var中。 - :param var: 指定的变量 :param str filename: 待载入的序列化文件。 + :return: 之前被序列化保存的文件 .. py:function:: roundUp(arg1[, arg2=0]) diff --git a/hikyuu/__init__.py b/hikyuu/__init__.py index ae6052c4..502b395a 100644 --- a/hikyuu/__init__.py +++ b/hikyuu/__init__.py @@ -50,6 +50,8 @@ SOFTWARE. import traceback import sys +import pickle + from .util import * try: @@ -92,6 +94,29 @@ if in_ipython_frontend(): iodog.open() +def hku_save(var, filename): + """ + 序列化,将hikyuu内建类型的变量(如Stock、TradeManager等)保存在指定的文件中,格式为XML。 + + :param var: hikyuu内建类型的变量 + :param str filename: 指定的文件名 + """ + with open(filename, 'wb') as f: + pickle.dump(var, f) + + +def hku_load(filename): + """ + 将通过 hku_save 保存的变量,读取到var中。 + + :param str filename: 待载入的序列化文件。 + :return: 之前被序列化保存的文件 + """ + with open(filename, 'rb') as f: + out = pickle.load(f) + return out + + # ============================================================================== # # 设置关键类型简称 From 8de406c1e94b1eb80fab5b7e0a5e2e2feb312b05 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 6 Apr 2024 01:24:22 +0800 Subject: [PATCH 164/601] update OrderBroker --- docs/source/trade_manage/OrderBroker.rst | 17 +++++++--------- .../examples/notebook/006-TradeManager.ipynb | 8 +++++--- hikyuu/trade_manage/broker.py | 20 +++++++++---------- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 10 ++++++++-- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 3 ++- 5 files changed, 31 insertions(+), 27 deletions(-) diff --git a/docs/source/trade_manage/OrderBroker.rst b/docs/source/trade_manage/OrderBroker.rst index 07807c52..971ab722 100644 --- a/docs/source/trade_manage/OrderBroker.rst +++ b/docs/source/trade_manage/OrderBroker.rst @@ -35,12 +35,14 @@ Python中的订单代理包装 sys.run(sm['sz000001'], Query(-150)) -.. py:class:: OrderBrokerWrap(broker[, real=True, slip=0.03]) +.. py:class:: OrderBrokerWrap 订单代理包装类,用户可以参考自定义自己的订单代理,加入额外的处理 - - :param bool real: 下单前是否重新实时获取实时分笔数据 - :param float slip: 如果当前的卖一价格和指示买入的价格绝对差值不超过slip则下单,否则忽略; 对卖出操作无效,立即以当前价卖出 + + .. py:method:: __init__(self, broker, name) + + :param broker: python broker 实例 + :param str name: 名称 .. py:method:: _buy(self, code, price, num) @@ -59,15 +61,10 @@ Python中的订单代理包装 :param int num: 卖出数量 -.. py:function:: crtOB(broker[, real=True, slip=0.03]) +.. py:function:: crtOB(broker[, name="NO_NAME"]) 快速生成订单代理包装对象 - :param broker: 订单代理示例,必须拥有buy和sell方法,并且参数为 code, price, num - :param bool real: 下单前是否重新实时获取实时分笔数据 - :param float slip: 如果当前的卖一价格和指示买入的价格绝对差值不超过slip则下单,否则忽略; 对卖出操作无效,立即以当前价卖出 - - 内建的订单代理类 ------------------ diff --git a/hikyuu/examples/notebook/006-TradeManager.ipynb b/hikyuu/examples/notebook/006-TradeManager.ipynb index be85860f..bbc5010e 100644 --- a/hikyuu/examples/notebook/006-TradeManager.ipynb +++ b/hikyuu/examples/notebook/006-TradeManager.ipynb @@ -292,7 +292,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": null, "metadata": {}, "outputs": [ { @@ -319,8 +319,10 @@ "my_tm = crtTM(init_cash=300000, date=Datetime(201701010000))\n", "\n", "#注册实盘交易订单代理\n", - "my_tm.reg_broker(crtOB(TestOrderBroker(), False)) #TestOerderBroker是测试用订单代理对象,只打印\n", - "#my_tm.regBroker(crtOB(MailOrderBroker(\"smtp.sina.com\", \"yourmail@sina.com\", \"yourpwd\", \"receivermail@XXX.yy)))\n", + "ob = crtOB(TestOrderBroker())\n", + "my_tm.reg_broker(ob) #TestOerderBroker是测试用订单代理对象,只打印\n", + "# 注意:pybind 不支持下面这种方式调用,必须先生成实例再传入!!!\n", + "# my_tm.reg_broker(crtOB(TestOrderBroker(), False))\n", "\n", "#根据需要修改订单代理最后的时间戳,后续只有大于该时间戳时,订单代理才会实际发出订单指令\n", "my_tm.broker_last_datetime=Datetime(201701010000)\n", diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index b352a7bb..d6195ecb 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -24,10 +24,10 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -#=============================================================================== +# =============================================================================== # History: # 1. 20170704, Added by fasiondog -#=============================================================================== +# =============================================================================== from hikyuu import OrderBrokerBase @@ -36,16 +36,13 @@ class OrderBrokerWrap(OrderBrokerBase): """订单代理包装类,用户可以参考自定义自己的订单代理,加入额外的处理 包装只有买卖操作参数只有(code, price, num)的交易接口类 """ - def __init__(self, broker, slip=0.03): + + def __init__(self, broker, name): """ 订单代理包装类,用户可以参考自定义自己的订单代理,加入额外的处理 - - :param float slip: 如果当前的卖一价格和指示买入的价格绝对差值不超过slip则下单, - 否则忽略; 对卖出操作无效,立即以当前价卖出 """ - super(OrderBrokerWrap, self).__init__() + super(OrderBrokerWrap, self).__init__(name) self._broker = broker - self._slip = slip def _buy(self, datetime, market, code, price, num): """实现 OrderBrokerBase 的 _buy 接口""" @@ -60,6 +57,7 @@ class OrderBrokerWrap(OrderBrokerBase): class TestOrderBroker: """用于测试的订单代理,仅在执行买入/卖出时打印信息""" + def __init__(self): pass @@ -70,12 +68,12 @@ class TestOrderBroker: print("卖出:%s %.3f %i" % (code, price, num)) -def crtOB(broker, slip=0.03): +def crtOB(broker, name="NO_NAME"): """ 快速生成订单代理包装对象 - + :param broker: 订单代理示例,必须拥有buy和sell方法,并且参数为 code, price, num :param float slip: 如果当前的卖一价格和指示买入的价格绝对差值不超过slip则下单, 否则忽略; 对卖出操作无效,立即以当前价卖出 """ - return OrderBrokerWrap(broker, slip) \ No newline at end of file + return OrderBrokerWrap(broker, name) diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index f49e85ac..7ea38c6e 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -30,8 +30,11 @@ Datetime OrderBrokerBase::buy(Datetime datetime, const string& market, const str Datetime tradetime; try { tradetime = _buy(datetime, market, code, price, num); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + tradetime = Null(); } catch (...) { - HKU_ERROR("Unknown error in BUY operation!!!"); + HKU_ERROR_UNKNOWN; tradetime = Null(); } return tradetime; @@ -42,8 +45,11 @@ Datetime OrderBrokerBase::sell(Datetime datetime, const string& market, const st Datetime tradetime; try { tradetime = _sell(datetime, market, code, price, num); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + tradetime = Null(); } catch (...) { - HKU_ERROR("Unknown error in SELL operation!!!"); + HKU_ERROR_UNKNOWN; tradetime = Null(); } return tradetime; diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index c9cbf2d1..40344702 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -22,7 +22,8 @@ public: Datetime _sell(Datetime datetime, const string& market, const string& code, price_t price, double num) override { - PYBIND11_OVERLOAD_PURE(Datetime, OrderBrokerBase, datetime, market, code, price, num); + PYBIND11_OVERLOAD_PURE(Datetime, OrderBrokerBase, _sell, datetime, market, code, price, + num); } }; From 20f22e53e11772e083b4652d7bd6a8c2606fcbe7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 6 Apr 2024 01:25:45 +0800 Subject: [PATCH 165/601] update example --- .../examples/notebook/006-TradeManager.ipynb | 89 ++++++++++--------- 1 file changed, 49 insertions(+), 40 deletions(-) diff --git a/hikyuu/examples/notebook/006-TradeManager.ipynb b/hikyuu/examples/notebook/006-TradeManager.ipynb index bbc5010e..f0c18449 100644 --- a/hikyuu/examples/notebook/006-TradeManager.ipynb +++ b/hikyuu/examples/notebook/006-TradeManager.ipynb @@ -5,24 +5,29 @@ "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-04-06 01:24:41,451 [INFO] hikyuu version: 2.0.0_202404040113_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:93) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "warning: can't import TA-Lib, will be ignored! You can fetch ta-lib from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib\n", - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2023-10-14 02:24:00.639 [HKU-I] - Using SQLITE3 BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2023-10-14 02:24:00.640 [HKU-I] - Loading market information... (StockManager.cpp:499)\n", - "2023-10-14 02:24:00.640 [HKU-I] - Loading stock type information... (StockManager.cpp:512)\n", - "2023-10-14 02:24:00.641 [HKU-I] - Loading stock information... (StockManager.cpp:426)\n", - "2023-10-14 02:24:00.691 [HKU-I] - Loading stock weight... (StockManager.cpp:529)\n", - "2023-10-14 02:24:01.039 [HKU-I] - Loading KData... (StockManager.cpp:134)\n", - "2023-10-14 02:24:01.043 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:157)\n", - "2023-10-14 02:24:01.043 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:160)\n", - "2023-10-14 02:24:01.044 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:163)\n", - "2023-10-14 02:24:01.055 [HKU-I] - 0.02s Loaded Data. (StockManager.cpp:145)\n", - "Wall time: 1.09 s\n" + "2024-04-06 01:24:41.867 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-04-06 01:24:41.890 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", + "2024-04-06 01:24:41.902 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", + "2024-04-06 01:24:41.914 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", + "2024-04-06 01:24:42.064 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", + "2024-04-06 01:24:43.250 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", + "2024-04-06 01:24:43.958 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", + "2024-04-06 01:24:43.959 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", + "2024-04-06 01:24:43.959 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", + "2024-04-06 01:24:43.963 [HKU-I] - 0.71s Loaded Data. (StockManager.cpp:150)\n", + "CPU times: total: 500 ms\n", + "Wall time: 3.35 s\n" ] } ], @@ -64,6 +69,7 @@ " firstDatetime: 2017-01-03 00:00:00,\n", " lastDatetime: 2017-01-03 00:00:00,\n", " TradeCostFunc: TradeCostFunc(TC_Zero, params[]),\n", + " current total funds: 100005.00,\n", " current cash: 99089.00,\n", " current market_value: 916.00,\n", " current short_market_value: 0.00,\n", @@ -72,7 +78,7 @@ " current borrow_cash: 0.00,\n", " current borrow_asset: 0.00,\n", " Position: \n", - " SZ000001 平安银行 2017-01-03 00:00:00 1646 100.00 911.00 1100.00 189.00 20.75% 0.19%\n", + " SZ000001 平安银行 2017-01-03 00:00:00 1762 100.00 911.00 1046.00 135.00 14.82% 0.14%\n", " Short Position: \n", " Borrow Stock: \n", "}\n" @@ -142,21 +148,21 @@ " SZ000001\n", " 平安银行\n", " 2017-01-03\n", - " 1646\n", + " 1762\n", " 100\n", " 911.0\n", - " 1100.0\n", - " 189.0\n", - " 20.746432\n", + " 1046.0\n", + " 135.0\n", + " 14.81888\n", " \n", " \n", "\n", "" ], "text/plain": [ - " 证券名称 买入日期 已持仓天数 持仓数量 投入金额 当前市值 盈亏金额 盈亏比例\n", - "证券代码 \n", - "SZ000001 平安银行 2017-01-03 1646 100 911.0 1100.0 189.0 20.746432" + " 证券名称 买入日期 已持仓天数 持仓数量 投入金额 当前市值 盈亏金额 盈亏比例\n", + "证券代码 \n", + "SZ000001 平安银行 2017-01-03 1762 100 911.0 1046.0 135.0 14.81888" ] }, "execution_count": 3, @@ -186,6 +192,7 @@ " firstDatetime: 2017-01-03 00:00:00,\n", " lastDatetime: 2017-02-21 00:00:00,\n", " TradeCostFunc: TradeCostFunc(TC_Zero, params[]),\n", + " current total funds: 100049.00,\n", " current cash: 100049.00,\n", " current market_value: 0.00,\n", " current short_market_value: 0.00,\n", @@ -252,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -264,14 +271,13 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "#载入已保存的TM对象\n", "#filename = \"{}/my_trade/my_trade_record_{}.xml\".format(sm.tmpdir(), date.today())\n", - "new_my_tm = crtTM()\n", - "hku_load(new_my_tm, filename)" + "new_my_tm = hku_load(filename)" ] }, { @@ -292,25 +298,28 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "买入:SZ000001 12.930 1000\n", - "卖出:SZ000001 12.480 1000\n", - "买入:SZ000001 12.940 1000\n", - "卖出:SZ000001 12.580 1000\n", - "买入:SZ000001 11.410 1000\n", - "卖出:SZ000001 11.230 1000\n", - "买入:SZ000001 11.330 1000\n", - "卖出:SZ000001 11.450 1000\n", - "买入:SZ000001 11.650 1000\n", - "卖出:SZ000001 11.690 1000\n", - "买入:SZ000001 11.400 1000\n", - "卖出:SZ000001 11.280 1000\n" + "买入:SZ000001 11.470 1000\n", + "卖出:SZ000001 11.140 1000\n", + "买入:SZ000001 11.500 1000\n", + "卖出:SZ000001 11.290 1000\n", + "买入:SZ000001 10.580 1000\n", + "卖出:SZ000001 10.460 1000\n", + "买入:SZ000001 9.420 1000\n", + "卖出:SZ000001 9.100 1000\n", + "买入:SZ000001 9.330 1000\n", + "卖出:SZ000001 9.160 1000\n", + "买入:SZ000001 9.330 1000\n", + "卖出:SZ000001 10.550 1000\n", + "买入:SZ000001 10.560 1000\n", + "卖出:SZ000001 10.350 1000\n", + "买入:SZ000001 10.560 1000\n" ] } ], @@ -362,7 +371,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.7" } }, "nbformat": 4, From f5b99995d3fd19662703a86acc638308524a8eb0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 6 Apr 2024 02:17:52 +0800 Subject: [PATCH 166/601] =?UTF-8?q?fixed=20TradeRecord=20=E5=9C=A8=20pytho?= =?UTF-8?q?n=20=E4=BA=A4=E4=BA=92=E7=8E=AF=E5=A2=83=E4=B8=8B=E6=89=93?= =?UTF-8?q?=E5=8D=B0=E8=BE=93=E5=87=BA=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/__init__.py | 4 ++++ hikyuu_cpp/hikyuu/global/sysinfo.cpp | 13 +++++++++++-- hikyuu_cpp/hikyuu/global/sysinfo.h | 7 +++++++ hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp | 2 +- hikyuu_pywrap/main.cpp | 1 + 5 files changed, 24 insertions(+), 3 deletions(-) diff --git a/hikyuu/__init__.py b/hikyuu/__init__.py index 502b395a..4cd400c5 100644 --- a/hikyuu/__init__.py +++ b/hikyuu/__init__.py @@ -87,6 +87,10 @@ class iodog: close_ostream_to_python() +if in_interactive_session(): + set_python_in_interactive(True) + + # 如果是在 jupyter 环境中运行,重定向C++ stdout/stderr输出至python if in_ipython_frontend(): set_python_in_jupyter(True) diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index cec0b18d..e1e5ff83 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -25,8 +25,9 @@ using json = nlohmann::json; namespace hku { std::atomic g_latest_version{0}; -std::atomic_bool g_runningInPython{false}; // 是否是在 python 中运行 -std::atomic_bool g_pythonInJupyter{false}; // python 是否为交互模式 +bool g_runningInPython{false}; // 是否是在 python 中运行 +bool g_pythonInInteractive{false}; // python 是否运行在交互模式下 +bool g_pythonInJupyter{false}; // python 是否运行在 Jupyter中 bool HKU_API runningInPython() { return g_runningInPython; @@ -36,6 +37,14 @@ void HKU_API setRunningInPython(bool inpython) { g_runningInPython = inpython; } +bool HKU_API pythonInInteractive() { + return g_pythonInInteractive; +} + +void HKU_API setPythonInInteractive(bool interactive) { + g_pythonInInteractive = interactive; +} + bool HKU_API pythonInJupyter() { return g_pythonInJupyter; } diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.h b/hikyuu_cpp/hikyuu/global/sysinfo.h index 097e0b5d..c67f6b29 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.h +++ b/hikyuu_cpp/hikyuu/global/sysinfo.h @@ -52,8 +52,15 @@ bool HKU_API runningInPython(); /** 当前是否运行在 Jupyter 环境中 */ bool HKU_API pythonInJupyter(); +/** python 是否运行在交互模式下 */ +bool HKU_API pythonInInteractive(); + +/** 设置是否运行在 python 下*/ void HKU_API setRunningInPython(bool inpython); +/** 设置 python 是否运行在交互模式下 */ +void HKU_API setPythonInInteractive(bool interactive); + /** 当前是否运行在 Jupyter 环境中 */ void HKU_API setPythonInJupyter(bool injupyter); diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp index ec530af8..8cf7b85a 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp @@ -127,7 +127,7 @@ string TradeRecord::toString() const { #if HKU_OS_WINDOWS return fmt::format("Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime, - market_code, runningInPython() && pythonInJupyter() ? name : UTF8ToGB(name), + market_code, pythonInInteractive() ? name : UTF8ToGB(name), getBusinessName(business), planPrice, realPrice, goalPrice, number, cost.commission, cost.stamptax, cost.transferfee, cost.others, getSystemPartName(from)); diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index 57cbe42e..061f73dc 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -108,6 +108,7 @@ PYBIND11_MODULE(core, m) { export_io_redirect(m); m.def("set_python_in_jupyter", setPythonInJupyter); + m.def("set_python_in_interactive", setPythonInInteractive); m.def("close_spend_time", close_spend_time, "全局关闭 c++ 部分耗时打印"); m.def("open_spend_time", close_spend_time, "全局开启 c++ 部分耗时打印"); From 100e6d1d818f34ba48d7c60e115da3c15bc3e9a8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 6 Apr 2024 23:54:06 +0800 Subject: [PATCH 167/601] =?UTF-8?q?add=20TURNOVER=20=E6=8D=A2=E6=89=8B?= =?UTF-8?q?=E7=8E=87=E6=8C=87=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/indicator/indicator.py | 8 +++--- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.cpp | 27 ++++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.h | 21 +++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp | 5 ++++ hikyuu_pywrap/indicator/_build_in.cpp | 7 +++++ 6 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.h diff --git a/hikyuu/indicator/indicator.py b/hikyuu/indicator/indicator.py index 478d0e49..ad10632e 100644 --- a/hikyuu/indicator/indicator.py +++ b/hikyuu/indicator/indicator.py @@ -62,8 +62,6 @@ Indicator.__getitem__ = indicator_getitem Indicator.__iter__ = indicator_iter -VALUE = PRICELIST - try: import numpy as np import pandas as pd @@ -89,8 +87,6 @@ except: "you can't use method Inidicator.to_np() and to_df!" ) -VALUE = PRICELIST - def concat_to_df(dates, ind_list, head_stock_code=True, head_ind_name=False): """将列表中的指标至合并在一张 pandas DataFrame 中 @@ -143,3 +139,7 @@ HIGH = C_HIGH() LOW = C_LOW() AMO = C_AMO() VOL = C_VOL() + +# 同名指标 +VALUE = PRICELIST +CAPITAL = LIUTONGPANG diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 684e68a5..b16101f3 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -98,6 +98,7 @@ #include "crt/TIME.h" #include "crt/TIMELINE.h" #include "crt/TIMELINEVOL.h" +#include "crt/TURNOVER.h" #include "crt/UPNDAY.h" #include "crt/VAR.h" #include "crt/VARP.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.cpp b/hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.cpp new file mode 100644 index 00000000..000104a2 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-06 + * Author: fasiondog + */ + +#include "KDATA.h" +#include "SUM.h" +#include "LIUTONGPAN.h" +#include "TURNOVER.h" + +namespace hku { + +// 不需要乘以 100,成交量已经是手数即100 +Indicator HKU_API TURNOVER(int n) { + HKU_ASSERT(n >= 1); + return n == 1 ? (VOL() / LIUTONGPAN()) : (SUM(VOL(), n) / SUM(LIUTONGPAN(), n)); +} + +Indicator HKU_API TURNOVER(const KData& kdata, int n) { + HKU_ASSERT(n >= 1); + return n == 1 ? (kdata.vol() / LIUTONGPAN(kdata)) + : (SUM(kdata.vol(), n) / SUM(LIUTONGPAN(kdata), n)); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.h b/hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.h new file mode 100644 index 00000000..d09646da --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/TURNOVER.h @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-06 + * Author: fasiondog + */ + +#include "../Indicator.h" + +namespace hku { + +/** + * @brief 换手率=股票成交量/流通股股数×100% + * @param n 窗口周期 + * @return Indicator + */ +Indicator HKU_API TURNOVER(int n = 1); + +Indicator HKU_API TURNOVER(const KData& kdata, int n = 1); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp index 3bab45eb..0feb6c56 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISum.cpp @@ -47,6 +47,11 @@ void ISum::_calculate(const Indicator& ind) { } m_discard = ind.discard(); + if (n == 1) { + memcpy(dst, src, total * sizeof(value_t)); + return; + } + price_t sum = 0.0; for (size_t i = m_discard, len = (m_discard + n) >= total ? total : m_discard + n; i < len; i++) { diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 0ac99eb7..fd497e3d 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1808,4 +1808,11 @@ void export_Indicator_build_in(py::module& m) { :param float nsigma: 剔除极值时使用的 nsigma 倍 sigma,默认 3.0 :param bool recursive: 是否进行递归剔除极值,默认 False :rtype: Indicator)"); + + m.def("TURNOVER", py::overload_cast(TURNOVER), py::arg("n") = 1); + m.def("TURNOVER", py::overload_cast(TURNOVER), py::arg("kdata"), + py::arg("n") = 1, R"(TURNOVER(data[,n=1]) + 换手率=股票成交量/流通股股数×100% + + :param int n: 时间窗口)"); } From b71904a1536affd10fd327a658eeea72017cc7c9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 00:45:57 +0800 Subject: [PATCH 168/601] =?UTF-8?q?fixed=20=E5=8C=97=E4=BA=A4=E6=89=80/?= =?UTF-8?q?=E7=A7=91=E5=88=9B=E6=9D=BF=20=E6=9C=80=E5=B0=8F=E4=BA=A4?= =?UTF-8?q?=E6=98=93=E9=87=8F=E4=B8=BA1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/base/constant.rst | 2 ++ hikyuu/data/mysql_upgrade/0012.sql | 5 ++++ hikyuu/data/sqlite_upgrade/0013.sql | 9 +++++++ hikyuu/gui/data/CollectToMemThread.py | 2 +- hikyuu/gui/spot_server.py | 3 ++- hikyuu/interactive.py | 4 +-- hikyuu/tools/update_block_info.py | 2 +- hikyuu_cpp/hikyuu/Stock.cpp | 8 +++--- hikyuu_cpp/hikyuu/StockTypeInfo.h | 23 +++++++++-------- hikyuu_pywrap/_Constant.cpp | 36 +++++++++++++++++++-------- 10 files changed, 64 insertions(+), 30 deletions(-) create mode 100644 hikyuu/data/mysql_upgrade/0012.sql create mode 100644 hikyuu/data/sqlite_upgrade/0013.sql diff --git a/docs/source/base/constant.rst b/docs/source/base/constant.rst index 155bd4a0..332398f6 100644 --- a/docs/source/base/constant.rst +++ b/docs/source/base/constant.rst @@ -80,5 +80,7 @@ Null 值及证券类别 .. py:attribute:: STOCKTYPE_GEM 股票类型-创业板 .. py:attribute:: STOCKTYPE_START 股票类型-创业板 + + .. py:attribute:: STOCKTYPE_A_BJ 股票类型-A股北交所 .. py:attribute:: STOCKTYPE_TMP 股票类型-临时CSV diff --git a/hikyuu/data/mysql_upgrade/0012.sql b/hikyuu/data/mysql_upgrade/0012.sql new file mode 100644 index 00000000..ed6428ff --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0012.sql @@ -0,0 +1,5 @@ +UPDATE `hku_base`.`stocktypeinfo` SET `minTradeNumber`=1 where `id`=9; +INSERT INTO `hku_base`.`stocktypeinfo` (`id`, `type`, `precision`, `tick`, `tickValue`, `minTradeNumber`, `maxTradeNumber`, `description`) VALUES (11, 11, 2, 0.01, 0.01, 1, 1000000, '北交所'); +UPDATE `hku_base`.`stock` set `type`=1 where `marketid`=3; + +UPDATE `hku_base`.`version` set `version` = 12; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0013.sql b/hikyuu/data/sqlite_upgrade/0013.sql new file mode 100644 index 00000000..e8187ae0 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0013.sql @@ -0,0 +1,9 @@ +BEGIN TRANSACTION; + +UPDATE `StockTypeInfo` set `minTradeNumber`=1 where `id`=9; +INSERT INTO "StockTypeInfo" ("tickValue", "precision", "id", "type", "description", "tick", "minTradeNumber", "maxTradeNumber") VALUES ('0.01', '2', '11', '11', '北交所', '0.01', '1', '1000000'); +UPDATE `stock` set `type`=11 where `marketid`=3; + +UPDATE `version` set `version` = 13; + +COMMIT; \ No newline at end of file diff --git a/hikyuu/gui/data/CollectToMemThread.py b/hikyuu/gui/data/CollectToMemThread.py index 14469955..1c59fd29 100644 --- a/hikyuu/gui/data/CollectToMemThread.py +++ b/hikyuu/gui/data/CollectToMemThread.py @@ -103,7 +103,7 @@ class CollectToMemThread(QThread): return [ stk.market_code.lower() for stk in sm if stk.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, - constant.STOCKTYPE_GEM) and stk.valid and stk.market.lower() == self.market.lower() + constant.STOCKTYPE_GEM, constant.STOCKTYPE_A_BJ) and stk.valid and stk.market.lower() == self.market.lower() ] def record_is_valid(self, record): diff --git a/hikyuu/gui/spot_server.py b/hikyuu/gui/spot_server.py index 0c0a3052..2be71e43 100644 --- a/hikyuu/gui/spot_server.py +++ b/hikyuu/gui/spot_server.py @@ -271,7 +271,8 @@ def collect(server, use_proxy, source, seconds, phase1, phase2, ignore_weekend): sm = StockManager.instance() stk_list = [ stk.market_code.lower() for stk in sm if stk.valid and stk.type in - (constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_GEM, constant.STOCKTYPE_START) + (constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_GEM, + constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ) ] _ = get_nng_senders() diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index d5ba30ed..51e48266 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -376,7 +376,7 @@ def realtime_update_from_sina_qq(source): tmpstr = queryStr for stock in sm: if stock.valid and stock.type in ( - constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, constant.STOCKTYPE_GEM + constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, constant.STOCKTYPE_GEM, constant.STOCKTYPE_A_BJ, ): tmpstr += ("%s,") % (stock.market_code.lower()) count = count + 1 @@ -414,7 +414,7 @@ def realtime_update_from_tushare(): code = df.ix[i][0] stock = get_stock('sh' + code) - if stock.isNull() == True or stock.type != constant.STOCKTYPE_A: + if stock.isNull() == True or (stock.type != constant.STOCKTYPE_A and stock.type != constant.STOCKTYPE_A_BJ): stock = get_stock('sz' + code) if stock.isNull() == True: continue diff --git a/hikyuu/tools/update_block_info.py b/hikyuu/tools/update_block_info.py index a9619825..05a4bffc 100644 --- a/hikyuu/tools/update_block_info.py +++ b/hikyuu/tools/update_block_info.py @@ -12,7 +12,7 @@ from hikyuu.interactive import * def get_code_market_dict(): ret = {} for s in sm: - if s.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_B, constant.STOCKTYPE_GEM, constant.STOCKTYPE_START): + if s.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_A_BJ, constant.STOCKTYPE_B, constant.STOCKTYPE_GEM, constant.STOCKTYPE_START): ret[s.code] = "0" if s.market == 'SH' else "1" return ret diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 4a7b7e81..d989d8b9 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -104,7 +104,7 @@ Stock::Data::Data(const string& market, const string& code, const string& name, string Stock::Data::marketCode() const { if (m_type == STOCKTYPE_CRYPTO) - return m_market + "/" + m_code; + return m_market + "/" + m_code; return m_market + m_code; } @@ -668,7 +668,8 @@ TransList Stock::getTransList(const KQuery& query) const { Parameter Stock::getFinanceInfo() const { Parameter result; - HKU_IF_RETURN(type() != STOCKTYPE_A && type() != STOCKTYPE_GEM && type() != STOCKTYPE_START, + HKU_IF_RETURN(type() != STOCKTYPE_A && type() != STOCKTYPE_GEM && type() != STOCKTYPE_START && + type() != STOCKTYPE_A_BJ, result); BaseInfoDriverPtr driver = StockManager::instance().getBaseInfoDriver(); @@ -681,7 +682,8 @@ Parameter Stock::getFinanceInfo() const { PriceList Stock::getHistoryFinanceInfo(const Datetime& date) const { PriceList result; - HKU_IF_RETURN(type() != STOCKTYPE_A && type() != STOCKTYPE_GEM && type() != STOCKTYPE_START, + HKU_IF_RETURN(type() != STOCKTYPE_A && type() != STOCKTYPE_GEM && type() != STOCKTYPE_START && + type() != STOCKTYPE_A_BJ, result); const StockManager& sm = StockManager::instance(); HistoryFinanceReader rd(sm.datadir() + "/downloads/finance"); diff --git a/hikyuu_cpp/hikyuu/StockTypeInfo.h b/hikyuu_cpp/hikyuu/StockTypeInfo.h index 8e8de56d..d0efef1f 100644 --- a/hikyuu_cpp/hikyuu/StockTypeInfo.h +++ b/hikyuu_cpp/hikyuu/StockTypeInfo.h @@ -13,17 +13,18 @@ namespace hku { -#define STOCKTYPE_BLOCK 0 /// 板块 -#define STOCKTYPE_A 1 /// A股 -#define STOCKTYPE_INDEX 2 /// 指数 -#define STOCKTYPE_B 3 /// B股 -#define STOCKTYPE_FUND 4 /// 基金 -#define STOCKTYPE_ETF 5 /// ETF -#define STOCKTYPE_ND 6 /// 国债 -#define STOCKTYPE_BOND 7 /// 债券 -#define STOCKTYPE_GEM 8 /// 创业板 -#define STOCKTYPE_START 9 /// 科创板 -#define STOCKTYPE_CRYPTO 10 /// 数字货币 +#define STOCKTYPE_BLOCK 0 /// 板块 +#define STOCKTYPE_A 1 /// A股 +#define STOCKTYPE_INDEX 2 /// 指数 +#define STOCKTYPE_B 3 /// B股 +#define STOCKTYPE_FUND 4 /// 基金 +#define STOCKTYPE_ETF 5 /// ETF +#define STOCKTYPE_ND 6 /// 国债 +#define STOCKTYPE_BOND 7 /// 债券 +#define STOCKTYPE_GEM 8 /// 创业板 +#define STOCKTYPE_START 9 /// 科创板 +#define STOCKTYPE_CRYPTO 10 /// 数字货币 +#define STOCKTYPE_A_BJ 11 /// 北交所(北交所的最小交易单位不是100股) #define STOCKTYPE_TMP 999 /// 用于临时Stock diff --git a/hikyuu_pywrap/_Constant.cpp b/hikyuu_pywrap/_Constant.cpp index 64ac3425..2befcb75 100644 --- a/hikyuu_pywrap/_Constant.cpp +++ b/hikyuu_pywrap/_Constant.cpp @@ -51,6 +51,14 @@ namespace py = pybind11; #undef STOCKTYPE_START #endif +#ifdef STOCKTYPE_CRYPTO +#undef STOCKTYPE_CRYPTO +#endif + +#ifdef STOCKTYPE_A_BJ +#undef STOCKTYPE_A_BJ +#endif + #ifdef STOCKTYPE_TMP #undef STOCKTYPE_TMP #endif @@ -76,6 +84,8 @@ struct Constant { STOCKTYPE_BOND(7), STOCKTYPE_GEM(8), STOCKTYPE_START(9), + STOCKTYPE_CRYPTO(10), + STOCKTYPE_A_BJ(11), STOCKTYPE_TMP(999) { #if HKU_PYTHON_SUPPORT_PICKLE pickle_support = true; @@ -95,17 +105,19 @@ struct Constant { int64_t null_int64; bool pickle_support; // 是否支持pickle - int STOCKTYPE_BLOCK; /// 板块 - int STOCKTYPE_A; /// A股 - int STOCKTYPE_INDEX; /// 指数 - int STOCKTYPE_B; /// B股 - int STOCKTYPE_FUND; /// 基金 - int STOCKTYPE_ETF; /// ETF - int STOCKTYPE_ND; /// 国债 - int STOCKTYPE_BOND; /// 债券 - int STOCKTYPE_GEM; /// 创业板 - int STOCKTYPE_START; /// 科创板 - int STOCKTYPE_TMP; /// 临时Stock + int STOCKTYPE_BLOCK; /// 板块 + int STOCKTYPE_A; /// A股 + int STOCKTYPE_INDEX; /// 指数 + int STOCKTYPE_B; /// B股 + int STOCKTYPE_FUND; /// 基金 + int STOCKTYPE_ETF; /// ETF + int STOCKTYPE_ND; /// 国债 + int STOCKTYPE_BOND; /// 债券 + int STOCKTYPE_GEM; /// 创业板 + int STOCKTYPE_START; /// 科创板 + int STOCKTYPE_CRYPTO; /// 数字币 + int STOCKTYPE_A_BJ; /// A股北交所 + int STOCKTYPE_TMP; /// 临时Stock }; void export_Constant(py::module& m) { @@ -131,6 +143,8 @@ void export_Constant(py::module& m) { .def_readonly("STOCKTYPE_BOND", &Constant::STOCKTYPE_BOND, "债券") .def_readonly("STOCKTYPE_GEM", &Constant::STOCKTYPE_GEM, "创业板") .def_readonly("STOCKTYPE_START", &Constant::STOCKTYPE_START, "科创板") + .def_readonly("STOCKTYPE_CRYPTO", &Constant::STOCKTYPE_START, "数字币") + .def_readonly("STOCKTYPE_A_BJ", &Constant::STOCKTYPE_START, "A股北交所") .def_readonly("STOCKTYPE_TMP", &Constant::STOCKTYPE_TMP, "临时Stock"); m.attr("constant") = Constant(); From 65955e0af4a9cd037b30b4b094716b6a5a2b098c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 00:55:22 +0800 Subject: [PATCH 169/601] fixed LIUTONGPANG->LIUTONGPAN --- hikyuu/indicator/indicator.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/indicator/indicator.py b/hikyuu/indicator/indicator.py index ad10632e..1f95d43e 100644 --- a/hikyuu/indicator/indicator.py +++ b/hikyuu/indicator/indicator.py @@ -142,4 +142,4 @@ VOL = C_VOL() # 同名指标 VALUE = PRICELIST -CAPITAL = LIUTONGPANG +CAPITAL = LIUTONGPAN From 0723e115c5ab5193dc5a0560aaeaff9f41fe4a78 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 02:36:39 +0800 Subject: [PATCH 170/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20PF/TradeRecord=20?= =?UTF-8?q?=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp | 8 -------- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 14 +------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp index 8cf7b85a..9f1502c3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp @@ -125,18 +125,10 @@ string TradeRecord::toString() const { name = stock.name(); } -#if HKU_OS_WINDOWS - return fmt::format("Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime, - market_code, pythonInInteractive() ? 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 { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index dbddba0e..20098d7f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -404,23 +404,11 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool auto funds = sys->getTM()->getFunds(date, m_query.kType()); size_t position = sys->getTM()->getHoldNumber(date, stk); KRecord krecord = stk.getKRecord(date, m_query.kType()); -#if HKU_OS_WINDOWS - auto stk_name = - runningInPython() && 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}| {:<12.2f}|", - stk.market_code(), stk_name, position, funds.market_value, funds.cash, - krecord.openPrice, krecord.closePrice); -#else auto stk_name = stk.name(); HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}| {:<12.2f}|", stk.market_code(), stk_name, position, funds.market_value, funds.cash, krecord.openPrice, krecord.closePrice); -#endif + // clang-format off HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); count++; From 92c39baa2a30b8ef078208a48917a31947f1ba37 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 12:56:58 +0800 Subject: [PATCH 171/601] =?UTF-8?q?=E6=81=A2=E5=A4=8D=20PF/TradeRecord=20?= =?UTF-8?q?=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp | 8 ++++++++ .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 14 +++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp index 9f1502c3..24341dd4 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp @@ -124,11 +124,19 @@ string TradeRecord::toString() const { market_code = stock.market_code(); name = stock.name(); } +#if HKU_OS_WINDOWS + return fmt::format("Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime, + market_code, runningInPython() && 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 { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 20098d7f..dbddba0e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -404,11 +404,23 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool auto funds = sys->getTM()->getFunds(date, m_query.kType()); size_t position = sys->getTM()->getHoldNumber(date, stk); KRecord krecord = stk.getKRecord(date, m_query.kType()); +#if HKU_OS_WINDOWS + auto stk_name = + runningInPython() && 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}| {:<12.2f}|", + stk.market_code(), stk_name, position, funds.market_value, funds.cash, + krecord.openPrice, krecord.closePrice); +#else auto stk_name = stk.name(); HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}| {:<12.2f}|", stk.market_code(), stk_name, position, funds.market_value, funds.cash, krecord.openPrice, krecord.closePrice); - +#endif // clang-format off HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); count++; From 648786da8c08052d7ebb56b2025388e518f8acba Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 13:07:16 +0800 Subject: [PATCH 172/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20PF=20/=20TradeReco?= =?UTF-8?q?rd=20=E6=89=93=E5=8D=B0=E7=BC=96=E7=A0=81=20:(?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp | 9 --------- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 13 ------------- 2 files changed, 22 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp index 24341dd4..64b88f57 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeRecord.cpp @@ -124,19 +124,10 @@ string TradeRecord::toString() const { market_code = stock.market_code(); name = stock.name(); } -#if HKU_OS_WINDOWS - return fmt::format("Trade({}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {})", datetime, - market_code, runningInPython() && 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 { diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index dbddba0e..1c11ef24 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -404,23 +404,10 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool auto funds = sys->getTM()->getFunds(date, m_query.kType()); size_t position = sys->getTM()->getHoldNumber(date, stk); KRecord krecord = stk.getKRecord(date, m_query.kType()); -#if HKU_OS_WINDOWS - auto stk_name = - runningInPython() && 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}| {:<12.2f}|", - stk.market_code(), stk_name, position, funds.market_value, funds.cash, - krecord.openPrice, krecord.closePrice); -#else auto stk_name = stk.name(); HKU_INFO("| {:<11}| {:<11}| {:<11}| {:<13.2f}| {:<13.2f}| {:<12.2f}| {:<12.2f}|", stk.market_code(), stk_name, position, funds.market_value, funds.cash, krecord.openPrice, krecord.closePrice); -#endif // clang-format off HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+"); count++; From 019cd887c80b4bbe6b56a45a63fdd5d67d4b9c76 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 14:03:21 +0800 Subject: [PATCH 173/601] release 2.0.1 --- docs/source/release.rst | 17 +++++++++++++++++ xmake.lua | 2 +- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 4680369f..96c9264c 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,23 @@ 版本发布说明 ======================= +2.0.1 - 2024年4月7日 +------------------------- + +1. 新增 TURNOVER (换手率指标) +2. 新增股票类型 STOCKTYPE_A_BJ (北交所), 修复科创板和北交所股票最小交易量为1 +3. fixed tm 建立日期小于参考日期时 sys_performance 报错 +4. hub 中的 prtflo 未 pf, 和内部叫法统一 +5. 调整 MF_MultiFactor getScores 方法命名(原为 getScore ),并调整为在指定日期不存在数据时返回空列表(原为抛出异常) +6. fixed python 中 TradeRecordList/PositionRecordList 中 to_df 方法失效 +7. hku_catch 中忽略对 KeyboardInterrupt 的捕获,避免 python 中 Ctrl-C 无法终止 +8. crtSL 更名为 crtSP (移滑价差算法),和内部其他叫法统一 +9. fixed 缺失 hku_save / hku_load 函数,导致示例运行失败 +10. fixed crtMM 补充缺失的接口 +11. 更新其他运行失败示例,如 OrderBroker (pybind需要先创建对象再传入方法) +12. python 中缺失 CAPITAL (流通盘), 原可使用 LIUTONGPAN, 但缺失对 CAPITAL 的同名指定 + + 2.0.0 - 2024年4月3日 ------------------------- diff --git a/xmake.lua b/xmake.lua index bbb5dafb..c0ffc819 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.0", {build = "%Y%m%d%H%M"}) +set_version("2.0.1", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 9e9d59b38f4f6ada3c98113efb72ee939f8ddc81 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 16:28:18 +0800 Subject: [PATCH 174/601] =?UTF-8?q?update=20=E7=89=88=E6=9C=AC=E6=9B=B4?= =?UTF-8?q?=E6=96=B0=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/HikyuuTDX.py | 3 ++- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 8 +++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index 3da39169..6a6b7161 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -628,7 +628,8 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): if can_upgrade(): self.import_detail_textEdit.append("========================================================") self.import_detail_textEdit.append( - "新版本 ({}) 已发布,建议更新".format(get_last_version())) + "Hikyuu 新版本 ({}) 已发布,建议更新".format(get_last_version())) + self.import_detail_textEdit.append("更新命令: pip instal hikyuu --upgrade") self.import_detail_textEdit.append("========================================================") self.import_running = False diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 62b152d7..8802ee32 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -76,10 +76,12 @@ void GlobalInitializer::init() { void GlobalInitializer::clean() { if (CanUpgrade()) { fmt::print( - "\n========================================================\n" - "A new version ({}) is available and can be upgraded.\n" + "\n====================================================================\n" + "The new version of Hikyuu is {}, and you can run the upgrade command:\n" + "Hikyuu 的最新新版本是 {}, 您可以运行升级命令:\n" + "pip install hikyuu --upgrade\n" "========================================================\n\n", - getLatestVersion()); + getLatestVersion(), getLatestVersion()); } releaseGlobalTaskGroup(); From 5f637b8eb412885db75a7e55bde5bd0286f38906 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 7 Apr 2024 20:31:33 +0800 Subject: [PATCH 175/601] =?UTF-8?q?fixed=20sys=5Fperformance=20=E5=B9=B4?= =?UTF-8?q?=E5=8C=96=E6=94=B6=E7=9B=8A=E7=8E=87=E6=98=BE=E7=A4=BA=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index ca9f3108..0993ad77 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -777,7 +777,7 @@ def sys_performance(sys, ref_stk=None): cur_fund = per['当前总资产'] t1 = '投入总资产: {:<.2f} 当前总资产: {:<.2f} 当前盈利: {:<.2f}'.format( invest_total, cur_fund, cur_fund - invest_total) - t2 = '当前策略收益: {:<.2f}% 年化收益率: {:<.2}% 最大回撤: {:<.2f}%'.format( + t2 = '当前策略收益: {:<.2f}% 年化收益率: {:<.2f}% 最大回撤: {:<.2f}%'.format( funds_return[-1], per["帐户平均年收益率%"], max_pullback) t3 = '系统胜率: {:<.2f}% 盈/亏比: 1 : {:<.2f} 夏普比率: {:<.2f}'.format( per['赢利交易比例%'], per['净赢利/亏损比例'], sharp) From 763c4b80f7a7e7ad135fde4f73adaa0f05dcb663 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 8 Apr 2024 00:35:02 +0800 Subject: [PATCH 176/601] =?UTF-8?q?=E6=B8=85=E7=90=86=E6=8E=89=E4=B8=8D?= =?UTF-8?q?=E7=94=A8=E7=9A=84=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_instance/ama_sys/AmaInstance.cpp | 83 ------------------- .../trade_instance/ama_sys/AmaInstance.h | 21 ----- hikyuu_pywrap/main.cpp | 2 - hikyuu_pywrap/trade_instance/_AmaInstance.cpp | 16 ---- .../trade_instance/instance_main.cpp | 16 ---- 5 files changed, 138 deletions(-) delete mode 100644 hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.cpp delete mode 100644 hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.h delete mode 100644 hikyuu_pywrap/trade_instance/_AmaInstance.cpp delete mode 100644 hikyuu_pywrap/trade_instance/instance_main.cpp diff --git a/hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.cpp b/hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.cpp deleted file mode 100644 index 1d951e0b..00000000 --- a/hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * AmaInstance.cpp - * - * Created on: 2015年3月12日 - * Author: fasiondog - */ - -#include "../../trade_sys/signal/build_in.h" -#include "../../indicator/crt/PRICELIST.h" -#include "AmaInstance.h" - -namespace hku { - -Indicator HKU_API AmaSpecial(const Block& block, KQuery query, Indicator ama) { - Indicator result; - StockManager& sm = StockManager::instance(); - - //计算每日股票总数 - DatetimeList dateList = sm.getTradingCalendar(query, "SH"); - - size_t dayTotal = dateList.size(); - if (dayTotal == 0) { - result = PRICELIST(PriceList()); - result.name("POS"); - return result; - } - - vector numberPerDay(dayTotal); - for (size_t i = 0; i < dayTotal; ++i) { - numberPerDay[i] = 0; - for (auto stk_iter = block.begin(); stk_iter != block.end(); ++stk_iter) { - if (stk_iter->startDatetime() <= dateList[i] && - dateList[i] <= stk_iter->lastDatetime()) { - numberPerDay[i]++; - } - } - } - - vector position(dayTotal); - size_t discard = ama.discard(); - for (auto stk_iter = block.begin(); stk_iter != block.end(); ++stk_iter) { - KData kdata = stk_iter->getKData(query); - if (kdata.empty()) - continue; - SignalPtr sg(SG_Single(ama)); - sg->setTO(kdata); - bool isHold = false; - size_t n_dis = 0; - for (size_t i = 0; i < dayTotal; ++i) { - if (isHold) { - if (sg->shouldSell(dateList[i])) { - isHold = false; - } else { - position[i]++; - } - - } else { - if (sg->shouldBuy(dateList[i])) { - position[i]++; - isHold = true; - } - } - - if (dateList[i] >= kdata[0].datetime) { - if (n_dis < discard) { - n_dis++; - numberPerDay[i]--; - } - } - } - } - - PriceList tmp_result(dayTotal, Null()); - for (auto i = discard; i < dayTotal; ++i) { - tmp_result[i] = numberPerDay[i] ? (double)position[i] / (double)numberPerDay[i] : 1.0; - } - - result = PRICELIST(tmp_result); - result.name("POS"); - return PRICELIST(result); -} - -} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.h b/hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.h deleted file mode 100644 index 9f905c78..00000000 --- a/hikyuu_cpp/hikyuu/trade_instance/ama_sys/AmaInstance.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * AmaInstance.h - * - * Created on: 2015年3月12日 - * Author: fasiondog - */ - -#pragma once -#ifndef AMAINSTANCE_H -#define AMAINSTANCE_H - -#include "../../indicator/Indicator.h" -#include "../../Block.h" - -namespace hku { - -Indicator HKU_API AmaSpecial(const Block& block, KQuery query, Indicator ind); - -} /* namespace hku*/ - -#endif /* AMAINSTANCE_H */ diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index 061f73dc..d02cc7fa 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -38,7 +38,6 @@ void export_io_redirect(py::module& m); void export_data_driver_main(py::module& m); void export_indicator_main(py::module& m); -void export_instance_main(py::module& m); void export_SystemPart(py::module& m); void export_trade_manage_main(py::module& m); void export_trade_sys_main(py::module& m); @@ -95,7 +94,6 @@ PYBIND11_MODULE(core, m) { export_data_driver_main(m); export_indicator_main(m); - export_instance_main(m); export_SystemPart(m); export_trade_manage_main(m); diff --git a/hikyuu_pywrap/trade_instance/_AmaInstance.cpp b/hikyuu_pywrap/trade_instance/_AmaInstance.cpp deleted file mode 100644 index b4a22992..00000000 --- a/hikyuu_pywrap/trade_instance/_AmaInstance.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * _AmaInstance.cpp - * - * Created on: 2015年3月14日 - * Author: fasiondog - */ - -#include -#include - -namespace py = pybind11; -using namespace hku; - -void export_AmaInstance(py::module& m) { - m.def("AmaSpecial", AmaSpecial); -} diff --git a/hikyuu_pywrap/trade_instance/instance_main.cpp b/hikyuu_pywrap/trade_instance/instance_main.cpp deleted file mode 100644 index 93fe745c..00000000 --- a/hikyuu_pywrap/trade_instance/instance_main.cpp +++ /dev/null @@ -1,16 +0,0 @@ -/* - * instance_main.cpp - * - * Created on: 2015年3月14日 - * Author: fasiondog - */ - -#include - -namespace py = pybind11; - -void export_AmaInstance(py::module& m); - -void export_instance_main(py::module& m) { - export_AmaInstance(m); -} From a55dc73723071e2ace2b9446f5236010e2e82cca Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 8 Apr 2024 15:42:38 +0800 Subject: [PATCH 177/601] update docs --- docs/source/trade_manage/OrderBroker.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/trade_manage/OrderBroker.rst b/docs/source/trade_manage/OrderBroker.rst index 971ab722..e7e1b29b 100644 --- a/docs/source/trade_manage/OrderBroker.rst +++ b/docs/source/trade_manage/OrderBroker.rst @@ -18,7 +18,8 @@ Python中的订单代理包装 my_tm = crtTM(init_cash = 300000) #注册实盘交易订单代理 - my_tm.reg_broker(crtOB(TestOrderBroker())) #TestOerderBroker是测试用订单代理对象,只打印 + ob = crtOB(TestOrderBroker()) + my_tm.reg_broker(ob) #TestOerderBroker是测试用订单代理对象,只打印 #my_tm.reg_broker(crtOB(MailOrderBroker("smtp.sina.com", "yourmail@sina.com", "yourpwd", "receivermail@XXX.yy))) #根据需要修改订单代理最后的时间戳,后续只有大于该时间戳时,订单代理才会实际发出订单指令 From 9bf998dd53d52c95ab0511102b50281bd0ab1344 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Apr 2024 02:36:30 +0800 Subject: [PATCH 178/601] =?UTF-8?q?add=20=E5=BE=AE=E4=BF=A1=E5=85=AC?= =?UTF-8?q?=E4=BC=97=E5=8F=B7=E5=9B=BE=E7=89=87=EF=BC=8C=E5=8E=BB=E6=8E=89?= =?UTF-8?q?=E6=8D=90=E8=B5=A0=E5=9B=BE=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.rst | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index 7a2f6462..ab27f995 100644 --- a/README.rst +++ b/README.rst @@ -43,11 +43,6 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 :alt: Gitee -**给作者加点油,每天扫扫红包,或者请作者喝杯咖啡** - -.. image:: http://fasiondog.gitee.io/hikyuu/images/juanzeng.jpg - - 示例: :: @@ -111,3 +106,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - 加入微信群(请注明“加入hikyuu”): .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_group.jpg + +- 关注公众号: + + .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_gongzhonghao.jpg \ No newline at end of file From 9dccaba7bfb1dccaf1f10d2e63f363672f0863e9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Apr 2024 05:00:04 +0800 Subject: [PATCH 179/601] update readme --- README.rst | 7 ------- 1 file changed, 7 deletions(-) diff --git a/README.rst b/README.rst index ab27f995..da00ce7c 100644 --- a/README.rst +++ b/README.rst @@ -35,13 +35,6 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 感谢网友提供的 Hikyuu Ubuntu虚拟机环境, 百度网盘下载(提取码: ht8j): ``_ -祝贺 HIKYUU 入选 GITEE 最有价值开源项目 GVP ------------------------------------------------ - -.. image:: http://fasiondog.gitee.io/hikyuu/images/gitee_GVP.png - :target: https://gitee.com/gvp - :alt: Gitee - 示例: From 498ccbfc1165786f2ecfc0f3211d7fb4fff585b4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Apr 2024 11:47:07 +0800 Subject: [PATCH 180/601] debug github action --- .github/workflows/windows.yml | 2 +- .github/workflows/windows_python.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 531e06e5..78c05f2e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -45,7 +45,7 @@ jobs: - name: configure shell: cmd run: | - xmake f --feedback=n -k shared -y + xmake f -c --feedback=n -k shared -y -vD - name: build shell: cmd diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 790f5b6e..fbd885fb 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -45,7 +45,7 @@ jobs: - name: configure shell: cmd run: | - xmake f -k shared -y + xmake f -c -k shared -y - name: build shell: cmd From 8c977486a880b33078e34c4ab4f204b45a76995b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Apr 2024 12:10:55 +0800 Subject: [PATCH 181/601] debug github action --- .github/workflows/windows.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 78c05f2e..0e508c99 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -24,23 +24,9 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Cache windows packages - id: cache-xmake-windows - uses: actions/cache@v4 - env: - cache-name: cache-windows-modules - with: - path: /Users/%USERNAME/.xmake - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - uses: xmake-io/github-action-setup-xmake@v1 with: xmake-version: branch@master - actions-cache-folder: '.xmake-cache' - name: configure shell: cmd From 01559a15fecf8169b1607557a61a9ae0e6383232 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Apr 2024 13:04:20 +0800 Subject: [PATCH 182/601] update readme --- README.rst | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/README.rst b/README.rst index da00ce7c..95bd4e38 100644 --- a/README.rst +++ b/README.rst @@ -88,10 +88,23 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 :alt: Star History Chart -遇到了问题?想要更多了解Hikyuu?请使用以下方式联系: +项目捐赠 -------------------------------------------------- -- 作者邮箱:fasiondog@sina.com +目前知识星球尚处于建设期,您的加入将视为对项目的捐赠(200元) + + .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu.jpg + + +想要更多了解Hikyuu?请使用以下方式联系: +-------------------------------------------------- + +**项目交流和问题答复将逐渐转移至知识星球-【Hikyuu量化】,详见前述“项目捐赠”。** + +- 关注公众号: + + .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_gongzhonghao.jpg + - QQ交流群:114910869, 或扫码加入: .. figure:: http://fasiondog.gitee.io/hikyuu/images/10003-qq.png @@ -99,7 +112,3 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - 加入微信群(请注明“加入hikyuu”): .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_group.jpg - -- 关注公众号: - - .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_gongzhonghao.jpg \ No newline at end of file From 9bd8e58f46f4de3e464dda3300ca50c0d3aa25ed Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Apr 2024 23:45:01 +0800 Subject: [PATCH 183/601] =?UTF-8?q?fixed=20=E8=8E=B7=E5=8F=96=E8=8A=82?= =?UTF-8?q?=E5=81=87=E6=97=A5=E5=87=BA=E7=8E=B0=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index ab2d1c7f..4a533b80 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -181,7 +181,7 @@ def get_new_holidays(): res.encoding = res.apparent_encoding ret = re.findall(r'', res.text, re.M)[0].strip() day = [d.split('|')[:4] for d in ret.split('\n')] - return [v[0] for v in day if v[2] == '中国'] + return [v[0] for v in day if len(v) >= 3 and v[2] == '中国'] @hku_catch(ret=[], trace=True) From d91874e0d1a8c3005eb55d4ea510ed098283cd4a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 12 Apr 2024 02:23:01 +0800 Subject: [PATCH 184/601] =?UTF-8?q?add=20RESULT=20=E6=8C=87=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/indicator/indicator.rst | 9 +++ docs/source/indicator/overview.rst | 1 + hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/RESULT.h | 20 +++++ hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp | 49 ++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IResult.h | 26 +++++++ .../hikyuu/indicator/test_RESULT.cpp | 76 +++++++++++++++++++ hikyuu_pywrap/indicator/_build_in.cpp | 9 +++ 8 files changed, 191 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/RESULT.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IResult.h create mode 100644 hikyuu_cpp/unit_test/hikyuu/indicator/test_RESULT.cpp diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 702112e0..48790cf6 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -796,6 +796,15 @@ :rtype: Indicator +.. py:function:: RESULT(data, result_ix) + + 以公式指标的方式返回指定指标中的指定结果集 + + :param Indicator data: 指定的指标 + :param int result_ix: 指定的结果集 + :rtype: Indicator + + .. py:function:: REVERSE([data]) 求相反数,REVERSE(X)返回-X diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index 8baefa4a..43b9ce93 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -10,6 +10,7 @@ * :py:func:`CVAL` - 创建指定长度的固定数值指标 * :py:func:`DROPNA` - 删除 nan 值 * :py:func:`PRICELIST` - 将PriceList或Indicator的结果集包装为Indicator,同名 VALUE +* :py:func:`RESULT` - 以指标公式的方式返回指定指标中相应的结果集 * :py:func:`WEAVE` - 将两个ind的结果合并到一个ind中 * :py:func:`ZSCORE` - ZScore 标准化 diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index b16101f3..16e4c762 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -73,6 +73,7 @@ #include "crt/PRICELIST.h" #include "crt/RECOVER.h" #include "crt/REF.h" +#include "crt/RESULT.h" #include "crt/REVERSE.h" #include "crt/ROC.h" #include "crt/ROCP.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/RESULT.h b/hikyuu_cpp/hikyuu/indicator/crt/RESULT.h new file mode 100644 index 00000000..682cb192 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/RESULT.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-12 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +Indicator HKU_API RESULT(int result_ix); + +inline Indicator HKU_API RESULT(const Indicator& ind, int result_ix) { + return RESULT(result_ix)(ind); +} + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp new file mode 100644 index 00000000..a3c91fe0 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-12 + * Author: fasiondog + */ + +#include "IResult.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IResult) +#endif + +namespace hku { + +IResult::IResult() : IndicatorImp("RESULT", 1) { + setParam("result_ix", 0); +} + +IResult::IResult(int result_ix) : IndicatorImp("RESULT", 1) { + setParam("result_ix", result_ix); + checkParam("result_ix"); +} + +void IResult::_checkParam(const string& name) const { + if ("result_ix" == name) { + int result_ix = getParam("result_ix"); + HKU_ASSERT(result_ix >= 0 && result_ix < MAX_RESULT_NUM); + } +} + +void IResult::_calculate(const Indicator& ind) { + int result_ix = getParam("result_ix"); + HKU_CHECK(result_ix < ind.getResultNumber(), + "The input indicator has only {} results, but result_ix({}) is out_of range!", + ind.getResultNumber(), result_ix); + m_discard = ind.discard(); + HKU_IF_RETURN(m_discard >= ind.size(), void()); + + const auto* src = ind.data(result_ix); + auto* dst = this->data(); + memcpy(dst + m_discard, src + m_discard, sizeof(value_t) * (ind.size() - m_discard)); +} + +Indicator HKU_API RESULT(int result_ix) { + return make_shared(result_ix); +} + +} // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IResult.h b/hikyuu_cpp/hikyuu/indicator/imp/IResult.h new file mode 100644 index 00000000..099f6177 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IResult.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-12 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class IResult : public IndicatorImp { + INDICATOR_IMP(IResult) + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IResult(); + explicit IResult(int reuslt_ix); + virtual ~IResult() = default; + + virtual void _checkParam(const string& name) const override; +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_RESULT.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_RESULT.cpp new file mode 100644 index 00000000..55b58858 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_RESULT.cpp @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-12 + * Author: fasiondog + */ + +#include "../test_config.h" +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_indicator_RESULT test_indicator_RESULT + * @ingroup test_hikyuu_indicator_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_RESULT") { + /** @arg 无效参数 */ + CHECK_THROWS_AS(RESULT(-1), std::exception); + CHECK_THROWS_AS(RESULT(6), std::exception); + + /** @arg 输入空指标 */ + auto ret = RESULT(Indicator(), 0); + CHECK_EQ(ret.empty(), true); + + /** @arg 正常获取 */ + auto k = getStock("SH000001").getKData(KQuery(-100)); + auto macd = MACD(CLOSE(), 0); + auto bar = RESULT(macd, 0); + auto diff = RESULT(macd, 1); + auto dea = RESULT(macd, 2); + auto expect = MACD(k.close(), 0); + CHECK_UNARY(bar(k).equal(expect.getResult(0))); + CHECK_UNARY(diff(k).equal(expect.getResult(1))); + CHECK_UNARY(dea(k).equal(expect.getResult(2))); +} + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_RESULT_export") { + StockManager& sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/RESULT.xml"; + + Stock stock = sm.getStock("sh000001"); + KData kdata = stock.getKData(KQuery(-20)); + Indicator ma1 = RESULT(MACD(CLOSE(kdata)), 0); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(ma1); + } + + Indicator ma2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(ma2); + } + + CHECK_EQ(ma1.size(), ma2.size()); + CHECK_UNARY(ma1.equal(ma2)); +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ \ No newline at end of file diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index fd497e3d..19a26ac1 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1815,4 +1815,13 @@ void export_Indicator_build_in(py::module& m) { 换手率=股票成交量/流通股股数×100% :param int n: 时间窗口)"); + + m.def("RESULT", py::overload_cast(RESULT)); + m.def("RESULT", py::overload_cast(RESULT), py::arg("data"), + py::arg("result_ix"), R"(RESULT(data, result_ix) + + 以公式指标的方式返回指定指标中的指定结果集 + + :param Indicator data: 指定的指标 + :param int result_ix: 指定的结果集)"); } From 790692fa3d6a78df845fe55ecb744d2a61d56955 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 12 Apr 2024 02:54:41 +0800 Subject: [PATCH 185/601] compile error on ubuntu --- hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp index a3c91fe0..e462bdb9 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IResult.cpp @@ -43,7 +43,7 @@ void IResult::_calculate(const Indicator& ind) { } Indicator HKU_API RESULT(int result_ix) { - return make_shared(result_ix); + return Indicator(make_shared(result_ix)); } } // namespace hku From 77ccadda300ce17d0ace9db721ad649e0fa0730e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 00:39:22 +0800 Subject: [PATCH 186/601] =?UTF-8?q?=E8=B4=A2=E5=8A=A1=E5=AD=97=E6=AE=B5?= =?UTF-8?q?=E6=98=A0=E5=B0=84=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0013.sql | 590 +++++++++++++++++++++++++++ hikyuu/data/sqlite_upgrade/0014.sql | 594 ++++++++++++++++++++++++++++ 2 files changed, 1184 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0013.sql create mode 100644 hikyuu/data/sqlite_upgrade/0014.sql diff --git a/hikyuu/data/mysql_upgrade/0013.sql b/hikyuu/data/mysql_upgrade/0013.sql new file mode 100644 index 00000000..f1d4b51e --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0013.sql @@ -0,0 +1,590 @@ +CREATE TABLE + IF NOT EXISTS `hku_base`.`HistoryFinanceField` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `name` VARCHAR(200) NOT NULL, + PRIMARY KEY(`id`) + ) COLLATE = 'utf8_general_ci' ENGINE = InnoDB; + +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (1, "基本每股收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (2, "扣除非经常性损益每股收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (3, "每股未分配利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (4, "每股净资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (5, "每股资本公积金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (6, "净资产收益率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (7, "每股经营现金流量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (8, "资产负债表_货币资金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (9, "资产负债表_交易性金融资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (10, "资产负债表_应收票据"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (11, "资产负债表_应收账款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (12, "资产负债表_预付款项"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (13, "资产负债表_其他应收款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (14, "资产负债表_应收关联公司款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (15, "资产负债表_应收利息"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (16, "资产负债表_应收股利"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (17, "资产负债表_存货"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (18, "资产负债表_消耗性生物资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (19, "资产负债表_一年内到期的非流动资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (20, "资产负债表_其他流动资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (21, "资产负债表_流动资产合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (22, "资产负债表_可供出售金融资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (23, "资产负债表_持有至到期投资"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (24, "资产负债表_长期应收款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (25, "资产负债表_长期股权投资"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (26, "资产负债表_投资性房地产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (27, "资产负债表_固定资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (28, "资产负债表_在建工程"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (29, "资产负债表_工程物资"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (30, "资产负债表_固定资产清理"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (31, "资产负债表_生产性生物资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (32, "资产负债表_油气资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (33, "资产负债表_无形资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (34, "资产负债表_开发支出"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (35, "资产负债表_商誉"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (36, "资产负债表_长期待摊费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (37, "资产负债表_递延所得税资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (38, "资产负债表_其他非流动资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (39, "资产负债表_非流动资产合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (40, "资产负债表_资产总计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (41, "资产负债表_短期借款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (42, "资产负债表_交易性金融负债"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (43, "资产负债表_应付票据"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (44, "资产负债表_应付账款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (45, "资产负债表_预收款项"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (46, "资产负债表_应付职工薪酬"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (47, "资产负债表_应交税费"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (48, "资产负债表_应付利息"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (49, "资产负债表_应付股利"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (50, "资产负债表_其他应付款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (51, "资产负债表_应付关联公司款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (52, "资产负债表_一年内到期的非流动负债"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (53, "资产负债表_其他流动负债"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (54, "资产负债表_流动负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (55, "资产负债表_长期借款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (56, "资产负债表_应付债券"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (57, "资产负债表_长期应付款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (58, "资产负债表_专项应付款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (59, "资产负债表_预计负债"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (60, "资产负债表_递延所得税负债"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (61, "资产负债表_其他非流动负债"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (62, "资产负债表_非流动负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (63, "资产负债表_负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (64, "资产负债表_实收资本(或股本)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (65, "资产负债表_资本公积"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (66, "资产负债表_盈余公积"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (67, "资产负债表_库存股"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (68, "资产负债表_未分配利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (69, "资产负债表_少数股东权益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (70, "资产负债表_外币报表折算价差"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (71, "资产负债表_非正常经营项目收益调整"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (72, "资产负债表_所有者权益(或股东权益)合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (73, "资产负债表_负债和所有者(或股东权益)合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (74, "利润表_营业收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (75, "利润表_营业成本"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (76, "利润表_营业税金及附加"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (77, "利润表_销售费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (78, "利润表_管理费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (79, "利润表_勘探费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (80, "利润表_财务费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (81, "利润表_资产减值损失"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (82, "利润表_公允价值变动净收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (83, "利润表_投资收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (84, "利润表_对联营企业和合营企业的投资收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (85, "利润表_影响营业利润的其他科目"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (86, "利润表_营业利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (87, "利润表_补贴收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (88, "利润表_营业外收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (89, "利润表_营业外支出"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (90, "利润表_非流动资产处置净损失"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (91, "利润表_影响利润总额的其他科目"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (92, "利润表_利润总额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (93, "利润表_所得税"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (94, "利润表_影响净利润的其他科目"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (95, "利润表_净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (96, "利润表_归属于母公司所有者的净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (97, "利润表_少数股东损益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (98, "现金流量表_销售商品、提供劳务收到的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (99, "现金流量表_收到的税费返还"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (100, "现金流量表_收到其他与经营活动有关的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (101, "现金流量表_经营活动现金流入小计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (102, "现金流量表_购买商品、接受劳务支付的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (103, "现金流量表_支付给职工以及为职工支付的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (104, "现金流量表_支付的各项税费"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (105, "现金流量表_支付其他与经营活动有关的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (106, "现金流量表_经营活动现金流出小计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (107, "现金流量表_经营活动产生的现金流量净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (108, "现金流量表_收回投资收到的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (109, "现金流量表_取得投资收益收到的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (110, "现金流量表_处置固定资产、无形资产和其他长期资产收回的现金净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (111, "现金流量表_处置子公司及其他营业单位收到的现金净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (112, "现金流量表_收到其他与投资活动有关的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (113, "现金流量表_投资活动现金流入小计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (114, "现金流量表_购建固定资产、无形资产和其他长期资产支付的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (115, "现金流量表_投资支付的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (116, "现金流量表_取得子公司及其他营业单位支付的现金净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (117, "现金流量表_支付其他与投资活动有关的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (118, "现金流量表_投资活动现金流出小计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (119, "现金流量表_投资活动产生的现金流量净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (120, "现金流量表_吸收投资收到的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (121, "现金流量表_取得借款收到的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (122, "现金流量表_收到其他与筹资活动有关的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (123, "现金流量表_筹资活动现金流入小计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (124, "现金流量表_偿还债务支付的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (125, "现金流量表_分配股利、利润或偿付利息支付的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (126, "现金流量表_支付其他与筹资活动有关的现金"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (127, "现金流量表_筹资活动现金流出小计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (128, "现金流量表_筹资活动产生的现金流量净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (129, "现金流量表_汇率变动对现金的影响"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (130, "现金流量表_其他原因对现金的影响"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (131, "现金流量表_现金及现金等价物净增加额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (132, "现金流量表_期初现金及现金等价物余额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (133, "现金流量表_期末现金及现金等价物余额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (134, "现金流量表_净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (135, "现金流量表_资产减值准备"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (136, "现金流量表_固定资产折旧、油气资产折耗、生产性生物资产折旧"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (137, "现金流量表_无形资产摊销"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (138, "现金流量表_长期待摊费用摊销"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (139, "现金流量表_处置固定资产、无形资产和其他长期资产的损失"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (140, "现金流量表_固定资产报废损失"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (141, "现金流量表_公允价值变动损失"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (142, "现金流量表_财务费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (143, "现金流量表_投资损失"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (144, "现金流量表_递延所得税资产减少"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (145, "现金流量表_递延所得税负债增加"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (146, "现金流量表_存货的减少"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (147, "现金流量表_经营性应收项目的减少"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (148, "现金流量表_经营性应付项目的增加"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (149, "现金流量表_其他"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (150, "现金流量表_经营活动产生的现金流量净额2"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (151, "现金流量表_债务转为资本"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (152, "现金流量表_一年内到期的可转换公司债券"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (153, "现金流量表_融资租入固定资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (154, "现金流量表_现金的期末余额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (155, "现金流量表_现金的期初余额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (156, "现金流量表_现金等价物的期末余额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (157, "现金流量表_现金等价物的期初余额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (158, "现金流量表_现金及现金等价物净增加额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (159, "偿债能力_流动比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (160, "偿债能力_速动比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (161, "偿债能力_现金比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (162, "偿债能力_利息保障倍数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (163, "偿债能力_非流动负债比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (164, "偿债能力_流动负债比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (165, "偿债能力_现金到期债务比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (166, "偿债能力_有形资产净值债务率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (167, "偿债能力_权益乘数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (168, "偿债能力_股东的权益/负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (169, "偿债能力_有形资产/负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (170, "偿债能力_经营活动产生的现金流量净额/负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (171, "偿债能力_EBITDA/负债合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (172, "营运能力_应收帐款周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (173, "营运能力_存货周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (174, "营运能力_运营资金周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (175, "营运能力_总资产周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (176, "营运能力_固定资产周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (177, "营运能力_应收帐款周转天数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (178, "营运能力_存货周转天数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (179, "营运能力_流动资产周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (180, "营运能力_流动资产周转天数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (181, "营运能力_总资产周转天数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (182, "营运能力_股东权益周转率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (183, "成长能力_营业收入增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (184, "成长能力_净利润增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (185, "成长能力_净资产增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (186, "成长能力_固定资产增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (187, "成长能力_总资产增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (188, "成长能力_投资收益增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (189, "成长能力_营业利润增长率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (190, "成长能力_扣非每股收益同比"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (191, "成长能力_扣非净利润同比"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (192, "成长能力_暂无"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (193, "盈利能力_成本费用利润率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (194, "盈利能力_营业利润率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (195, "盈利能力_营业税金率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (196, "盈利能力_营业成本率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (197, "盈利能力_净资产收益率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (198, "盈利能力_投资收益率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (199, "盈利能力_销售净利率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (200, "盈利能力_总资产报酬率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (201, "盈利能力_净利润率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (202, "盈利能力_销售毛利率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (203, "盈利能力_三费比重"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (204, "盈利能力_管理费用率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (205, "盈利能力_财务费用率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (206, "盈利能力_扣除非经常性损益后的净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (207, "盈利能力_息税前利润(EBIT)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (208, "盈利能力_息税折旧摊销前利润(EBITDA)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (209, "盈利能力_EBITDA/营业总收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (210, "资本结构_资产负债率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (211, "资本结构_流动资产比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (212, "资本结构_货币资金比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (213, "资本结构_存货比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (214, "资本结构_固定资产比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (215, "资本结构_负债结构比"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (216, "资本结构_归属于母公司股东权益/全部投入资本"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (217, "资本结构_股东的权益/带息债务"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (218, "资本结构_有形资产/净债务"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (219, "现金能力_每股经营性现金流"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (220, "现金能力_营业收入现金含量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (221, "现金能力_经营活动产生的现金流量净额/经营活动净收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (222, "现金能力_销售商品提供劳务收到的现金/营业收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (223, "现金能力_经营活动产生的现金流量净额/营业收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (224, "现金能力_资本支出/折旧和摊销"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (225, "现金能力_每股现金流量净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (226, "现金能力_经营净现金比率(短期债务)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (227, "现金能力_经营净现金比率(全部债务)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (228, "现金能力_经营活动现金净流量与净利润比率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (229, "现金能力_全部资产现金回收率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (230, "利润表_营业收入_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (231, "利润表_营业利润_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (232, "利润表_归属于母公司所有者的净利润_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (233, "利润表_扣除非经常性损益后的净利润_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (234, "现金流量表_经营活动产生的现金流量净额_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (235, "现金流量表_投资活动产生的现金流量净额_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (236, "现金流量表_筹资活动产生的现金流量净额_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (237, "现金流量表_现金及现金等价物净增加额_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (238, "股本股东_总股本"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (239, "股本股东_已上市流通A股"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (240, "股本股东_已上市流通B股"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (241, "股本股东_已上市流通H股"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (242, "股本股东_股东人数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (243, "股本股东_第一大股东的持股数量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (244, "股本股东_十大流通股东持股数量合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (245, "股本股东_十大股东持股数量合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (246, "股本股东_机构总量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (247, "股本股东_机构持股总量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (248, "股本股东_QFII机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (249, "股本股东_QFII持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (250, "股本股东_券商机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (251, "股本股东_券商持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (252, "股本股东_保险机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (253, "股本股东_保险持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (254, "股本股东_基金机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (255, "股本股东_基金持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (256, "股本股东_社保机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (257, "股本股东_社保持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (258, "股本股东_私募机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (259, "股本股东_私募持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (260, "股本股东_财务公司机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (261, "股本股东_财务公司持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (262, "股本股东_年金机构数"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (263, "股本股东_年金持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (264, "股本股东_十大流通股东中持有A股合计"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (265, "股本股东_第一大流通股东持股量"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (266, "股本股东_自由流通股"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (267, "股本股东_受限流通A股"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (268, "资产负债表_一般风险准备"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (269, "利润表_其他综合收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (270, "利润表_综合收益总额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (271, "资产负债表_归属于母公司股东权益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (272, "股本股东_银行机构数(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (273, "股本股东_银行持股量(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (274, "股本股东_一般法人机构数(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (275, "股本股东_一般法人持股量(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (276, "利润表_近一年净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (277, "股本股东_信托机构数(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (278, "股本股东_信托持股量(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (279, "股本股东_特殊法人机构数(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (280, "股本股东_特殊法人持股量(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (281, "盈利能力_加权净资产收益率(每股指标)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (282, "利润表_扣非每股收益_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (283, "利润表_最近一年营业收入(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (284, "股本股东_国家队持股数量(万股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (285, "业绩预告_本期净利润同比增幅下限%"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (286, "业绩预告_本期净利润同比增幅上限%"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (287, "业绩快报_归母净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (288, "业绩快报_扣非净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (289, "业绩快报_总资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (290, "业绩快报_净资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (291, "业绩快报_每股收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (292, "业绩快报_摊薄净资产收益率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (293, "业绩快报_加权净资产收益率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (294, "业绩快报_每股净资产"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (295, "资产负债表_应付票据及应付账款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (296, "资产负债表_应收票据及应收账款"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (297, "资产负债表_递延收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (298, "资产负债表_其他综合收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (299, "资产负债表_其他权益工具"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (300, "利润表_其他收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (301, "利润表_资产处置收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (302, "利润表_持续经营净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (303, "利润表_终止经营净利润"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (304, "利润表_研发费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (305, "利润表_利息费用"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (306, "利润表_利息收入"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (307, "现金流量表_近一年经营活动现金流净额"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (308, "现金流量表_近一年归母净利润(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (309, "现金流量表_近一年扣非净利润(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (310, "现金流量表_近一年现金净流量(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (311, "利润表_基本每股收益_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (312, "利润表_营业总收入(万元)_单季度"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (313, "业绩预告公告日期 "); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (314, "财报公告日期"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (315, "业绩快报公告日期"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (316, "现金流量表_近一年投资活动现金流净额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (317, "业绩预告_业绩预告-本期净利润下限(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (318, "业绩预告_业绩预告-本期净利润上限(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (319, "利润表_营业总收入TTM(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (320, "员工总数(人)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (321, "现金流量表_每股企业自由现金流"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (322, "现金流量表_每股股东自由现金流"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (323, "近一年营业利润(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (324, "净利润(单季度)(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (325, "北上资金数(家)(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (326, "北上资金持股量(股)(机构持股)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (327, "有息负债率"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (328, "营业成本(单季度)(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (329, "投入资本回报率(ROIC)(获利能力分析)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (330, "业绩快报-营业收入(本期)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (331, "业绩快报-营业收入(上期)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (332, "业绩快报-营业利润(本期)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (333, "业绩快报-营业利润(上期)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (334, "业绩快报-利润总额(本期)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (335, "业绩快报-利润总额(上期)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (336, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (337, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (338, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (339, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (340, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (341, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (342, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (343, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (344, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (345, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (346, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (347, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (348, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (349, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (350, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (351, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (352, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (353, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (354, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (355, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (356, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (357, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (358, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (359, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (360, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (361, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (362, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (363, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (364, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (365, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (366, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (367, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (368, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (369, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (370, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (371, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (372, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (373, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (374, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (375, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (376, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (377, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (378, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (379, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (380, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (381, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (382, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (383, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (384, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (385, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (386, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (387, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (388, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (389, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (390, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (391, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (392, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (393, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (394, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (395, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (396, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (397, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (398, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (399, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (400, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (401, "资产负债表_专项储备(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (402, "资产负债表_结算备付金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (403, "资产负债表_拆出资金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (404, "资产负债表_发放贷款及垫款(万元)(流动资产科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (405, "资产负债表_衍生金融资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (406, "资产负债表_应收保费(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (407, "资产负债表_应收分保账款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (408, "资产负债表_应收分保合同准备金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (409, "资产负债表_买入返售金融资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (410, "资产负债表_划分为持有待售的资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (411, "资产负债表_发放贷款及垫款(万元)(非流动资产科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (412, "资产负债表_向中央银行借款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (413, "资产负债表_吸收存款及同业存放(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (414, "资产负债表_拆入资金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (415, "资产负债表_衍生金融负债(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (416, "资产负债表_卖出回购金融资产款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (417, "资产负债表_应付手续费及佣金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (418, "资产负债表_应付分保账款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (419, "资产负债表_保险合同准备金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (420, "资产负债表_代理买卖证券款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (421, "资产负债表_代理承销证券款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (422, "资产负债表_划分为持有待售的负债(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (423, "资产负债表_预计负债(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (424, "资产负债表_递延收益(万元)(流动负债科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (425, "资产负债表_其中:优先股(万元)(非流动负债科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (426, "资产负债表_永续债(万元)(非流动负债科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (427, "资产负债表_长期应付职工薪酬(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (428, "资产负债表_其中:优先股(万元)(所有者权益科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (429, "资产负债表_永续债(万元)(所有者权益科目)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (430, "资产负债表_债权投资(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (431, "资产负债表_其他债权投资(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (432, "资产负债表_其他权益工具投资(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (433, "资产负债表_其他非流动金融资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (434, "资产负债表_合同负债(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (435, "资产负债表_合同资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (436, "资产负债表_其他资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (437, "资产负债表_应收款项融资(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (438, "资产负债表_使用权资产(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (439, "资产负债表_租赁负债(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (440, "发放贷款及垫款(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (441, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (442, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (443, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (444, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (445, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (446, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (447, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (448, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (449, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (450, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (451, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (452, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (453, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (454, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (455, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (456, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (457, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (458, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (459, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (460, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (461, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (462, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (463, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (464, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (465, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (466, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (467, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (468, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (469, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (470, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (471, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (472, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (473, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (474, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (475, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (476, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (477, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (478, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (479, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (480, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (481, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (482, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (483, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (484, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (485, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (486, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (487, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (488, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (489, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (490, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (491, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (492, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (493, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (494, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (495, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (496, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (497, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (498, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (499, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (500, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (501, "利润表_稀释每股收益"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (502, "利润表_营业总收入(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (503, "利润表_汇兑收益(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (504, "利润表_其中:归属于母公司综合收益(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (505, "利润表_其中:归属于少数股东综合收益(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (506, "利润表_利息收入(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (507, "利润表_已赚保费(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (508, "利润表_手续费及佣金收入(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (509, "利润表_利息支出(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (510, "利润表_手续费及佣金支出(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (511, "利润表_退保金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (512, "利润表_赔付支出净额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (513, "利润表_提取保险合同准备金净额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (514, "利润表_保单红利支出(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (515, "利润表_分保费用(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (516, "利润表_其中:非流动资产处置利得(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (517, "利润表_信用减值损失(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (518, "利润表_净敞口套期收益(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (519, "利润表_营业总成本(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (520, "利润表_信用减值损失(万元、2019格式)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (521, "利润表_资产减值损失(万元、2019格式)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (522, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (523, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (524, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (525, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (526, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (527, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (528, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (529, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (530, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (531, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (532, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (533, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (534, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (535, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (536, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (537, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (538, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (539, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (540, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (541, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (542, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (543, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (544, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (545, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (546, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (547, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (548, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (549, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (550, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (551, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (552, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (553, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (554, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (555, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (556, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (557, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (558, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (559, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (560, "未知"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (561, "现金流量表_加:其他原因对现金的影响(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (562, "现金流量表_客户存款和同业存放款项净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (563, "现金流量表_向中央银行借款净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (564, "现金流量表_向其他金融机构拆入资金净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (565, "现金流量表_收到原保险合同保费取得的现金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (566, "现金流量表_收到再保险业务现金净额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (567, "现金流量表_保户储金及投资款净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (568, "现金流量表_处置以公允价值计量且其变动计入当期损益的金融资产净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (569, "现金流量表_收取利息、手续费及佣金的现金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (570, "现金流量表_拆入资金净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (571, "现金流量表_回购业务资金净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (572, "现金流量表_客户贷款及垫款净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (573, "现金流量表_存放中央银行和同业款项净增加额(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (574, "现金流量表_支付原保险合同赔付款项的现金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (575, "现金流量表_支付利息、手续费及佣金的现金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (576, "现金流量表_支付保单红利的现金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (577, "现金流量表_其中:子公司吸收少数股东投资收到的现金(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (578, "现金流量表_其中:子公司支付给少数股东的股利、利润(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (579, "现金流量表_投资性房地产的折旧及摊销(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (580, "现金流量表_信用减值损失(万元)"); +INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (581, "使用权资产折旧(万元)"); + +UPDATE `hku_base`.`version` set `version` = 13; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0014.sql b/hikyuu/data/sqlite_upgrade/0014.sql new file mode 100644 index 00000000..7ab5eb55 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0014.sql @@ -0,0 +1,594 @@ +BEGIN TRANSACTION; + +CREATE TABLE + IF NOT EXISTS `HistoryFinanceField` ( + `id` INTEGER, + `name` VARCHAR(200) NOT NULL, + PRIMARY KEY(`id`) + ); + +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (1, "基本每股收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (2, "扣除非经常性损益每股收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (3, "每股未分配利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (4, "每股净资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (5, "每股资本公积金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (6, "净资产收益率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (7, "每股经营现金流量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (8, "资产负债表_货币资金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (9, "资产负债表_交易性金融资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (10, "资产负债表_应收票据"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (11, "资产负债表_应收账款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (12, "资产负债表_预付款项"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (13, "资产负债表_其他应收款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (14, "资产负债表_应收关联公司款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (15, "资产负债表_应收利息"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (16, "资产负债表_应收股利"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (17, "资产负债表_存货"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (18, "资产负债表_消耗性生物资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (19, "资产负债表_一年内到期的非流动资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (20, "资产负债表_其他流动资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (21, "资产负债表_流动资产合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (22, "资产负债表_可供出售金融资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (23, "资产负债表_持有至到期投资"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (24, "资产负债表_长期应收款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (25, "资产负债表_长期股权投资"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (26, "资产负债表_投资性房地产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (27, "资产负债表_固定资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (28, "资产负债表_在建工程"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (29, "资产负债表_工程物资"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (30, "资产负债表_固定资产清理"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (31, "资产负债表_生产性生物资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (32, "资产负债表_油气资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (33, "资产负债表_无形资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (34, "资产负债表_开发支出"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (35, "资产负债表_商誉"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (36, "资产负债表_长期待摊费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (37, "资产负债表_递延所得税资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (38, "资产负债表_其他非流动资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (39, "资产负债表_非流动资产合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (40, "资产负债表_资产总计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (41, "资产负债表_短期借款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (42, "资产负债表_交易性金融负债"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (43, "资产负债表_应付票据"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (44, "资产负债表_应付账款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (45, "资产负债表_预收款项"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (46, "资产负债表_应付职工薪酬"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (47, "资产负债表_应交税费"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (48, "资产负债表_应付利息"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (49, "资产负债表_应付股利"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (50, "资产负债表_其他应付款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (51, "资产负债表_应付关联公司款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (52, "资产负债表_一年内到期的非流动负债"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (53, "资产负债表_其他流动负债"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (54, "资产负债表_流动负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (55, "资产负债表_长期借款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (56, "资产负债表_应付债券"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (57, "资产负债表_长期应付款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (58, "资产负债表_专项应付款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (59, "资产负债表_预计负债"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (60, "资产负债表_递延所得税负债"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (61, "资产负债表_其他非流动负债"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (62, "资产负债表_非流动负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (63, "资产负债表_负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (64, "资产负债表_实收资本(或股本)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (65, "资产负债表_资本公积"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (66, "资产负债表_盈余公积"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (67, "资产负债表_库存股"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (68, "资产负债表_未分配利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (69, "资产负债表_少数股东权益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (70, "资产负债表_外币报表折算价差"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (71, "资产负债表_非正常经营项目收益调整"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (72, "资产负债表_所有者权益(或股东权益)合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (73, "资产负债表_负债和所有者(或股东权益)合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (74, "利润表_营业收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (75, "利润表_营业成本"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (76, "利润表_营业税金及附加"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (77, "利润表_销售费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (78, "利润表_管理费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (79, "利润表_勘探费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (80, "利润表_财务费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (81, "利润表_资产减值损失"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (82, "利润表_公允价值变动净收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (83, "利润表_投资收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (84, "利润表_对联营企业和合营企业的投资收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (85, "利润表_影响营业利润的其他科目"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (86, "利润表_营业利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (87, "利润表_补贴收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (88, "利润表_营业外收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (89, "利润表_营业外支出"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (90, "利润表_非流动资产处置净损失"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (91, "利润表_影响利润总额的其他科目"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (92, "利润表_利润总额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (93, "利润表_所得税"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (94, "利润表_影响净利润的其他科目"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (95, "利润表_净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (96, "利润表_归属于母公司所有者的净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (97, "利润表_少数股东损益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (98, "现金流量表_销售商品、提供劳务收到的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (99, "现金流量表_收到的税费返还"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (100, "现金流量表_收到其他与经营活动有关的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (101, "现金流量表_经营活动现金流入小计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (102, "现金流量表_购买商品、接受劳务支付的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (103, "现金流量表_支付给职工以及为职工支付的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (104, "现金流量表_支付的各项税费"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (105, "现金流量表_支付其他与经营活动有关的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (106, "现金流量表_经营活动现金流出小计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (107, "现金流量表_经营活动产生的现金流量净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (108, "现金流量表_收回投资收到的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (109, "现金流量表_取得投资收益收到的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (110, "现金流量表_处置固定资产、无形资产和其他长期资产收回的现金净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (111, "现金流量表_处置子公司及其他营业单位收到的现金净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (112, "现金流量表_收到其他与投资活动有关的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (113, "现金流量表_投资活动现金流入小计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (114, "现金流量表_购建固定资产、无形资产和其他长期资产支付的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (115, "现金流量表_投资支付的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (116, "现金流量表_取得子公司及其他营业单位支付的现金净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (117, "现金流量表_支付其他与投资活动有关的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (118, "现金流量表_投资活动现金流出小计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (119, "现金流量表_投资活动产生的现金流量净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (120, "现金流量表_吸收投资收到的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (121, "现金流量表_取得借款收到的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (122, "现金流量表_收到其他与筹资活动有关的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (123, "现金流量表_筹资活动现金流入小计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (124, "现金流量表_偿还债务支付的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (125, "现金流量表_分配股利、利润或偿付利息支付的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (126, "现金流量表_支付其他与筹资活动有关的现金"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (127, "现金流量表_筹资活动现金流出小计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (128, "现金流量表_筹资活动产生的现金流量净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (129, "现金流量表_汇率变动对现金的影响"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (130, "现金流量表_其他原因对现金的影响"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (131, "现金流量表_现金及现金等价物净增加额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (132, "现金流量表_期初现金及现金等价物余额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (133, "现金流量表_期末现金及现金等价物余额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (134, "现金流量表_净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (135, "现金流量表_资产减值准备"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (136, "现金流量表_固定资产折旧、油气资产折耗、生产性生物资产折旧"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (137, "现金流量表_无形资产摊销"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (138, "现金流量表_长期待摊费用摊销"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (139, "现金流量表_处置固定资产、无形资产和其他长期资产的损失"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (140, "现金流量表_固定资产报废损失"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (141, "现金流量表_公允价值变动损失"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (142, "现金流量表_财务费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (143, "现金流量表_投资损失"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (144, "现金流量表_递延所得税资产减少"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (145, "现金流量表_递延所得税负债增加"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (146, "现金流量表_存货的减少"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (147, "现金流量表_经营性应收项目的减少"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (148, "现金流量表_经营性应付项目的增加"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (149, "现金流量表_其他"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (150, "现金流量表_经营活动产生的现金流量净额2"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (151, "现金流量表_债务转为资本"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (152, "现金流量表_一年内到期的可转换公司债券"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (153, "现金流量表_融资租入固定资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (154, "现金流量表_现金的期末余额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (155, "现金流量表_现金的期初余额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (156, "现金流量表_现金等价物的期末余额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (157, "现金流量表_现金等价物的期初余额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (158, "现金流量表_现金及现金等价物净增加额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (159, "偿债能力_流动比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (160, "偿债能力_速动比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (161, "偿债能力_现金比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (162, "偿债能力_利息保障倍数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (163, "偿债能力_非流动负债比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (164, "偿债能力_流动负债比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (165, "偿债能力_现金到期债务比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (166, "偿债能力_有形资产净值债务率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (167, "偿债能力_权益乘数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (168, "偿债能力_股东的权益/负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (169, "偿债能力_有形资产/负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (170, "偿债能力_经营活动产生的现金流量净额/负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (171, "偿债能力_EBITDA/负债合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (172, "营运能力_应收帐款周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (173, "营运能力_存货周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (174, "营运能力_运营资金周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (175, "营运能力_总资产周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (176, "营运能力_固定资产周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (177, "营运能力_应收帐款周转天数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (178, "营运能力_存货周转天数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (179, "营运能力_流动资产周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (180, "营运能力_流动资产周转天数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (181, "营运能力_总资产周转天数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (182, "营运能力_股东权益周转率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (183, "成长能力_营业收入增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (184, "成长能力_净利润增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (185, "成长能力_净资产增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (186, "成长能力_固定资产增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (187, "成长能力_总资产增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (188, "成长能力_投资收益增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (189, "成长能力_营业利润增长率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (190, "成长能力_扣非每股收益同比"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (191, "成长能力_扣非净利润同比"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (192, "成长能力_暂无"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (193, "盈利能力_成本费用利润率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (194, "盈利能力_营业利润率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (195, "盈利能力_营业税金率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (196, "盈利能力_营业成本率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (197, "盈利能力_净资产收益率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (198, "盈利能力_投资收益率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (199, "盈利能力_销售净利率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (200, "盈利能力_总资产报酬率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (201, "盈利能力_净利润率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (202, "盈利能力_销售毛利率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (203, "盈利能力_三费比重"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (204, "盈利能力_管理费用率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (205, "盈利能力_财务费用率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (206, "盈利能力_扣除非经常性损益后的净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (207, "盈利能力_息税前利润(EBIT)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (208, "盈利能力_息税折旧摊销前利润(EBITDA)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (209, "盈利能力_EBITDA/营业总收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (210, "资本结构_资产负债率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (211, "资本结构_流动资产比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (212, "资本结构_货币资金比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (213, "资本结构_存货比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (214, "资本结构_固定资产比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (215, "资本结构_负债结构比"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (216, "资本结构_归属于母公司股东权益/全部投入资本"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (217, "资本结构_股东的权益/带息债务"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (218, "资本结构_有形资产/净债务"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (219, "现金能力_每股经营性现金流"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (220, "现金能力_营业收入现金含量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (221, "现金能力_经营活动产生的现金流量净额/经营活动净收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (222, "现金能力_销售商品提供劳务收到的现金/营业收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (223, "现金能力_经营活动产生的现金流量净额/营业收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (224, "现金能力_资本支出/折旧和摊销"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (225, "现金能力_每股现金流量净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (226, "现金能力_经营净现金比率(短期债务)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (227, "现金能力_经营净现金比率(全部债务)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (228, "现金能力_经营活动现金净流量与净利润比率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (229, "现金能力_全部资产现金回收率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (230, "利润表_营业收入_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (231, "利润表_营业利润_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (232, "利润表_归属于母公司所有者的净利润_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (233, "利润表_扣除非经常性损益后的净利润_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (234, "现金流量表_经营活动产生的现金流量净额_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (235, "现金流量表_投资活动产生的现金流量净额_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (236, "现金流量表_筹资活动产生的现金流量净额_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (237, "现金流量表_现金及现金等价物净增加额_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (238, "股本股东_总股本"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (239, "股本股东_已上市流通A股"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (240, "股本股东_已上市流通B股"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (241, "股本股东_已上市流通H股"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (242, "股本股东_股东人数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (243, "股本股东_第一大股东的持股数量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (244, "股本股东_十大流通股东持股数量合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (245, "股本股东_十大股东持股数量合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (246, "股本股东_机构总量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (247, "股本股东_机构持股总量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (248, "股本股东_QFII机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (249, "股本股东_QFII持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (250, "股本股东_券商机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (251, "股本股东_券商持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (252, "股本股东_保险机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (253, "股本股东_保险持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (254, "股本股东_基金机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (255, "股本股东_基金持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (256, "股本股东_社保机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (257, "股本股东_社保持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (258, "股本股东_私募机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (259, "股本股东_私募持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (260, "股本股东_财务公司机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (261, "股本股东_财务公司持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (262, "股本股东_年金机构数"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (263, "股本股东_年金持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (264, "股本股东_十大流通股东中持有A股合计"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (265, "股本股东_第一大流通股东持股量"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (266, "股本股东_自由流通股"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (267, "股本股东_受限流通A股"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (268, "资产负债表_一般风险准备"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (269, "利润表_其他综合收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (270, "利润表_综合收益总额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (271, "资产负债表_归属于母公司股东权益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (272, "股本股东_银行机构数(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (273, "股本股东_银行持股量(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (274, "股本股东_一般法人机构数(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (275, "股本股东_一般法人持股量(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (276, "利润表_近一年净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (277, "股本股东_信托机构数(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (278, "股本股东_信托持股量(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (279, "股本股东_特殊法人机构数(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (280, "股本股东_特殊法人持股量(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (281, "盈利能力_加权净资产收益率(每股指标)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (282, "利润表_扣非每股收益_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (283, "利润表_最近一年营业收入(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (284, "股本股东_国家队持股数量(万股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (285, "业绩预告_本期净利润同比增幅下限%"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (286, "业绩预告_本期净利润同比增幅上限%"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (287, "业绩快报_归母净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (288, "业绩快报_扣非净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (289, "业绩快报_总资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (290, "业绩快报_净资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (291, "业绩快报_每股收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (292, "业绩快报_摊薄净资产收益率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (293, "业绩快报_加权净资产收益率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (294, "业绩快报_每股净资产"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (295, "资产负债表_应付票据及应付账款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (296, "资产负债表_应收票据及应收账款"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (297, "资产负债表_递延收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (298, "资产负债表_其他综合收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (299, "资产负债表_其他权益工具"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (300, "利润表_其他收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (301, "利润表_资产处置收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (302, "利润表_持续经营净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (303, "利润表_终止经营净利润"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (304, "利润表_研发费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (305, "利润表_利息费用"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (306, "利润表_利息收入"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (307, "现金流量表_近一年经营活动现金流净额"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (308, "现金流量表_近一年归母净利润(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (309, "现金流量表_近一年扣非净利润(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (310, "现金流量表_近一年现金净流量(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (311, "利润表_基本每股收益_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (312, "利润表_营业总收入(万元)_单季度"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (313, "业绩预告公告日期 "); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (314, "财报公告日期"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (315, "业绩快报公告日期"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (316, "现金流量表_近一年投资活动现金流净额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (317, "业绩预告_业绩预告-本期净利润下限(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (318, "业绩预告_业绩预告-本期净利润上限(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (319, "利润表_营业总收入TTM(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (320, "员工总数(人)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (321, "现金流量表_每股企业自由现金流"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (322, "现金流量表_每股股东自由现金流"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (323, "近一年营业利润(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (324, "净利润(单季度)(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (325, "北上资金数(家)(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (326, "北上资金持股量(股)(机构持股)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (327, "有息负债率"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (328, "营业成本(单季度)(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (329, "投入资本回报率(ROIC)(获利能力分析)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (330, "业绩快报-营业收入(本期)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (331, "业绩快报-营业收入(上期)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (332, "业绩快报-营业利润(本期)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (333, "业绩快报-营业利润(上期)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (334, "业绩快报-利润总额(本期)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (335, "业绩快报-利润总额(上期)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (336, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (337, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (338, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (339, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (340, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (341, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (342, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (343, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (344, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (345, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (346, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (347, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (348, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (349, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (350, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (351, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (352, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (353, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (354, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (355, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (356, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (357, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (358, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (359, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (360, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (361, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (362, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (363, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (364, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (365, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (366, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (367, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (368, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (369, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (370, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (371, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (372, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (373, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (374, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (375, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (376, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (377, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (378, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (379, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (380, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (381, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (382, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (383, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (384, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (385, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (386, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (387, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (388, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (389, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (390, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (391, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (392, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (393, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (394, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (395, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (396, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (397, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (398, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (399, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (400, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (401, "资产负债表_专项储备(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (402, "资产负债表_结算备付金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (403, "资产负债表_拆出资金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (404, "资产负债表_发放贷款及垫款(万元)(流动资产科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (405, "资产负债表_衍生金融资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (406, "资产负债表_应收保费(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (407, "资产负债表_应收分保账款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (408, "资产负债表_应收分保合同准备金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (409, "资产负债表_买入返售金融资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (410, "资产负债表_划分为持有待售的资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (411, "资产负债表_发放贷款及垫款(万元)(非流动资产科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (412, "资产负债表_向中央银行借款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (413, "资产负债表_吸收存款及同业存放(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (414, "资产负债表_拆入资金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (415, "资产负债表_衍生金融负债(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (416, "资产负债表_卖出回购金融资产款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (417, "资产负债表_应付手续费及佣金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (418, "资产负债表_应付分保账款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (419, "资产负债表_保险合同准备金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (420, "资产负债表_代理买卖证券款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (421, "资产负债表_代理承销证券款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (422, "资产负债表_划分为持有待售的负债(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (423, "资产负债表_预计负债(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (424, "资产负债表_递延收益(万元)(流动负债科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (425, "资产负债表_其中:优先股(万元)(非流动负债科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (426, "资产负债表_永续债(万元)(非流动负债科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (427, "资产负债表_长期应付职工薪酬(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (428, "资产负债表_其中:优先股(万元)(所有者权益科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (429, "资产负债表_永续债(万元)(所有者权益科目)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (430, "资产负债表_债权投资(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (431, "资产负债表_其他债权投资(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (432, "资产负债表_其他权益工具投资(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (433, "资产负债表_其他非流动金融资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (434, "资产负债表_合同负债(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (435, "资产负债表_合同资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (436, "资产负债表_其他资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (437, "资产负债表_应收款项融资(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (438, "资产负债表_使用权资产(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (439, "资产负债表_租赁负债(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (440, "发放贷款及垫款(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (441, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (442, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (443, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (444, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (445, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (446, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (447, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (448, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (449, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (450, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (451, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (452, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (453, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (454, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (455, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (456, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (457, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (458, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (459, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (460, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (461, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (462, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (463, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (464, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (465, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (466, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (467, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (468, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (469, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (470, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (471, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (472, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (473, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (474, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (475, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (476, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (477, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (478, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (479, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (480, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (481, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (482, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (483, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (484, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (485, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (486, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (487, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (488, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (489, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (490, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (491, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (492, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (493, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (494, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (495, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (496, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (497, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (498, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (499, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (500, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (501, "利润表_稀释每股收益"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (502, "利润表_营业总收入(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (503, "利润表_汇兑收益(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (504, "利润表_其中:归属于母公司综合收益(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (505, "利润表_其中:归属于少数股东综合收益(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (506, "利润表_利息收入(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (507, "利润表_已赚保费(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (508, "利润表_手续费及佣金收入(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (509, "利润表_利息支出(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (510, "利润表_手续费及佣金支出(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (511, "利润表_退保金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (512, "利润表_赔付支出净额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (513, "利润表_提取保险合同准备金净额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (514, "利润表_保单红利支出(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (515, "利润表_分保费用(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (516, "利润表_其中:非流动资产处置利得(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (517, "利润表_信用减值损失(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (518, "利润表_净敞口套期收益(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (519, "利润表_营业总成本(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (520, "利润表_信用减值损失(万元、2019格式)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (521, "利润表_资产减值损失(万元、2019格式)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (522, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (523, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (524, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (525, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (526, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (527, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (528, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (529, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (530, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (531, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (532, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (533, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (534, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (535, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (536, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (537, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (538, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (539, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (540, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (541, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (542, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (543, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (544, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (545, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (546, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (547, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (548, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (549, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (550, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (551, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (552, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (553, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (554, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (555, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (556, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (557, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (558, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (559, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (560, "未知"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (561, "现金流量表_加:其他原因对现金的影响(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (562, "现金流量表_客户存款和同业存放款项净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (563, "现金流量表_向中央银行借款净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (564, "现金流量表_向其他金融机构拆入资金净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (565, "现金流量表_收到原保险合同保费取得的现金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (566, "现金流量表_收到再保险业务现金净额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (567, "现金流量表_保户储金及投资款净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (568, "现金流量表_处置以公允价值计量且其变动计入当期损益的金融资产净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (569, "现金流量表_收取利息、手续费及佣金的现金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (570, "现金流量表_拆入资金净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (571, "现金流量表_回购业务资金净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (572, "现金流量表_客户贷款及垫款净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (573, "现金流量表_存放中央银行和同业款项净增加额(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (574, "现金流量表_支付原保险合同赔付款项的现金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (575, "现金流量表_支付利息、手续费及佣金的现金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (576, "现金流量表_支付保单红利的现金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (577, "现金流量表_其中:子公司吸收少数股东投资收到的现金(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (578, "现金流量表_其中:子公司支付给少数股东的股利、利润(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (579, "现金流量表_投资性房地产的折旧及摊销(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (580, "现金流量表_信用减值损失(万元)"); +INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (581, "使用权资产折旧(万元)"); + +UPDATE `version` set `version` = 14; + +COMMIT; \ No newline at end of file From 8f150653c764311bb5fb2e3ca0347dd2a52aad14 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 03:34:47 +0800 Subject: [PATCH 187/601] =?UTF-8?q?mysql=20=E5=AF=BC=E5=85=A5=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E8=B4=A2=E5=8A=A1=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 50 +++++++++++++++++++++ hikyuu/data/mysql_upgrade/0013.sql | 14 +++++- hikyuu/data/pytdx_finance_to_mysql.py | 22 ++++++--- hikyuu/gui/data/ImportHistoryFinanceTask.py | 37 ++++++++------- hikyuu/gui/data/UsePytdxImportToH5Thread.py | 2 +- 5 files changed, 103 insertions(+), 22 deletions(-) diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index 4a533b80..025cc856 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -191,3 +191,53 @@ def get_china_bond10_rate(start_date="19901219"): bond_zh_us_rate_df = ak.bond_zh_us_rate(start_date) df = bond_zh_us_rate_df[['中国国债收益率10年', '日期']].dropna() return [(v[1].strftime('%Y%m%d'), int(v[0]*10000)) for v in df.values] + + +def modifiy_code(code): + if code.startswith(('0', '3')): + return 'SZ' + code + if code.startswith(('4', '8')): + return 'BJ' + code + if code.startswith('6'): + return 'SH' + code + else: + hku_warn("Unknow code: {}", code) + return None + + +def historyfinancialreader(filepath): + """ + 读取解析通达信目录的历史财务数据(来源: onefish, 公众号:一鱼策略) + :param filepath: 字符串类型。传入文件路径 + :return: DataFrame格式。返回解析出的财务文件内容 + """ + import struct + + cw_file = open(filepath, 'rb') + header_pack_format = '<1hI1H3L' + header_size = struct.calcsize(header_pack_format) + stock_item_size = struct.calcsize("<6s1c1L") + data_header = cw_file.read(header_size) + stock_header = struct.unpack(header_pack_format, data_header) + max_count = stock_header[2] + file_date = stock_header[1] + print(file_date) + report_size = stock_header[4] + report_fields_count = int(report_size / 4) + report_pack_format = '<{}f'.format(report_fields_count) + results = [] + for stock_idx in range(0, max_count): + cw_file.seek(header_size + stock_idx * struct.calcsize("<6s1c1L")) + si = cw_file.read(stock_item_size) + stock_item = struct.unpack("<6s1c1L", si) + code = stock_item[0].decode("utf-8") + foa = stock_item[2] + cw_file.seek(foa) + info_data = cw_file.read(struct.calcsize(report_pack_format)) + cw_info = list(struct.unpack(report_pack_format, info_data)) + report_date = int(cw_info[313]) # 财务公告日期 + report_date = 19000000 + report_date if report_date > 800000 else 20000000 + report_date + # results.append((modifiy_code(code), report_date, cw_info)) + results.append((file_date, modifiy_code(code), report_date, info_data)) + cw_file.close() + return results diff --git a/hikyuu/data/mysql_upgrade/0013.sql b/hikyuu/data/mysql_upgrade/0013.sql index f1d4b51e..65968e69 100644 --- a/hikyuu/data/mysql_upgrade/0013.sql +++ b/hikyuu/data/mysql_upgrade/0013.sql @@ -3,7 +3,19 @@ CREATE TABLE `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(200) NOT NULL, PRIMARY KEY(`id`) - ) COLLATE = 'utf8_general_ci' ENGINE = InnoDB; + ) COLLATE = 'utf8_general_ci' ENGINE = MyISAM; + +CREATE TABLE + IF NOT EXISTS `hku_base`.`HistoryFinance` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `file_date` INT UNSIGNED NOT NULL, + `market_code` VARCHAR(60) NOT NULL, + `report_date` INT UNSIGNED NOT NULL, + `values` BLOB NOT NULL, + PRIMARY KEY (`id`), + INDEX `ix1_on_history_finance` (`file_date`), + INDEX `ix2_on_history_finance` (`market_code`, `report_date`) + ) COLLATE = 'utf8_general_ci' ENGINE = MyISAM; INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (1, "基本每股收益"); INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (2, "扣除非经常性损益每股收益"); diff --git a/hikyuu/data/pytdx_finance_to_mysql.py b/hikyuu/data/pytdx_finance_to_mysql.py index d1bf3926..13b1638c 100644 --- a/hikyuu/data/pytdx_finance_to_mysql.py +++ b/hikyuu/data/pytdx_finance_to_mysql.py @@ -22,10 +22,11 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from hikyuu.data.common import MARKETID, STOCKTYPE +from hikyuu.data.common import MARKETID, STOCKTYPE, historyfinancialreader from hikyuu.data.common_mysql import get_marketid from hikyuu.util import * + @hku_catch(trace=True) def pytdx_import_finance_to_mysql(db_connect, pytdx_connect, market): """导入公司财务信息""" @@ -41,7 +42,7 @@ def pytdx_import_finance_to_mysql(db_connect, pytdx_connect, market): records = [] for stk in all_list: x = pytdx_connect.get_finance_info(1 if stk[1] == MARKETID.SH else 0, stk[2]) - #print(stk[2]) + # print(stk[2]) if x is not None and x['code'] == stk[2]: cur.execute( "select updated_date from `hku_base`.`stkfinance` where stockid={} and updated_date={}".format( @@ -51,9 +52,9 @@ def pytdx_import_finance_to_mysql(db_connect, pytdx_connect, market): a = cur.fetchall() a = [x[0] for x in a] if a: - #print(a) + # print(a) continue - #else: + # else: # print(market, stk[2]) records.append( ( @@ -114,4 +115,15 @@ def pytdx_import_finance_to_mysql(db_connect, pytdx_connect, market): db_connect.commit() cur.close() - return len(records) \ No newline at end of file + return len(records) + + +def history_finance_import_mysql(connect, filename): + file_date = filename[-12:-4] + ret = historyfinancialreader(filename) + cur = connect.cursor() + cur.execute(f"delete from `hku_base`.`HistoryFinance` where file_date={file_date}") + cur.executemany( + "insert into `hku_base`.`HistoryFinance` (`file_date`, `market_code`, `report_date`, `values`) values (%s, %s, %s, %s)", ret) + cur.close() + connect.commit() diff --git a/hikyuu/gui/data/ImportHistoryFinanceTask.py b/hikyuu/gui/data/ImportHistoryFinanceTask.py index e4ac2007..06bbaa71 100644 --- a/hikyuu/gui/data/ImportHistoryFinanceTask.py +++ b/hikyuu/gui/data/ImportHistoryFinanceTask.py @@ -26,13 +26,15 @@ import os import shutil import hashlib from pytdx.hq import TdxHq_API +from hikyuu.data.pytdx_finance_to_mysql import history_finance_import_mysql from hikyuu.util import * class ImportHistoryFinanceTask: - def __init__(self, log_queue, queue, dest_dir): + def __init__(self, log_queue, queue, config, dest_dir): self.log_queue = log_queue self.queue = queue + self.config = config self.dest_dir = dest_dir + '/downloads/finance' if not os.path.lexists(self.dest_dir): os.makedirs(self.dest_dir) @@ -55,19 +57,23 @@ class ImportHistoryFinanceTask: return [l2d(i.strip().split(',')) for i in content] - def download(self): - data_list = self.get_list_info() - for item in data_list: - dest_file = '{}/{}'.format(self.dest_dir, item['filename']) - if not os.path.exists(dest_file): - self.download_file(item) - else: - new_md5 = '' - with open(dest_file, 'rb') as f: - new_md5 = hashlib.md5(f.read()).hexdigest() - if new_md5 != item['hash']: - #print(dest_file, new_md5, item['hash']) - self.download_file(item) + def import_to_db(self, filename): + use_mysql = False + if self.config is not None and not self.config.getboolean('hdf5', 'enable', fallback=True): + use_mysql = True + if not use_mysql: + return + + db_config = { + 'user': self.config['mysql']['usr'], + 'password': self.config['mysql']['pwd'], + 'host': self.config['mysql']['host'], + 'port': self.config['mysql']['port'] + } + import mysql.connector + connect = mysql.connector.connect(**db_config) + history_finance_import_mysql(connect, filename) + connect.close() def download_file(self, item): filename = item['filename'] @@ -79,6 +85,7 @@ class ImportHistoryFinanceTask: f.write(data) shutil.unpack_archive(dest_file_name, extract_dir=self.dest_dir) hku_info(f"Download finance file: {filename}") + self.import_to_db(f'{self.dest_dir}/{filename[0:-4]}.dat') @hku_catch(trace=True) def __call__(self): @@ -108,6 +115,6 @@ class ImportHistoryFinanceTask: if __name__ == "__main__": - task = ImportHistoryFinanceTask(None, "c:\\stock") + task = ImportHistoryFinanceTask(None, None, None, "c:\\stock") task() print("over!") diff --git a/hikyuu/gui/data/UsePytdxImportToH5Thread.py b/hikyuu/gui/data/UsePytdxImportToH5Thread.py index 1b8d8a7a..ee0b4f02 100644 --- a/hikyuu/gui/data/UsePytdxImportToH5Thread.py +++ b/hikyuu/gui/data/UsePytdxImportToH5Thread.py @@ -94,7 +94,7 @@ class UsePytdxImportToH5Thread(QThread): if self.config.getboolean('finance', 'enable', fallback=True): self.tasks.append( - ImportHistoryFinanceTask(self.log_queue, self.queue, dest_dir)) + ImportHistoryFinanceTask(self.log_queue, self.queue, self.config, dest_dir)) self.tasks.append(ImportBlockInfoTask(self.log_queue, self.queue, self.config, ('行业板块', '概念板块', '地域板块', '指数板块'))) From b0fb33ecc1dc7114f7b81fafbff67e05af626deb Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 13:56:26 +0800 Subject: [PATCH 188/601] =?UTF-8?q?sqlite=20=E5=AF=BC=E5=85=A5=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E8=B4=A2=E5=8A=A1=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/pytdx_finance_to_mysql.py | 2 +- hikyuu/data/pytdx_finance_to_sqlite.py | 23 +++++++++---- hikyuu/data/sqlite_upgrade/0014.sql | 13 +++++++ hikyuu/gui/data/ImportHistoryFinanceTask.py | 38 +++++++++++++-------- 4 files changed, 54 insertions(+), 22 deletions(-) diff --git a/hikyuu/data/pytdx_finance_to_mysql.py b/hikyuu/data/pytdx_finance_to_mysql.py index 13b1638c..5aa98658 100644 --- a/hikyuu/data/pytdx_finance_to_mysql.py +++ b/hikyuu/data/pytdx_finance_to_mysql.py @@ -125,5 +125,5 @@ def history_finance_import_mysql(connect, filename): cur.execute(f"delete from `hku_base`.`HistoryFinance` where file_date={file_date}") cur.executemany( "insert into `hku_base`.`HistoryFinance` (`file_date`, `market_code`, `report_date`, `values`) values (%s, %s, %s, %s)", ret) - cur.close() connect.commit() + cur.close() diff --git a/hikyuu/data/pytdx_finance_to_sqlite.py b/hikyuu/data/pytdx_finance_to_sqlite.py index 359cb2ba..ed82356c 100644 --- a/hikyuu/data/pytdx_finance_to_sqlite.py +++ b/hikyuu/data/pytdx_finance_to_sqlite.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from hikyuu.data.common import MARKETID, STOCKTYPE +from hikyuu.data.common import MARKETID, STOCKTYPE, historyfinancialreader from hikyuu.data.common_sqlite3 import get_marketid, create_database @@ -39,7 +39,7 @@ def pytdx_import_finance_to_sqlite(db_connect, pytdx_connect, market): records = [] for stk in all_list: x = pytdx_connect.get_finance_info(1 if stk[1] == MARKETID.SH else 0, stk[2]) - #print(stk[2]) + # print(stk[2]) if x is not None and x['code'] == stk[2]: cur.execute( "select updated_date from stkfinance where stockid={} and updated_date={}".format( @@ -49,9 +49,9 @@ def pytdx_import_finance_to_sqlite(db_connect, pytdx_connect, market): a = cur.fetchall() a = [x[0] for x in a] if a: - #print(a) + # print(a) continue - #else: + # else: # print(market, stk[2]) records.append( ( @@ -115,6 +115,17 @@ def pytdx_import_finance_to_sqlite(db_connect, pytdx_connect, market): return len(records) +def history_finance_import_sqlite(connect, filename): + file_date = filename[-12:-4] + ret = historyfinancialreader(filename) + cur = connect.cursor() + cur.execute(f"delete from `HistoryFinance` where file_date={file_date}") + cur.executemany( + "insert into `HistoryFinance` (`file_date`, `market_code`, `report_date`, `values`) values (?,?,?,?)", ret) + connect.commit() + cur.close() + + if __name__ == '__main__': import os import time @@ -123,7 +134,7 @@ if __name__ == '__main__': starttime = time.time() dest_dir = "d:\\stock" - tdx_server = '120.76.152.87' #'119.147.212.81' + tdx_server = '120.76.152.87' # '119.147.212.81' tdx_port = 7709 connect = sqlite3.connect(dest_dir + "\\stock.db") @@ -148,4 +159,4 @@ if __name__ == '__main__': endtime = time.time() print("\nTotal time:") print("%.2fs" % (endtime - starttime)) - print("%.2fm" % ((endtime - starttime) / 60)) \ No newline at end of file + print("%.2fm" % ((endtime - starttime) / 60)) diff --git a/hikyuu/data/sqlite_upgrade/0014.sql b/hikyuu/data/sqlite_upgrade/0014.sql index 7ab5eb55..c533945c 100644 --- a/hikyuu/data/sqlite_upgrade/0014.sql +++ b/hikyuu/data/sqlite_upgrade/0014.sql @@ -6,6 +6,19 @@ CREATE TABLE `name` VARCHAR(200) NOT NULL, PRIMARY KEY(`id`) ); + +CREATE TABLE + IF NOT EXISTS `HistoryFinance` ( + `id` INTEGER, + `file_date` INTEGER NOT NULL, + `market_code` TEXT NOT NULL, + `report_date` INTEGER NOT NULL, + `values` BLOB NOT NULL, + PRIMARY KEY (`id` AUTOINCREMENT) + ); + +CREATE INDEX "ix1_on_history_finance" ON "HistoryFinance" (file_date); +CREATE INDEX "ix2_on_history_finance" ON "HistoryFinance" (market_code, report_date); INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (1, "基本每股收益"); INSERT INTO `HistoryFinanceField` (`id`, `name`) VALUES (2, "扣除非经常性损益每股收益"); diff --git a/hikyuu/gui/data/ImportHistoryFinanceTask.py b/hikyuu/gui/data/ImportHistoryFinanceTask.py index 06bbaa71..22073a18 100644 --- a/hikyuu/gui/data/ImportHistoryFinanceTask.py +++ b/hikyuu/gui/data/ImportHistoryFinanceTask.py @@ -25,8 +25,11 @@ import os import shutil import hashlib +import sqlite3 +import mysql.connector from pytdx.hq import TdxHq_API from hikyuu.data.pytdx_finance_to_mysql import history_finance_import_mysql +from hikyuu.data.pytdx_finance_to_sqlite import history_finance_import_sqlite from hikyuu.util import * @@ -58,22 +61,27 @@ class ImportHistoryFinanceTask: return [l2d(i.strip().split(',')) for i in content] def import_to_db(self, filename): - use_mysql = False - if self.config is not None and not self.config.getboolean('hdf5', 'enable', fallback=True): - use_mysql = True - if not use_mysql: - return + if self.config.getboolean('hdf5', 'enable', fallback=True): + sqlite_file = "{}/stock.db".format(self.config['hdf5']['dir']) + connect = sqlite3.connect(sqlite_file, timeout=1800) + history_finance_import = history_finance_import_sqlite + else: + db_config = { + 'user': self.config['mysql']['usr'], + 'password': self.config['mysql']['pwd'], + 'host': self.config['mysql']['host'], + 'port': self.config['mysql']['port'] + } + connect = mysql.connector.connect(**db_config) + history_finance_import = history_finance_import_mysql - db_config = { - 'user': self.config['mysql']['usr'], - 'password': self.config['mysql']['pwd'], - 'host': self.config['mysql']['host'], - 'port': self.config['mysql']['port'] - } - import mysql.connector - connect = mysql.connector.connect(**db_config) - history_finance_import_mysql(connect, filename) - connect.close() + try: + history_finance_import(connect, filename) + except Exception as e: + hku_error(str(e)) + finally: + connect.commit() + connect.close() def download_file(self, item): filename = item['filename'] From 4879f9a1f36ec074c04a50a100223a2e72890ef8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 14:23:42 +0800 Subject: [PATCH 189/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E6=96=B9=E5=BC=8F=E6=9D=83=E6=81=AF=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 27 ++++++++++++++----- .../hikyuu/data_driver/BaseInfoDriver.h | 2 +- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 284da471..a794bc4c 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -560,14 +560,27 @@ void StockManager::loadAllHolidays() { void StockManager::loadAllStockWeights() { HKU_INFO("Loading stock weight..."); - auto all_stkweight_dict = m_baseInfoDriver->getAllStockWeightList(); - std::lock_guard lock1(*m_stockDict_mutex); - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - auto weight_iter = all_stkweight_dict.find(iter->first); - if (weight_iter != all_stkweight_dict.end()) { + if (m_context.isAll()) { + auto all_stkweight_dict = m_baseInfoDriver->getAllStockWeightList(); + std::lock_guard lock1(*m_stockDict_mutex); + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { + auto weight_iter = all_stkweight_dict.find(iter->first); + if (weight_iter != all_stkweight_dict.end()) { + Stock& stock = iter->second; + std::lock_guard lock2(stock.m_data->m_weight_mutex); + stock.m_data->m_weightList.swap(weight_iter->second); + } + } + } else { + std::lock_guard lock1(*m_stockDict_mutex); + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { Stock& stock = iter->second; - std::lock_guard lock2(stock.m_data->m_weight_mutex); - stock.m_data->m_weightList.swap(weight_iter->second); + auto sw_list = m_baseInfoDriver->getStockWeightList( + stock.market(), stock.code(), stock.startDatetime(), Null()); + { + std::lock_guard lock2(stock.m_data->m_weight_mutex); + stock.m_data->m_weightList = std::move(sw_list); + } } } } diff --git a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h index cb02a2bd..25a246dc 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h @@ -103,7 +103,7 @@ public: virtual StockInfo getStockInfo(string market, const string& code) = 0; /** - * 获取指定日期范围内 [start, end) 的权限列表 + * 获取指定日期范围内 [start, end) 的权息列表 * @param market 市场简称 * @param code 证券代码 * @param start 起始日期 From 9b1e2d99116666c41086aa640931999ca8fae061 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 14:38:29 +0800 Subject: [PATCH 190/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87=E6=96=B9=E5=BC=8F=E4=B8=8B=E6=9D=83=E6=81=AF=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index a794bc4c..1a381817 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -576,7 +576,7 @@ void StockManager::loadAllStockWeights() { for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { Stock& stock = iter->second; auto sw_list = m_baseInfoDriver->getStockWeightList( - stock.market(), stock.code(), stock.startDatetime(), Null()); + stock.market(), stock.code(), m_context.startDatetime(), Null()); { std::lock_guard lock2(stock.m_data->m_weight_mutex); stock.m_data->m_weightList = std::move(sw_list); From 6a4df74f952153f6df2086185e938bafe4208e10 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 17:16:18 +0800 Subject: [PATCH 191/601] =?UTF-8?q?=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=8A=A0=E8=BD=BD(continue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.h | 3 + hikyuu_cpp/hikyuu/StockManager.cpp | 10 ++++ hikyuu_cpp/hikyuu/StockManager.h | 24 ++++++++ .../hikyuu/data_driver/BaseInfoDriver.h | 20 +++++++ .../base_info/mysql/MySQLBaseInfoDriver.cpp | 20 +++++++ .../base_info/mysql/MySQLBaseInfoDriver.h | 2 + .../base_info/sqlite/SQLiteBaseInfoDriver.cpp | 57 +++++++++++++++++++ .../base_info/sqlite/SQLiteBaseInfoDriver.h | 4 ++ .../table/HistoryFinanceFieldTable.h | 19 +++++++ .../base_info/table/HistoryFinanceTable.h | 23 ++++++++ 10 files changed, 182 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceFieldTable.h create mode 100644 hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 621ac38c..48b45baf 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -245,6 +245,9 @@ struct HKU_API Stock::Data { StockWeightList m_weightList; // 权息信息列表 std::mutex m_weight_mutex; + vector> m_history_finance; // 历史财务信息 [财务报告日期, 字段1, 字段2, ...] + std::mutex m_history_finance_mutex; + price_t m_tick; price_t m_tickValue; price_t m_unit; diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 1a381817..6954352d 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -125,6 +125,7 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa loadAllStocks(); loadAllStockWeights(); loadAllZhBond10(); + loadHistoryFinanceField(); // 获取板块驱动 m_blockDriver = DataDriverFactory::getBlockDriver(blockParam); @@ -273,6 +274,7 @@ void StockManager::reload() { loadAllStocks(); loadAllStockWeights(); loadAllZhBond10(); + loadHistoryFinanceField(); m_blockDriver->load(); @@ -589,4 +591,12 @@ void StockManager::loadAllZhBond10() { m_zh_bond10 = m_baseInfoDriver->getAllZhBond10(); } +void StockManager::loadHistoryFinanceField() { + auto fields = m_baseInfoDriver->getHistoryFinanceField(); + for (const auto& field : fields) { + m_field_ix_to_name[field.first] = field.second; + m_field_name_to_ix[field.second] = field.first; + } +} + } // namespace hku diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 39ae4f28..d129495f 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -161,6 +161,11 @@ public: */ bool isHoliday(const Datetime& d) const; + const string& getHistoryFinanceFieldName(size_t ix) const; + size_t getHistoryFinanceFieldIndex(const string& name) const; + + vector> getHistoryFinance(const Stock& stk, Datetime start, Datetime end); + /** * 添加Stock,仅供临时增加的特殊Stock使用 * @param stock @@ -233,6 +238,9 @@ private: /** 加载10年期中国国债收益率数据 */ void loadAllZhBond10(); + /** 加载历史财经字段索引 */ + void loadHistoryFinanceField(); + private: StockManager(); @@ -261,6 +269,9 @@ private: ZhBond10List m_zh_bond10; // 10年期中国国债收益率数据 + unordered_map m_field_name_to_ix; // 财经字段名称到字段索引映射 + unordered_map m_field_ix_to_name; // 财经字段索引到字段名称映射 + Parameter m_baseInfoDriverParam; Parameter m_blockDriverParam; Parameter m_kdataDriverParam; @@ -309,6 +320,19 @@ inline BaseInfoDriverPtr StockManager::getBaseInfoDriver() const { return m_baseInfoDriver; } +inline const string& StockManager::getHistoryFinanceFieldName(size_t ix) const { + return m_field_ix_to_name.at(ix); +} + +inline size_t StockManager::getHistoryFinanceFieldIndex(const string& name) const { + return m_field_name_to_ix.at(name); +} + +inline vector> StockManager::getHistoryFinance(const Stock& stk, Datetime start, + Datetime end) { + return m_baseInfoDriver->getHistoryFinance(stk.market(), stk.code(), start, end); +} + } // namespace hku #endif /* STOCKMANAGER_H_ */ diff --git a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h index 25a246dc..66a7f256 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h @@ -117,6 +117,26 @@ public: return ret; } + /** + * 获取历史财务信息 + * @param market 市场简称 + * @param code 证券代码 + * @param start 财务报告发布起始日期 + * @return vector [[财务报告发布日期(ymd), 字段1, 字段2, ...], ...] + */ + virtual vector> getHistoryFinance(const string& market, const string& code, + Datetime start, Datetime end) { + return vector>(); + } + + /** + * 获取历史财务信息字段序号与名称 + * @return vector> + */ + virtual vector> getHistoryFinanceField() { + return vector>(); + } + /** * 获取当前财务信息 * @param market 市场标识 diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index 36f27fe1..ce5ed9cc 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -17,6 +17,8 @@ #include "../table/StockTable.h" #include "../table/HolidayTable.h" #include "../table/ZhBond10Table.h" +#include "../table/HistoryFinanceTable.h" +#include "../table/HistoryFinanceFieldTable.h" namespace hku { @@ -368,4 +370,22 @@ ZhBond10List MySQLBaseInfoDriver::getAllZhBond10() { return result; } +vector> MySQLBaseInfoDriver::getHistoryFinanceField() { + vector> result; + auto con = m_pool->getConnect(); + try { + vector fields; + con->batchLoad(fields); + size_t total = fields.size(); + result.resize(total); + for (size_t i = 0; i < total; i++) { + result[i].first = size_t(fields[i].id()); + result[i].second = std::move(fields[i].name); + } + + } catch (...) { + } + return result; +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h index db7e4f34..c2ede8d6 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h @@ -41,6 +41,8 @@ public: virtual std::unordered_set getAllHolidays() override; virtual ZhBond10List getAllZhBond10() override; + virtual vector> getHistoryFinanceField() override; + private: ConnectPool* m_pool; }; diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index e942d62d..646a9d36 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -16,6 +16,8 @@ #include "../table/StockTable.h" #include "../table/HolidayTable.h" #include "../table/ZhBond10Table.h" +#include "../table/HistoryFinanceTable.h" +#include "../table/HistoryFinanceFieldTable.h" namespace hku { @@ -358,4 +360,59 @@ ZhBond10List SQLiteBaseInfoDriver::getAllZhBond10() { return result; } +vector> SQLiteBaseInfoDriver::getHistoryFinanceField() { + vector> result; + auto con = m_pool->getConnect(); + try { + vector fields; + con->batchLoad(fields); + size_t total = fields.size(); + result.resize(total); + for (size_t i = 0; i < total; i++) { + result[i].first = size_t(fields[i].id()); + result[i].second = std::move(fields[i].name); + } + + } catch (...) { + } + return result; +} + +vector> SQLiteBaseInfoDriver::getHistoryFinance(const string& market, + const string& code, Datetime start, + Datetime end) { + vector> result; + + Datetime new_start = start.isNull() ? Datetime::min() : start; + Datetime new_end = end.isNull() ? Datetime::max() : end; + HKU_IF_RETURN(start >= end, result); + + auto con = m_pool->getConnect(); + try { + string market_code(fmt::format("{}{}", market, code)); + to_upper(market_code); + vector finances; + con->batchLoad(finances, ((Field("market_code") == market_code) & + (Field("report_date") >= start.ymd())) + + ASC("report_date")); + size_t total = finances.size(); + result.resize(total); + for (size_t i = 0; i < total; i++) { + const auto& finance = finances[i]; + auto& cur = result[i]; + size_t len = finance.values.size() / sizeof(float); + cur.resize(len + 1); + auto* data = cur.data(); + data[0] = static_cast(finance.report_date); + memcpy(data + 1, finance.values.data(), len * sizeof(float)); + } + + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + } + + return result; +} + } // namespace hku diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h index 1cd63a47..6367c1b2 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h @@ -37,6 +37,10 @@ public: virtual std::unordered_set getAllHolidays() override; virtual ZhBond10List getAllZhBond10() override; + virtual vector> getHistoryFinanceField() override; + virtual vector> getHistoryFinance(const string& market, const string& code, + Datetime start, Datetime end) override; + private: // 股票基本信息数据库实例 ConnectPool* m_pool; diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceFieldTable.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceFieldTable.h new file mode 100644 index 00000000..5626b4d6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceFieldTable.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/utilities/db_connect/TableMacro.h" + +namespace hku { + +struct HistoryFinanceFieldTable { + TABLE_BIND1(HistoryFinanceFieldTable, HistoryFinanceField, name) + std::string name; +}; + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h new file mode 100644 index 00000000..8025f62d --- /dev/null +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/utilities/db_connect/TableMacro.h" + +namespace hku { + +struct HistoryFinanceTable { + TABLE_BIND4(HistoryFinanceTable, HistoryFinanceField, file_date, market_code, report_date, + values) + uint64_t file_date; + uint64_t report_date; + std::string market_code; + std::string values; +}; + +} // namespace hku \ No newline at end of file From 0b93b1835a63932a09fa8407a0de665dc1bf9721 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 13 Apr 2024 18:05:25 +0800 Subject: [PATCH 192/601] =?UTF-8?q?=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp | 26 ++++++++++++++++++ hikyuu_cpp/hikyuu/HistoryFinanceInfo.h | 27 +++++++++++++++++++ hikyuu_cpp/hikyuu/Stock.cpp | 10 +++++++ hikyuu_cpp/hikyuu/Stock.h | 9 +++++-- hikyuu_cpp/hikyuu/StockManager.cpp | 4 +-- hikyuu_cpp/hikyuu/StockManager.h | 6 ++--- .../hikyuu/data_driver/BaseInfoDriver.h | 7 ++--- .../base_info/sqlite/SQLiteBaseInfoDriver.cpp | 16 +++++------ .../base_info/sqlite/SQLiteBaseInfoDriver.h | 4 +-- hikyuu_pywrap/_StockManager.cpp | 10 +++++++ 10 files changed, 99 insertions(+), 20 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp create mode 100644 hikyuu_cpp/hikyuu/HistoryFinanceInfo.h diff --git a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp new file mode 100644 index 00000000..53cd77f7 --- /dev/null +++ b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#include "HistoryFinanceInfo.h" + +namespace hku { + +HistoryFinanceInfo& HistoryFinanceInfo::operator=(const HistoryFinanceInfo& other) { + HKU_IF_RETURN(this == &other, *this); + reportDate = other.reportDate; + values = other.values; + return *this; +} + +HistoryFinanceInfo& HistoryFinanceInfo::operator=(HistoryFinanceInfo&& other) { + HKU_IF_RETURN(this == &other, *this); + reportDate = std::move(other.reportDate); + values = std::move(other.values); + return *this; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h new file mode 100644 index 00000000..d1d5c57c --- /dev/null +++ b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#pragma once + +#include "DataType.h" + +namespace hku { + +struct HKU_API HistoryFinanceInfo { + Datetime reportDate; + vector values; + + HistoryFinanceInfo() = default; + HistoryFinanceInfo(const HistoryFinanceInfo&) = default; + HistoryFinanceInfo(HistoryFinanceInfo&& rv) + : reportDate(std::move(rv.reportDate)), values(std::move(rv.values)) {} + + HistoryFinanceInfo& operator=(const HistoryFinanceInfo&); + HistoryFinanceInfo& operator=(HistoryFinanceInfo&&); +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index d989d8b9..dc0a6a61 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -748,6 +748,16 @@ void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { } } +const vector& Stock::getHistoryFinance() const { + std::lock_guard lock(m_data->m_history_finance_mutex); + if (!m_data->m_history_finance_ready) { + m_data->m_history_finance = + StockManager::instance().getHistoryFinance(*this, Datetime::min(), Null()); + m_data->m_history_finance_ready = true; + } + return m_data->m_history_finance; +} + Stock HKU_API getStock(const string& querystr) { const StockManager& sm = StockManager::instance(); return sm.getStock(querystr); diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 48b45baf..39b5a74f 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -14,6 +14,7 @@ #include "KQuery.h" #include "TimeLineRecord.h" #include "TransRecord.h" +#include "HistoryFinanceInfo.h" namespace hku { @@ -180,6 +181,8 @@ public: */ PriceList getHistoryFinanceInfo(const Datetime& date) const; + const vector& getHistoryFinance() const; + /** 设置权息信息, 仅供初始化时调用 */ void setWeightList(const StockWeightList&); @@ -245,8 +248,10 @@ struct HKU_API Stock::Data { StockWeightList m_weightList; // 权息信息列表 std::mutex m_weight_mutex; - vector> m_history_finance; // 历史财务信息 [财务报告日期, 字段1, 字段2, ...] - std::mutex m_history_finance_mutex; + mutable vector + m_history_finance; // 历史财务信息 [财务报告日期, 字段1, 字段2, ...] + mutable bool m_history_finance_ready{false}; + mutable std::mutex m_history_finance_mutex; price_t m_tick; price_t m_tickValue; diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 6954352d..68fb5743 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -594,8 +594,8 @@ void StockManager::loadAllZhBond10() { void StockManager::loadHistoryFinanceField() { auto fields = m_baseInfoDriver->getHistoryFinanceField(); for (const auto& field : fields) { - m_field_ix_to_name[field.first] = field.second; - m_field_name_to_ix[field.second] = field.first; + m_field_ix_to_name[field.first - 1] = field.second; + m_field_name_to_ix[field.second] = field.first - 1; } } diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index d129495f..b293c1e7 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -164,7 +164,7 @@ public: const string& getHistoryFinanceFieldName(size_t ix) const; size_t getHistoryFinanceFieldIndex(const string& name) const; - vector> getHistoryFinance(const Stock& stk, Datetime start, Datetime end); + vector getHistoryFinance(const Stock& stk, Datetime start, Datetime end); /** * 添加Stock,仅供临时增加的特殊Stock使用 @@ -328,8 +328,8 @@ inline size_t StockManager::getHistoryFinanceFieldIndex(const string& name) cons return m_field_name_to_ix.at(name); } -inline vector> StockManager::getHistoryFinance(const Stock& stk, Datetime start, - Datetime end) { +inline vector StockManager::getHistoryFinance(const Stock& stk, Datetime start, + Datetime end) { return m_baseInfoDriver->getHistoryFinance(stk.market(), stk.code(), start, end); } diff --git a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h index 66a7f256..eaf5cc53 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h @@ -15,6 +15,7 @@ #include "../StockTypeInfo.h" #include "../Stock.h" #include "../ZhBond10.h" +#include "../HistoryFinanceInfo.h" #include "../utilities/db_connect/SQLStatementBase.h" namespace hku { @@ -124,9 +125,9 @@ public: * @param start 财务报告发布起始日期 * @return vector [[财务报告发布日期(ymd), 字段1, 字段2, ...], ...] */ - virtual vector> getHistoryFinance(const string& market, const string& code, - Datetime start, Datetime end) { - return vector>(); + virtual vector getHistoryFinance(const string& market, const string& code, + Datetime start, Datetime end) { + return vector(); } /** diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index 646a9d36..7122c3f9 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -378,10 +378,10 @@ vector> SQLiteBaseInfoDriver::getHistoryFinanceField() return result; } -vector> SQLiteBaseInfoDriver::getHistoryFinance(const string& market, - const string& code, Datetime start, - Datetime end) { - vector> result; +vector SQLiteBaseInfoDriver::getHistoryFinance(const string& market, + const string& code, + Datetime start, Datetime end) { + vector result; Datetime new_start = start.isNull() ? Datetime::min() : start; Datetime new_end = end.isNull() ? Datetime::max() : end; @@ -400,11 +400,11 @@ vector> SQLiteBaseInfoDriver::getHistoryFinance(const string& mark for (size_t i = 0; i < total; i++) { const auto& finance = finances[i]; auto& cur = result[i]; + cur.reportDate = Datetime(finance.report_date); size_t len = finance.values.size() / sizeof(float); - cur.resize(len + 1); - auto* data = cur.data(); - data[0] = static_cast(finance.report_date); - memcpy(data + 1, finance.values.data(), len * sizeof(float)); + cur.values.resize(len + 1); + auto* data = cur.values.data(); + memcpy(data, finance.values.data(), len * sizeof(float)); } } catch (const std::exception& e) { diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h index 6367c1b2..002adf80 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.h @@ -38,8 +38,8 @@ public: virtual ZhBond10List getAllZhBond10() override; virtual vector> getHistoryFinanceField() override; - virtual vector> getHistoryFinance(const string& market, const string& code, - Datetime start, Datetime end) override; + virtual vector getHistoryFinance(const string& market, const string& code, + Datetime start, Datetime end) override; private: // 股票基本信息数据库实例 diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 03923d39..5f2a51b1 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -172,6 +172,16 @@ void export_StockManager(py::module& m) { :param Datetime d: 待判断的日期)") + .def("get_history_finance_field_name", &StockManager::getHistoryFinanceFieldName, + py::return_value_policy::copy, R"(get_history_finance_field_name(self, index) + + 根据字段索引,获取历史财务信息相应字段名)") + + .def("get_history_finance_field_index", &StockManager::getHistoryFinanceFieldIndex, + R"(get_history_finance_field_index(self, name) + + 根据字段名称,获取历史财务信息相应字段索引)") + .def("__len__", &StockManager::size, "返回证券数量") .def("__getitem__", &StockManager::getStock, "同 get_stock") .def( From 473f6c867e708c8469a718f5f25bd69f2dba1a82 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 14 Apr 2024 03:39:45 +0800 Subject: [PATCH 193/601] =?UTF-8?q?=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E6=8C=87=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 3 +- hikyuu/gui/data/ImportHistoryFinanceTask.py | 3 +- hikyuu_cpp/hikyuu/StockManager.cpp | 12 ++ hikyuu_cpp/hikyuu/StockManager.h | 1 + .../base_info/mysql/MySQLBaseInfoDriver.cpp | 35 ++++++ .../base_info/mysql/MySQLBaseInfoDriver.h | 2 + .../base_info/sqlite/SQLiteBaseInfoDriver.cpp | 8 +- .../base_info/table/HistoryFinanceTable.h | 5 +- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/FINANCE.h | 20 ++++ hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp | 106 ++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IFinance.h | 25 +++++ hikyuu_pywrap/_Stock.cpp | 10 ++ hikyuu_pywrap/_StockManager.cpp | 10 ++ hikyuu_pywrap/indicator/_build_in.cpp | 5 + hikyuu_pywrap/main.cpp | 9 ++ hikyuu_pywrap/pybind_utils.h | 4 +- 17 files changed, 247 insertions(+), 12 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/FINANCE.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IFinance.h diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index 025cc856..90ac66ce 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -27,6 +27,7 @@ import re import akshare as ak import pandas as pd import datetime +from hikyuu.core import cpp_bytes_to_vector_float_blob from hikyuu.util import * @@ -238,6 +239,6 @@ def historyfinancialreader(filepath): report_date = int(cw_info[313]) # 财务公告日期 report_date = 19000000 + report_date if report_date > 800000 else 20000000 + report_date # results.append((modifiy_code(code), report_date, cw_info)) - results.append((file_date, modifiy_code(code), report_date, info_data)) + results.append((file_date, modifiy_code(code), report_date, cpp_bytes_to_vector_float_blob(info_data))) cw_file.close() return results diff --git a/hikyuu/gui/data/ImportHistoryFinanceTask.py b/hikyuu/gui/data/ImportHistoryFinanceTask.py index 22073a18..2b54755d 100644 --- a/hikyuu/gui/data/ImportHistoryFinanceTask.py +++ b/hikyuu/gui/data/ImportHistoryFinanceTask.py @@ -88,12 +88,13 @@ class ImportHistoryFinanceTask: # filesize = item['filesize'] # get_report_file_by_size 传入实际的 filesize,实际会出错 data = self.api.get_report_file_by_size(f'tdxfin/{filename}', 0) + hku_info(f"Download finance file: {filename}") dest_file_name = self.dest_dir + "/" + filename with open(dest_file_name, 'wb') as f: f.write(data) shutil.unpack_archive(dest_file_name, extract_dir=self.dest_dir) - hku_info(f"Download finance file: {filename}") self.import_to_db(f'{self.dest_dir}/{filename[0:-4]}.dat') + hku_info(f"Import finance file: {self.dest_dir}/{filename[0:-4]}.dat") @hku_catch(trace=True) def __call__(self): diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 68fb5743..b3adf7b0 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -599,4 +599,16 @@ void StockManager::loadHistoryFinanceField() { } } +vector> StockManager::getHistoryFinanceAllFields() const { + vector> ret; + for (auto iter = m_field_ix_to_name.begin(); iter != m_field_ix_to_name.end(); ++iter) { + ret.emplace_back(iter->first, iter->second); + } + std::sort(ret.begin(), ret.end(), + [](const std::pair& a, const std::pair& b) { + return a.first < b.first; + }); + return ret; +} + } // namespace hku diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index b293c1e7..9fe70af5 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -163,6 +163,7 @@ public: const string& getHistoryFinanceFieldName(size_t ix) const; size_t getHistoryFinanceFieldIndex(const string& name) const; + vector> getHistoryFinanceAllFields() const; vector getHistoryFinance(const Stock& stk, Datetime start, Datetime end); diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index ce5ed9cc..a939520e 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -388,4 +388,39 @@ vector> MySQLBaseInfoDriver::getHistoryFinanceField() return result; } +vector MySQLBaseInfoDriver::getHistoryFinance(const string &market, + const string &code, + Datetime start, Datetime end) { + vector result; + + Datetime new_start = start.isNull() ? Datetime::min() : start; + Datetime new_end = end.isNull() ? Datetime::max() : end; + HKU_IF_RETURN(start >= end, result); + + auto con = m_pool->getConnect(); + try { + string market_code(fmt::format("{}{}", market, code)); + to_upper(market_code); + vector finances; + con->batchLoad(finances, ((Field("market_code") == market_code) & + (Field("report_date") >= start.ymd())) + + ASC("report_date")); + size_t total = finances.size(); + result.resize(total); + for (size_t i = 0; i < total; i++) { + auto &finance = finances[i]; + auto &cur = result[i]; + cur.reportDate = Datetime(finance.report_date); + cur.values = std::move(finance.values); + } + + } catch (const std::exception &e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } + + return result; +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h index c2ede8d6..aee5152a 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.h @@ -42,6 +42,8 @@ public: virtual ZhBond10List getAllZhBond10() override; virtual vector> getHistoryFinanceField() override; + virtual vector getHistoryFinance(const string& market, const string& code, + Datetime start, Datetime end) override; private: ConnectPool* m_pool; diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index 7122c3f9..4f7f51ed 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -398,18 +398,16 @@ vector SQLiteBaseInfoDriver::getHistoryFinance(const string& size_t total = finances.size(); result.resize(total); for (size_t i = 0; i < total; i++) { - const auto& finance = finances[i]; + auto& finance = finances[i]; auto& cur = result[i]; cur.reportDate = Datetime(finance.report_date); - size_t len = finance.values.size() / sizeof(float); - cur.values.resize(len + 1); - auto* data = cur.values.data(); - memcpy(data, finance.values.data(), len * sizeof(float)); + cur.values = std::move(finance.values); } } catch (const std::exception& e) { HKU_ERROR(e.what()); } catch (...) { + HKU_ERROR_UNKNOWN; } return result; diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h index 8025f62d..a7c58057 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h @@ -12,12 +12,11 @@ namespace hku { struct HistoryFinanceTable { - TABLE_BIND4(HistoryFinanceTable, HistoryFinanceField, file_date, market_code, report_date, - values) + TABLE_BIND4(HistoryFinanceTable, HistoryFinance, file_date, market_code, report_date, values) uint64_t file_date; uint64_t report_date; std::string market_code; - std::string values; + std::vector values; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 16e4c762..2a48adbf 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -44,6 +44,7 @@ #include "crt/EXIST.h" #include "crt/EVERY.h" #include "crt/FILTER.h" +#include "crt/FINANCE.h" #include "crt/FLOOR.h" #include "crt/HHV.h" #include "crt/HHVBARS.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/FINANCE.h b/hikyuu_cpp/hikyuu/indicator/crt/FINANCE.h new file mode 100644 index 00000000..e8d0877d --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/FINANCE.h @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +Indicator HKU_API FINANCE(int field_ix); +Indicator HKU_API FINANCE(const KData& k, int field_ix); + +Indicator HKU_API FINANCE(const string& field_name); +Indicator HKU_API FINANCE(const KData& k, const string& field_name); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp new file mode 100644 index 00000000..3cffe90c --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#include "hikyuu/StockManager.h" +#include "IFinance.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IFinance) +#endif + +namespace hku { + +IFinance::IFinance() : IndicatorImp("FINANCE", 1) { + setParam("field_ix", 0); + setParam("field_name", ""); +} + +IFinance::IFinance(const KData& k) : IndicatorImp("FINANCE", 1) { + setParam("field_ix", 0); + setParam("field_name", ""); + setParam("kdata", k); + IFinance::_calculate(Indicator()); +} + +void IFinance::_calculate(const Indicator& data) { + HKU_WARN_IF(!isLeaf() && !data.empty(), + "The input is ignored because {} depends on the context!", m_name); + + KData kdata = getContext(); + size_t total = kdata.size(); + if (total == 0) { + return; + } + + _readyBuffer(total, 1); + + Stock stock = kdata.getStock(); + auto finances = stock.getHistoryFinance(); + if (finances.empty()) { + m_discard = total; + return; + } + + int field_ix = getParam("field_ix"); + string field_name = getParam("field_name"); + if (field_ix < 0 && !field_name.empty()) { + field_ix = static_cast( + StockManager::instance().getHistoryFinanceFieldIndex(getParam("field_name"))); + } + + auto* dst = this->data(); + auto dates = kdata.getDatetimeList(); + auto* k = kdata.data(); + + size_t finances_total = finances.size(); + size_t cur_kix = 0; + size_t pos = 0; + while (pos < finances_total && cur_kix < total) { + auto value = finances[pos].values.at(field_ix); + if (pos + 1 == finances_total) { + while (cur_kix < total && finances[pos].reportDate <= k[cur_kix].datetime) { + dst[cur_kix] = value; + cur_kix++; + } + } else { + while (cur_kix < total && finances[pos].reportDate <= k[cur_kix].datetime && + finances[pos + 1].reportDate > k[cur_kix].datetime) { + dst[cur_kix] = value; + cur_kix++; + } + } + pos++; + } +} + +Indicator HKU_API FINANCE(int field_ix) { + auto p = make_shared(); + p->setParam("field_ix", field_ix); + return Indicator(p); +} + +Indicator HKU_API FINANCE(const KData& k, int field_ix) { + auto p = make_shared(k); + p->setParam("field_ix", field_ix); + return Indicator(p); +} + +Indicator HKU_API FINANCE(const string& field_name) { + auto p = make_shared(); + p->setParam("field_ix", -1); + p->setParam("field_name", field_name); + return Indicator(p); +} + +Indicator HKU_API FINANCE(const KData& k, const string& field_name) { + auto p = make_shared(k); + p->setParam("field_ix", -1); + p->setParam("field_name", field_name); + return Indicator(p); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.h b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.h new file mode 100644 index 00000000..6b32b849 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-13 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class IFinance : public IndicatorImp { + INDICATOR_IMP(IFinance) + INDICATOR_NEED_CONTEXT + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IFinance(); + explicit IFinance(const KData&); + virtual ~IFinance() = default; +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index 8dec404c..c84d5bbf 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -166,6 +166,16 @@ void export_Stock(py::module& m) { :param Datetime end: 结束时刻 :rtype: StockWeightList)") + .def("get_history_finance", + [](const Stock& stk) { + auto finances = stk.getHistoryFinance(); + py::list ret; + for (const auto& f : finances) { + ret.append(py::make_tuple(f.reportDate, f.values)); + } + return ret; + }) + .def("load_kdata_to_buffer", &Stock::loadKDataToBuffer, R"(load_kdata_to_buffer(self, ktype) diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 5f2a51b1..3dfab717 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -182,6 +182,16 @@ void export_StockManager(py::module& m) { 根据字段名称,获取历史财务信息相应字段索引)") + .def("get_history_finance_all_fields", + [](const StockManager& sm) { + auto fields = sm.getHistoryFinanceAllFields(); + py::list ret; + for (const auto& f : fields) { + ret.append(py::make_tuple(f.first, f.second)); + } + return ret; + }) + .def("__len__", &StockManager::size, "返回证券数量") .def("__getitem__", &StockManager::getStock, "同 get_stock") .def( diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 19a26ac1..63a16765 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1824,4 +1824,9 @@ void export_Indicator_build_in(py::module& m) { :param Indicator data: 指定的指标 :param int result_ix: 指定的结果集)"); + + m.def("FINANCE", py::overload_cast(FINANCE)); + m.def("FINANCE", py::overload_cast(FINANCE)); + m.def("FINANCE", py::overload_cast(FINANCE)); + m.def("FINANCE", py::overload_cast(FINANCE)); } diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index d02cc7fa..5fbfadf0 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -163,4 +163,13 @@ PYBIND11_MODULE(core, m) { :param int end: 结束日期 :param Query.KType ktype: K 线类型, 'DAY'|'WEEK'|'MONTH'|'QUARTER'|'HALFYEAR'|'YEAR'|'MIN'|'MIN5'|'MIN15'|'MIN30'|'MIN60' :param Query.RecoverType recover_type: 复权类型)"); + + // 仅供导入历史财务数据时将其转成 cpp 的 blob 格式 + m.def("cpp_bytes_to_vector_float_blob", [](const py::bytes& obj) { + vector c_vector = python_bytes_to_vector(obj); + std::ostringstream sout; + boost::archive::binary_oarchive oa(sout); + oa << BOOST_SERIALIZATION_NVP(c_vector); + return py::bytes(sout.str()); + }); } diff --git a/hikyuu_pywrap/pybind_utils.h b/hikyuu_pywrap/pybind_utils.h index 60c2b028..c115c194 100644 --- a/hikyuu_pywrap/pybind_utils.h +++ b/hikyuu_pywrap/pybind_utils.h @@ -39,12 +39,12 @@ std::vector python_bytes_to_vector(const py::bytes& obj) { std::vector result(vect_len); char* buffer = nullptr; - ssize_t length = 0; + Py_ssize_t length = 0; if (PyBytes_AsStringAndSize(obj.ptr(), &buffer, &length) != 0) { throw std::runtime_error("trans bytes to vector failed!"); } - if (length != static_cast(vect_len * sizeof(T))) { + if (length != static_cast(vect_len * sizeof(T))) { throw std::runtime_error("The length bytes not match!"); } From ff8bdf4d4d27f300635d418a273b23c4bf9e5733 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 14 Apr 2024 04:04:55 +0800 Subject: [PATCH 194/601] =?UTF-8?q?=E9=94=81=E5=AE=9A=20cpp-httplib=20?= =?UTF-8?q?=E7=89=88=E6=9C=AC=EF=BC=8C=E9=81=BF=E5=85=8D=20github=20ci=20?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xmake.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index c0ffc819..8f56e263 100644 --- a/xmake.lua +++ b/xmake.lua @@ -161,6 +161,7 @@ local boost_version = "1.84.0" local hdf5_version = "1.12.2" local fmt_version = "10.2.1" local flatbuffers_version = "23.5.26" +local cpp_httplib_version = "0.14.3" local sqlite_version = "3.43.0+200" local mysql_version = "8.0.31" if is_plat("windows") or (is_plat("linux", "cross") and is_arch("aarch64", "arm64.*")) then @@ -217,7 +218,7 @@ add_requires("sqlite3 " .. sqlite_version, {system = false, configs = {shared = add_requires("flatbuffers v" .. flatbuffers_version, {system = false}) add_requires("nng", {system = false, configs = {cxflags = "-fPIC"}}) add_requires("nlohmann_json", {system = false}) -add_requires("cpp-httplib", {system = false, configs = {zlib = true, ssl = true}}) +add_requires("cpp-httplib " .. cpp_httplib_version, {system = false, configs = {zlib = true, ssl = true}}) add_requires("zlib", {system = false}) add_defines("SPDLOG_DISABLE_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger From 6ebad215df901036f3ec1db3fa902f0163c68f63 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 14 Apr 2024 18:10:52 +0800 Subject: [PATCH 195/601] update --- README.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/README.rst b/README.rst index 95bd4e38..2dd8131c 100644 --- a/README.rst +++ b/README.rst @@ -88,18 +88,18 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 :alt: Star History Chart -项目捐赠 +项目捐赠(加入星球) -------------------------------------------------- -目前知识星球尚处于建设期,您的加入将视为对项目的捐赠(200元) +目前知识星球属于前期建设,您的加入将视为对项目的捐赠 - .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu.jpg + .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu_youhui.png 想要更多了解Hikyuu?请使用以下方式联系: -------------------------------------------------- -**项目交流和问题答复将逐渐转移至知识星球-【Hikyuu量化】,详见前述“项目捐赠”。** +**项目交流和问题答复将转移至知识星球-【Hikyuu量化】。** - 关注公众号: From 3f6afd17f67747944651392dd3f5c5f36e068f53 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 15 Apr 2024 13:40:58 +0800 Subject: [PATCH 196/601] =?UTF-8?q?jupyter=20=E4=B8=AD=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E6=97=B6=E7=BC=BA=E5=A4=B11min=E7=AD=89=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=97=B6=E6=89=93=E5=8D=B0=E8=BF=87=E5=A4=9A=E5=8D=A1?= =?UTF-8?q?=E6=AD=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data_driver/kdata/hdf5/H5KDataDriver.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/hdf5/H5KDataDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/hdf5/H5KDataDriver.cpp index 6d98768c..4f748947 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/hdf5/H5KDataDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/hdf5/H5KDataDriver.cpp @@ -6,6 +6,7 @@ */ #include +#include "hikyuu/utilities/os.h" #include "H5KDataDriver.h" #if H5_VERSION_GE(1, 12, 0) @@ -102,9 +103,14 @@ bool H5KDataDriver::_init() { to_upper(market); to_upper(ktype); + filename = getParam(*iter); + if (!existFile(filename)) { + // HKU_ERROR("Can't open h5file: {}", filename); + continue; + } + try { if (ktype == KQuery::getKTypeName(KQuery::DAY)) { - filename = getParam(*iter); H5FilePtr h5file(new H5::H5File(filename, H5F_ACC_RDONLY), Hdf5FileCloser()); m_h5file_map[market + "_DAY"] = h5file; m_h5file_map[market + "_WEEK"] = h5file; @@ -114,12 +120,10 @@ bool H5KDataDriver::_init() { m_h5file_map[market + "_YEAR"] = h5file; } else if (ktype == KQuery::getKTypeName(KQuery::MIN)) { - filename = getParam(*iter); H5FilePtr h5file(new H5::H5File(filename, H5F_ACC_RDONLY), Hdf5FileCloser()); m_h5file_map[market + "_MIN"] = h5file; } else if (ktype == KQuery::getKTypeName(KQuery::MIN5)) { - filename = getParam(*iter); H5FilePtr h5file(new H5::H5File(filename, H5F_ACC_RDONLY), Hdf5FileCloser()); m_h5file_map[market + "_MIN5"] = h5file; m_h5file_map[market + "_MIN15"] = h5file; @@ -128,18 +132,16 @@ bool H5KDataDriver::_init() { m_h5file_map[market + "_HOUR2"] = h5file; } else if (ktype == "TIME") { - filename = getParam(*iter); H5FilePtr h5file(new H5::H5File(filename, H5F_ACC_RDONLY), Hdf5FileCloser()); m_h5file_map[market + "_TIME"] = h5file; } else if (ktype == "TRANS") { - filename = getParam(*iter); H5FilePtr h5file(new H5::H5File(filename, H5F_ACC_RDONLY), Hdf5FileCloser()); m_h5file_map[market + "_TRANS"] = h5file; } } catch (...) { - HKU_ERROR("Can't open h5file: {}", filename); + // HKU_ERROR("Can't open h5file: {}", filename); } } @@ -213,7 +215,10 @@ bool H5KDataDriver::_getH5FileAndGroup(const string& market, const string& code, string key(format("{}_{}", market, kType)); to_upper(key); - out_file = m_h5file_map[key]; + auto iter = m_h5file_map.find(key); + HKU_IF_RETURN(iter == m_h5file_map.end(), false); + + out_file = iter->second; if (!out_file) { return false; From 0b553a518c0c98c7e7d097698288c39d72006365 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 15 Apr 2024 23:57:55 +0800 Subject: [PATCH 197/601] =?UTF-8?q?fixed=20addTempCsvStock=20=E5=A6=82?= =?UTF-8?q?=E6=9E=9C=20cvs=20=E6=B2=A1=E6=9C=89=E6=95=B0=E6=8D=AE=EF=BC=8C?= =?UTF-8?q?=E5=88=99=E6=B2=A1=E6=B3=95=E5=86=8D=E9=80=9A=E8=BF=87=20stock.?= =?UTF-8?q?realtime=5Fupdate=20=E6=B7=BB=E5=8A=A0=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index dc0a6a61..d8a96a39 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -298,7 +298,6 @@ void Stock::loadKDataToBuffer(KQuery::KType inkType) { auto driver = m_kdataDriver->getConnect(); size_t total = driver->getCount(m_data->m_market, m_data->m_code, kType); - HKU_IF_RETURN(total == 0, void()); int start = total <= max_num ? 0 : total - max_num; { std::unique_lock lock(*(m_data->pMutex[kType])); @@ -308,8 +307,10 @@ void Stock::loadKDataToBuffer(KQuery::KType inkType) { } KRecordList* ptr_klist = new KRecordList; m_data->pKData[kType] = ptr_klist; - (*ptr_klist) = driver->getKRecordList(m_data->m_market, m_data->m_code, - KQuery(start, Null(), kType)); + if (total != 0) { + (*ptr_klist) = driver->getKRecordList(m_data->m_market, m_data->m_code, + KQuery(start, Null(), kType)); + } } } From 5db89b4309c6a60bb50636bdbb25deb4bb93be61 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 16 Apr 2024 18:05:23 +0800 Subject: [PATCH 198/601] add temp stock support setKRecordList --- hikyuu_cpp/hikyuu/Stock.cpp | 48 +++++++++++++++++++ hikyuu_cpp/hikyuu/Stock.h | 7 +++ .../hikyuu/data_driver/DataDriverFactory.cpp | 2 + .../data_driver/kdata/DoNothingKDataDriver.h | 37 ++++++++++++++ hikyuu_pywrap/_Stock.cpp | 18 +++++++ hikyuu_pywrap/_StockManager.cpp | 5 ++ 6 files changed, 117 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index d8a96a39..1442b92b 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -749,6 +749,54 @@ void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { } } +void Stock::setKRecordList(const KRecordList& ks, const KQuery::KType& ktype) { + HKU_IF_RETURN(ks.empty(), void()); + string nktype(ktype); + to_upper(nktype); + + // 写锁 + std::unique_lock lock(*(m_data->pMutex[ktype])); + HKU_CHECK(m_data->pKData.find(nktype) != m_data->pKData.end(), "Invalid ktype: {}", ktype); + + if (!m_data->pKData[nktype]) { + m_data->pKData[nktype] = new KRecordList(); + } + + (*(m_data->pKData[nktype])) = ks; + + Parameter param; + param.set("type", "DoNothin"); + m_kdataDriver = DataDriverFactory::getKDataDriverPool(param); + + m_data->m_valid = true; + m_data->m_startDate = ks.front().datetime; + m_data->m_lastDate = ks.back().datetime; +} + +void Stock::setKRecordList(KRecordList&& ks, const KQuery::KType& ktype) { + HKU_IF_RETURN(ks.empty(), void()); + string nktype(ktype); + to_upper(nktype); + + // 写锁 + std::unique_lock lock(*(m_data->pMutex[ktype])); + HKU_CHECK(m_data->pKData.find(nktype) != m_data->pKData.end(), "Invalid ktype: {}", ktype); + + if (!m_data->pKData[nktype]) { + m_data->pKData[nktype] = new KRecordList(); + } + + (*m_data->pKData[nktype]) = std::move(ks); + + Parameter param; + param.set("type", "DoNothin"); + m_kdataDriver = DataDriverFactory::getKDataDriverPool(param); + + m_data->m_valid = true; + m_data->m_startDate = ks.front().datetime; + m_data->m_lastDate = ks.back().datetime; +} + const vector& Stock::getHistoryFinance() const { std::lock_guard lock(m_data->m_history_finance_mutex); if (!m_data->m_history_finance_ready) { diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 39b5a74f..60309d42 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -216,6 +216,13 @@ public: /** (临时函数)只用于更新缓存中的K线数据 **/ void realtimeUpdate(KRecord, KQuery::KType ktype = KQuery::DAY); + /** + * 部分临时创建的 Stock, 直接设置KRecordList + * @note 谨慎调用,通常供外部数据源直接设定数据 + */ + void setKRecordList(const KRecordList& ks, const KQuery::KType& ktype = KQuery::DAY); + void setKRecordList(KRecordList&& ks, const KQuery::KType& ktype = KQuery::DAY); + /** 仅用于python的__str__ */ string toString() const; diff --git a/hikyuu_cpp/hikyuu/data_driver/DataDriverFactory.cpp b/hikyuu_cpp/hikyuu/data_driver/DataDriverFactory.cpp index bd0bb3d2..dd19cb91 100644 --- a/hikyuu_cpp/hikyuu/data_driver/DataDriverFactory.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/DataDriverFactory.cpp @@ -8,6 +8,7 @@ #include "../GlobalInitializer.h" #include #include "block_info/qianlong/QLBlockInfoDriver.h" +#include "kdata/DoNothingKDataDriver.h" #include "kdata/cvs/KDataTempCsvDriver.h" #include "DataDriverFactory.h" #include "KDataDriver.h" @@ -61,6 +62,7 @@ void DataDriverFactory::init() { m_kdataPrototypeDrivers = new map(); m_kdataDriverPools = new map(); + DataDriverFactory::regKDataDriver(make_shared()); DataDriverFactory::regKDataDriver(make_shared()); #if HKU_ENABLE_TDX_KDATA diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h b/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h new file mode 100644 index 00000000..e35e9377 --- /dev/null +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-16 + * Author: fasiondog + */ + +#include "../KDataDriver.h" + +namespace hku { + +/** + * 一个特殊的 KDatadriver,不实际读取数据,用于增加外部临时Stock时使用 + */ +class DoNothingKDataDriver : public KDataDriver { +public: + DoNothingKDataDriver() : KDataDriver("DoNothin") {} + virtual ~DoNothingKDataDriver() = default; + + virtual KDataDriverPtr _clone() override { + return std::make_shared(); + } + + virtual bool _init() override { + return true; + } + + virtual bool isIndexFirst() override { + return true; + } + + virtual bool canParallelLoad() override { + return true; + } +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index c84d5bbf..e134a3e7 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -190,6 +190,24 @@ void export_Stock(py::module& m) { :param Query.KType ktype: K线类型)") + .def( + "set_krecord_list", + [](Stock& self, const py::object& obj) { + if (py::isinstance(obj)) { + const auto& ks = obj.cast(); + self.setKRecordList(ks); + } else if (py::isinstance(obj)) { + auto seq = obj.cast(); + auto ks = python_list_to_vector(seq); + self.setKRecordList(ks); + } else { + HKU_THROW("Unusable input data type"); + } + }, + R"(set_krecord_list(self, krecord_list) + + "谨慎调用!!!直接设置当前内存 KRecordList, 仅供需临时增加的外部 Stock 设置 K 线数据)") + .def(py::self == py::self) .def(py::self != py::self) diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 3dfab717..992210f8 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -192,6 +192,11 @@ void export_StockManager(py::module& m) { return ret; }) + .def("add_stock", &StockManager::addStock, R"(add_stock(self, stock) + + 谨慎调用!!!仅供增加某些临时的外部 Stock) + @return True | False)") + .def("__len__", &StockManager::size, "返回证券数量") .def("__getitem__", &StockManager::getStock, "同 get_stock") .def( From c498050f45d0e3f678e3ef05e8280d84eb287cb8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 16 Apr 2024 22:48:19 +0800 Subject: [PATCH 199/601] update docs --- docs/source/trade_sys/multifactor.rst | 10 +++++----- hikyuu_cpp/hikyuu/StockManager.cpp | 1 + hikyuu_pywrap/_StockManager.cpp | 2 ++ 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/source/trade_sys/multifactor.rst b/docs/source/trade_sys/multifactor.rst index bfc39b76..ea9a4023 100644 --- a/docs/source/trade_sys/multifactor.rst +++ b/docs/source/trade_sys/multifactor.rst @@ -5,7 +5,7 @@ ============ 内建对因子合成算法 ----------------- +-------------------------------- .. py:function:: MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5]) @@ -19,7 +19,7 @@ :rtype: MultiFactorBase -.. py:function:: MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) +.. py:function:: MF_ICWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) 滚动IC权重合成因子 @@ -32,7 +32,7 @@ :rtype: MultiFactorBase -.. py:function:: MF_EqualWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) +.. py:function:: MF_ICIRWeight(inds, stks, query, ref_stk[, ic_n=5, ic_rolling_n=120]) 滚动ICIR权重合成因子 @@ -46,7 +46,7 @@ 自定义多因子合成算法基类 --------------------- +-------------------------------------- 自定义多因子合成算法接口: @@ -54,7 +54,7 @@ 多因子合成算法基类 ------------------------- +--------------------------------------- .. py:class:: MultiFactorBase diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index b3adf7b0..841838bd 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -442,6 +442,7 @@ Stock StockManager::addTempCsvStock(const string& code, const string& day_filena void StockManager::removeTempCsvStock(const string& code) { string query_str = "TMP" + code; to_upper(query_str); + std::lock_guard lock(*m_stockDict_mutex); auto iter = m_stockDict.find(query_str); if (iter != m_stockDict.end()) { m_stockDict.erase(iter); diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 3dfab717..79e3fbb4 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -147,6 +147,8 @@ void export_StockManager(py::module& m) { CSV文件第一行为标题,需含有 Datetime(或Date、日期)、OPEN(或开盘价)、HIGH(或最高价)、LOW(或最低价)、CLOSE(或收盘价)、AMOUNT(或成交金额)、VOLUME(或VOL、COUNT、成交量)。 + 注意:请确保 csv 使用 utf8 格式存储,否则无法识别中文 + :param str code: 自行编号的证券代码,不能和已有的Stock相同,否则将返回Null :param str day_filename: 日线CSV文件名 :param str min_filename: 分钟线CSV文件名 From 0736df0af236e4d824fb7c16391a6b30ccdadf29 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 16 Apr 2024 23:19:24 +0800 Subject: [PATCH 200/601] =?UTF-8?q?StockManager=E5=A2=9E=E5=8A=A0=20remove?= =?UTF-8?q?Stock,=20=E4=BE=9B=E5=88=A0=E9=99=A4=E5=A4=96=E9=83=A8=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E4=BD=BF=E7=94=A8=E5=A2=9E=E5=8A=A0=E7=9A=84Stock?= =?UTF-8?q?=EF=BC=9Bfixed=20temp=5Fcsv=5Fstock=20=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 26 +++++++++---------- hikyuu_cpp/hikyuu/StockManager.h | 6 +++++ .../kdata/cvs/KDataTempCsvDriver.cpp | 2 +- hikyuu_pywrap/_StockManager.cpp | 4 +++ 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index b3adf7b0..072d91dc 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -429,23 +429,13 @@ Stock StockManager::addTempCsvStock(const string& code, const string& day_filena p->setDayFileName(day_filename); p->setMinFileName(min_filename); result.setKDataDriver(driver_pool); - const auto& preload_param = getPreloadParameter(); - if (preload_param.tryGet("day", true)) { - result.loadKDataToBuffer(KQuery::DAY); - } - if (preload_param.tryGet("min", false)) { - result.loadKDataToBuffer(KQuery::MIN); - } + result.loadKDataToBuffer(KQuery::DAY); + result.loadKDataToBuffer(KQuery::MIN); return addStock(result) ? result : Null(); } void StockManager::removeTempCsvStock(const string& code) { - string query_str = "TMP" + code; - to_upper(query_str); - auto iter = m_stockDict.find(query_str); - if (iter != m_stockDict.end()) { - m_stockDict.erase(iter); - } + removeStock(fmt::format("TMP{}", code)); } bool StockManager::addStock(const Stock& stock) { @@ -458,6 +448,16 @@ bool StockManager::addStock(const Stock& stock) { return true; } +void StockManager::removeStock(const string& market_code) { + string n_market_code(market_code); + to_upper(n_market_code); + std::lock_guard lock(*m_stockDict_mutex); + auto iter = m_stockDict.find(n_market_code); + if (iter != m_stockDict.end()) { + m_stockDict.erase(iter); + } +} + void StockManager::loadAllStocks() { HKU_INFO("Loading stock information..."); vector stockInfos; diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 9fe70af5..366d42be 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -174,6 +174,12 @@ public: */ bool addStock(const Stock& stock); + /** + * 从 StockManager 中移除相应的 Stock,一般用于将临时增加的 Stock 从 sm 中移除 + * @param market_code + */ + void removeStock(const string& market_code); + /** * 从CSV文件(K线数据)增加临时的Stock,可用于只有CSV格式的K线数据时,进行临时测试 * @details 增加的临时Stock,其market为“TMP” diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp index ec447123..6ab9fcd9 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp @@ -153,7 +153,7 @@ KRecordList KDataTempCsvDriver::_getKRecordListByIndex(const string& market, con } std::ifstream infile(filename.c_str()); - HKU_ERROR_IF_RETURN(!infile, result, "Can't open this file: {}", filename); + HKU_ERROR_IF_RETURN(!infile, result, "Can't open this file: {}, ktype: {}", filename, kType); string line; if (!std::getline(infile, line)) { infile.close(); diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 992210f8..833eca22 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -197,6 +197,10 @@ void export_StockManager(py::module& m) { 谨慎调用!!!仅供增加某些临时的外部 Stock) @return True | False)") + .def("remove_stock", &StockManager::removeStock, R"(remove_stock(self, market_code) + + 从 sm 中移除 market_code 代表的证券,谨慎使用!!!通常用于移除临时增加的外布 Stock)") + .def("__len__", &StockManager::size, "返回证券数量") .def("__getitem__", &StockManager::getStock, "同 get_stock") .def( From 1f42fc64f7762bcd161051f6f189d111375f5e3b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 16 Apr 2024 23:44:41 +0800 Subject: [PATCH 201/601] update docs --- docs/source/stock_manager.rst | 22 ++++++++++++++++++++++ hikyuu_pywrap/_StockManager.cpp | 6 ++++-- 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/docs/source/stock_manager.rst b/docs/source/stock_manager.rst index 550c63bc..eb215af1 100644 --- a/docs/source/stock_manager.rst +++ b/docs/source/stock_manager.rst @@ -240,6 +240,19 @@ StockManager/Block/Stock 移除增加的临时Stock :param str code: 创建时自定义的编码 + + + .. py:method:: add_stock(self, stock) + + 谨慎调用!!!仅供增加某些临时的外部 Stock, 通常配合 Stock.set_krecord_list 方法直接使用外部来源的数据 + + :param Stock stock: sm 外部自行创建的 Stock + + .. py:method:: remove_stock(self, market_code) + + 从 sm 中移除 market_code 代表的证券,谨慎使用!!!通常用于移除临时增加的外布 Stock + + :param str market_code: 证券市场标识 .. py:class:: Stock @@ -368,6 +381,12 @@ StockManager/Block/Stock :param Datetime date: 指定日期必须是0331、0630、0930、1231,如 Datetime(201109300000) :rtype: list + .. py:method:: set_krecord_list(self, krecord_list) + + 谨慎调用!!!直接设置当前内存 KRecordList, 仅供需临时增加的外部 Stock 设置 K 线数据 + + :param sequence krecord_list: 一个可迭代变量获取 KRecord 实例的对象,如: list (仅包含 KRecord 实例) + .. py:method:: realtime_update(self, krecord) (临时函数)只用于更新内存缓存中的日线数据 @@ -385,6 +404,9 @@ StockManager/Block/Stock 释放指定类别的内存K线数据 :param Query.KType ktype: K线类型 + + + .. py:class:: Block diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 47219d9c..03b0a786 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -196,12 +196,14 @@ void export_StockManager(py::module& m) { .def("add_stock", &StockManager::addStock, R"(add_stock(self, stock) - 谨慎调用!!!仅供增加某些临时的外部 Stock) + 谨慎调用!!!仅供增加某些临时的外部 Stock @return True | False)") .def("remove_stock", &StockManager::removeStock, R"(remove_stock(self, market_code) - 从 sm 中移除 market_code 代表的证券,谨慎使用!!!通常用于移除临时增加的外布 Stock)") + 从 sm 中移除 market_code 代表的证券,谨慎使用!!!通常用于移除临时增加的外布 Stock + + :param str market_code: 证券市场标识)") .def("__len__", &StockManager::size, "返回证券数量") .def("__getitem__", &StockManager::getStock, "同 get_stock") From dc41000c6ebd3352b1bd944bdbce6b5ef2a6e724 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Apr 2024 01:30:48 +0800 Subject: [PATCH 202/601] fixed warns and errors on ubuntu --- hikyuu_cpp/hikyuu/Stock.cpp | 19 ++++++++++++------- .../base_info/mysql/MySQLBaseInfoDriver.cpp | 2 +- .../base_info/table/HistoryFinanceTable.h | 2 +- .../data_driver/kdata/DoNothingKDataDriver.h | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 1442b92b..75304f60 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -290,15 +290,20 @@ void Stock::loadKDataToBuffer(KQuery::KType inkType) { releaseKDataBuffer(kType); - const auto& param = StockManager::instance().getPreloadParameter(); - string preload_type = fmt::format("{}_max", kType); - to_lower(preload_type); - int max_num = param.tryGet(preload_type, 4096); - HKU_ERROR_IF_RETURN(max_num < 0, void(), "Invalid preload {} param: {}", preload_type, max_num); - + int start = 0; auto driver = m_kdataDriver->getConnect(); size_t total = driver->getCount(m_data->m_market, m_data->m_code, kType); - int start = total <= max_num ? 0 : total - max_num; + + if (driver->name() != "TMPCSV" && driver->name() != "DoNothing") { + const auto& param = StockManager::instance().getPreloadParameter(); + string preload_type = fmt::format("{}_max", kType); + to_lower(preload_type); + int max_num = param.tryGet(preload_type, 4096); + HKU_ERROR_IF_RETURN(max_num < 0, void(), "Invalid preload {} param: {}", preload_type, + max_num); + start = total <= max_num ? 0 : total - max_num; + } + { std::unique_lock lock(*(m_data->pMutex[kType])); // 需要对是否已缓存进行二次判定,防止加锁之前已被缓存 diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index a939520e..f5916367 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -403,7 +403,7 @@ vector MySQLBaseInfoDriver::getHistoryFinance(const string & to_upper(market_code); vector finances; con->batchLoad(finances, ((Field("market_code") == market_code) & - (Field("report_date") >= start.ymd())) + + (Field("report_date") >= new_start.ymd())) + ASC("report_date")); size_t total = finances.size(); result.resize(total); diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h index a7c58057..43762137 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h @@ -12,7 +12,7 @@ namespace hku { struct HistoryFinanceTable { - TABLE_BIND4(HistoryFinanceTable, HistoryFinance, file_date, market_code, report_date, values) + TABLE_BIND4(HistoryFinanceTable, HistoryFinance, file_date, report_date, market_code, values) uint64_t file_date; uint64_t report_date; std::string market_code; diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h b/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h index e35e9377..a8894579 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h @@ -14,7 +14,7 @@ namespace hku { */ class DoNothingKDataDriver : public KDataDriver { public: - DoNothingKDataDriver() : KDataDriver("DoNothin") {} + DoNothingKDataDriver() : KDataDriver("DoNothing") {} virtual ~DoNothingKDataDriver() = default; virtual KDataDriverPtr _clone() override { From 6754cd0706fcaae0bc270dbfda37eb5c40d7aa18 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Apr 2024 01:32:46 +0800 Subject: [PATCH 203/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 75304f60..3ac09831 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -294,7 +294,8 @@ void Stock::loadKDataToBuffer(KQuery::KType inkType) { auto driver = m_kdataDriver->getConnect(); size_t total = driver->getCount(m_data->m_market, m_data->m_code, kType); - if (driver->name() != "TMPCSV" && driver->name() != "DoNothing") { + // CSV 直接全部加载至内存,其他类型依据配置的预加载参数进行加载 + if (driver->name() != "TMPCSV") { const auto& param = StockManager::instance().getPreloadParameter(); string preload_type = fmt::format("{}_max", kType); to_lower(preload_type); From 20c04245570c3a8e7a4209ef6000b6d1c5770603 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Apr 2024 13:42:25 +0800 Subject: [PATCH 204/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 2dd8131c..dc689633 100644 --- a/README.rst +++ b/README.rst @@ -93,7 +93,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 目前知识星球属于前期建设,您的加入将视为对项目的捐赠 - .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu_youhui.png + .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu.jpg 想要更多了解Hikyuu?请使用以下方式联系: From f4710b2eb1dc20619426526b0a1de447ed21d4e1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Apr 2024 13:42:44 +0800 Subject: [PATCH 205/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index dc689633..82b04d0a 100644 --- a/README.rst +++ b/README.rst @@ -91,7 +91,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 项目捐赠(加入星球) -------------------------------------------------- -目前知识星球属于前期建设,您的加入将视为对项目的捐赠 +目前知识星球属于前期建设,您的加入将视为对项目的捐赠(200元) .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu.jpg From 2909eb806d1c8cb3e8914e1d7e38d9d50a969c26 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Apr 2024 23:16:42 +0800 Subject: [PATCH 206/601] update python test code --- hikyuu/test/MoneyManager.py | 24 ++++++++++++------------ hikyuu/test/Slippage.py | 30 ++++++++++++++---------------- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/hikyuu/test/MoneyManager.py b/hikyuu/test/MoneyManager.py index 9b19c9c6..930102ef 100644 --- a/hikyuu/test/MoneyManager.py +++ b/hikyuu/test/MoneyManager.py @@ -2,10 +2,10 @@ # -*- coding: utf8 -*- # gb18030 -#=============================================================================== +# =============================================================================== # 作者:fasiondog # 历史:1)20130316, Added by fasiondog -#=============================================================================== +# =============================================================================== import unittest @@ -18,7 +18,7 @@ class MoneyManagerPython(MoneyManagerBase): self.set_param("n", 10) self._m_flag = False - def getBuyNumber(self, datetime, stock, price, risk): + def get_buy_num(self, datetime, stock, price, risk): if self._m_flag: return 10 else: @@ -44,14 +44,14 @@ class MoneyManagerTest(unittest.TestCase): self.assertEqual(p.get_param("n"), 10) p.set_param("n", 20) self.assertEqual(p.get_param("n"), 20) - self.assertEqual(p.getBuyNumber(Datetime(200101010000), stock, 10.0, 0.0), 20) + self.assertEqual(p.get_buy_num(Datetime(200101010000), stock, 10.0, 0.0), 20) p.reset() - self.assertEqual(p.getBuyNumber(Datetime(200101010000), stock, 10.0, 0.0), 10) + self.assertEqual(p.get_buy_num(Datetime(200101010000), stock, 10.0, 0.0), 10) p_clone = p.clone() self.assertEqual(p_clone.name, "MoneyManagerPython") self.assertEqual(p_clone.get_param("n"), 20) - self.assertEqual(p_clone.getBuyNumber(Datetime(200101010000), stock, 10, 0.0), 10) + self.assertEqual(p_clone.get_buy_num(Datetime(200101010000), stock, 10, 0.0), 10) p.set_param("n", 1) p_clone.set_param("n", 3) @@ -63,18 +63,18 @@ def testCrtMM(self): pass -def testgetBuyNumber(self, datetime, stock, price, risk): +def testget_buy_num(self, datetime, stock, price, risk, part): return 10.0 if datetime == Datetime(200101010000) else 0.0 class TestCrtMM(unittest.TestCase): def test_crt_mm(self): - p = crtMM(testCrtMM, params={'n': 10}, name="TestMM") - p.getBuyNumber = testgetBuyNumber + p = crtMM(testget_buy_num, testCrtMM, params={'n': 10}, name="TestMM") + p.tm = crtTM(Datetime(200101010000)) self.assertEqual(p.name, "TestMM") stock = sm['sh000001'] - self.assertEqual(p.getBuyNumber(p, Datetime(200101010000), stock, 1.0, 1.0), 10.0) - self.assertEqual(p.getBuyNumber(p, Datetime(200101020000), stock, 1.0, 1.0), 0.0) + self.assertEqual(p.get_buy_num(Datetime(200101010000), stock, 1.0, 1.0, SystemPart.MM), 10.0) + self.assertEqual(p.get_buy_num(Datetime(200101020000), stock, 1.0, 1.0, SystemPart.MM), 0.0) p_clone = p.clone() self.assertEqual(p_clone.name, "TestMM") @@ -85,4 +85,4 @@ def suite(): def suiteTestCrtMM(): - return unittest.TestLoader().loadTestsFromTestCase(TestCrtMM) \ No newline at end of file + return unittest.TestLoader().loadTestsFromTestCase(TestCrtMM) diff --git a/hikyuu/test/Slippage.py b/hikyuu/test/Slippage.py index 1e0783e0..3720900e 100644 --- a/hikyuu/test/Slippage.py +++ b/hikyuu/test/Slippage.py @@ -2,10 +2,10 @@ # -*- coding: utf8 -*- # gb18030 -#=============================================================================== +# =============================================================================== # 作者:fasiondog # 历史:1)20130321, Added by fasiondog -#=============================================================================== +# =============================================================================== import unittest @@ -17,12 +17,12 @@ class SlippagePython(SlippageBase): super(SlippagePython, self).__init__("SlippagePython") self._x = 0 - def getRealBuyPrice(self, datetime, price): + def get_real_buy_price(self, datetime, price): if self._x < 10: return 0.0 return 1.0 - def getRealSellPrice(self, datetime, price): + def get_real_sell_price(self, datetime, price): if self._x < 10: return 0.0 return 1.0 @@ -43,14 +43,14 @@ class SlippageTest(unittest.TestCase): def test_SlippageBase(self): p = SlippagePython() self.assertEqual(p.name, "SlippagePython") - self.assertEqual(p.getRealBuyPrice(Datetime(200101010000), 1.0), 0.0) - self.assertEqual(p.getRealSellPrice(Datetime(200101010000), 1.0), 0.0) + self.assertEqual(p.get_real_buy_price(Datetime(200101010000), 1.0), 0.0) + self.assertEqual(p.get_real_sell_price(Datetime(200101010000), 1.0), 0.0) self.assertEqual(p._x, 0) p._x = 10 self.assertEqual(p._x, 10) - self.assertEqual(p.getRealBuyPrice(Datetime(200101010000), 1.0), 1.0) - self.assertEqual(p.getRealSellPrice(Datetime(200101010000), 1.0), 1.0) + self.assertEqual(p.get_real_buy_price(Datetime(200101010000), 1.0), 1.0) + self.assertEqual(p.get_real_sell_price(Datetime(200101010000), 1.0), 1.0) p.reset() self.assertEqual(p._x, 0) @@ -66,18 +66,16 @@ def test_crtSL_func(self): pass -def test_getRealBuyPrice_func(self, datetime, price): +def test_get_real_buy_price_func(self, datetime, price): return 10.0 if datetime == Datetime(200101010000) else 0.0 class TestCrtSL(unittest.TestCase): - def test_crtSL(self): - p = crtSL(test_crtSL_func, params={'n': 10}, name="TestSL") - p.getRealBuyPrice = test_getRealBuyPrice_func + def test_crtSP(self): + p = crtSP(test_get_real_buy_price_func, test_crtSL_func, params={'n': 10}, name="TestSL") self.assertEqual(p.name, "TestSL") - self.assertEqual(p.getRealBuyPrice(p, Datetime(200101010000), 1.0), 10.0) - self.assertEqual(p.getRealBuyPrice(p, Datetime(200101020000), 1.0), 0.0) - + self.assertEqual(p.get_real_buy_price(Datetime(200101010000), 1.0), 10.0) + self.assertEqual(p.get_real_buy_price(Datetime(200101020000), 1.0), 0.0) p_clone = p.clone() self.assertEqual(p_clone.name, "TestSL") @@ -87,4 +85,4 @@ def suite(): def suiteTestCrtSL(): - return unittest.TestLoader().loadTestsFromTestCase(TestCrtSL) \ No newline at end of file + return unittest.TestLoader().loadTestsFromTestCase(TestCrtSL) From 9c7abcfaebd1b8d415563885553d9ca5fc448456 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Apr 2024 23:17:21 +0800 Subject: [PATCH 207/601] =?UTF-8?q?=E5=BC=80=E6=94=BE=20stock=20=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E5=8F=AF=E5=86=99=EF=BC=8C=E4=BE=9B=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=A4=96=E9=83=A8=E6=95=B0=E6=8D=AE=E6=BA=90=E6=97=B6=E4=BD=BF?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 159 ++++++++++++++++++++++-- hikyuu_cpp/hikyuu/Stock.h | 20 ++- hikyuu_pywrap/_MarketInfo.cpp | 9 +- hikyuu_pywrap/_Stock.cpp | 45 ++++--- hikyuu_pywrap/trade_sys/_SystemPart.cpp | 13 +- 5 files changed, 217 insertions(+), 29 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 3ac09831..a96645a9 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -145,17 +145,16 @@ Stock& Stock::operator=(Stock&& x) { } Stock::Stock(const string& market, const string& code, const string& name) { - m_data = - shared_ptr(new Data(market, code, name, default_type, default_valid, default_startDate, - default_lastDate, default_tick, default_tickValue, - default_precision, default_minTradeNumber, default_maxTradeNumber)); + m_data = make_shared(market, code, name, default_type, default_valid, default_startDate, + default_lastDate, default_tick, default_tickValue, default_precision, + default_minTradeNumber, default_maxTradeNumber); } Stock::Stock(const string& market, const string& code, const string& name, uint32_t type, bool valid, const Datetime& startDate, const Datetime& lastDate) { - m_data = shared_ptr(new Data(market, code, name, type, valid, startDate, lastDate, - default_tick, default_tickValue, default_precision, - default_minTradeNumber, default_maxTradeNumber)); + m_data = make_shared(market, code, name, type, valid, startDate, lastDate, default_tick, + default_tickValue, default_precision, default_minTradeNumber, + default_maxTradeNumber); } Stock::Stock(const string& market, const string& code, const string& name, uint32_t type, @@ -194,11 +193,11 @@ bool Stock::valid() const { return m_data ? m_data->m_valid : default_valid; } -Datetime Stock::startDatetime() const { +const Datetime& Stock::startDatetime() const { return m_data ? m_data->m_startDate : default_startDate; } -Datetime Stock::lastDatetime() const { +const Datetime& Stock::lastDatetime() const { return m_data ? m_data->m_lastDate : default_lastDate; } @@ -230,6 +229,148 @@ double Stock::maxTradeNumber() const { return m_data ? m_data->m_maxTradeNumber : default_maxTradeNumber; } +void Stock::market(const string& market_) { + if (!m_data) { + m_data = + make_shared(market_, default_code, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_market = market_; + } +} + +void Stock::code(const string& code_) { + if (!m_data) { + m_data = + make_shared(default_market, code_, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_code = code_; + } +} + +void Stock::name(const string& name_) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, name_, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_name = name_; + } +} + +void Stock::type(uint32_t type_) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, type_, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_type = type_; + } +} + +void Stock::valid(bool valid_) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, valid_, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_valid = valid_; + } +} + +void Stock::precision(int precision_) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + precision_, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_precision = precision_; + } +} + +void Stock::startDatetime(const Datetime& date) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + date, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_startDate = date; + } +} + +void Stock::lastDatetime(const Datetime& date) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + default_startDate, date, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } else { + m_data->m_lastDate = date; + } +} + +void Stock::tick(price_t tick_) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } + m_data->m_tick = tick_; + if (0.0 == m_data->m_tick) { + HKU_WARN("tick should not be zero! now use as 1.0"); + m_data->m_unit = 1.0; + } else { + m_data->m_unit = m_data->m_tickValue / m_data->m_tick; + } +} + +void Stock::tickValue(price_t val) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, default_maxTradeNumber); + } + m_data->m_tickValue = val; + if (0.0 == m_data->m_tick) { + HKU_WARN("tick should not be zero! now use as 1.0"); + m_data->m_unit = 1.0; + } else { + m_data->m_unit = m_data->m_tickValue / m_data->m_tick; + } +} + +void Stock::minTradeNumber(double num) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, num, default_maxTradeNumber); + } else { + m_data->m_minTradeNumber = num; + } +} + +void Stock::maxTradeNumber(double num) { + if (!m_data) { + m_data = + make_shared(default_market, default_code, default_name, default_type, default_valid, + default_startDate, default_lastDate, default_tick, default_tickValue, + default_precision, default_minTradeNumber, num); + } else { + m_data->m_maxTradeNumber = num; + } +} + void Stock::setKDataDriver(const KDataDriverConnectPoolPtr& kdataDriver) { HKU_CHECK(kdataDriver, "kdataDriver is nullptr!"); m_kdataDriver = kdataDriver; diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 60309d42..d3d87b51 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -95,10 +95,10 @@ public: bool valid() const; /** 获取证券起始日期 */ - Datetime startDatetime() const; + const Datetime& startDatetime() const; /** 获取证券最后日期 */ - Datetime lastDatetime() const; + const Datetime& lastDatetime() const; /** 获取最小跳动量 */ price_t tick() const; @@ -121,6 +121,22 @@ public: /** 获取最大交易量 */ double maxTradeNumber() const; + void market(const string& market_); + void code(const string& code_); + void name(const string& name_); + void type(uint32_t type_); + void valid(bool valid_); + void precision(int precision_); + void startDatetime(const Datetime&); + void lastDatetime(const Datetime&); + void tick(price_t tick_); + void tickValue(price_t val); + void minTradeNumber(double num); + void maxTradeNumber(double num); + void atom(double num) { + return minTradeNumber(num); + } + /** * 获取指定时间段[start,end)内的权息信息 * @param start 起始日期 diff --git a/hikyuu_pywrap/_MarketInfo.cpp b/hikyuu_pywrap/_MarketInfo.cpp index 30f409ef..fe51d254 100644 --- a/hikyuu_pywrap/_MarketInfo.cpp +++ b/hikyuu_pywrap/_MarketInfo.cpp @@ -21,11 +21,14 @@ void export_MarketInfo(py::module& m) { .def("__repr__", &MarketInfo::toString) .def_property_readonly("market", py::overload_cast<>(&MarketInfo::market, py::const_), - "市场标识(如:沪市“SH”, 深市“SZ”)") - .def_property_readonly("name", py::overload_cast<>(&MarketInfo::name, py::const_), "市场全称") + py::return_value_policy::copy, "市场标识(如:沪市“SH”, 深市“SZ”)") + .def_property_readonly("name", py::overload_cast<>(&MarketInfo::name, py::const_), + py::return_value_policy::copy, "市场全称") .def_property_readonly("description", - py::overload_cast<>(&MarketInfo::description, py::const_), "描述说明") + py::overload_cast<>(&MarketInfo::description, py::const_), + py::return_value_policy::copy, "描述说明") .def_property_readonly("code", py::overload_cast<>(&MarketInfo::code, py::const_), + py::return_value_policy::copy, "该市场对应的主要指数代码,用于获取交易日历") .def_property_readonly("last_datetime", &MarketInfo::lastDate, "该市场K线数据最后交易日期") diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index e134a3e7..53989b7a 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -29,23 +29,40 @@ void export_Stock(py::module& m) { .def("__repr__", &Stock::toString) .def_property_readonly("id", &Stock::id, "内部id") - .def_property_readonly("market", py::overload_cast<>(&Stock::market, py::const_), - "所属市场简称,市场简称是市场的唯一标识") - .def_property_readonly("code", py::overload_cast<>(&Stock::code, py::const_), "证券代码") + .def_property("market", py::overload_cast<>(&Stock::market, py::const_), + py::overload_cast(&Stock::market), py::return_value_policy::copy, + "所属市场简称,市场简称是市场的唯一标识") + .def_property("code", py::overload_cast<>(&Stock::code, py::const_), + py::overload_cast(&Stock::code), py::return_value_policy::copy, + "证券代码") .def_property_readonly("market_code", py::overload_cast<>(&Stock::market_code, py::const_), "市场简称+证券代码,如: sh000001") - .def_property_readonly("name", py::overload_cast<>(&Stock::name, py::const_), "证券名称") - .def_property_readonly("type", &Stock::type, "证券类型,参见:constant") - .def_property_readonly("valid", &Stock::valid, "该证券当前是否有效") - .def_property_readonly("start_datetime", &Stock::startDatetime, "证券起始日期") - .def_property_readonly("last_datetime", &Stock::lastDatetime, "证券最后日期") - .def_property_readonly("tick", &Stock::tick, "最小跳动量") - .def_property_readonly("tick_value", &Stock::tickValue, "最小跳动量价值") + .def_property("name", py::overload_cast<>(&Stock::name, py::const_), + py::overload_cast(&Stock::name), py::return_value_policy::copy, + "证券名称") + .def_property("type", py::overload_cast<>(&Stock::type, py::const_), + py::overload_cast(&Stock::type), "证券类型,参见:constant") + .def_property("valid", py::overload_cast<>(&Stock::valid, py::const_), + py::overload_cast(&Stock::valid), "该证券当前是否有效") + .def_property("start_datetime", py::overload_cast<>(&Stock::startDatetime, py::const_), + py::overload_cast(&Stock::startDatetime), + py::return_value_policy::copy, "证券起始日期") + .def_property("last_datetime", py::overload_cast<>(&Stock::lastDatetime, py::const_), + py::overload_cast(&Stock::lastDatetime), + py::return_value_policy::copy, "证券最后日期") + .def_property("tick", py::overload_cast<>(&Stock::tick, py::const_), + py::overload_cast(&Stock::tick), "最小跳动量") + .def_property("tick_value", py::overload_cast<>(&Stock::tickValue, py::const_), + py::overload_cast(&Stock::tickValue), "最小跳动量价值") .def_property_readonly("unit", &Stock::unit, "每单位价值 = tickValue / tick") - .def_property_readonly("precision", &Stock::precision, "价格精度") - .def_property_readonly("atom", &Stock::atom, "最小交易数量,同min_tradeNumber") - .def_property_readonly("min_trade_number", &Stock::minTradeNumber, "最小交易数量") - .def_property_readonly("max_trade_number", &Stock::maxTradeNumber, "最大交易数量") + .def_property("precision", py::overload_cast<>(&Stock::precision, py::const_), + py::overload_cast(&Stock::precision), "价格精度") + .def_property("atom", py::overload_cast<>(&Stock::atom, py::const_), + py::overload_cast(&Stock::atom), "最小交易数量,同min_tradeNumber") + .def_property("min_trade_number", py::overload_cast<>(&Stock::minTradeNumber, py::const_), + py::overload_cast(&Stock::minTradeNumber), "最小交易数量") + .def_property("max_trade_number", py::overload_cast<>(&Stock::maxTradeNumber, py::const_), + py::overload_cast(&Stock::maxTradeNumber), "最大交易数量") .def("is_null", &Stock::isNull, R"(is_null(self) diff --git a/hikyuu_pywrap/trade_sys/_SystemPart.cpp b/hikyuu_pywrap/trade_sys/_SystemPart.cpp index 71a43ff4..3807f48e 100644 --- a/hikyuu_pywrap/trade_sys/_SystemPart.cpp +++ b/hikyuu_pywrap/trade_sys/_SystemPart.cpp @@ -22,5 +22,16 @@ void export_SystemPart(py::module& m) { .value("PROFITGOAL", PART_PROFITGOAL, "盈利目标策略") .value("SLIPPAGE", PART_SLIPPAGE, "移滑价差算法") .value("ALLOCATEFUNDS", PART_ALLOCATEFUNDS, "资产分配算法") - .value("INVALID", PART_INVALID, "无效系统部件"); + .value("INVALID", PART_INVALID, "无效系统部件") + + // 支持简写 + .value("EV", PART_ENVIRONMENT, "外部环境") + .value("CN", PART_CONDITION, "系统前提条件") + .value("SG", PART_SIGNAL, "信号产生器") + .value("ST", PART_STOPLOSS, "止损策略") + .value("TP", PART_TAKEPROFIT, "止赢策略") + .value("MM", PART_MONEYMANAGER, "资金管理策略") + .value("PG", PART_PROFITGOAL, "盈利目标策略") + .value("SP", PART_SLIPPAGE, "移滑价差算法") + .value("AF", PART_ALLOCATEFUNDS, "资产分配算法"); } \ No newline at end of file From fd4720ba76577edeb1cbe76d10e064c15af3f8b9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 00:35:37 +0800 Subject: [PATCH 208/601] =?UTF-8?q?stock=E6=9A=B4=E9=9C=B2=E9=83=A8?= =?UTF-8?q?=E5=88=86=E5=B1=9E=E6=80=A7=E5=8F=AF=E5=86=99=EF=BC=8C=E6=96=B9?= =?UTF-8?q?=E4=BE=BF=E4=BD=BF=E7=94=A8=E5=A4=96=E9=83=A8=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/test/Stock.py | 67 +++++++++++++++++++++++++++++++++++-- hikyuu_cpp/hikyuu/Stock.cpp | 12 ++++--- 2 files changed, 73 insertions(+), 6 deletions(-) diff --git a/hikyuu/test/Stock.py b/hikyuu/test/Stock.py index aa993662..0de49e40 100644 --- a/hikyuu/test/Stock.py +++ b/hikyuu/test/Stock.py @@ -2,10 +2,10 @@ # -*- coding: utf8 -*- # gb18030 -#=============================================================================== +# =============================================================================== # 作者:fasiondog # 历史:1)20120928, Added by fasiondog -#=============================================================================== +# =============================================================================== import unittest @@ -59,6 +59,69 @@ class StockTest(unittest.TestCase): self.assertEqual(stock.name, b.name) self.assertEqual(b.market_code, 'SH000001') + def test_external_stock(self): + stk = Stock("xy", "00z", "测试") + self.assertEqual(stk.name, "测试") + self.assertEqual(stk.market, "XY") + self.assertEqual(stk.code, "00z") + self.assertEqual(stk.market_code, "XY00z") + + stk.market = "aB" + self.assertEqual(stk.market, "AB") + self.assertEqual(stk.market_code, "AB00z") + + stk.code = "000001" + self.assertEqual(stk.code, "000001") + self.assertEqual(stk.market_code, "AB000001") + + stk.name = "test" + self.assertEqual(stk.name, "test") + + self.assertEqual(stk.valid, False) + stk.valid = True + self.assert_(stk.valid) + + self.assertNotEqual(stk.type, constant.STOCKTYPE_A) + stk.type = constant.STOCKTYPE_A + self.assertEqual(stk.type, constant.STOCKTYPE_A) + + self.assertNotEqual(stk.tick, 1) + stk.tick = 1 + self.assertEqual(stk.tick, 1) + + self.assertNotEqual(stk.tick_value, 2) + stk.tick_value = 2 + self.assertEqual(stk.tick_value, 2) + self.assertEqual(stk.unit, 2) + + self.assertNotEqual(stk.atom, 2) + stk.atom = 2 + self.assertEqual(stk.atom, 2) + + self.assertNotEqual(stk.min_trade_number, 1) + stk.min_trade_number = 1 + self.assertEqual(stk.min_trade_number, 1) + + self.assertNotEqual(stk.max_trade_number, 10000) + stk.max_trade_number = 10000 + self.assertEqual(stk.max_trade_number, 10000) + + ks = [KRecord(Datetime(20010101), 5.0, 9.0, 4.0, 6.5, 1000.0, 100000.0)] + stk.set_krecord_list(ks) + self.assertEqual(stk.get_count(), 1) + k = stk.get_kdata(Query(0)) + self.assertEqual(len(k), 1) + self.assertEqual(k[0], KRecord(Datetime(20010101), 5.0, 9.0, 4.0, 6.5, 1000.0, 100000.0)) + + self.assert_(stk not in sm) + sm.add_stock(stk) + self.assert_(stk in sm) + stk2 = sm['ab000001'] + self.assert_(not stk2.is_null()) + self.assert_(stk2, stk) + sm.remove_stock("ab000001") + self.assert_(stk not in sm) + def suite(): return unittest.TestLoader().loadTestsFromTestCase(StockTest) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index a96645a9..16da4326 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -230,13 +230,16 @@ double Stock::maxTradeNumber() const { } void Stock::market(const string& market_) { + string n_market(market_); + to_upper(n_market); if (!m_data) { m_data = - make_shared(market_, default_code, default_name, default_type, default_valid, + make_shared(n_market, default_code, default_name, default_type, default_valid, default_startDate, default_lastDate, default_tick, default_tickValue, default_precision, default_minTradeNumber, default_maxTradeNumber); } else { - m_data->m_market = market_; + m_data->m_market = n_market; + m_data->m_market_code = m_data->marketCode(); } } @@ -248,6 +251,7 @@ void Stock::code(const string& code_) { default_precision, default_minTradeNumber, default_maxTradeNumber); } else { m_data->m_code = code_; + m_data->m_market_code = m_data->marketCode(); } } @@ -912,7 +916,7 @@ void Stock::setKRecordList(const KRecordList& ks, const KQuery::KType& ktype) { (*(m_data->pKData[nktype])) = ks; Parameter param; - param.set("type", "DoNothin"); + param.set("type", "DoNothing"); m_kdataDriver = DataDriverFactory::getKDataDriverPool(param); m_data->m_valid = true; @@ -936,7 +940,7 @@ void Stock::setKRecordList(KRecordList&& ks, const KQuery::KType& ktype) { (*m_data->pKData[nktype]) = std::move(ks); Parameter param; - param.set("type", "DoNothin"); + param.set("type", "DoNothing"); m_kdataDriver = DataDriverFactory::getKDataDriverPool(param); m_data->m_valid = true; From 339ab7b4aec4a1b8fdae5f7059423b63cc18bd4b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 00:59:17 +0800 Subject: [PATCH 209/601] =?UTF-8?q?fixed=20stock.get=5Ftimeline=5Flist=20?= =?UTF-8?q?=E7=BC=BA=E5=A4=B1=20to=5Fdf?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/extend.py | 155 ++++++++++++++++------------------ hikyuu/indicator/indicator.py | 34 +++----- hikyuu/trade_manage/trade.py | 127 ++++++++++++++-------------- 3 files changed, 150 insertions(+), 166 deletions(-) diff --git a/hikyuu/extend.py b/hikyuu/extend.py index 0017a7d6..8e78ad22 100644 --- a/hikyuu/extend.py +++ b/hikyuu/extend.py @@ -1,9 +1,9 @@ # # 对 C++ 引出类和函数进行扩展, pybind11 对小函数到导出效率不如 python 直接执行 # - +import numpy as np +import pandas as pd from datetime import * -from .util.slice import list_getitem from .core import * # ------------------------------------------------------------------ @@ -293,94 +293,87 @@ def new_Query_init(self, start=0, end=None, ktype=Query.DAY, recover_type=Query. Query.__init__ = new_Query_init + # ------------------------------------------------------------------ # 增加转化为 np.array、pandas.DataFrame 的功能 # ------------------------------------------------------------------ - -try: - import numpy as np - import pandas as pd - - def KData_to_np(kdata): - """转化为numpy结构数组""" - if kdata.get_query().ktype in ('DAY', 'WEEK', 'MONTH', 'QUARTER', 'HALFYEAR', 'YEAR'): - k_type = np.dtype( - { - 'names': ['datetime', 'open', 'high', 'low', 'close', 'amount', 'volume'], - 'formats': ['datetime64[D]', 'd', 'd', 'd', 'd', 'd', 'd'] - } - ) - else: - k_type = np.dtype( - { - 'names': ['datetime', 'open', 'high', 'low', 'close', 'amount', 'volume'], - 'formats': ['datetime64[ms]', 'd', 'd', 'd', 'd', 'd', 'd'] - } - ) - return np.array( - [(k.datetime.datetime(), k.open, k.high, k.low, k.close, k.amount, k.volume) for k in kdata], dtype=k_type - ) - - def KData_to_df(kdata): - """转化为pandas的DataFrame""" - return pd.DataFrame.from_records(KData_to_np(kdata), index='datetime') - - KData.to_np = KData_to_np - KData.to_df = KData_to_df - - def PriceList_to_np(data): - """仅在安装了numpy模块时生效,转换为numpy.array""" - return np.array(data, dtype='d') - - def PriceList_to_df(data): - """仅在安装了pandas模块时生效,转换为pandas.DataFrame""" - return pd.DataFrame(data.to_np(), columns=('Value', )) - - PriceList.to_np = PriceList_to_np - PriceList.to_df = PriceList_to_df - - def DatetimeList_to_np(data): - """仅在安装了numpy模块时生效,转换为numpy.array""" - return np.array(data, dtype='datetime64[D]') - - def DatetimeList_to_df(data): - """仅在安装了pandas模块时生效,转换为pandas.DataFrame""" - return pd.DataFrame(data.to_np(), columns=('Datetime', )) - - DatetimeList.to_np = DatetimeList_to_np - DatetimeList.to_df = DatetimeList_to_df - - def TimeLine_to_np(data): - """转化为numpy结构数组""" - t_type = np.dtype({'names': ['datetime', 'price', 'vol'], 'formats': ['datetime64[ms]', 'd', 'd']}) - return np.array([(t.date.datetime(), t.price, t.vol) for t in data], dtype=t_type) - - def TimeLine_to_df(kdata): - """转化为pandas的DataFrame""" - return pd.DataFrame.from_records(TimeLine_to_np(kdata), index='datetime') - - TimeLineList.to_np = TimeLine_to_np - TimeLineList.to_df = TimeLine_to_df - - def TransList_to_np(data): - """转化为numpy结构数组""" - t_type = np.dtype( +def KData_to_np(kdata): + """转化为numpy结构数组""" + if kdata.get_query().ktype in ('DAY', 'WEEK', 'MONTH', 'QUARTER', 'HALFYEAR', 'YEAR'): + k_type = np.dtype( { - 'names': ['datetime', 'price', 'vol', 'direct'], - 'formats': ['datetime64[ms]', 'd', 'd', 'd'] + 'names': ['datetime', 'open', 'high', 'low', 'close', 'amount', 'volume'], + 'formats': ['datetime64[D]', 'd', 'd', 'd', 'd', 'd', 'd'] } ) - return np.array([(t.date.datetime(), t.price, t.vol, t.direct) for t in data], dtype=t_type) + else: + k_type = np.dtype( + { + 'names': ['datetime', 'open', 'high', 'low', 'close', 'amount', 'volume'], + 'formats': ['datetime64[ms]', 'd', 'd', 'd', 'd', 'd', 'd'] + } + ) + return np.array( + [(k.datetime.datetime(), k.open, k.high, k.low, k.close, k.amount, k.volume) for k in kdata], dtype=k_type + ) - def TransList_to_df(kdata): - """转化为pandas的DataFrame""" - return pd.DataFrame.from_records(TransList_to_np(kdata), index='datetime') - TransList.to_np = TransList_to_np - TransList.to_df = TransList_to_df +def KData_to_df(kdata): + """转化为pandas的DataFrame""" + return pd.DataFrame.from_records(KData_to_np(kdata), index='datetime') -except: - pass + +KData.to_np = KData_to_np +KData.to_df = KData_to_df + + +def DatetimeList_to_np(data): + """仅在安装了numpy模块时生效,转换为numpy.array""" + return np.array(data, dtype='datetime64[D]') + + +def DatetimeList_to_df(data): + """仅在安装了pandas模块时生效,转换为pandas.DataFrame""" + return pd.DataFrame(data.to_np(), columns=('Datetime', )) + + +DatetimeList.to_np = DatetimeList_to_np +DatetimeList.to_df = DatetimeList_to_df + + +def TimeLine_to_np(data): + """转化为numpy结构数组""" + t_type = np.dtype({'names': ['datetime', 'price', 'vol'], 'formats': ['datetime64[ms]', 'd', 'd']}) + return np.array([(t.date.datetime(), t.price, t.vol) for t in data], dtype=t_type) + + +def TimeLine_to_df(kdata): + """转化为pandas的DataFrame""" + return pd.DataFrame.from_records(TimeLine_to_np(kdata), index='datetime') + + +TimeLineList.to_np = TimeLine_to_np +TimeLineList.to_df = TimeLine_to_df + + +def TransList_to_np(data): + """转化为numpy结构数组""" + t_type = np.dtype( + { + 'names': ['datetime', 'price', 'vol', 'direct'], + 'formats': ['datetime64[ms]', 'd', 'd', 'd'] + } + ) + return np.array([(t.date.datetime(), t.price, t.vol, t.direct) for t in data], dtype=t_type) + + +def TransList_to_df(kdata): + """转化为pandas的DataFrame""" + return pd.DataFrame.from_records(TransList_to_np(kdata), index='datetime') + + +TransList.to_np = TransList_to_np +TransList.to_df = TransList_to_df # ------------------------------------------------------------------ # 增强 Parameter diff --git a/hikyuu/indicator/indicator.py b/hikyuu/indicator/indicator.py index 1f95d43e..c393c946 100644 --- a/hikyuu/indicator/indicator.py +++ b/hikyuu/indicator/indicator.py @@ -62,30 +62,20 @@ Indicator.__getitem__ = indicator_getitem Indicator.__iter__ = indicator_iter -try: - import numpy as np - import pandas as pd +def indicator_to_df(indicator): + """转化为pandas.DataFrame""" + if indicator.get_result_num() == 1: + return pd.DataFrame(indicator.to_np(), columns=[indicator.name]) + data = {} + name = indicator.name + columns = [] + for i in range(indicator.get_result_num()): + data[name + str(i)] = indicator.get_result(i) + columns.append(name + str(i + 1)) + return pd.DataFrame(data, columns=columns) - def indicator_to_df(indicator): - """转化为pandas.DataFrame""" - if indicator.get_result_num() == 1: - return pd.DataFrame(indicator.to_np(), columns=[indicator.name]) - data = {} - name = indicator.name - columns = [] - for i in range(indicator.get_result_num()): - data[name + str(i)] = indicator.get_result(i) - columns.append(name + str(i + 1)) - return pd.DataFrame(data, columns=columns) - - Indicator.to_df = indicator_to_df - -except: - print( - "warning:can't import numpy or pandas lib, ", - "you can't use method Inidicator.to_np() and to_df!" - ) +Indicator.to_df = indicator_to_df def concat_to_df(dates, ind_list, head_stock_code=True, head_ind_name=False): diff --git a/hikyuu/trade_manage/trade.py b/hikyuu/trade_manage/trade.py index 4f7780dc..da2ccb38 100644 --- a/hikyuu/trade_manage/trade.py +++ b/hikyuu/trade_manage/trade.py @@ -31,80 +31,81 @@ from hikyuu.util.slice import list_getitem from hikyuu import * +import numpy as np +import pandas as pd -try: - import numpy as np - import pandas as pd - def TradeList_to_np(t_list): - """转化为numpy结构数组""" - t_type = np.dtype( - { - 'names': [ - '交易日期', '证券代码', '证券名称', '业务名称', '计划交易价格', '实际成交价格', '目标价格', '成交数量', '佣金', '印花税', '过户费', '其它成本', - '交易总成本', '止损价', '现金余额', '信号来源' - ], - 'formats': - ['datetime64[D]', 'U10', 'U20', 'U10', 'd', 'd', 'd', 'i', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'U5'] - } - ) - return np.array( - [ - ( - t.datetime, t.stock.market_code, t.stock.name, get_business_name(t.business), t.plan_price, - t.real_price, t.goal_price, t.number, t.cost.commission, t.cost.stamptax, t.cost.transferfee, - t.cost.others, t.cost.total, t.stoploss, t.cash, get_system_part_name(t.part) - ) for t in t_list +def TradeList_to_np(t_list): + """转化为numpy结构数组""" + t_type = np.dtype( + { + 'names': [ + '交易日期', '证券代码', '证券名称', '业务名称', '计划交易价格', '实际成交价格', '目标价格', '成交数量', '佣金', '印花税', '过户费', '其它成本', + '交易总成本', '止损价', '现金余额', '信号来源' ], - dtype=t_type - ) + 'formats': + ['datetime64[D]', 'U10', 'U20', 'U10', 'd', 'd', 'd', 'i', 'd', 'd', 'd', 'd', 'd', 'd', 'd', 'U5'] + } + ) + return np.array( + [ + ( + t.datetime, t.stock.market_code, t.stock.name, get_business_name(t.business), t.plan_price, + t.real_price, t.goal_price, t.number, t.cost.commission, t.cost.stamptax, t.cost.transferfee, + t.cost.others, t.cost.total, t.stoploss, t.cash, get_system_part_name(t.part) + ) for t in t_list + ], + dtype=t_type + ) - def TradeList_to_df(t): - """转化为pandas的DataFrame""" - return pd.DataFrame.from_records(TradeList_to_np(t), index='交易日期') - TradeRecordList.to_np = TradeList_to_np - TradeRecordList.to_df = TradeList_to_df +def TradeList_to_df(t): + """转化为pandas的DataFrame""" + return pd.DataFrame.from_records(TradeList_to_np(t), index='交易日期') - def PositionList_to_np(pos_list): - """转化为numpy结构数组""" - t_type = np.dtype( - { - 'names': ['证券代码', '证券名称', '买入日期', '已持仓天数', '持仓数量', '投入金额', '当前市值', '盈亏金额', '盈亏比例'], - 'formats': ['U10', 'U20', 'datetime64[D]', 'i', 'i', 'd', 'd', 'd', 'd'] - } - ) - sm = StockManager.instance() - query = Query(-1) - data = [] - for pos in pos_list: - invest = pos.buy_money - pos.sell_money + pos.total_cost - k = pos.stock.get_kdata(query) - cur_val = k[0].close * pos.number - bonus = cur_val - invest - date_list = sm.get_trading_calendar(Query(Datetime(pos.take_datetime.date()))) - data.append( - ( - pos.stock.market_code, pos.stock.name, pos.take_datetime, len(date_list), pos.number, invest, - cur_val, bonus, 100 * bonus / invest - ) +TradeRecordList.to_np = TradeList_to_np +TradeRecordList.to_df = TradeList_to_df + + +def PositionList_to_np(pos_list): + """转化为numpy结构数组""" + t_type = np.dtype( + { + 'names': ['证券代码', '证券名称', '买入日期', '已持仓天数', '持仓数量', '投入金额', '当前市值', '盈亏金额', '盈亏比例'], + 'formats': ['U10', 'U20', 'datetime64[D]', 'i', 'i', 'd', 'd', 'd', 'd'] + } + ) + sm = StockManager.instance() + query = Query(-1) + data = [] + for pos in pos_list: + invest = pos.buy_money - pos.sell_money + pos.total_cost + k = pos.stock.get_kdata(query) + cur_val = k[0].close * pos.number + bonus = cur_val - invest + date_list = sm.get_trading_calendar(Query(Datetime(pos.take_datetime.date()))) + data.append( + ( + pos.stock.market_code, pos.stock.name, pos.take_datetime, len(date_list), pos.number, invest, + cur_val, bonus, 100 * bonus / invest ) + ) + return np.array(data, dtype=t_type) - return np.array(data, dtype=t_type) - def PositionList_to_df(pos_list): - """转化为pandas的DataFrame""" - return pd.DataFrame.from_records(PositionList_to_np(pos_list), index='证券代码') +def PositionList_to_df(pos_list): + """转化为pandas的DataFrame""" + return pd.DataFrame.from_records(PositionList_to_np(pos_list), index='证券代码') - PositionRecordList.to_np = PositionList_to_np - PositionRecordList.to_df = PositionList_to_df - def Performance_to_df(per): - """将 Performance 统计结果转换为 DataFrame 格式""" - return pd.DataFrame(dict(name=per.names(), value=per.values())) +PositionRecordList.to_np = PositionList_to_np +PositionRecordList.to_df = PositionList_to_df - Performance.to_df = Performance_to_df -except: - pass +def Performance_to_df(per): + """将 Performance 统计结果转换为 DataFrame 格式""" + return pd.DataFrame(dict(name=per.names(), value=per.values())) + + +Performance.to_df = Performance_to_df From c8da188d31344d416c0a06a9b3cd39b3c86af1ce Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 02:06:44 +0800 Subject: [PATCH 210/601] =?UTF-8?q?=E7=A7=BB=E9=99=A4=20Stock.get=5Fhistor?= =?UTF-8?q?y=5Ffinance=5Finfo=EF=BC=8C=E5=90=8E=E7=BB=AD=E6=94=B9=E7=94=A8?= =?UTF-8?q?=20FINANCE=20=E6=8C=87=E6=A0=87=E8=8E=B7=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/deprecated.py | 15 +++++----- hikyuu_cpp/hikyuu/Stock.cpp | 12 -------- hikyuu_cpp/hikyuu/Stock.h | 3 -- .../unit_test/hikyuu/hikyuu/test_Stock.cpp | 29 +++---------------- hikyuu_pywrap/_Stock.cpp | 8 ----- hikyuu_pywrap/_StockManager.cpp | 21 ++++++++------ hikyuu_pywrap/indicator/_build_in.cpp | 19 +++++++++--- 7 files changed, 38 insertions(+), 69 deletions(-) diff --git a/hikyuu/deprecated.py b/hikyuu/deprecated.py index 20da1f7b..cdd567fb 100644 --- a/hikyuu/deprecated.py +++ b/hikyuu/deprecated.py @@ -8,7 +8,7 @@ def deprecated_func(new_func, old_func_name, new_func_name): def wrap_deprecated_func(func): def wrapfunc(*args, **kwarg): print( - #'Deprecated warning: "{}" will be deprecated, please use: "{}"'.format( + # 'Deprecated warning: "{}" will be deprecated, please use: "{}"'.format( '警告: "{}" 函数即将废弃,请使用 "{}" 代替'.format(old_func_name, new_func_name) ) return new_func(*args, **kwarg) @@ -25,11 +25,11 @@ def deprecated_attr(name_dict): clzname = self.__class__.__name__ if name in name_dict: if name_dict[name] is None: - #print('Removed warning: the {}.{} will be removed!'.format(clzname, name)) + # print('Removed warning: the {}.{} will be removed!'.format(clzname, name)) print('警告: "{}.{}" 接口已被删除!'.format(clzname, name)) else: print( - #'Deprecated warning: the "{}.{}" will be deprecated, please use: "{}.{}"'. + # 'Deprecated warning: the "{}.{}" will be deprecated, please use: "{}.{}"'. '警告: "{}.{}" 即将被废弃,请使用 "{}.{}" 代替'.format(clzname, name, clzname, name_dict[name]) ) return func(self, name_dict[name]) @@ -42,11 +42,11 @@ def deprecated_attr(name_dict): return wrap_deprecated_attr -#-------------------------------------------------------------------- +# -------------------------------------------------------------------- # # 待废弃函数 # -#-------------------------------------------------------------------- +# -------------------------------------------------------------------- @deprecated_func(get_version, 'getVersion', 'get_version') @@ -104,11 +104,11 @@ def SL_FixedValue(*args, **kwargs): pass -#-------------------------------------------------------------------- +# -------------------------------------------------------------------- # # 待废弃属性 # -#-------------------------------------------------------------------- +# -------------------------------------------------------------------- @deprecated_attr( @@ -486,7 +486,6 @@ SlippageBase.__getattr__ = SlippageBase_getattr 'getKRecordList': 'get_krecord_list', 'getDatetimeList': 'get_datetime_list', 'getFinanceInfo': 'get_finance_info', - 'getHistoryFinanceInfo': 'get_history_finance_info', 'realtimeUpdate': 'realtime_update', 'getWeight': 'get_weight', 'loadKDataToBuffer': 'load_kdata_to_buffer', diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 16da4326..b1253722 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -8,7 +8,6 @@ #include "GlobalInitializer.h" #include "StockManager.h" #include "data_driver/KDataDriver.h" -#include "data_driver/HistoryFinanceReader.h" #include "KData.h" namespace hku { @@ -832,17 +831,6 @@ Parameter Stock::getFinanceInfo() const { return result; } -PriceList Stock::getHistoryFinanceInfo(const Datetime& date) const { - PriceList result; - HKU_IF_RETURN(type() != STOCKTYPE_A && type() != STOCKTYPE_GEM && type() != STOCKTYPE_START && - type() != STOCKTYPE_A_BJ, - result); - const StockManager& sm = StockManager::instance(); - HistoryFinanceReader rd(sm.datadir() + "/downloads/finance"); - result = rd.getHistoryFinanceInfo(date, market(), code()); - return result; -} - // 判断是否在交易时间段内(不判断日期) bool Stock::isTransactionTime(Datetime time) { MarketInfo market_info = StockManager::instance().getMarketInfo(market()); diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index d3d87b51..5bd1d0c3 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -193,10 +193,7 @@ public: /** * 获取历史财务信息 - * @param date 指定日期必须是0331、0630、0930、1231,如 Datetime(201109300000) */ - PriceList getHistoryFinanceInfo(const Datetime& date) const; - const vector& getHistoryFinance() const; /** 设置权息信息, 仅供初始化时调用 */ diff --git a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Stock.cpp b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Stock.cpp index 32fb208a..31299720 100644 --- a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Stock.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Stock.cpp @@ -1842,7 +1842,7 @@ TEST_CASE("test_Stock_getKRecord_By_Date") { Stock stock = sm.getStock("sh000001"); KRecord record; - ///测试日线 + /// 测试日线 ///=================================== /** @arg 日期小于第一条记录 */ @@ -1873,7 +1873,7 @@ TEST_CASE("test_Stock_getKRecord_By_Date") { CHECK_EQ(record, Null()); ///=================================== - ///测试周线 + /// 测试周线 ///=================================== /** @arg 日期小于第一条记录 */ @@ -1904,7 +1904,7 @@ TEST_CASE("test_Stock_getKRecord_By_Date") { CHECK_EQ(record, Null()); ///=================================== - ///测试月线 + /// 测试月线 ///=================================== /** @arg 日期小于第一条记录 */ @@ -1935,7 +1935,7 @@ TEST_CASE("test_Stock_getKRecord_By_Date") { CHECK_EQ(record, Null()); ///=================================== - ///测试15分钟线 + /// 测试15分钟线 ///=================================== /** @arg 日期小于第一条记录 */ @@ -2051,27 +2051,6 @@ TEST_CASE("test_Stock_id_map") { MEMORY_CHECK; } -/** @par 检测点 */ -TEST_CASE("test_Stock_getHistoryFinanceInfo") { - StockManager& sm = StockManager::instance(); - Stock stk = getStock("sh600000"); - PriceList result = stk.getHistoryFinanceInfo(Datetime(201109300000)); - CHECK_EQ(result.size(), 286); - CHECK_EQ(result[0], doctest::Approx(1.067).epsilon(0.00001)); - CHECK_EQ(result[1], doctest::Approx(1.061).epsilon(0.00001)); - CHECK_EQ(result[2], doctest::Approx(1.360).epsilon(0.00001)); - CHECK_EQ(result[3], doctest::Approx(7.482).epsilon(0.00001)); - CHECK_EQ(result[9], doctest::Approx(0.0).epsilon(0.00001)); - CHECK_EQ(result[14], doctest::Approx(7.87818e+09).epsilon(0.00001)); - CHECK_EQ(result[282], doctest::Approx(6.327156e+06).epsilon(0.00001)); - CHECK_EQ(result[285], doctest::Approx(0.0).epsilon(0.00001)); - // for (int i = 0; i < 286; i++) { - // std::cout << result[i] << std::endl; - //} - - MEMORY_CHECK; -} - /** @par 检测点 */ TEST_CASE("test_Stock_getFinanceInfo") { StockManager& sm = StockManager::instance(); diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index 53989b7a..c98a411f 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -156,14 +156,6 @@ void export_Stock(py::module& m) { :rtype: Parameter)") - .def("get_history_finance_info", &Stock::getHistoryFinanceInfo, - R"(get_history_finance_info(self, date) - - 获取历史财务信息, 字段含义参见:https://hikyuu.org/finance_fields.html - - :param Datetime date: 指定日期必须是0331、0630、0930、1231,如 Datetime(201109300000) - :rtype: list)") - .def("realtime_update", &Stock::realtimeUpdate, py::arg("krecord"), py::arg("ktype") = KQuery::DAY, R"(realtime_update(self, krecord) diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 833eca22..9add464d 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -182,15 +182,18 @@ void export_StockManager(py::module& m) { 根据字段名称,获取历史财务信息相应字段索引)") - .def("get_history_finance_all_fields", - [](const StockManager& sm) { - auto fields = sm.getHistoryFinanceAllFields(); - py::list ret; - for (const auto& f : fields) { - ret.append(py::make_tuple(f.first, f.second)); - } - return ret; - }) + .def( + "get_history_finance_all_fields", + [](const StockManager& sm) { + auto fields = sm.getHistoryFinanceAllFields(); + py::list ret; + for (const auto& f : fields) { + ret.append(py::make_tuple(f.first, f.second)); + } + return ret; + }, + R"(get_history_finance_all_fields(self) + 获取所有历史财务信息字段及其索引)") .def("add_stock", &StockManager::addStock, R"(add_stock(self, stock) diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 63a16765..c58ec12f 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1825,8 +1825,19 @@ void export_Indicator_build_in(py::module& m) { :param Indicator data: 指定的指标 :param int result_ix: 指定的结果集)"); - m.def("FINANCE", py::overload_cast(FINANCE)); - m.def("FINANCE", py::overload_cast(FINANCE)); - m.def("FINANCE", py::overload_cast(FINANCE)); - m.def("FINANCE", py::overload_cast(FINANCE)); + m.def("FINANCE", py::overload_cast(FINANCE), py::arg("ix")); + m.def("FINANCE", py::overload_cast(FINANCE), py::arg("name")); + m.def("FINANCE", py::overload_cast(FINANCE), py::arg("kdata"), + py::arg("ix")); + m.def("FINANCE", py::overload_cast(FINANCE), py::arg("kdata"), + py::arg("name"), + R"(FINANCE([kdata, ix, name]) + + 获取历史财务信息。(可通过 StockManager.get_history_finance_all_fields 查询相应的历史财务字段信息) + + ix, name 使用时,为二选一。即要不使用 ix,要不就使用 name 进行获取。 + + :param KData kdata: K线数据 + :param int ix: 历史财务信息字段索引 + :param int name: 历史财务信息字段名称)"); } From 0134e31701b1321839c9f2fd861bd86729e49af1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 02:13:08 +0800 Subject: [PATCH 211/601] update docs --- docs/source/stock_manager.rst | 15 +++++++++++++-- docs/source/trade_sys/system.rst | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/source/stock_manager.rst b/docs/source/stock_manager.rst index eb215af1..0d2bffa5 100644 --- a/docs/source/stock_manager.rst +++ b/docs/source/stock_manager.rst @@ -241,7 +241,6 @@ StockManager/Block/Stock :param str code: 创建时自定义的编码 - .. py:method:: add_stock(self, stock) 谨慎调用!!!仅供增加某些临时的外部 Stock, 通常配合 Stock.set_krecord_list 方法直接使用外部来源的数据 @@ -253,7 +252,19 @@ StockManager/Block/Stock 从 sm 中移除 market_code 代表的证券,谨慎使用!!!通常用于移除临时增加的外布 Stock :param str market_code: 证券市场标识 - + + .. py:method:: get_history_finance_all_fields(self) + + 获取所有历史财务信息字段及其索引 + + .. py:method:: get_history_finance_field_index(self, name) + + 根据字段名称,获取历史财务信息相应字段索引 + + .. py:method:: get_history_finance_field_name(self, index) + + 根据字段索引,获取历史财务信息相应字段名 + .. py:class:: Stock diff --git a/docs/source/trade_sys/system.rst b/docs/source/trade_sys/system.rst index 49aba3b3..091a0e27 100644 --- a/docs/source/trade_sys/system.rst +++ b/docs/source/trade_sys/system.rst @@ -212,7 +212,7 @@ 复位,但不包括已有的交易对象,以及共享的部件 - .. py:methon:: force_reset_all(self) + .. py:method:: force_reset_all(self) 强制复位所有组件以及清空已有的交易对象,忽略组件的共享属性 From ce4ce8b2deb04018c0380748c7971d1951541d6e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 03:11:55 +0800 Subject: [PATCH 212/601] update --- .../hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp | 3 ++- .../data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index f5916367..46afd6ec 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -403,7 +403,8 @@ vector MySQLBaseInfoDriver::getHistoryFinance(const string & to_upper(market_code); vector finances; con->batchLoad(finances, ((Field("market_code") == market_code) & - (Field("report_date") >= new_start.ymd())) + + (Field("report_date") >= new_start.ymd()) & + Field("report_date") < new_end.ymd()) + ASC("report_date")); size_t total = finances.size(); result.resize(total); diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index 4f7f51ed..541fedc7 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -393,7 +393,8 @@ vector SQLiteBaseInfoDriver::getHistoryFinance(const string& to_upper(market_code); vector finances; con->batchLoad(finances, ((Field("market_code") == market_code) & - (Field("report_date") >= start.ymd())) + + (Field("report_date") >= new_start.ymd()) & + (Field("report_date") < new_end.ymd())) + ASC("report_date")); size_t total = finances.size(); result.resize(total); From 2953f4668c062373fe47c9230be8c7098afd66fa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 15:22:40 +0800 Subject: [PATCH 213/601] =?UTF-8?q?fixed=20=E6=96=B0=E5=A2=9E=E7=9A=84?= =?UTF-8?q?=E5=8C=97=E4=BA=A4=E6=89=80=E8=82=A1=E7=A5=A8=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E8=BF=98=E6=98=AFA=E8=82=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0014.sql | 3 +++ hikyuu/data/sqlite_upgrade/0015.sql | 7 +++++++ 2 files changed, 10 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0014.sql create mode 100644 hikyuu/data/sqlite_upgrade/0015.sql diff --git a/hikyuu/data/mysql_upgrade/0014.sql b/hikyuu/data/mysql_upgrade/0014.sql new file mode 100644 index 00000000..fc6bf3f2 --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0014.sql @@ -0,0 +1,3 @@ +UPDATE `hku_base`.`stock` set `type`=11 where `marketid`=3; +UPDATE `hku_base`.`coderuletype` set `type`=11 where `marketid`=3; +UPDATE `hku_base`.`version` set `version` = 14; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0015.sql b/hikyuu/data/sqlite_upgrade/0015.sql new file mode 100644 index 00000000..6af49a66 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0015.sql @@ -0,0 +1,7 @@ +BEGIN TRANSACTION; + +UPDATE `Stock` set `type`=11 where `marketid`=3; +UPDATE `CodeRuleType` set `type`=11 where `marketid`=3; +UPDATE `version` set `version` = 15; + +COMMIT; \ No newline at end of file From 993860e657fe1312d623d41424e90a2f6fd88e7b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 18:51:26 +0800 Subject: [PATCH 214/601] =?UTF-8?q?fixed=20=E6=96=B0=E5=A2=9E=E7=9A=84?= =?UTF-8?q?=E5=8C=97=E4=BA=A4=E6=89=80=E8=82=A1=E7=A5=A8=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E8=BF=98=E6=98=AFA=E8=82=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 10 ++++++++-- hikyuu/data/pytdx_finance_to_mysql.py | 6 +++--- hikyuu/data/pytdx_finance_to_sqlite.py | 6 +++--- hikyuu/data/pytdx_to_h5.py | 3 ++- hikyuu/data/pytdx_to_mysql.py | 3 ++- .../base_info/mysql/MySQLBaseInfoDriver.cpp | 2 +- .../hikyuu/data_driver/kdata/DoNothingKDataDriver.h | 4 ---- 7 files changed, 19 insertions(+), 15 deletions(-) diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index 90ac66ce..f4065103 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -57,6 +57,12 @@ class STOCKTYPE: BOND = 7 # 其他债券 GEM = 8 # 创业板 START = 9 # 科创板 + A_BJ = 11 # 北交所A股 + + +def get_a_stktype_list(): + """获取A股市场证券类型元组,含B股""" + return (STOCKTYPE.A, STOCKTYPE.INDEX, STOCKTYPE.B, STOCKTYPE.GEM, STOCKTYPE.START, STOCKTYPE.A_BJ) def get_stktype_list(quotations=None): @@ -68,13 +74,13 @@ def get_stktype_list(quotations=None): :return: 股票类别元组 """ if not quotations: - return (1, 2, 3, 4, 5, 6, 7, 8, 9) + return (1, 2, 3, 4, 5, 6, 7, 8, 9, 11) result = [] for quotation in quotations: new_quotation = quotation.lower() if new_quotation == 'stock': - result += [STOCKTYPE.A, STOCKTYPE.INDEX, STOCKTYPE.B, STOCKTYPE.GEM, STOCKTYPE.START] + result += list(get_a_stktype_list()) elif new_quotation == 'fund': result += [STOCKTYPE.FUND, STOCKTYPE.ETF] elif new_quotation == 'bond': diff --git a/hikyuu/data/pytdx_finance_to_mysql.py b/hikyuu/data/pytdx_finance_to_mysql.py index 5aa98658..dd7093d5 100644 --- a/hikyuu/data/pytdx_finance_to_mysql.py +++ b/hikyuu/data/pytdx_finance_to_mysql.py @@ -22,7 +22,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from hikyuu.data.common import MARKETID, STOCKTYPE, historyfinancialreader +from hikyuu.data.common import MARKETID, STOCKTYPE, historyfinancialreader, get_a_stktype_list from hikyuu.data.common_mysql import get_marketid from hikyuu.util import * @@ -31,8 +31,8 @@ from hikyuu.util import * def pytdx_import_finance_to_mysql(db_connect, pytdx_connect, market): """导入公司财务信息""" marketid = get_marketid(db_connect, market) - sql = "select `stockid`, `marketid`, `code`, `valid`, `type` from `hku_base`.`stock` where marketid={} and type = {} and valid=1"\ - .format(marketid, STOCKTYPE.A) + sql = "select `stockid`, `marketid`, `code`, `valid`, `type` from `hku_base`.`stock` where marketid={} and type in {} and valid=1"\ + .format(marketid, get_a_stktype_list()) cur = db_connect.cursor() a = cur.execute(sql) diff --git a/hikyuu/data/pytdx_finance_to_sqlite.py b/hikyuu/data/pytdx_finance_to_sqlite.py index ed82356c..398e7f8c 100644 --- a/hikyuu/data/pytdx_finance_to_sqlite.py +++ b/hikyuu/data/pytdx_finance_to_sqlite.py @@ -22,15 +22,15 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -from hikyuu.data.common import MARKETID, STOCKTYPE, historyfinancialreader +from hikyuu.data.common import MARKETID, STOCKTYPE, historyfinancialreader, get_a_stktype_list from hikyuu.data.common_sqlite3 import get_marketid, create_database def pytdx_import_finance_to_sqlite(db_connect, pytdx_connect, market): """导入公司财务信息""" marketid = get_marketid(db_connect, market) - sql = "select stockid, marketid, code, valid, type from stock where marketid={} and type = {} and valid=1"\ - .format(marketid, STOCKTYPE.A) + sql = "select stockid, marketid, code, valid, type from stock where marketid={} and type in {} and valid=1"\ + .format(marketid, get_a_stktype_list()) cur = db_connect.cursor() all_list = cur.execute(sql).fetchall() diff --git a/hikyuu/data/pytdx_to_h5.py b/hikyuu/data/pytdx_to_h5.py index fff5db0f..aac33e88 100644 --- a/hikyuu/data/pytdx_to_h5.py +++ b/hikyuu/data/pytdx_to_h5.py @@ -454,8 +454,9 @@ def import_trans(connect, market, quotations, api, dest_dir, max_days=30, progre stock_list = get_stock_list(connect, market, quotations) total = len(stock_list) + a_stktype_list = get_a_stktype_list() for i, stock in enumerate(stock_list): - if stock[3] == 0 or len(stock[2]) != 6 or stock[4] not in (STOCKTYPE.A, STOCKTYPE.B, STOCKTYPE.GEM): + if stock[3] == 0 or len(stock[2]) != 6 or stock[4] not in a_stktype_list: if progress: progress(i, total) continue diff --git a/hikyuu/data/pytdx_to_mysql.py b/hikyuu/data/pytdx_to_mysql.py index 85a54714..26de578e 100644 --- a/hikyuu/data/pytdx_to_mysql.py +++ b/hikyuu/data/pytdx_to_mysql.py @@ -559,8 +559,9 @@ def import_trans( stock_list = get_stock_list(connect, market, quotations) total = len(stock_list) + a_stktype_list = get_a_stktype_list() for i, stock in enumerate(stock_list): - if stock[3] == 0 or len(stock[2]) != 6 or stock[4] not in (STOCKTYPE.A, STOCKTYPE.B, STOCKTYPE.GEM): + if stock[3] == 0 or len(stock[2]) != 6 or stock[4] not in a_stktype_list: if progress: progress(i, total) continue diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index 46afd6ec..af3487f4 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -404,7 +404,7 @@ vector MySQLBaseInfoDriver::getHistoryFinance(const string & vector finances; con->batchLoad(finances, ((Field("market_code") == market_code) & (Field("report_date") >= new_start.ymd()) & - Field("report_date") < new_end.ymd()) + + (Field("report_date") < new_end.ymd())) + ASC("report_date")); size_t total = finances.size(); result.resize(total); diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h b/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h index a8894579..6a63c436 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/DoNothingKDataDriver.h @@ -21,10 +21,6 @@ public: return std::make_shared(); } - virtual bool _init() override { - return true; - } - virtual bool isIndexFirst() override { return true; } From 1f832f3e9d7209685794f0a612e5f4ea6d2042e6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 22:31:25 +0800 Subject: [PATCH 215/601] update --- docs/source/release.rst | 14 ++++++++++++++ xmake.lua | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 96c9264c..2ba0efb7 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,20 @@ 版本发布说明 ======================= +2.0.2 - 2024年4月19日 +------------------------- + +1. 新增特性 + - 历史财务信息入库,并增加指标 FINANCE 获取相应历史财务数据 + - 新增 RESULT 指标,以便对存在多个结果集的指标可以通过指标公式的方式获取结果 + - Stock 开放部分属性可在运行时修改,增加 set_krecord_list 方法,可以希望使用其他数据源时生成临时的 Stock 并获取 K 线数据 + +2. 缺陷修复 + - fixed 获取节假日信息时出现错误 + - fixed hdf5 在只有日线数据时,运行在 jupyter 中,初始化会出现卡死 + - fixed 新增的北交所股票类型未修改全,导入数据后又变成了 A 股类型 + + 2.0.1 - 2024年4月7日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 8f56e263..6e21dd46 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.1", {build = "%Y%m%d%H%M"}) +set_version("2.0.2", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 6c5e8b802153bc4b42ce41f8cdee6392319902f0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 23:01:44 +0800 Subject: [PATCH 216/601] add ZONGGUBEN indicator --- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/ZONGGUBEN.h | 17 ++++ .../hikyuu/indicator/imp/IZongGuBen.cpp | 77 +++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.h | 25 ++++++ hikyuu_pywrap/indicator/_build_in.cpp | 8 ++ 5 files changed, 128 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/ZONGGUBEN.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.h diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 2a48adbf..19c92a07 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -106,6 +106,7 @@ #include "crt/VARP.h" #include "crt/VIGOR.h" #include "crt/ZHBOND10.h" +#include "crt/ZONGGUBEN.h" #include "crt/ZSCORE.h" #endif /* INDICATOR_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ZONGGUBEN.h b/hikyuu_cpp/hikyuu/indicator/crt/ZONGGUBEN.h new file mode 100644 index 00000000..1e049ab0 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/ZONGGUBEN.h @@ -0,0 +1,17 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-18 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +Indicator HKU_API ZONGGUBEN(); +Indicator HKU_API ZONGGUBEN(const KData&); + +} // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.cpp new file mode 100644 index 00000000..904146d7 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.cpp @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-18 + * Author: fasiondog + */ + +#include "IZongGuBen.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IZongGuBen) +#endif + +namespace hku { + +IZongGuBen::IZongGuBen() : IndicatorImp("ZONGGUBEN", 1) {} + +IZongGuBen::~IZongGuBen() {} + +IZongGuBen::IZongGuBen(const KData& k) : IndicatorImp("ZONGGUBEN", 1) { + setParam("kdata", k); + IZongGuBen::_calculate(Indicator()); +} + +void IZongGuBen::_calculate(const Indicator& data) { + HKU_WARN_IF(!isLeaf() && !data.empty(), + "The input is ignored because {} depends on the context!", m_name); + + KData k = getContext(); + size_t total = k.size(); + if (total == 0) { + return; + } + + _readyBuffer(total, 1); + + Stock stock = k.getStock(); + StockWeightList sw_list = stock.getWeight(); + if (sw_list.size() == 0) { + return; + } + + auto* dst = this->data(); + size_t pos = 0; + auto sw_iter = sw_list.begin(); + price_t pre_total_count = sw_iter->totalCount(); + for (; sw_iter != sw_list.end(); ++sw_iter) { + price_t total_count = sw_iter->totalCount(); + if (total_count == 0) { + continue; // 忽略流通盘为0的权息 + } + + while (pos < total && k[pos].datetime < sw_iter->datetime()) { + dst[pos] = pre_total_count; + pos++; + } + + pre_total_count = total_count; + if (pos >= total) { + break; + } + } + + for (; pos < total; pos++) { + dst[pos] = pre_total_count; + } +} + +Indicator HKU_API ZONGGUBEN() { + return make_shared()->calculate(); +} + +Indicator HKU_API ZONGGUBEN(const KData& k) { + return Indicator(make_shared(k)); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.h b/hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.h new file mode 100644 index 00000000..d97b4afc --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IZongGuBen.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-04-18 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +class IZongGuBen : public IndicatorImp { + INDICATOR_IMP(IZongGuBen) + INDICATOR_NEED_CONTEXT + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IZongGuBen(); + explicit IZongGuBen(const KData&); + virtual ~IZongGuBen(); +}; + +} /* namespace hku */ diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index c58ec12f..561f48b6 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -889,6 +889,14 @@ void export_Indicator_build_in(py::module& m) { 获取流通盘(单位:万股),同 CAPITAL + :param KData kdata: k线数据 + :rtype: Indicator)"); + + m.def("ZONGGUBEN", py::overload_cast<>(ZONGGUBEN)); + m.def("ZONGGUBEN", py::overload_cast(ZONGGUBEN), R"(ZONGGUBEN(kdata) + + 获取总股本(单位:万股) + :param KData kdata: k线数据 :rtype: Indicator)"); From 68ab02f5489b7ae2868d6ca99c6f258be32eadec Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Apr 2024 23:52:24 +0800 Subject: [PATCH 217/601] fixed FINANCE --- hikyuu_cpp/hikyuu/HistoryFinanceInfo.h | 9 ++++++-- hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp | 4 ++-- hikyuu_pywrap/_Stock.cpp | 22 ++++++++++++-------- 3 files changed, 22 insertions(+), 13 deletions(-) diff --git a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h index d1d5c57c..8bdce0ab 100644 --- a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h +++ b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h @@ -11,9 +11,14 @@ namespace hku { +/** + * 历史财务信息记录 + * @ingroup StockManage + */ struct HKU_API HistoryFinanceInfo { - Datetime reportDate; - vector values; + Datetime reportDate; ///< 财务报告日期 + vector + values; ///< 详细财务信息,字段索引可使用 StockManager.getHistoryFinanceAllFields 查询 HistoryFinanceInfo() = default; HistoryFinanceInfo(const HistoryFinanceInfo&) = default; diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp index 3cffe90c..9ac8e374 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp @@ -86,7 +86,7 @@ Indicator HKU_API FINANCE(int field_ix) { Indicator HKU_API FINANCE(const KData& k, int field_ix) { auto p = make_shared(k); p->setParam("field_ix", field_ix); - return Indicator(p); + return p->calculate(); } Indicator HKU_API FINANCE(const string& field_name) { @@ -100,7 +100,7 @@ Indicator HKU_API FINANCE(const KData& k, const string& field_name) { auto p = make_shared(k); p->setParam("field_ix", -1); p->setParam("field_name", field_name); - return Indicator(p); + return p->calculate(); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index c98a411f..86fd4fe3 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -175,15 +175,19 @@ void export_Stock(py::module& m) { :param Datetime end: 结束时刻 :rtype: StockWeightList)") - .def("get_history_finance", - [](const Stock& stk) { - auto finances = stk.getHistoryFinance(); - py::list ret; - for (const auto& f : finances) { - ret.append(py::make_tuple(f.reportDate, f.values)); - } - return ret; - }) + .def( + "get_history_finance", + [](const Stock& stk) { + auto finances = stk.getHistoryFinance(); + py::list ret; + for (const auto& f : finances) { + ret.append(py::make_tuple(f.reportDate, f.values)); + } + return ret; + }, + R"(get_history_finance(self) + + 获取所有历史财务信息历史记录)") .def("load_kdata_to_buffer", &Stock::loadKDataToBuffer, R"(load_kdata_to_buffer(self, ktype) From 7999b8c08128cb49730f1f9ca7b8578a993373aa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 19 Apr 2024 00:08:58 +0800 Subject: [PATCH 218/601] update --- hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp index 9ac8e374..ab690a48 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp @@ -86,7 +86,8 @@ Indicator HKU_API FINANCE(int field_ix) { Indicator HKU_API FINANCE(const KData& k, int field_ix) { auto p = make_shared(k); p->setParam("field_ix", field_ix); - return p->calculate(); + p->setContext(k); + return Indicator(p); } Indicator HKU_API FINANCE(const string& field_name) { @@ -100,7 +101,8 @@ Indicator HKU_API FINANCE(const KData& k, const string& field_name) { auto p = make_shared(k); p->setParam("field_ix", -1); p->setParam("field_name", field_name); - return p->calculate(); + p->setContext(k); + return Indicator(p); } } // namespace hku \ No newline at end of file From e80ed76b0c2e552db9fc8daba850b40acb8d146e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 19 Apr 2024 01:10:00 +0800 Subject: [PATCH 219/601] update docs --- docs/source/indicator/indicator.rst | 20 ++++++++++++++++++++ docs/source/indicator/overview.rst | 2 ++ docs/source/install.rst | 2 +- 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 48790cf6..9c66331e 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -392,6 +392,18 @@ :rtype: Indicator +.. py:function:: FINANCE([kdata, ix, name]) + + 获取历史财务信息。(可通过 StockManager.get_history_finance_all_fields 查询相应的历史财务字段信息) + + ix, name 使用时,为二选一。即要不使用 ix,要不就使用 name 进行获取。 + + :param KData kdata: K线数据 + :param int ix: 历史财务信息字段索引 + :param int name: 历史财务信息字段名称 + :rtype: Indicator + + .. py:function:: FLOOR([data]) 向下舍入(向数值减小方向舍入)取整 @@ -1128,6 +1140,14 @@ :param float default_val: 如果输入的日期早于已有国债数据的最早记录,则使用此默认值 +.. py:function:: ZONGGUBEN([data]) + + 获取总股本(单位:万股) + + :param KData kdata: k线数据 + :rtype: Indicator + + .. py:function:: ZSCORE([data, out_extreme, nsigma, recursive]) 对数据进行标准化(归一),可选进行极值排除 diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index 43b9ce93..912b0f1c 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -29,11 +29,13 @@ * :py:func:`RECOVER_BACKWARD` - 后向复权 * :py:func:`RECOVER_EQUAL_FORWARD` - 等比前向复权 * :py:func:`RECOVER_EQUAL_BACKWARD` - 等比后向复权 +* :py:func:`FINANCE` - 历史财务信息 * :py:func:`HSL` - 换手率 * :py:func:`CAPITAL` - 流通盘,同名:LIUTONGPAN * :py:func:`TIMELINE` - 分时价格 * :py:func:`TIMELINEVOL` - 分时成交量 * :py:func:`ZHBOND10` - 10年期中国国债收益率 +* :py:func:`ZONGGUBEN` - 总股本 **大盘指标** diff --git a/docs/source/install.rst b/docs/source/install.rst index 34ec2ca3..380f9af2 100644 --- a/docs/source/install.rst +++ b/docs/source/install.rst @@ -6,7 +6,7 @@ 支持的操作系统:64位 Windows7及以上版本、Linux请使用源码自行编译安装。 -Python环境:>= Python3.8 +Python环境:>= Python3.9 .. note:: From 69f9baedfaa0b51366fbbd921d9f2e006b544d00 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 19 Apr 2024 04:06:32 +0800 Subject: [PATCH 220/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20mysql=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=AD=97=E7=AC=A6=E9=9B=86=EF=BC=9BHikyuuTdX=20?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E5=8F=B0=E6=97=A5=E5=BF=97=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E7=BA=A7=E5=88=AB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common_mysql.py | 36 +- hikyuu/data/mysql_upgrade/0001.sql | 9 +- hikyuu/data/mysql_upgrade/0009.sql | 78 +- hikyuu/data/mysql_upgrade/0010.sql | 12 +- hikyuu/data/mysql_upgrade/0011.sql | 11 +- hikyuu/data/mysql_upgrade/0013.sql | 2983 +++++++++++++++++++----- hikyuu/data/mysql_upgrade/createdb.sql | 352 ++- hikyuu/data/pytdx_to_mysql.py | 4 +- hikyuu/gui/HikyuuTDX.py | 4 +- hikyuu/util/mylog.py | 2 - 10 files changed, 2702 insertions(+), 789 deletions(-) diff --git a/hikyuu/data/common_mysql.py b/hikyuu/data/common_mysql.py index 9df95df9..eb6b160e 100644 --- a/hikyuu/data/common_mysql.py +++ b/hikyuu/data/common_mysql.py @@ -61,20 +61,20 @@ def create_database(connect): with open(filename, 'r', encoding='utf8') as f: sql = f.read() for x in cur.execute(sql, multi=True): - #print(x.statement) + # print(x.statement) pass db_version = get_db_version(connect) - files = [x for x in Path(sql_dir).iterdir() \ - if x.is_file() \ - and x.name != 'createdb.sql' \ - and x.name != '__init__.py' \ - and int(x.stem) > db_version and not x.is_dir()] + files = [x for x in Path(sql_dir).iterdir() + if x.is_file() + and x.name != 'createdb.sql' + and x.name != '__init__.py' + and int(x.stem) > db_version and not x.is_dir()] files.sort() for file in files: sql = file.read_text(encoding='utf8') for x in cur.execute(sql, multi=True): - #print(x.statement) + # print(x.statement) pass connect.commit() @@ -177,7 +177,7 @@ def get_table(connect, market, code, ktype): `count` DOUBLE UNSIGNED NOT NULL, PRIMARY KEY (`date`) ) - COLLATE='utf8_general_ci' + COLLATE='utf8mb4_general_ci' ENGINE=MyISAM ; """.format(schema=schema, name=tablename) @@ -252,7 +252,7 @@ def update_extern_data(connect, market, code, data_type): startdate = newdate + 1401 enddate = newdate + 1500 return (startdate, enddate) - + def getHour2Date(olddate): mint = olddate - olddate // 10000 * 10000 newdate = olddate // 10000 * 10000 @@ -263,7 +263,7 @@ def update_extern_data(connect, market, code, data_type): startdate = newdate + 1301 enddate = newdate + 1500 return (startdate, enddate) - + def getMin15Date(olddate): mint = olddate - olddate // 10000 * 10000 newdate = olddate // 10000 * 10000 @@ -374,7 +374,7 @@ def update_extern_data(connect, market, code, data_type): base_table = get_table(connect, market, code, 'day') else: index_list = ('min15', 'min30', 'min60', 'hour2') - #index_list = ('min15', ) + # index_list = ('min15', ) base_table = get_table(connect, market, code, 'min5') base_lastdate = get_lastdatetime(connect, base_table) @@ -407,7 +407,7 @@ def update_extern_data(connect, market, code, data_type): update_buffer = [] insert_buffer = [] - #for current_base in base_list: + # for current_base in base_list: length_base_all = len(base_list) for x in range(length_base_all): current_date = base_list[x][0] @@ -415,15 +415,15 @@ def update_extern_data(connect, market, code, data_type): continue last_start_date, last_end_date = getNewDate(index_type, current_date) - #cur = connect.cursor() - #cur.execute( + # cur = connect.cursor() + # cur.execute( # 'select date, open, high, low, close, amount, count from {} \ # where date>={} and date<={} order by date asc'.format( # base_table, last_start_date, last_end_date # ) - #) - #base_record_list = [r for r in cur] - #cur.close() + # ) + # base_record_list = [r for r in cur] + # cur.close() base_record_list = [] start_ix = x ix_date = current_date @@ -482,4 +482,4 @@ if __name__ == '__main__': cnx = mysql.connector.connect(user=usr, password=pwd, host=host, port=port) update_extern_data(cnx, 'SH', '000001', 'day') - cnx.close() \ No newline at end of file + cnx.close() diff --git a/hikyuu/data/mysql_upgrade/0001.sql b/hikyuu/data/mysql_upgrade/0001.sql index a732332e..45db3d11 100644 --- a/hikyuu/data/mysql_upgrade/0001.sql +++ b/hikyuu/data/mysql_upgrade/0001.sql @@ -1,6 +1,5 @@ CREATE TABLE `hku_base`.`version` ( - `version` INT(11) UNSIGNED NOT NULL -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB; -INSERT INTO `hku_base`.`version` (version) VALUES (1); + `version` INT(11) UNSIGNED NOT NULL +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; + +INSERT INTO `hku_base`.`version` (version) VALUES (1); \ No newline at end of file diff --git a/hikyuu/data/mysql_upgrade/0009.sql b/hikyuu/data/mysql_upgrade/0009.sql index 61a6b91e..be997601 100644 --- a/hikyuu/data/mysql_upgrade/0009.sql +++ b/hikyuu/data/mysql_upgrade/0009.sql @@ -1,48 +1,36 @@ CREATE TABLE IF NOT EXISTS `hku_base`.`stkfinance` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `stockid` INT UNSIGNED NOT NULL, - `updated_date` INT NOT NULL, - `ipo_date` INT NOT NULL, -- 42.上市日期 - `province` DOUBLE NOT NULL, - `industry` DOUBLE NOT NULL, - `zongguben` DOUBLE NOT NULL, -- 1.总股本(股) - `liutongguben` DOUBLE NOT NULL, -- 7.流通A股(股) - `guojiagu` DOUBLE NOT NULL, -- 2.国家股(股) - `faqirenfarengu` DOUBLE NOT NULL, -- 3.发起人法人股(股) - `farengu` DOUBLE NOT NULL, -- 4.法人股(股) - `bgu` DOUBLE NOT NULL, -- 5.B股(股) - `hgu` DOUBLE NOT NULL, -- 6.H股(股) - `zhigonggu` DOUBLE NOT NULL, -- 8.职工股(股) - `zongzichan` DOUBLE NOT NULL, -- 10.总资产(元) - `liudongzichan` DOUBLE NOT NULL, -- 11.流动资产(元) - `gudingzichan` DOUBLE NOT NULL, -- 12.固定资产(元) - `wuxingzichan` DOUBLE NOT NULL, -- 13.无形资产(元) - `gudongrenshu` DOUBLE NOT NULL, -- 股东人数 - `liudongfuzhai` DOUBLE NOT NULL, -- 15.流动负债 - `changqifuzhai` DOUBLE NOT NULL, -- 16.长期负债 - `zibengongjijin` DOUBLE NOT NULL, -- 17.资本公积金 - `jingzichan` DOUBLE NOT NULL, -- 净资产(元) - `zhuyingshouru` DOUBLE NOT NULL, -- 20.主营收入 - `zhuyinglirun` DOUBLE NOT NULL, -- 21.主营利润 - `yingshouzhangkuan` DOUBLE NOT NULL, -- 应收账款 - `yingyelirun` DOUBLE NOT NULL, -- 23.营业利润 - `touzishouyu` DOUBLE NOT NULL, -- 投资收益 - `jingyingxianjinliu` DOUBLE NOT NULL, -- 经营现金流 - `zongxianjinliu` DOUBLE NOT NULL, -- 总现金流 - `cunhuo` DOUBLE NOT NULL, -- 存货 - `lirunzonghe` DOUBLE NOT NULL, -- 28.利润总额 - `shuihoulirun` DOUBLE NOT NULL, -- 29.税后利润 - `jinglirun` DOUBLE NOT NULL, -- 30.净利润 - `weifenpeilirun` DOUBLE NOT NULL, -- 31.未分配利润 - `meigujingzichan` DOUBLE NOT NULL, -- 34.每股净资产 - `baoliu2` DOUBLE NOT NULL, - PRIMARY KEY (`id`), - FOREIGN KEY(`stockid`) REFERENCES `hku_base`.`stock` (`stockid`), - INDEX `ix_stkfinance_date` (`updated_date`), - INDEX `ix_stkfinance_stock_date` (`stockid`, `updated_date`) -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB -; + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `stockid` INT UNSIGNED NOT NULL, `updated_date` INT NOT NULL, `ipo_date` INT NOT NULL, -- 42.上市日期 + `province` DOUBLE NOT NULL, `industry` DOUBLE NOT NULL, `zongguben` DOUBLE NOT NULL, -- 1.总股本(股) + `liutongguben` DOUBLE NOT NULL, -- 7.流通A股(股) + `guojiagu` DOUBLE NOT NULL, -- 2.国家股(股) + `faqirenfarengu` DOUBLE NOT NULL, -- 3.发起人法人股(股) + `farengu` DOUBLE NOT NULL, -- 4.法人股(股) + `bgu` DOUBLE NOT NULL, -- 5.B股(股) + `hgu` DOUBLE NOT NULL, -- 6.H股(股) + `zhigonggu` DOUBLE NOT NULL, -- 8.职工股(股) + `zongzichan` DOUBLE NOT NULL, -- 10.总资产(元) + `liudongzichan` DOUBLE NOT NULL, -- 11.流动资产(元) + `gudingzichan` DOUBLE NOT NULL, -- 12.固定资产(元) + `wuxingzichan` DOUBLE NOT NULL, -- 13.无形资产(元) + `gudongrenshu` DOUBLE NOT NULL, -- 股东人数 + `liudongfuzhai` DOUBLE NOT NULL, -- 15.流动负债 + `changqifuzhai` DOUBLE NOT NULL, -- 16.长期负债 + `zibengongjijin` DOUBLE NOT NULL, -- 17.资本公积金 + `jingzichan` DOUBLE NOT NULL, -- 净资产(元) + `zhuyingshouru` DOUBLE NOT NULL, -- 20.主营收入 + `zhuyinglirun` DOUBLE NOT NULL, -- 21.主营利润 + `yingshouzhangkuan` DOUBLE NOT NULL, -- 应收账款 + `yingyelirun` DOUBLE NOT NULL, -- 23.营业利润 + `touzishouyu` DOUBLE NOT NULL, -- 投资收益 + `jingyingxianjinliu` DOUBLE NOT NULL, -- 经营现金流 + `zongxianjinliu` DOUBLE NOT NULL, -- 总现金流 + `cunhuo` DOUBLE NOT NULL, -- 存货 + `lirunzonghe` DOUBLE NOT NULL, -- 28.利润总额 + `shuihoulirun` DOUBLE NOT NULL, -- 29.税后利润 + `jinglirun` DOUBLE NOT NULL, -- 30.净利润 + `weifenpeilirun` DOUBLE NOT NULL, -- 31.未分配利润 + `meigujingzichan` DOUBLE NOT NULL, -- 34.每股净资产 + `baoliu2` DOUBLE NOT NULL, PRIMARY KEY (`id`), FOREIGN KEY (`stockid`) REFERENCES `hku_base`.`stock` (`stockid`), INDEX `ix_stkfinance_date` (`updated_date`), INDEX `ix_stkfinance_stock_date` (`stockid`, `updated_date`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; UPDATE `hku_base`.`version` set `version` = 9; \ No newline at end of file diff --git a/hikyuu/data/mysql_upgrade/0010.sql b/hikyuu/data/mysql_upgrade/0010.sql index 3719fdaf..6a08365d 100644 --- a/hikyuu/data/mysql_upgrade/0010.sql +++ b/hikyuu/data/mysql_upgrade/0010.sql @@ -1,11 +1,5 @@ -CREATE TABLE - IF NOT EXISTS `hku_base`.`block` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `category` VARCHAR(100) NOT NULL, - `name` VARCHAR(100) NOT NULL, - `market_code` VARCHAR(30) NOT NULL, - PRIMARY KEY (`id`), - INDEX `ix_block` (`category`, `name`) - ) COLLATE = 'utf8_general_ci' ENGINE = InnoDB; +CREATE TABLE IF NOT EXISTS `hku_base`.`block` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `category` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `market_code` VARCHAR(30) NOT NULL, PRIMARY KEY (`id`), INDEX `ix_block` (`category`, `name`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; UPDATE `hku_base`.`version` set `version` = 10; \ No newline at end of file diff --git a/hikyuu/data/mysql_upgrade/0011.sql b/hikyuu/data/mysql_upgrade/0011.sql index e3d06fbc..cc37a38b 100644 --- a/hikyuu/data/mysql_upgrade/0011.sql +++ b/hikyuu/data/mysql_upgrade/0011.sql @@ -1,10 +1,5 @@ -CREATE TABLE - IF NOT EXISTS `hku_base`.`zh_bond10` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `date` INT UNSIGNED NOT NULL, - `value` INT NOT NULL, - PRIMARY KEY (`id`), - INDEX `ix_date_on_zh_bond10` (`date`) - ) COLLATE = 'utf8_general_ci' ENGINE = InnoDB; +CREATE TABLE IF NOT EXISTS `hku_base`.`zh_bond10` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `date` INT UNSIGNED NOT NULL, `value` INT NOT NULL, PRIMARY KEY (`id`), INDEX `ix_date_on_zh_bond10` (`date`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; UPDATE `hku_base`.`version` set `version` = 11; \ No newline at end of file diff --git a/hikyuu/data/mysql_upgrade/0013.sql b/hikyuu/data/mysql_upgrade/0013.sql index 65968e69..998bc07e 100644 --- a/hikyuu/data/mysql_upgrade/0013.sql +++ b/hikyuu/data/mysql_upgrade/0013.sql @@ -1,602 +1,2389 @@ -CREATE TABLE - IF NOT EXISTS `hku_base`.`HistoryFinanceField` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `name` VARCHAR(200) NOT NULL, - PRIMARY KEY(`id`) - ) COLLATE = 'utf8_general_ci' ENGINE = MyISAM; +CREATE TABLE IF NOT EXISTS `hku_base`.`HistoryFinanceField` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `name` VARCHAR(200) NOT NULL, PRIMARY KEY (`id`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = MyISAM; -CREATE TABLE - IF NOT EXISTS `hku_base`.`HistoryFinance` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `file_date` INT UNSIGNED NOT NULL, - `market_code` VARCHAR(60) NOT NULL, - `report_date` INT UNSIGNED NOT NULL, - `values` BLOB NOT NULL, - PRIMARY KEY (`id`), - INDEX `ix1_on_history_finance` (`file_date`), - INDEX `ix2_on_history_finance` (`market_code`, `report_date`) - ) COLLATE = 'utf8_general_ci' ENGINE = MyISAM; +CREATE TABLE IF NOT EXISTS `hku_base`.`HistoryFinance` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `file_date` INT UNSIGNED NOT NULL, `market_code` VARCHAR(60) NOT NULL, `report_date` INT UNSIGNED NOT NULL, `values` BLOB NOT NULL, PRIMARY KEY (`id`), INDEX `ix1_on_history_finance` (`file_date`), INDEX `ix2_on_history_finance` (`market_code`, `report_date`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = MyISAM; -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (1, "基本每股收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (2, "扣除非经常性损益每股收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (3, "每股未分配利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (4, "每股净资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (5, "每股资本公积金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (6, "净资产收益率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (7, "每股经营现金流量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (8, "资产负债表_货币资金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (9, "资产负债表_交易性金融资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (10, "资产负债表_应收票据"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (11, "资产负债表_应收账款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (12, "资产负债表_预付款项"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (13, "资产负债表_其他应收款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (14, "资产负债表_应收关联公司款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (15, "资产负债表_应收利息"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (16, "资产负债表_应收股利"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (17, "资产负债表_存货"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (18, "资产负债表_消耗性生物资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (19, "资产负债表_一年内到期的非流动资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (20, "资产负债表_其他流动资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (21, "资产负债表_流动资产合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (22, "资产负债表_可供出售金融资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (23, "资产负债表_持有至到期投资"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (24, "资产负债表_长期应收款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (25, "资产负债表_长期股权投资"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (26, "资产负债表_投资性房地产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (27, "资产负债表_固定资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (28, "资产负债表_在建工程"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (29, "资产负债表_工程物资"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (30, "资产负债表_固定资产清理"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (31, "资产负债表_生产性生物资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (32, "资产负债表_油气资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (33, "资产负债表_无形资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (34, "资产负债表_开发支出"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (35, "资产负债表_商誉"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (36, "资产负债表_长期待摊费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (37, "资产负债表_递延所得税资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (38, "资产负债表_其他非流动资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (39, "资产负债表_非流动资产合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (40, "资产负债表_资产总计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (41, "资产负债表_短期借款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (42, "资产负债表_交易性金融负债"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (43, "资产负债表_应付票据"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (44, "资产负债表_应付账款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (45, "资产负债表_预收款项"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (46, "资产负债表_应付职工薪酬"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (47, "资产负债表_应交税费"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (48, "资产负债表_应付利息"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (49, "资产负债表_应付股利"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (50, "资产负债表_其他应付款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (51, "资产负债表_应付关联公司款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (52, "资产负债表_一年内到期的非流动负债"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (53, "资产负债表_其他流动负债"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (54, "资产负债表_流动负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (55, "资产负债表_长期借款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (56, "资产负债表_应付债券"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (57, "资产负债表_长期应付款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (58, "资产负债表_专项应付款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (59, "资产负债表_预计负债"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (60, "资产负债表_递延所得税负债"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (61, "资产负债表_其他非流动负债"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (62, "资产负债表_非流动负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (63, "资产负债表_负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (64, "资产负债表_实收资本(或股本)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (65, "资产负债表_资本公积"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (66, "资产负债表_盈余公积"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (67, "资产负债表_库存股"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (68, "资产负债表_未分配利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (69, "资产负债表_少数股东权益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (70, "资产负债表_外币报表折算价差"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (71, "资产负债表_非正常经营项目收益调整"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (72, "资产负债表_所有者权益(或股东权益)合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (73, "资产负债表_负债和所有者(或股东权益)合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (74, "利润表_营业收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (75, "利润表_营业成本"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (76, "利润表_营业税金及附加"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (77, "利润表_销售费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (78, "利润表_管理费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (79, "利润表_勘探费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (80, "利润表_财务费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (81, "利润表_资产减值损失"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (82, "利润表_公允价值变动净收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (83, "利润表_投资收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (84, "利润表_对联营企业和合营企业的投资收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (85, "利润表_影响营业利润的其他科目"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (86, "利润表_营业利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (87, "利润表_补贴收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (88, "利润表_营业外收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (89, "利润表_营业外支出"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (90, "利润表_非流动资产处置净损失"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (91, "利润表_影响利润总额的其他科目"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (92, "利润表_利润总额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (93, "利润表_所得税"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (94, "利润表_影响净利润的其他科目"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (95, "利润表_净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (96, "利润表_归属于母公司所有者的净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (97, "利润表_少数股东损益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (98, "现金流量表_销售商品、提供劳务收到的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (99, "现金流量表_收到的税费返还"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (100, "现金流量表_收到其他与经营活动有关的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (101, "现金流量表_经营活动现金流入小计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (102, "现金流量表_购买商品、接受劳务支付的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (103, "现金流量表_支付给职工以及为职工支付的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (104, "现金流量表_支付的各项税费"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (105, "现金流量表_支付其他与经营活动有关的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (106, "现金流量表_经营活动现金流出小计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (107, "现金流量表_经营活动产生的现金流量净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (108, "现金流量表_收回投资收到的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (109, "现金流量表_取得投资收益收到的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (110, "现金流量表_处置固定资产、无形资产和其他长期资产收回的现金净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (111, "现金流量表_处置子公司及其他营业单位收到的现金净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (112, "现金流量表_收到其他与投资活动有关的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (113, "现金流量表_投资活动现金流入小计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (114, "现金流量表_购建固定资产、无形资产和其他长期资产支付的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (115, "现金流量表_投资支付的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (116, "现金流量表_取得子公司及其他营业单位支付的现金净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (117, "现金流量表_支付其他与投资活动有关的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (118, "现金流量表_投资活动现金流出小计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (119, "现金流量表_投资活动产生的现金流量净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (120, "现金流量表_吸收投资收到的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (121, "现金流量表_取得借款收到的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (122, "现金流量表_收到其他与筹资活动有关的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (123, "现金流量表_筹资活动现金流入小计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (124, "现金流量表_偿还债务支付的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (125, "现金流量表_分配股利、利润或偿付利息支付的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (126, "现金流量表_支付其他与筹资活动有关的现金"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (127, "现金流量表_筹资活动现金流出小计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (128, "现金流量表_筹资活动产生的现金流量净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (129, "现金流量表_汇率变动对现金的影响"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (130, "现金流量表_其他原因对现金的影响"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (131, "现金流量表_现金及现金等价物净增加额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (132, "现金流量表_期初现金及现金等价物余额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (133, "现金流量表_期末现金及现金等价物余额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (134, "现金流量表_净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (135, "现金流量表_资产减值准备"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (136, "现金流量表_固定资产折旧、油气资产折耗、生产性生物资产折旧"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (137, "现金流量表_无形资产摊销"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (138, "现金流量表_长期待摊费用摊销"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (139, "现金流量表_处置固定资产、无形资产和其他长期资产的损失"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (140, "现金流量表_固定资产报废损失"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (141, "现金流量表_公允价值变动损失"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (142, "现金流量表_财务费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (143, "现金流量表_投资损失"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (144, "现金流量表_递延所得税资产减少"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (145, "现金流量表_递延所得税负债增加"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (146, "现金流量表_存货的减少"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (147, "现金流量表_经营性应收项目的减少"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (148, "现金流量表_经营性应付项目的增加"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (149, "现金流量表_其他"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (150, "现金流量表_经营活动产生的现金流量净额2"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (151, "现金流量表_债务转为资本"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (152, "现金流量表_一年内到期的可转换公司债券"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (153, "现金流量表_融资租入固定资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (154, "现金流量表_现金的期末余额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (155, "现金流量表_现金的期初余额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (156, "现金流量表_现金等价物的期末余额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (157, "现金流量表_现金等价物的期初余额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (158, "现金流量表_现金及现金等价物净增加额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (159, "偿债能力_流动比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (160, "偿债能力_速动比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (161, "偿债能力_现金比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (162, "偿债能力_利息保障倍数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (163, "偿债能力_非流动负债比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (164, "偿债能力_流动负债比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (165, "偿债能力_现金到期债务比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (166, "偿债能力_有形资产净值债务率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (167, "偿债能力_权益乘数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (168, "偿债能力_股东的权益/负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (169, "偿债能力_有形资产/负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (170, "偿债能力_经营活动产生的现金流量净额/负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (171, "偿债能力_EBITDA/负债合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (172, "营运能力_应收帐款周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (173, "营运能力_存货周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (174, "营运能力_运营资金周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (175, "营运能力_总资产周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (176, "营运能力_固定资产周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (177, "营运能力_应收帐款周转天数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (178, "营运能力_存货周转天数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (179, "营运能力_流动资产周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (180, "营运能力_流动资产周转天数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (181, "营运能力_总资产周转天数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (182, "营运能力_股东权益周转率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (183, "成长能力_营业收入增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (184, "成长能力_净利润增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (185, "成长能力_净资产增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (186, "成长能力_固定资产增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (187, "成长能力_总资产增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (188, "成长能力_投资收益增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (189, "成长能力_营业利润增长率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (190, "成长能力_扣非每股收益同比"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (191, "成长能力_扣非净利润同比"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (192, "成长能力_暂无"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (193, "盈利能力_成本费用利润率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (194, "盈利能力_营业利润率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (195, "盈利能力_营业税金率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (196, "盈利能力_营业成本率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (197, "盈利能力_净资产收益率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (198, "盈利能力_投资收益率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (199, "盈利能力_销售净利率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (200, "盈利能力_总资产报酬率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (201, "盈利能力_净利润率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (202, "盈利能力_销售毛利率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (203, "盈利能力_三费比重"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (204, "盈利能力_管理费用率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (205, "盈利能力_财务费用率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (206, "盈利能力_扣除非经常性损益后的净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (207, "盈利能力_息税前利润(EBIT)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (208, "盈利能力_息税折旧摊销前利润(EBITDA)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (209, "盈利能力_EBITDA/营业总收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (210, "资本结构_资产负债率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (211, "资本结构_流动资产比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (212, "资本结构_货币资金比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (213, "资本结构_存货比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (214, "资本结构_固定资产比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (215, "资本结构_负债结构比"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (216, "资本结构_归属于母公司股东权益/全部投入资本"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (217, "资本结构_股东的权益/带息债务"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (218, "资本结构_有形资产/净债务"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (219, "现金能力_每股经营性现金流"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (220, "现金能力_营业收入现金含量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (221, "现金能力_经营活动产生的现金流量净额/经营活动净收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (222, "现金能力_销售商品提供劳务收到的现金/营业收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (223, "现金能力_经营活动产生的现金流量净额/营业收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (224, "现金能力_资本支出/折旧和摊销"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (225, "现金能力_每股现金流量净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (226, "现金能力_经营净现金比率(短期债务)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (227, "现金能力_经营净现金比率(全部债务)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (228, "现金能力_经营活动现金净流量与净利润比率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (229, "现金能力_全部资产现金回收率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (230, "利润表_营业收入_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (231, "利润表_营业利润_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (232, "利润表_归属于母公司所有者的净利润_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (233, "利润表_扣除非经常性损益后的净利润_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (234, "现金流量表_经营活动产生的现金流量净额_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (235, "现金流量表_投资活动产生的现金流量净额_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (236, "现金流量表_筹资活动产生的现金流量净额_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (237, "现金流量表_现金及现金等价物净增加额_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (238, "股本股东_总股本"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (239, "股本股东_已上市流通A股"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (240, "股本股东_已上市流通B股"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (241, "股本股东_已上市流通H股"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (242, "股本股东_股东人数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (243, "股本股东_第一大股东的持股数量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (244, "股本股东_十大流通股东持股数量合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (245, "股本股东_十大股东持股数量合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (246, "股本股东_机构总量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (247, "股本股东_机构持股总量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (248, "股本股东_QFII机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (249, "股本股东_QFII持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (250, "股本股东_券商机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (251, "股本股东_券商持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (252, "股本股东_保险机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (253, "股本股东_保险持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (254, "股本股东_基金机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (255, "股本股东_基金持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (256, "股本股东_社保机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (257, "股本股东_社保持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (258, "股本股东_私募机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (259, "股本股东_私募持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (260, "股本股东_财务公司机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (261, "股本股东_财务公司持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (262, "股本股东_年金机构数"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (263, "股本股东_年金持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (264, "股本股东_十大流通股东中持有A股合计"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (265, "股本股东_第一大流通股东持股量"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (266, "股本股东_自由流通股"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (267, "股本股东_受限流通A股"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (268, "资产负债表_一般风险准备"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (269, "利润表_其他综合收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (270, "利润表_综合收益总额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (271, "资产负债表_归属于母公司股东权益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (272, "股本股东_银行机构数(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (273, "股本股东_银行持股量(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (274, "股本股东_一般法人机构数(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (275, "股本股东_一般法人持股量(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (276, "利润表_近一年净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (277, "股本股东_信托机构数(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (278, "股本股东_信托持股量(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (279, "股本股东_特殊法人机构数(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (280, "股本股东_特殊法人持股量(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (281, "盈利能力_加权净资产收益率(每股指标)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (282, "利润表_扣非每股收益_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (283, "利润表_最近一年营业收入(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (284, "股本股东_国家队持股数量(万股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (285, "业绩预告_本期净利润同比增幅下限%"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (286, "业绩预告_本期净利润同比增幅上限%"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (287, "业绩快报_归母净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (288, "业绩快报_扣非净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (289, "业绩快报_总资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (290, "业绩快报_净资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (291, "业绩快报_每股收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (292, "业绩快报_摊薄净资产收益率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (293, "业绩快报_加权净资产收益率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (294, "业绩快报_每股净资产"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (295, "资产负债表_应付票据及应付账款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (296, "资产负债表_应收票据及应收账款"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (297, "资产负债表_递延收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (298, "资产负债表_其他综合收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (299, "资产负债表_其他权益工具"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (300, "利润表_其他收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (301, "利润表_资产处置收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (302, "利润表_持续经营净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (303, "利润表_终止经营净利润"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (304, "利润表_研发费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (305, "利润表_利息费用"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (306, "利润表_利息收入"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (307, "现金流量表_近一年经营活动现金流净额"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (308, "现金流量表_近一年归母净利润(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (309, "现金流量表_近一年扣非净利润(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (310, "现金流量表_近一年现金净流量(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (311, "利润表_基本每股收益_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (312, "利润表_营业总收入(万元)_单季度"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (313, "业绩预告公告日期 "); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (314, "财报公告日期"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (315, "业绩快报公告日期"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (316, "现金流量表_近一年投资活动现金流净额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (317, "业绩预告_业绩预告-本期净利润下限(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (318, "业绩预告_业绩预告-本期净利润上限(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (319, "利润表_营业总收入TTM(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (320, "员工总数(人)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (321, "现金流量表_每股企业自由现金流"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (322, "现金流量表_每股股东自由现金流"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (323, "近一年营业利润(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (324, "净利润(单季度)(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (325, "北上资金数(家)(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (326, "北上资金持股量(股)(机构持股)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (327, "有息负债率"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (328, "营业成本(单季度)(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (329, "投入资本回报率(ROIC)(获利能力分析)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (330, "业绩快报-营业收入(本期)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (331, "业绩快报-营业收入(上期)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (332, "业绩快报-营业利润(本期)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (333, "业绩快报-营业利润(上期)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (334, "业绩快报-利润总额(本期)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (335, "业绩快报-利润总额(上期)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (336, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (337, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (338, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (339, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (340, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (341, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (342, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (343, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (344, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (345, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (346, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (347, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (348, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (349, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (350, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (351, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (352, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (353, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (354, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (355, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (356, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (357, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (358, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (359, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (360, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (361, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (362, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (363, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (364, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (365, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (366, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (367, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (368, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (369, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (370, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (371, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (372, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (373, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (374, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (375, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (376, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (377, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (378, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (379, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (380, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (381, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (382, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (383, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (384, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (385, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (386, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (387, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (388, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (389, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (390, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (391, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (392, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (393, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (394, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (395, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (396, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (397, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (398, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (399, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (400, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (401, "资产负债表_专项储备(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (402, "资产负债表_结算备付金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (403, "资产负债表_拆出资金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (404, "资产负债表_发放贷款及垫款(万元)(流动资产科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (405, "资产负债表_衍生金融资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (406, "资产负债表_应收保费(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (407, "资产负债表_应收分保账款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (408, "资产负债表_应收分保合同准备金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (409, "资产负债表_买入返售金融资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (410, "资产负债表_划分为持有待售的资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (411, "资产负债表_发放贷款及垫款(万元)(非流动资产科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (412, "资产负债表_向中央银行借款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (413, "资产负债表_吸收存款及同业存放(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (414, "资产负债表_拆入资金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (415, "资产负债表_衍生金融负债(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (416, "资产负债表_卖出回购金融资产款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (417, "资产负债表_应付手续费及佣金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (418, "资产负债表_应付分保账款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (419, "资产负债表_保险合同准备金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (420, "资产负债表_代理买卖证券款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (421, "资产负债表_代理承销证券款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (422, "资产负债表_划分为持有待售的负债(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (423, "资产负债表_预计负债(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (424, "资产负债表_递延收益(万元)(流动负债科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (425, "资产负债表_其中:优先股(万元)(非流动负债科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (426, "资产负债表_永续债(万元)(非流动负债科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (427, "资产负债表_长期应付职工薪酬(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (428, "资产负债表_其中:优先股(万元)(所有者权益科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (429, "资产负债表_永续债(万元)(所有者权益科目)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (430, "资产负债表_债权投资(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (431, "资产负债表_其他债权投资(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (432, "资产负债表_其他权益工具投资(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (433, "资产负债表_其他非流动金融资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (434, "资产负债表_合同负债(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (435, "资产负债表_合同资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (436, "资产负债表_其他资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (437, "资产负债表_应收款项融资(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (438, "资产负债表_使用权资产(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (439, "资产负债表_租赁负债(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (440, "发放贷款及垫款(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (441, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (442, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (443, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (444, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (445, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (446, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (447, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (448, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (449, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (450, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (451, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (452, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (453, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (454, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (455, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (456, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (457, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (458, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (459, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (460, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (461, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (462, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (463, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (464, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (465, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (466, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (467, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (468, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (469, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (470, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (471, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (472, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (473, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (474, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (475, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (476, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (477, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (478, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (479, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (480, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (481, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (482, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (483, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (484, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (485, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (486, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (487, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (488, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (489, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (490, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (491, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (492, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (493, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (494, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (495, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (496, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (497, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (498, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (499, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (500, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (501, "利润表_稀释每股收益"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (502, "利润表_营业总收入(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (503, "利润表_汇兑收益(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (504, "利润表_其中:归属于母公司综合收益(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (505, "利润表_其中:归属于少数股东综合收益(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (506, "利润表_利息收入(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (507, "利润表_已赚保费(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (508, "利润表_手续费及佣金收入(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (509, "利润表_利息支出(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (510, "利润表_手续费及佣金支出(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (511, "利润表_退保金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (512, "利润表_赔付支出净额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (513, "利润表_提取保险合同准备金净额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (514, "利润表_保单红利支出(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (515, "利润表_分保费用(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (516, "利润表_其中:非流动资产处置利得(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (517, "利润表_信用减值损失(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (518, "利润表_净敞口套期收益(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (519, "利润表_营业总成本(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (520, "利润表_信用减值损失(万元、2019格式)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (521, "利润表_资产减值损失(万元、2019格式)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (522, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (523, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (524, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (525, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (526, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (527, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (528, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (529, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (530, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (531, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (532, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (533, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (534, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (535, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (536, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (537, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (538, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (539, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (540, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (541, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (542, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (543, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (544, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (545, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (546, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (547, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (548, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (549, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (550, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (551, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (552, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (553, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (554, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (555, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (556, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (557, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (558, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (559, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (560, "未知"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (561, "现金流量表_加:其他原因对现金的影响(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (562, "现金流量表_客户存款和同业存放款项净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (563, "现金流量表_向中央银行借款净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (564, "现金流量表_向其他金融机构拆入资金净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (565, "现金流量表_收到原保险合同保费取得的现金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (566, "现金流量表_收到再保险业务现金净额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (567, "现金流量表_保户储金及投资款净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (568, "现金流量表_处置以公允价值计量且其变动计入当期损益的金融资产净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (569, "现金流量表_收取利息、手续费及佣金的现金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (570, "现金流量表_拆入资金净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (571, "现金流量表_回购业务资金净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (572, "现金流量表_客户贷款及垫款净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (573, "现金流量表_存放中央银行和同业款项净增加额(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (574, "现金流量表_支付原保险合同赔付款项的现金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (575, "现金流量表_支付利息、手续费及佣金的现金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (576, "现金流量表_支付保单红利的现金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (577, "现金流量表_其中:子公司吸收少数股东投资收到的现金(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (578, "现金流量表_其中:子公司支付给少数股东的股利、利润(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (579, "现金流量表_投资性房地产的折旧及摊销(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (580, "现金流量表_信用减值损失(万元)"); -INSERT INTO `hku_base`.`HistoryFinanceField` (`id`, `name`) VALUES (581, "使用权资产折旧(万元)"); +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (1, "基本每股收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (2, "扣除非经常性损益每股收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (3, "每股未分配利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (4, "每股净资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (5, "每股资本公积金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (6, "净资产收益率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (7, "每股经营现金流量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (8, "资产负债表_货币资金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (9, "资产负债表_交易性金融资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (10, "资产负债表_应收票据"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (11, "资产负债表_应收账款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (12, "资产负债表_预付款项"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (13, "资产负债表_其他应收款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (14, "资产负债表_应收关联公司款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (15, "资产负债表_应收利息"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (16, "资产负债表_应收股利"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (17, "资产负债表_存货"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (18, "资产负债表_消耗性生物资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (19, "资产负债表_一年内到期的非流动资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (20, "资产负债表_其他流动资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (21, "资产负债表_流动资产合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (22, "资产负债表_可供出售金融资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (23, "资产负债表_持有至到期投资"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (24, "资产负债表_长期应收款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (25, "资产负债表_长期股权投资"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (26, "资产负债表_投资性房地产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (27, "资产负债表_固定资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (28, "资产负债表_在建工程"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (29, "资产负债表_工程物资"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (30, "资产负债表_固定资产清理"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (31, "资产负债表_生产性生物资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (32, "资产负债表_油气资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (33, "资产负债表_无形资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (34, "资产负债表_开发支出"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (35, "资产负债表_商誉"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (36, "资产负债表_长期待摊费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (37, "资产负债表_递延所得税资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (38, "资产负债表_其他非流动资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (39, "资产负债表_非流动资产合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (40, "资产负债表_资产总计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (41, "资产负债表_短期借款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (42, "资产负债表_交易性金融负债"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (43, "资产负债表_应付票据"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (44, "资产负债表_应付账款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (45, "资产负债表_预收款项"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (46, "资产负债表_应付职工薪酬"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (47, "资产负债表_应交税费"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (48, "资产负债表_应付利息"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (49, "资产负债表_应付股利"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (50, "资产负债表_其他应付款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (51, "资产负债表_应付关联公司款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (52, "资产负债表_一年内到期的非流动负债"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (53, "资产负债表_其他流动负债"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (54, "资产负债表_流动负债合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (55, "资产负债表_长期借款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (56, "资产负债表_应付债券"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (57, "资产负债表_长期应付款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (58, "资产负债表_专项应付款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (59, "资产负债表_预计负债"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (60, "资产负债表_递延所得税负债"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (61, "资产负债表_其他非流动负债"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (62, "资产负债表_非流动负债合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (63, "资产负债表_负债合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (64, "资产负债表_实收资本(或股本)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (65, "资产负债表_资本公积"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (66, "资产负债表_盈余公积"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (67, "资产负债表_库存股"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (68, "资产负债表_未分配利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (69, "资产负债表_少数股东权益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (70, "资产负债表_外币报表折算价差"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (71, "资产负债表_非正常经营项目收益调整"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (72, "资产负债表_所有者权益(或股东权益)合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (73, "资产负债表_负债和所有者(或股东权益)合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (74, "利润表_营业收入"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (75, "利润表_营业成本"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (76, "利润表_营业税金及附加"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (77, "利润表_销售费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (78, "利润表_管理费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (79, "利润表_勘探费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (80, "利润表_财务费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (81, "利润表_资产减值损失"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (82, "利润表_公允价值变动净收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (83, "利润表_投资收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (84, "利润表_对联营企业和合营企业的投资收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (85, "利润表_影响营业利润的其他科目"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (86, "利润表_营业利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (87, "利润表_补贴收入"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (88, "利润表_营业外收入"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (89, "利润表_营业外支出"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (90, "利润表_非流动资产处置净损失"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (91, "利润表_影响利润总额的其他科目"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (92, "利润表_利润总额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (93, "利润表_所得税"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (94, "利润表_影响净利润的其他科目"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (95, "利润表_净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (96, "利润表_归属于母公司所有者的净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (97, "利润表_少数股东损益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (98, "现金流量表_销售商品、提供劳务收到的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (99, "现金流量表_收到的税费返还"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (100, "现金流量表_收到其他与经营活动有关的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (101, "现金流量表_经营活动现金流入小计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (102, "现金流量表_购买商品、接受劳务支付的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (103, "现金流量表_支付给职工以及为职工支付的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (104, "现金流量表_支付的各项税费"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (105, "现金流量表_支付其他与经营活动有关的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (106, "现金流量表_经营活动现金流出小计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (107, "现金流量表_经营活动产生的现金流量净额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (108, "现金流量表_收回投资收到的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (109, "现金流量表_取得投资收益收到的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 110, "现金流量表_处置固定资产、无形资产和其他长期资产收回的现金净额" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 111, "现金流量表_处置子公司及其他营业单位收到的现金净额" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (112, "现金流量表_收到其他与投资活动有关的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (113, "现金流量表_投资活动现金流入小计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 114, "现金流量表_购建固定资产、无形资产和其他长期资产支付的现金" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (115, "现金流量表_投资支付的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 116, "现金流量表_取得子公司及其他营业单位支付的现金净额" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (117, "现金流量表_支付其他与投资活动有关的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (118, "现金流量表_投资活动现金流出小计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (119, "现金流量表_投资活动产生的现金流量净额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (120, "现金流量表_吸收投资收到的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (121, "现金流量表_取得借款收到的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (122, "现金流量表_收到其他与筹资活动有关的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (123, "现金流量表_筹资活动现金流入小计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (124, "现金流量表_偿还债务支付的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 125, "现金流量表_分配股利、利润或偿付利息支付的现金" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (126, "现金流量表_支付其他与筹资活动有关的现金"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (127, "现金流量表_筹资活动现金流出小计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (128, "现金流量表_筹资活动产生的现金流量净额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (129, "现金流量表_汇率变动对现金的影响"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (130, "现金流量表_其他原因对现金的影响"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (131, "现金流量表_现金及现金等价物净增加额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (132, "现金流量表_期初现金及现金等价物余额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (133, "现金流量表_期末现金及现金等价物余额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (134, "现金流量表_净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (135, "现金流量表_资产减值准备"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 136, "现金流量表_固定资产折旧、油气资产折耗、生产性生物资产折旧" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (137, "现金流量表_无形资产摊销"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (138, "现金流量表_长期待摊费用摊销"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 139, "现金流量表_处置固定资产、无形资产和其他长期资产的损失" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (140, "现金流量表_固定资产报废损失"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (141, "现金流量表_公允价值变动损失"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (142, "现金流量表_财务费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (143, "现金流量表_投资损失"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (144, "现金流量表_递延所得税资产减少"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (145, "现金流量表_递延所得税负债增加"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (146, "现金流量表_存货的减少"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (147, "现金流量表_经营性应收项目的减少"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (148, "现金流量表_经营性应付项目的增加"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (149, "现金流量表_其他"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (150, "现金流量表_经营活动产生的现金流量净额2"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (151, "现金流量表_债务转为资本"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (152, "现金流量表_一年内到期的可转换公司债券"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (153, "现金流量表_融资租入固定资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (154, "现金流量表_现金的期末余额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (155, "现金流量表_现金的期初余额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (156, "现金流量表_现金等价物的期末余额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (157, "现金流量表_现金等价物的期初余额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (158, "现金流量表_现金及现金等价物净增加额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (159, "偿债能力_流动比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (160, "偿债能力_速动比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (161, "偿债能力_现金比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (162, "偿债能力_利息保障倍数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (163, "偿债能力_非流动负债比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (164, "偿债能力_流动负债比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (165, "偿债能力_现金到期债务比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (166, "偿债能力_有形资产净值债务率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (167, "偿债能力_权益乘数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (168, "偿债能力_股东的权益/负债合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (169, "偿债能力_有形资产/负债合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 170, "偿债能力_经营活动产生的现金流量净额/负债合计" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (171, "偿债能力_EBITDA/负债合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (172, "营运能力_应收帐款周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (173, "营运能力_存货周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (174, "营运能力_运营资金周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (175, "营运能力_总资产周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (176, "营运能力_固定资产周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (177, "营运能力_应收帐款周转天数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (178, "营运能力_存货周转天数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (179, "营运能力_流动资产周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (180, "营运能力_流动资产周转天数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (181, "营运能力_总资产周转天数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (182, "营运能力_股东权益周转率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (183, "成长能力_营业收入增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (184, "成长能力_净利润增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (185, "成长能力_净资产增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (186, "成长能力_固定资产增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (187, "成长能力_总资产增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (188, "成长能力_投资收益增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (189, "成长能力_营业利润增长率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (190, "成长能力_扣非每股收益同比"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (191, "成长能力_扣非净利润同比"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (192, "成长能力_暂无"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (193, "盈利能力_成本费用利润率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (194, "盈利能力_营业利润率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (195, "盈利能力_营业税金率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (196, "盈利能力_营业成本率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (197, "盈利能力_净资产收益率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (198, "盈利能力_投资收益率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (199, "盈利能力_销售净利率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (200, "盈利能力_总资产报酬率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (201, "盈利能力_净利润率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (202, "盈利能力_销售毛利率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (203, "盈利能力_三费比重"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (204, "盈利能力_管理费用率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (205, "盈利能力_财务费用率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (206, "盈利能力_扣除非经常性损益后的净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (207, "盈利能力_息税前利润(EBIT)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (208, "盈利能力_息税折旧摊销前利润(EBITDA)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (209, "盈利能力_EBITDA/营业总收入"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (210, "资本结构_资产负债率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (211, "资本结构_流动资产比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (212, "资本结构_货币资金比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (213, "资本结构_存货比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (214, "资本结构_固定资产比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (215, "资本结构_负债结构比"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (216, "资本结构_归属于母公司股东权益/全部投入资本"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (217, "资本结构_股东的权益/带息债务"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (218, "资本结构_有形资产/净债务"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (219, "现金能力_每股经营性现金流"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (220, "现金能力_营业收入现金含量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 221, "现金能力_经营活动产生的现金流量净额/经营活动净收益" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 222, "现金能力_销售商品提供劳务收到的现金/营业收入" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 223, "现金能力_经营活动产生的现金流量净额/营业收入" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (224, "现金能力_资本支出/折旧和摊销"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (225, "现金能力_每股现金流量净额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (226, "现金能力_经营净现金比率(短期债务)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (227, "现金能力_经营净现金比率(全部债务)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (228, "现金能力_经营活动现金净流量与净利润比率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (229, "现金能力_全部资产现金回收率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (230, "利润表_营业收入_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (231, "利润表_营业利润_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (232, "利润表_归属于母公司所有者的净利润_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (233, "利润表_扣除非经常性损益后的净利润_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 234, "现金流量表_经营活动产生的现金流量净额_单季度" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 235, "现金流量表_投资活动产生的现金流量净额_单季度" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 236, "现金流量表_筹资活动产生的现金流量净额_单季度" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (237, "现金流量表_现金及现金等价物净增加额_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (238, "股本股东_总股本"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (239, "股本股东_已上市流通A股"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (240, "股本股东_已上市流通B股"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (241, "股本股东_已上市流通H股"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (242, "股本股东_股东人数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (243, "股本股东_第一大股东的持股数量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (244, "股本股东_十大流通股东持股数量合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (245, "股本股东_十大股东持股数量合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (246, "股本股东_机构总量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (247, "股本股东_机构持股总量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (248, "股本股东_QFII机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (249, "股本股东_QFII持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (250, "股本股东_券商机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (251, "股本股东_券商持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (252, "股本股东_保险机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (253, "股本股东_保险持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (254, "股本股东_基金机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (255, "股本股东_基金持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (256, "股本股东_社保机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (257, "股本股东_社保持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (258, "股本股东_私募机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (259, "股本股东_私募持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (260, "股本股东_财务公司机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (261, "股本股东_财务公司持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (262, "股本股东_年金机构数"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (263, "股本股东_年金持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (264, "股本股东_十大流通股东中持有A股合计"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (265, "股本股东_第一大流通股东持股量"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (266, "股本股东_自由流通股"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (267, "股本股东_受限流通A股"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (268, "资产负债表_一般风险准备"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (269, "利润表_其他综合收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (270, "利润表_综合收益总额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (271, "资产负债表_归属于母公司股东权益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (272, "股本股东_银行机构数(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (273, "股本股东_银行持股量(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (274, "股本股东_一般法人机构数(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (275, "股本股东_一般法人持股量(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (276, "利润表_近一年净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (277, "股本股东_信托机构数(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (278, "股本股东_信托持股量(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (279, "股本股东_特殊法人机构数(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (280, "股本股东_特殊法人持股量(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (281, "盈利能力_加权净资产收益率(每股指标)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (282, "利润表_扣非每股收益_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (283, "利润表_最近一年营业收入(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (284, "股本股东_国家队持股数量(万股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (285, "业绩预告_本期净利润同比增幅下限%"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (286, "业绩预告_本期净利润同比增幅上限%"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (287, "业绩快报_归母净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (288, "业绩快报_扣非净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (289, "业绩快报_总资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (290, "业绩快报_净资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (291, "业绩快报_每股收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (292, "业绩快报_摊薄净资产收益率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (293, "业绩快报_加权净资产收益率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (294, "业绩快报_每股净资产"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (295, "资产负债表_应付票据及应付账款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (296, "资产负债表_应收票据及应收账款"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (297, "资产负债表_递延收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (298, "资产负债表_其他综合收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (299, "资产负债表_其他权益工具"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (300, "利润表_其他收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (301, "利润表_资产处置收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (302, "利润表_持续经营净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (303, "利润表_终止经营净利润"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (304, "利润表_研发费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (305, "利润表_利息费用"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (306, "利润表_利息收入"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (307, "现金流量表_近一年经营活动现金流净额"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (308, "现金流量表_近一年归母净利润(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (309, "现金流量表_近一年扣非净利润(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (310, "现金流量表_近一年现金净流量(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (311, "利润表_基本每股收益_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (312, "利润表_营业总收入(万元)_单季度"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (313, "业绩预告公告日期 "); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (314, "财报公告日期"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (315, "业绩快报公告日期"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (316, "现金流量表_近一年投资活动现金流净额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (317, "业绩预告_业绩预告-本期净利润下限(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (318, "业绩预告_业绩预告-本期净利润上限(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (319, "利润表_营业总收入TTM(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (320, "员工总数(人)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (321, "现金流量表_每股企业自由现金流"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (322, "现金流量表_每股股东自由现金流"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (323, "近一年营业利润(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (324, "净利润(单季度)(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (325, "北上资金数(家)(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (326, "北上资金持股量(股)(机构持股)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (327, "有息负债率"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (328, "营业成本(单季度)(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (329, "投入资本回报率(ROIC)(获利能力分析)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (330, "业绩快报-营业收入(本期)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (331, "业绩快报-营业收入(上期)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (332, "业绩快报-营业利润(本期)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (333, "业绩快报-营业利润(上期)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (334, "业绩快报-利润总额(本期)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (335, "业绩快报-利润总额(上期)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (336, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (337, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (338, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (339, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (340, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (341, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (342, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (343, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (344, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (345, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (346, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (347, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (348, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (349, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (350, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (351, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (352, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (353, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (354, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (355, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (356, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (357, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (358, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (359, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (360, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (361, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (362, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (363, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (364, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (365, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (366, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (367, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (368, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (369, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (370, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (371, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (372, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (373, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (374, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (375, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (376, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (377, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (378, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (379, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (380, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (381, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (382, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (383, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (384, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (385, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (386, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (387, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (388, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (389, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (390, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (391, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (392, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (393, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (394, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (395, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (396, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (397, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (398, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (399, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (400, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (401, "资产负债表_专项储备(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (402, "资产负债表_结算备付金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (403, "资产负债表_拆出资金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 404, "资产负债表_发放贷款及垫款(万元)(流动资产科目)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (405, "资产负债表_衍生金融资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (406, "资产负债表_应收保费(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (407, "资产负债表_应收分保账款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (408, "资产负债表_应收分保合同准备金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (409, "资产负债表_买入返售金融资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (410, "资产负债表_划分为持有待售的资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 411, "资产负债表_发放贷款及垫款(万元)(非流动资产科目)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (412, "资产负债表_向中央银行借款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (413, "资产负债表_吸收存款及同业存放(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (414, "资产负债表_拆入资金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (415, "资产负债表_衍生金融负债(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (416, "资产负债表_卖出回购金融资产款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (417, "资产负债表_应付手续费及佣金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (418, "资产负债表_应付分保账款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (419, "资产负债表_保险合同准备金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (420, "资产负债表_代理买卖证券款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (421, "资产负债表_代理承销证券款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (422, "资产负债表_划分为持有待售的负债(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (423, "资产负债表_预计负债(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (424, "资产负债表_递延收益(万元)(流动负债科目)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 425, "资产负债表_其中:优先股(万元)(非流动负债科目)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (426, "资产负债表_永续债(万元)(非流动负债科目)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (427, "资产负债表_长期应付职工薪酬(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 428, "资产负债表_其中:优先股(万元)(所有者权益科目)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (429, "资产负债表_永续债(万元)(所有者权益科目)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (430, "资产负债表_债权投资(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (431, "资产负债表_其他债权投资(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (432, "资产负债表_其他权益工具投资(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (433, "资产负债表_其他非流动金融资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (434, "资产负债表_合同负债(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (435, "资产负债表_合同资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (436, "资产负债表_其他资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (437, "资产负债表_应收款项融资(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (438, "资产负债表_使用权资产(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (439, "资产负债表_租赁负债(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (440, "发放贷款及垫款(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (441, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (442, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (443, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (444, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (445, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (446, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (447, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (448, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (449, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (450, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (451, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (452, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (453, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (454, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (455, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (456, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (457, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (458, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (459, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (460, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (461, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (462, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (463, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (464, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (465, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (466, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (467, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (468, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (469, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (470, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (471, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (472, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (473, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (474, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (475, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (476, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (477, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (478, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (479, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (480, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (481, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (482, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (483, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (484, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (485, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (486, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (487, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (488, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (489, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (490, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (491, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (492, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (493, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (494, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (495, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (496, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (497, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (498, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (499, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (500, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (501, "利润表_稀释每股收益"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (502, "利润表_营业总收入(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (503, "利润表_汇兑收益(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (504, "利润表_其中:归属于母公司综合收益(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (505, "利润表_其中:归属于少数股东综合收益(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (506, "利润表_利息收入(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (507, "利润表_已赚保费(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (508, "利润表_手续费及佣金收入(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (509, "利润表_利息支出(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (510, "利润表_手续费及佣金支出(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (511, "利润表_退保金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (512, "利润表_赔付支出净额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (513, "利润表_提取保险合同准备金净额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (514, "利润表_保单红利支出(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (515, "利润表_分保费用(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (516, "利润表_其中:非流动资产处置利得(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (517, "利润表_信用减值损失(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (518, "利润表_净敞口套期收益(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (519, "利润表_营业总成本(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (520, "利润表_信用减值损失(万元、2019格式)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (521, "利润表_资产减值损失(万元、2019格式)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (522, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (523, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (524, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (525, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (526, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (527, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (528, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (529, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (530, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (531, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (532, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (533, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (534, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (535, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (536, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (537, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (538, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (539, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (540, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (541, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (542, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (543, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (544, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (545, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (546, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (547, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (548, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (549, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (550, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (551, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (552, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (553, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (554, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (555, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (556, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (557, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (558, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (559, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (560, "未知"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (561, "现金流量表_加:其他原因对现金的影响(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 562, "现金流量表_客户存款和同业存放款项净增加额(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (563, "现金流量表_向中央银行借款净增加额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 564, "现金流量表_向其他金融机构拆入资金净增加额(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 565, "现金流量表_收到原保险合同保费取得的现金(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (566, "现金流量表_收到再保险业务现金净额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (567, "现金流量表_保户储金及投资款净增加额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 568, "现金流量表_处置以公允价值计量且其变动计入当期损益的金融资产净增加额(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 569, "现金流量表_收取利息、手续费及佣金的现金(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (570, "现金流量表_拆入资金净增加额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (571, "现金流量表_回购业务资金净增加额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (572, "现金流量表_客户贷款及垫款净增加额(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 573, "现金流量表_存放中央银行和同业款项净增加额(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 574, "现金流量表_支付原保险合同赔付款项的现金(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 575, "现金流量表_支付利息、手续费及佣金的现金(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (576, "现金流量表_支付保单红利的现金(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 577, "现金流量表_其中:子公司吸收少数股东投资收到的现金(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES ( + 578, "现金流量表_其中:子公司支付给少数股东的股利、利润(万元)" + ); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (579, "现金流量表_投资性房地产的折旧及摊销(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (580, "现金流量表_信用减值损失(万元)"); + +INSERT INTO + `hku_base`.`HistoryFinanceField` (`id`, `name`) +VALUES (581, "使用权资产折旧(万元)"); UPDATE `hku_base`.`version` set `version` = 13; \ No newline at end of file diff --git a/hikyuu/data/mysql_upgrade/createdb.sql b/hikyuu/data/mysql_upgrade/createdb.sql index 8a6ea6e7..24b15a5a 100644 --- a/hikyuu/data/mysql_upgrade/createdb.sql +++ b/hikyuu/data/mysql_upgrade/createdb.sql @@ -1,113 +1,265 @@ CREATE SCHEMA `hku_base`; CREATE TABLE `hku_base`.`market` ( - `marketid` INT(10) UNSIGNED NOT NULL, - `market` VARCHAR(10) NULL DEFAULT NULL, - `name` VARCHAR(60) NULL DEFAULT NULL, - `description` VARCHAR(100) NULL DEFAULT NULL, - `code` VARCHAR(20) NULL DEFAULT NULL, - `lastDate` BIGINT(20) UNSIGNED NULL DEFAULT NULL, - PRIMARY KEY (`marketid`) -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB -; -INSERT INTO `hku_base`.`market` (marketid,market,name,description,code,lastDate) VALUES (1,'SH','上海证券交易所','上海市场','000001',19901219); -INSERT INTO `hku_base`.`market` (marketid,market,name,description,code,lastDate) VALUES (2,'SZ','深圳证券交易所','深圳市场','399001',19901219); + `marketid` INT(10) UNSIGNED NOT NULL, `market` VARCHAR(10) NULL DEFAULT NULL, `name` VARCHAR(60) NULL DEFAULT NULL, `description` VARCHAR(100) NULL DEFAULT NULL, `code` VARCHAR(20) NULL DEFAULT NULL, `lastDate` BIGINT(20) UNSIGNED NULL DEFAULT NULL, PRIMARY KEY (`marketid`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; +INSERT INTO + `hku_base`.`market` ( + marketid, market, name, description, code, lastDate + ) +VALUES ( + 1, 'SH', '上海证券交易所', '上海市场', '000001', 19901219 + ); + +INSERT INTO + `hku_base`.`market` ( + marketid, market, name, description, code, lastDate + ) +VALUES ( + 2, 'SZ', '深圳证券交易所', '深圳市场', '399001', 19901219 + ); CREATE TABLE `hku_base`.`stkweight` ( - `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `stockid` INT(10) UNSIGNED NOT NULL, - `date` BIGINT(20) UNSIGNED NOT NULL, - `countAsGift` DOUBLE UNSIGNED NOT NULL, - `countForSell` DOUBLE UNSIGNED NOT NULL, - `priceForSell` DOUBLE UNSIGNED NOT NULL, - `bonus` DOUBLE UNSIGNED NOT NULL, - `countOfIncreasement` DOUBLE UNSIGNED NOT NULL, - `totalCount` DOUBLE UNSIGNED NOT NULL, - `freeCount` DOUBLE UNSIGNED NOT NULL, - PRIMARY KEY (`id`), - INDEX `ix_stockid_date` (`stockid`, `date`) -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB -; + `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `stockid` INT(10) UNSIGNED NOT NULL, `date` BIGINT(20) UNSIGNED NOT NULL, `countAsGift` DOUBLE UNSIGNED NOT NULL, `countForSell` DOUBLE UNSIGNED NOT NULL, `priceForSell` DOUBLE UNSIGNED NOT NULL, `bonus` DOUBLE UNSIGNED NOT NULL, `countOfIncreasement` DOUBLE UNSIGNED NOT NULL, `totalCount` DOUBLE UNSIGNED NOT NULL, `freeCount` DOUBLE UNSIGNED NOT NULL, PRIMARY KEY (`id`), INDEX `ix_stockid_date` (`stockid`, `date`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; CREATE TABLE `hku_base`.`stock` ( - `stockid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, - `marketid` INT(10) UNSIGNED NULL DEFAULT NULL, - `code` VARCHAR(20) NULL DEFAULT NULL, - `name` VARCHAR(60) NULL DEFAULT NULL, - `type` INT(10) UNSIGNED NULL DEFAULT NULL, - `valid` INT(10) UNSIGNED NULL DEFAULT NULL, - `startDate` BIGINT(20) UNSIGNED NULL DEFAULT NULL, - `endDate` BIGINT(20) UNSIGNED NULL DEFAULT NULL, - PRIMARY KEY (`stockid`) -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB -; + `stockid` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `marketid` INT(10) UNSIGNED NULL DEFAULT NULL, `code` VARCHAR(20) NULL DEFAULT NULL, `name` VARCHAR(60) NULL DEFAULT NULL, `type` INT(10) UNSIGNED NULL DEFAULT NULL, `valid` INT(10) UNSIGNED NULL DEFAULT NULL, `startDate` BIGINT(20) UNSIGNED NULL DEFAULT NULL, `endDate` BIGINT(20) UNSIGNED NULL DEFAULT NULL, PRIMARY KEY (`stockid`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; CREATE TABLE `hku_base`.`stocktypeinfo` ( - `id` INT(10) UNSIGNED NOT NULL, - `type` INT(10) UNSIGNED NULL DEFAULT NULL, - `precision` INT(11) NULL DEFAULT NULL, - `tick` DOUBLE NULL DEFAULT NULL, - `tickValue` DOUBLE NULL DEFAULT NULL, - `minTradeNumber` INT(32) UNSIGNED NULL DEFAULT NULL, - `maxTradeNumber` INT(32) UNSIGNED NULL DEFAULT NULL, - `description` VARCHAR(100) NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB -; -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (100,2,0,0,'Block',100,100,1000000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.01,2,1,1,'A股',0.01,100,1000000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.001,3,2,2,'指数',0.001,1,1000000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.001,3,3,3,'B股',0.001,100,1000000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.001,3,4,4,'基金(不含ETF)',0.001,100,1000000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.001,3,5,5,'ETF',0.001,1000,1000000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.01,2,6,6,'国债',0.01,10,10000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.01,2,7,7,'其他债券',0.01,10,10000); -INSERT INTO `hku_base`.`stocktypeinfo` (tickValue,`precision`,id,type,description,tick,minTradeNumber,maxTradeNumber) VALUES (0.01,2,8,8,'创业板',0.01,100,1000000); + `id` INT(10) UNSIGNED NOT NULL, `type` INT(10) UNSIGNED NULL DEFAULT NULL, `precision` INT(11) NULL DEFAULT NULL, `tick` DOUBLE NULL DEFAULT NULL, `tickValue` DOUBLE NULL DEFAULT NULL, `minTradeNumber` INT(32) UNSIGNED NULL DEFAULT NULL, `maxTradeNumber` INT(32) UNSIGNED NULL DEFAULT NULL, `description` VARCHAR(100) NULL DEFAULT NULL, PRIMARY KEY (`id`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 100, 2, 0, 0, 'Block', 100, 100, 1000000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.01, 2, 1, 1, 'A股', 0.01, 100, 1000000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.001, 3, 2, 2, '指数', 0.001, 1, 1000000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.001, 3, 3, 3, 'B股', 0.001, 100, 1000000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.001, 3, 4, 4, '基金(不含ETF)', 0.001, 100, 1000000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.001, 3, 5, 5, 'ETF', 0.001, 1000, 1000000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.01, 2, 6, 6, '国债', 0.01, 10, 10000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.01, 2, 7, 7, '其他债券', 0.01, 10, 10000 + ); + +INSERT INTO + `hku_base`.`stocktypeinfo` ( + tickValue, `precision`, id, type, description, tick, minTradeNumber, maxTradeNumber + ) +VALUES ( + 0.01, 2, 8, 8, '创业板', 0.01, 100, 1000000 + ); CREATE TABLE `hku_base`.`coderuletype` ( - `id` INT(11) NOT NULL, - `marketid` INT(11) NULL DEFAULT NULL, - `codepre` VARCHAR(20) NULL DEFAULT NULL, - `TYPE` INT(11) NULL DEFAULT NULL, - `description` VARCHAR(100) NULL DEFAULT NULL, - PRIMARY KEY (`id`) -) -COLLATE='utf8_general_ci' -ENGINE=InnoDB -; -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (1,1,'000',2,'上证指数'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (2,1,'60',1,'上证A股'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (4,1,'900',3,'上证B股'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (5,2,'00',1,'深证A股'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (6,2,'20',3,'深证B股'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (7,2,'39',2,'深证指数'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (8,2,'150',4,'深证基金'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (9,2,'16',4,'深证基金'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (10,2,'18',4,'深证基金'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (11,2,'159',5,'深证ETF'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (12,1,'51',5,'上证ETF'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (13,1,'50',4,'上证基金'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (18,2,'300',8,'创业板'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (20,1,'519',4,'上证基金'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (21,1,'009',6,'国债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (22,1,'010',6,'国债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (23,1,'10',7,'可转债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (24,1,'11',7,'可转债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (25,1,'12',7,'其他债券'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (26,1,'13',7,'地方政府债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (27,2,'100',6,'国债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (28,2,'101',6,'国债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (29,2,'108',7,'贴债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (30,2,'109',7,'地方政府债'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (31,2,'11',7,'其他债券'); -INSERT INTO `hku_base`.`coderuletype` (id,marketid,codepre,type,description) VALUES (32,2,'12',7,'其他债券'); + `id` INT(11) NOT NULL, `marketid` INT(11) NULL DEFAULT NULL, `codepre` VARCHAR(20) NULL DEFAULT NULL, `TYPE` INT(11) NULL DEFAULT NULL, `description` VARCHAR(100) NULL DEFAULT NULL, PRIMARY KEY (`id`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (1, 1, '000', 2, '上证指数'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (2, 1, '60', 1, '上证A股'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (4, 1, '900', 3, '上证B股'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (5, 2, '00', 1, '深证A股'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (6, 2, '20', 3, '深证B股'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (7, 2, '39', 2, '深证指数'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (8, 2, '150', 4, '深证基金'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (9, 2, '16', 4, '深证基金'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (10, 2, '18', 4, '深证基金'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (11, 2, '159', 5, '深证ETF'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (12, 1, '51', 5, '上证ETF'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (13, 1, '50', 4, '上证基金'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (18, 2, '300', 8, '创业板'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (20, 1, '519', 4, '上证基金'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (21, 1, '009', 6, '国债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (22, 1, '010', 6, '国债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (23, 1, '10', 7, '可转债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (24, 1, '11', 7, '可转债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (25, 1, '12', 7, '其他债券'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (26, 1, '13', 7, '地方政府债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (27, 2, '100', 6, '国债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (28, 2, '101', 6, '国债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (29, 2, '108', 7, '贴债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (30, 2, '109', 7, '地方政府债'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (31, 2, '11', 7, '其他债券'); + +INSERT INTO + `hku_base`.`coderuletype` ( + id, marketid, codepre, type, description + ) +VALUES (32, 2, '12', 7, '其他债券'); \ No newline at end of file diff --git a/hikyuu/data/pytdx_to_mysql.py b/hikyuu/data/pytdx_to_mysql.py index 26de578e..d37874f7 100644 --- a/hikyuu/data/pytdx_to_mysql.py +++ b/hikyuu/data/pytdx_to_mysql.py @@ -466,7 +466,7 @@ def get_trans_table(connect, market, code): `buyorsell` INT NOT NULL, PRIMARY KEY (`date`) ) - COLLATE='utf8_general_ci' + COLLATE='utf8mb4_general_ci' ENGINE=MyISAM ; """.format( @@ -603,7 +603,7 @@ def get_time_table(connect, market, code): `vol` DOUBLE NOT NULL, PRIMARY KEY (`date`) ) - COLLATE='utf8_general_ci' + COLLATE='utf8mb4_general_ci' ENGINE=MyISAM ; """.format( diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index 6a6b7161..b6c63e2d 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -231,8 +231,8 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): for name in logging.Logger.manager.loggerDict.keys(): logger = logging.getLogger(name) logger.addHandler(self.log_handler) - logger.setLevel(logging.DEBUG) - # logger.setLevel(logging.INFO) + # logger.setLevel(logging.DEBUG) + logger.setLevel(logging.INFO) # 多进程日志队列 self.mp_log_q = multiprocessing.Queue() diff --git a/hikyuu/util/mylog.py b/hikyuu/util/mylog.py index 6b3c7c04..971a8edb 100644 --- a/hikyuu/util/mylog.py +++ b/hikyuu/util/mylog.py @@ -51,8 +51,6 @@ def class_logger(cls, enable=False): logger.setLevel(logging.DEBUG) elif enable == 'info': logger.setLevel(logging.INFO) - cls._should_log_debug = logger.isEnabledFor(logging.DEBUG) - cls._should_log_info = logger.isEnabledFor(logging.INFO) cls.logger = logger From a79da6606cd017f17b4ad65595300d5da692192e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 19 Apr 2024 17:44:36 +0800 Subject: [PATCH 221/601] =?UTF-8?q?mysql=20=E8=B7=A8=E5=B9=B3=E5=8F=B0?= =?UTF-8?q?=E6=97=B6=20blob=20=E8=AF=BB=E5=8F=96=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 3 ++- .../base_info/mysql/MySQLBaseInfoDriver.cpp | 5 +++- .../base_info/sqlite/SQLiteBaseInfoDriver.cpp | 5 +++- .../base_info/table/HistoryFinanceTable.h | 3 ++- .../utilities/db_connect/SQLStatementBase.h | 25 +++++++++++++------ .../db_connect/mysql/MySQLStatement.cpp | 24 +++++++++++++++--- .../db_connect/mysql/MySQLStatement.h | 3 ++- .../db_connect/sqlite/SQLiteStatement.cpp | 15 +++++++++-- .../db_connect/sqlite/SQLiteStatement.h | 3 ++- 9 files changed, 68 insertions(+), 18 deletions(-) diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index f4065103..fe82abca 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -245,6 +245,7 @@ def historyfinancialreader(filepath): report_date = int(cw_info[313]) # 财务公告日期 report_date = 19000000 + report_date if report_date > 800000 else 20000000 + report_date # results.append((modifiy_code(code), report_date, cw_info)) - results.append((file_date, modifiy_code(code), report_date, cpp_bytes_to_vector_float_blob(info_data))) + # results.append((file_date, modifiy_code(code), report_date, cpp_bytes_to_vector_float_blob(info_data))) + results.append((file_date, modifiy_code(code), report_date, info_data)) cw_file.close() return results diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index af3487f4..09665dc1 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -412,7 +412,10 @@ vector MySQLBaseInfoDriver::getHistoryFinance(const string & auto &finance = finances[i]; auto &cur = result[i]; cur.reportDate = Datetime(finance.report_date); - cur.values = std::move(finance.values); + // cur.values = std::move(finance.values); + size_t count = finance.values.size() / sizeof(float); + cur.values.resize(count); + memcpy(cur.values.data(), finance.values.data(), count * sizeof(float)); } } catch (const std::exception &e) { diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index 541fedc7..0435ea14 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -402,7 +402,10 @@ vector SQLiteBaseInfoDriver::getHistoryFinance(const string& auto& finance = finances[i]; auto& cur = result[i]; cur.reportDate = Datetime(finance.report_date); - cur.values = std::move(finance.values); + // cur.values = std::move(finance.values); + size_t count = finance.values.size() / sizeof(float); + cur.values.resize(count); + memcpy(cur.values.data(), finance.values.data(), count * sizeof(float)); } } catch (const std::exception& e) { diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h index 43762137..2120830c 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h @@ -16,7 +16,8 @@ struct HistoryFinanceTable { uint64_t file_date; uint64_t report_date; std::string market_code; - std::vector values; + // std::vector values; + std::vector values; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h index 869300ee..ecc1ace3 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h @@ -80,11 +80,8 @@ public: /** * 将 item 的值绑定至 idx 指定的 SQL 参数中 - * @param idx sql参数序号 - * @param item 二进制数据起始指针 - * @param len 二进制数据长度 */ - void bindBlob(int idx, const char *item, size_t len); + void bindBlob(int idx, const std::vector &time); /** 将 item 的值绑定至 idx 指定的 SQL 参数中 */ template @@ -94,6 +91,8 @@ public: template typename std::enable_if::is_integer>::type bind(int idx, const T &item); + void bind(int idx, const std::vector &item); + /** 将 item 的值绑定至 idx 指定的 SQL 参数中 */ template void bind(int idx, const T &, const Args &...rest); @@ -116,6 +115,8 @@ public: /** 获取 idx 指定的数据至 item */ void getColumn(int idx, Datetime &item); + void getColumn(int idx, std::vector &); + /** 获取 idx 指定的数据至 item */ template typename std::enable_if::is_integer>::type getColumn(int idx, T &); @@ -142,7 +143,7 @@ public: virtual void sub_bindText(int idx, const std::string &item) = 0; ///< 子类接口 @see bind virtual void sub_bindText(int idx, const char *item, size_t len) = 0; ///< 子类接口 @see bind virtual void sub_bindBlob(int idx, const std::string &item) = 0; ///< 子类接口 @see bind - virtual void sub_bindBlob(int idx, const char *item, size_t len) = 0; ///< 子类接口 @see bind + virtual void sub_bindBlob(int idx, const std::vector &item) = 0; ///< 子类接口 @see bind virtual int sub_getNumColumns() const = 0; ///< 子类接口 @see getNumColumns virtual void sub_getColumnAsInt64(int idx, int64_t &) = 0; ///< 子类接口 @see getColumn @@ -150,6 +151,8 @@ public: virtual void sub_getColumnAsDatetime(int idx, Datetime &) = 0; ///< 子类接口 @see getColumn virtual void sub_getColumnAsText(int idx, std::string &) = 0; ///< 子类接口 @see getColumn virtual void sub_getColumnAsBlob(int idx, std::string &) = 0; ///< 子类接口 @see getColumn + virtual void sub_getColumnAsBlob(int idx, + std::vector &) = 0; ///< 子类接口 @see getColumn private: SQLStatementBase() = delete; @@ -210,8 +213,8 @@ inline void SQLStatementBase::bindBlob(int idx, const std::string &item) { sub_bindBlob(idx, item); } -inline void SQLStatementBase::bindBlob(int idx, const char *item, size_t len) { - sub_bindBlob(idx, item, len); +inline void SQLStatementBase::bindBlob(int idx, const std::vector &item) { + sub_bindBlob(idx, item); } inline uint64_t SQLStatementBase::getLastRowid() { @@ -240,6 +243,10 @@ inline void SQLStatementBase::getColumn(int idx, std::string &item) { sub_getColumnAsText(idx, item); } +inline void SQLStatementBase::bind(int idx, const std::vector &item) { + sub_bindBlob(idx, item); +} + template typename std::enable_if::is_integer>::type SQLStatementBase::bind( int idx, const T &item) { @@ -263,6 +270,10 @@ typename std::enable_if::is_integer>::type SQLStatementBa item = (T)temp; } +inline void SQLStatementBase::getColumn(int idx, std::vector &item) { + sub_getColumnAsBlob(idx, item); +} + template typename std::enable_if::is_integer>::type SQLStatementBase::getColumn( int idx, T &item) { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp index c994aea3..4e6a3a59 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp @@ -266,12 +266,12 @@ void MySQLStatement::sub_bindBlob(int idx, const string& item) { m_param_bind[idx].is_null = 0; } -void MySQLStatement::sub_bindBlob(int idx, const char* item, size_t len) { +void MySQLStatement::sub_bindBlob(int idx, const std::vector& item) { HKU_CHECK(idx < m_param_bind.size(), "idx out of range! idx: {}, total: {}", idx, m_param_bind.size()); - m_param_buffer.push_back(std::string(item)); + m_param_buffer.push_back(item); auto& buf = m_param_buffer.back(); - std::string* p = boost::any_cast(&buf); + std::vector* p = boost::any_cast>(&buf); m_param_bind[idx].buffer_type = MYSQL_TYPE_BLOB; m_param_bind[idx].buffer = (void*)p->data(); m_param_bind[idx].buffer_length = p->size(); @@ -403,6 +403,24 @@ void MySQLStatement::sub_getColumnAsBlob(int idx, string& item) { } } +void MySQLStatement::sub_getColumnAsBlob(int idx, std::vector& item) { + HKU_CHECK(idx < m_result_buffer.size(), "idx out of range! idx: {}, total: {}", + m_result_buffer.size()); + + HKU_CHECK(m_result_error[idx] == 0, "Error occurred in sub_getColumnAsBlob! idx: {}", idx); + + if (m_result_is_null[idx]) { + item.clear(); + return; + } + + try { + item = boost::any_cast>(m_result_buffer[idx]); + } catch (...) { + HKU_THROW("Field type mismatch! idx: {}", idx); + } +} + } // namespace hku #ifdef _MSC_VER diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h index b71d0086..66b6a111 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h @@ -49,7 +49,7 @@ public: virtual void sub_bindText(int idx, const std::string& item) override; virtual void sub_bindText(int idx, const char* item, size_t len) override; virtual void sub_bindBlob(int idx, const std::string& item) override; - virtual void sub_bindBlob(int idx, const char* item, size_t len) override; + virtual void sub_bindBlob(int idx, const std::vector& item) override; virtual int sub_getNumColumns() const override; virtual void sub_getColumnAsInt64(int idx, int64_t& item) override; @@ -57,6 +57,7 @@ public: virtual void sub_getColumnAsDatetime(int idx, Datetime& item) override; virtual void sub_getColumnAsText(int idx, std::string& item) override; virtual void sub_getColumnAsBlob(int idx, std::string& item) override; + virtual void sub_getColumnAsBlob(int idx, std::vector& item) override; private: void _reset(); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp index f1fc5f27..b9fd634c 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp @@ -129,9 +129,10 @@ void SQLiteStatement::sub_bindBlob(int idx, const string &item) { SQL_CHECK(status == SQLITE_OK, status, sqlite3_errmsg(m_db)); } -void SQLiteStatement::sub_bindBlob(int idx, const char *item, size_t len) { +void SQLiteStatement::sub_bindBlob(int idx, const std::vector &item) { _reset(); - int status = sqlite3_bind_blob(m_stmt, idx + 1, item, (int)len, SQLITE_TRANSIENT); + int status = + sqlite3_bind_blob(m_stmt, idx + 1, item.data(), (int)item.size(), SQLITE_TRANSIENT); SQL_CHECK(status == SQLITE_OK, status, sqlite3_errmsg(m_db)); } @@ -163,6 +164,16 @@ void SQLiteStatement::sub_getColumnAsBlob(int idx, std::string &item) { item = std::string(data, size); } +void SQLiteStatement::sub_getColumnAsBlob(int idx, std::vector &item) { + const char *data = static_cast(sqlite3_column_blob(m_stmt, idx)); + if (data == NULL) { + throw null_blob_exception(); + } + const int size = sqlite3_column_bytes(m_stmt, idx); + item.resize(size); + memcpy(item.data(), data, size); +} + uint64_t SQLiteStatement::sub_getLastRowid() { return sqlite3_last_insert_rowid(m_db); } diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h index bf632b35..72df5a86 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h @@ -44,7 +44,7 @@ public: virtual void sub_bindText(int idx, const std::string &item) override; virtual void sub_bindText(int idx, const char *item, size_t len) override; virtual void sub_bindBlob(int idx, const std::string &item) override; - virtual void sub_bindBlob(int idx, const char *item, size_t len) override; + virtual void sub_bindBlob(int idx, const std::vector &item) override; virtual int sub_getNumColumns() const override; virtual void sub_getColumnAsInt64(int idx, int64_t &item) override; @@ -52,6 +52,7 @@ public: virtual void sub_getColumnAsDatetime(int idx, Datetime &item) override; virtual void sub_getColumnAsText(int idx, std::string &item) override; virtual void sub_getColumnAsBlob(int idx, std::string &item) override; + virtual void sub_getColumnAsBlob(int idx, std::vector &item) override; private: void _reset(); From e8a6a8fca7948e8dd9d835b8e72b7f3c6a9da684 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 20 Apr 2024 21:29:22 +0800 Subject: [PATCH 222/601] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=20FINANCE,=20?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20only=5Fyear=5Freport=20=E5=92=8C=20dynamic?= =?UTF-8?q?=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp | 2 + hikyuu_cpp/hikyuu/HistoryFinanceInfo.h | 5 +- .../base_info/mysql/MySQLBaseInfoDriver.cpp | 2 +- .../base_info/sqlite/SQLiteBaseInfoDriver.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp | 58 ++++++++++++++++++- hikyuu_pywrap/_Stock.cpp | 2 +- 6 files changed, 65 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp index 53cd77f7..a2fc9fa3 100644 --- a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp +++ b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.cpp @@ -11,6 +11,7 @@ namespace hku { HistoryFinanceInfo& HistoryFinanceInfo::operator=(const HistoryFinanceInfo& other) { HKU_IF_RETURN(this == &other, *this); + fileDate = other.fileDate; reportDate = other.reportDate; values = other.values; return *this; @@ -18,6 +19,7 @@ HistoryFinanceInfo& HistoryFinanceInfo::operator=(const HistoryFinanceInfo& othe HistoryFinanceInfo& HistoryFinanceInfo::operator=(HistoryFinanceInfo&& other) { HKU_IF_RETURN(this == &other, *this); + fileDate = std::move(other.fileDate); reportDate = std::move(other.reportDate); values = std::move(other.values); return *this; diff --git a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h index 8bdce0ab..a86732e7 100644 --- a/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h +++ b/hikyuu_cpp/hikyuu/HistoryFinanceInfo.h @@ -16,6 +16,7 @@ namespace hku { * @ingroup StockManage */ struct HKU_API HistoryFinanceInfo { + Datetime fileDate; ///< 用于区分一季报、半年报、三季报、年报 Datetime reportDate; ///< 财务报告日期 vector values; ///< 详细财务信息,字段索引可使用 StockManager.getHistoryFinanceAllFields 查询 @@ -23,7 +24,9 @@ struct HKU_API HistoryFinanceInfo { HistoryFinanceInfo() = default; HistoryFinanceInfo(const HistoryFinanceInfo&) = default; HistoryFinanceInfo(HistoryFinanceInfo&& rv) - : reportDate(std::move(rv.reportDate)), values(std::move(rv.values)) {} + : fileDate(std::move(rv.fileDate)), + reportDate(std::move(rv.reportDate)), + values(std::move(rv.values)) {} HistoryFinanceInfo& operator=(const HistoryFinanceInfo&); HistoryFinanceInfo& operator=(HistoryFinanceInfo&&); diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index 09665dc1..3fde4b3f 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -411,8 +411,8 @@ vector MySQLBaseInfoDriver::getHistoryFinance(const string & for (size_t i = 0; i < total; i++) { auto &finance = finances[i]; auto &cur = result[i]; + cur.fileDate = Datetime(finance.file_date); cur.reportDate = Datetime(finance.report_date); - // cur.values = std::move(finance.values); size_t count = finance.values.size() / sizeof(float); cur.values.resize(count); memcpy(cur.values.data(), finance.values.data(), count * sizeof(float)); diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index 0435ea14..06b9bf89 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -401,8 +401,8 @@ vector SQLiteBaseInfoDriver::getHistoryFinance(const string& for (size_t i = 0; i < total; i++) { auto& finance = finances[i]; auto& cur = result[i]; + cur.fileDate = Datetime(finance.file_date); cur.reportDate = Datetime(finance.report_date); - // cur.values = std::move(finance.values); size_t count = finance.values.size() / sizeof(float); cur.values.resize(count); memcpy(cur.values.data(), finance.values.data(), count * sizeof(float)); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp index ab690a48..82003551 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp @@ -17,12 +17,20 @@ namespace hku { IFinance::IFinance() : IndicatorImp("FINANCE", 1) { setParam("field_ix", 0); setParam("field_name", ""); + + // 某些信息如每股收益,只使用年报计算 + setParam("only_year_report", false); + + // 某些信息如每股收益,需要动态计算,如全年收益在只有一季报时,使用一季报*4进行预估 + setParam("dynamic", false); } IFinance::IFinance(const KData& k) : IndicatorImp("FINANCE", 1) { setParam("field_ix", 0); setParam("field_name", ""); setParam("kdata", k); + setParam("only_year_report", false); + setParam("dynamic", false); IFinance::_calculate(Indicator()); } @@ -40,6 +48,17 @@ void IFinance::_calculate(const Indicator& data) { Stock stock = kdata.getStock(); auto finances = stock.getHistoryFinance(); + + if (getParam("only_year_report")) { + vector tmp_finances; + for (auto&& finance : finances) { + if (finance.fileDate.month() == 12L) { + tmp_finances.emplace_back(std::move(finance)); + } + } + finances = std::move(tmp_finances); + } + if (finances.empty()) { m_discard = total; return; @@ -52,6 +71,7 @@ void IFinance::_calculate(const Indicator& data) { StockManager::instance().getHistoryFinanceFieldIndex(getParam("field_name"))); } + bool dynamic = getParam("dynamic"); auto* dst = this->data(); auto dates = kdata.getDatetimeList(); auto* k = kdata.data(); @@ -63,13 +83,47 @@ void IFinance::_calculate(const Indicator& data) { auto value = finances[pos].values.at(field_ix); if (pos + 1 == finances_total) { while (cur_kix < total && finances[pos].reportDate <= k[cur_kix].datetime) { - dst[cur_kix] = value; + if (dynamic) { + long month = finances[pos].fileDate.month(); + if (3L == month) { + // 一季报 + dst[cur_kix] = value * 4; + } else if (6L == month) { + // 半年报 + dst[cur_kix] = value * 2; + } else if (9L == month) { + // 三季报 + dst[cur_kix] = value / 3.0 * 4.0; + } else { + // 年报 + dst[cur_kix] = value; + } + } else { + dst[cur_kix] = value; + } cur_kix++; } } else { while (cur_kix < total && finances[pos].reportDate <= k[cur_kix].datetime && finances[pos + 1].reportDate > k[cur_kix].datetime) { - dst[cur_kix] = value; + if (dynamic) { + long month = finances[pos].fileDate.month(); + if (3L == month) { + // 一季报 + dst[cur_kix] = value * 4; + } else if (6L == month) { + // 半年报 + dst[cur_kix] = value * 2; + } else if (9L == month) { + // 三季报 + dst[cur_kix] = value / 3.0 * 4.0; + } else { + // 年报 + dst[cur_kix] = value; + } + } else { + dst[cur_kix] = value; + } cur_kix++; } } diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index 86fd4fe3..50b25289 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -181,7 +181,7 @@ void export_Stock(py::module& m) { auto finances = stk.getHistoryFinance(); py::list ret; for (const auto& f : finances) { - ret.append(py::make_tuple(f.reportDate, f.values)); + ret.append(py::make_tuple(f.fileDate, f.reportDate, f.values)); } return ret; }, From 3efefcb1fce63d0aba7b203c595976a94ffa8a44 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 20 Apr 2024 21:56:16 +0800 Subject: [PATCH 223/601] =?UTF-8?q?indicator=20=E7=BB=98=E5=88=B6=E6=97=B6?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=20x=20=E8=BD=B4=E4=B8=BA=E6=97=A5=E6=9C=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 0993ad77..cd7a747e 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -147,7 +147,7 @@ class StockFuncFormatter(object): def getDayLocatorAndFormatter(dates): """获取显示日线时使用的Major Locator和Major Formatter""" - sep = len(dates) / 8 + sep = len(dates) / 10 loc = [ (i, str(d) if (i != (len(dates) - 1)) and (i % sep != 0) else "{}-{}-{}".format(d.year, d.month, d.day)) for i, d in enumerate(dates) @@ -394,6 +394,10 @@ def iplot( axes.set_xlim(-1, len(indicator) + 1) if kref: ax_set_locator_formatter(axes, kref.get_datetime_list(), kref.get_query().ktype) + else: + k = indicator.get_context() + if len(k) > 0: + ax_set_locator_formatter(axes, k.get_datetime_list(), k.get_query().ktype) # draw() @@ -474,6 +478,10 @@ def ibar( axes.set_xlim(-1, len(indicator) + 1) if kref: ax_set_locator_formatter(axes, kref.get_datetime_list(), kref.get_query().ktype) + else: + k = indicator.get_context() + if len(k) > 0: + ax_set_locator_formatter(axes, k.get_datetime_list(), k.get_query().ktype) # draw() From f6b663cbb61ac63d5b0bc83c4a6920e4b5cf67ff Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 21 Apr 2024 02:38:04 +0800 Subject: [PATCH 224/601] =?UTF-8?q?fixed=20=E6=9D=BF=E5=9D=97=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=AF=BC=E5=85=A5=E6=97=B6=EF=BC=8C=E5=A6=82=E6=9E=9C?= =?UTF-8?q?=E7=BD=91=E7=BB=9C=E4=B8=8D=E5=A5=BD=EF=BC=8C=E6=9C=AA=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=88=B0=E5=BD=93=E5=89=8D=E6=9D=BF=E5=9D=97=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E6=97=B6=EF=BC=8C=E4=BC=9A=E6=8A=8A=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E7=9A=84=E6=9D=BF=E5=9D=97=E4=BF=A1=E6=81=AF=E5=88=A0=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/em_block_to_mysql.py | 32 +++++++++++++++++++++++++++---- hikyuu/data/em_block_to_sqlite.py | 28 +++++++++++++++++++++++++-- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/hikyuu/data/em_block_to_mysql.py b/hikyuu/data/em_block_to_mysql.py index dd8977a4..6ae517ca 100644 --- a/hikyuu/data/em_block_to_mysql.py +++ b/hikyuu/data/em_block_to_mysql.py @@ -14,33 +14,57 @@ from hikyuu.fetcher.stock.zh_block_em import * def em_import_block_to_mysql(connect, code_market_dict, categorys=('行业板块', '概念板块', '地域板块', '指数板块')): all_block_info = {} + success_fetch_hy = False if '行业板块' in categorys: hku_info("获取行业板块信息") x = get_all_hybk_info(code_market_dict) if x: all_block_info["行业板块"] = x + success_fetch_hy = True - if '行业板块' in categorys: + success_fetch_gn = False + if '概念板块' in categorys: hku_info("获取概念板块信息") x = get_all_gnbk_info(code_market_dict) if x: all_block_info["概念板块"] = x + success_fetch_gn = True - if '行业板块' in categorys: + success_fetch_dy = False + if '地域板块' in categorys: hku_info("获取地域板块信息") x = get_all_dybk_info(code_market_dict) if x: all_block_info["地域板块"] = x + success_fetch_dy = True - if '行业板块' in categorys: + success_fetch_zs = False + if '指数板块' in categorys: hku_info("获取指数板块信息") x = get_all_zsbk_info(code_market_dict) if x: all_block_info["指数板块"] = x + success_fetch_zs = True + + blks = [] + if success_fetch_hy: + blks.append('行业板块') + if success_fetch_gn: + blks.append('概念板块') + if success_fetch_dy: + blks.append('地域板块') + if success_fetch_zs: + blks.append('指数板块') + + if not blks: + return hku_info("更新数据库") cur = connect.cursor() - sql = "delete from hku_base.block where category in ('行业板块', '概念板块', '地域板块', '指数板块')" + if len(blks) == 1: + sql = f"delete from hku_base.block where category in ({blks[0]})" + else: + sql = f"delete from hku_base.block where category in {tuple(blks)}" cur.execute(sql) insert_records = [] diff --git a/hikyuu/data/em_block_to_sqlite.py b/hikyuu/data/em_block_to_sqlite.py index 0f4f0e3b..751fe45a 100644 --- a/hikyuu/data/em_block_to_sqlite.py +++ b/hikyuu/data/em_block_to_sqlite.py @@ -14,33 +14,58 @@ from hikyuu.fetcher.stock.zh_block_em import * def em_import_block_to_sqlite(connect, code_market_dict, categorys=('行业板块', '概念板块', '地域板块', '指数板块')): all_block_info = {} + success_fetch_hy = False if '行业板块' in categorys: hku_info("获取行业板块信息") x = get_all_hybk_info(code_market_dict) if x: all_block_info["行业板块"] = x + success_fetch_hy = True + success_fetch_gn = False if '概念板块' in categorys: hku_info("获取概念板块信息") x = get_all_gnbk_info(code_market_dict) if x: all_block_info["概念板块"] = x + success_fetch_gn = True + success_fetch_dy = False if '地域板块' in categorys: hku_info("获取地域板块信息") x = get_all_dybk_info(code_market_dict) if x: all_block_info["地域板块"] = x + success_fetch_dy = True + success_fetch_zs = False if '指数板块' in categorys: hku_info("获取指数板块信息") x = get_all_zsbk_info(code_market_dict) if x: all_block_info["指数板块"] = x + success_fetch_zs = True + + blks = [] + if success_fetch_hy: + blks.append('行业板块') + if success_fetch_gn: + blks.append('概念板块') + if success_fetch_dy: + blks.append('地域板块') + if success_fetch_zs: + blks.append('指数板块') + + if not blks: + return hku_info("更新数据库") cur = connect.cursor() - sql = "delete from block where category in ('行业板块', '概念板块', '地域板块', '指数板块')" + if len(blks) == 1: + sql = f"delete from block where category in ({blks[0]})" + else: + sql = f"delete from block where category in {tuple(blks)}" + hku_info(sql) cur.execute(sql) insert_records = [] @@ -56,7 +81,6 @@ def em_import_block_to_sqlite(connect, code_market_dict, categorys=('行业板 connect.commit() cur.close() - pass if __name__ == "__main__": From d077583fb7e729c476be350edbe4b0a6e4bf3719 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 22 Apr 2024 12:40:58 +0800 Subject: [PATCH 225/601] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8C=97=E8=AF=8192?= =?UTF-8?q?=E5=8F=B7=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0015sql | 2 ++ hikyuu/data/sqlite_upgrade/0016.sql | 6 ++++++ 2 files changed, 8 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0015sql create mode 100644 hikyuu/data/sqlite_upgrade/0016.sql diff --git a/hikyuu/data/mysql_upgrade/0015sql b/hikyuu/data/mysql_upgrade/0015sql new file mode 100644 index 00000000..f5190939 --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0015sql @@ -0,0 +1,2 @@ +INSERT INTO `hku_base`.`coderuletype` (`marketid`,`codepre`,`type`,`description`) VALUES (3,'92',11,'北证A股'); +UPDATE `hku_base`.`version` set `version` = 15; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0016.sql b/hikyuu/data/sqlite_upgrade/0016.sql new file mode 100644 index 00000000..0c0b1590 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0016.sql @@ -0,0 +1,6 @@ +BEGIN TRANSACTION; + +INSERT INTO `coderuletype` (marketid,codepre,type,description) VALUES (3,'92',11,'北证A股'); +UPDATE `version` set `version` = 16; + +COMMIT; \ No newline at end of file From 9aec12ec61ff6520b3b02ec33faed0664c021c3a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 22 Apr 2024 14:49:04 +0800 Subject: [PATCH 226/601] =?UTF-8?q?=E7=A7=BB=E9=99=A4python=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E5=88=B0=E7=9A=84import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/em_block_to_mysql.py | 2 -- hikyuu/data/em_block_to_sqlite.py | 2 -- 2 files changed, 4 deletions(-) diff --git a/hikyuu/data/em_block_to_mysql.py b/hikyuu/data/em_block_to_mysql.py index 6ae517ca..00d3c8c8 100644 --- a/hikyuu/data/em_block_to_mysql.py +++ b/hikyuu/data/em_block_to_mysql.py @@ -4,8 +4,6 @@ # Create on: 20240102 # Author: fasiondog -import json -import akshare # 这里必须再导入 akshare 下,否则get_all_zsbk_info会报错 from hikyuu.data.common import MARKET, get_stk_code_name_list from hikyuu.util import * from hikyuu.fetcher.stock.zh_block_em import * diff --git a/hikyuu/data/em_block_to_sqlite.py b/hikyuu/data/em_block_to_sqlite.py index 751fe45a..ef419999 100644 --- a/hikyuu/data/em_block_to_sqlite.py +++ b/hikyuu/data/em_block_to_sqlite.py @@ -4,8 +4,6 @@ # Create on: 20240102 # Author: fasiondog -import json -import akshare # 这里必须再导入 akshare 下,否则get_all_zsbk_info会报错 from hikyuu.data.common import MARKET, get_stk_code_name_list from hikyuu.util import * from hikyuu.fetcher.stock.zh_block_em import * From 813eea963bac46f3ca421da888f6b090832ad994 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 22 Apr 2024 20:42:35 +0800 Subject: [PATCH 227/601] updaate --- hikyuu/data/mysql_upgrade/0015.sql | 3 +++ hikyuu/data/mysql_upgrade/0015sql | 2 -- 2 files changed, 3 insertions(+), 2 deletions(-) create mode 100644 hikyuu/data/mysql_upgrade/0015.sql delete mode 100644 hikyuu/data/mysql_upgrade/0015sql diff --git a/hikyuu/data/mysql_upgrade/0015.sql b/hikyuu/data/mysql_upgrade/0015.sql new file mode 100644 index 00000000..54be379b --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0015.sql @@ -0,0 +1,3 @@ +ALTER TABLE `hku_base`.`coderuletype` modify `id` int(11) auto_increment; +INSERT INTO `hku_base`.`coderuletype` (`marketid`,`codepre`,`type`,`description`) VALUES (3,'92',11,'北证A股'); +UPDATE `hku_base`.`version` set `version` = 15; \ No newline at end of file diff --git a/hikyuu/data/mysql_upgrade/0015sql b/hikyuu/data/mysql_upgrade/0015sql deleted file mode 100644 index f5190939..00000000 --- a/hikyuu/data/mysql_upgrade/0015sql +++ /dev/null @@ -1,2 +0,0 @@ -INSERT INTO `hku_base`.`coderuletype` (`marketid`,`codepre`,`type`,`description`) VALUES (3,'92',11,'北证A股'); -UPDATE `hku_base`.`version` set `version` = 15; \ No newline at end of file From 862cdea36ae024aded3b71504ec3353dcd0f33ce Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 22 Apr 2024 22:32:51 +0800 Subject: [PATCH 228/601] add blockindex table --- hikyuu/data/mysql_upgrade/0016.sql | 4 ++++ hikyuu/data/sqlite_upgrade/0017.sql | 16 ++++++++++++++++ 2 files changed, 20 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0016.sql create mode 100644 hikyuu/data/sqlite_upgrade/0017.sql diff --git a/hikyuu/data/mysql_upgrade/0016.sql b/hikyuu/data/mysql_upgrade/0016.sql new file mode 100644 index 00000000..1594a2d2 --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0016.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS `hku_base`.`BlockIndex` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `category` VARCHAR(100) NOT NULL, `name` VARCHAR(100) NOT NULL, `market_code` VARCHAR(30) NOT NULL, PRIMARY KEY (`id`), INDEX `ix_block` (`category`, `name`) +) COLLATE = 'utf8mb4_general_ci' ENGINE = InnoDB; +UPDATE `hku_base`.`version` set `version` = 16; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0017.sql b/hikyuu/data/sqlite_upgrade/0017.sql new file mode 100644 index 00000000..573b4553 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0017.sql @@ -0,0 +1,16 @@ +BEGIN TRANSACTION; + +CREATE TABLE + IF NOT EXISTS `BlockIndex` ( + "id" INTEGER, + `category` VARCHAR(100) NOT NULL, + `name` VARCHAR(100) NOT NULL, + `market_code` VARCHAR(30) NOT NULL, + PRIMARY KEY("id" AUTOINCREMENT) + ); + +CREATE INDEX "ix_blockindex_category_name" ON "block" (category, name); + +UPDATE `version` set `version` = 17; + +COMMIT; \ No newline at end of file From 6f1755a93507e1fbe3b2491e9f68eea941dfbbc2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 23 Apr 2024 02:38:50 +0800 Subject: [PATCH 229/601] update --- hikyuu/data/common_mysql.py | 2 +- hikyuu/data/common_sqlite3.py | 14 +++++++------- hikyuu/data/mysql_upgrade/0015.sql | 1 + hikyuu/data/sqlite_upgrade/0016.sql | 2 ++ 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/hikyuu/data/common_mysql.py b/hikyuu/data/common_mysql.py index eb6b160e..eb604c64 100644 --- a/hikyuu/data/common_mysql.py +++ b/hikyuu/data/common_mysql.py @@ -94,7 +94,7 @@ def get_codepre_list(connect, marketid, quotations): """获取前缀代码表""" stktype_list = get_stktype_list(quotations) sql = "select codepre, type from `hku_base`.`coderuletype` " \ - "where marketid={marketid} and type in {type_list}"\ + "where marketid={marketid} and type in {type_list} ORDER by length(codepre) DESC"\ .format(marketid=marketid, type_list=stktype_list) cur = connect.cursor() cur.execute(sql) diff --git a/hikyuu/data/common_sqlite3.py b/hikyuu/data/common_sqlite3.py index 722a0a45..44e3abd1 100644 --- a/hikyuu/data/common_sqlite3.py +++ b/hikyuu/data/common_sqlite3.py @@ -62,11 +62,11 @@ def create_database(connect): cur.executescript(f.read()) db_version = get_db_version(connect) - files = [x for x in Path(sql_dir).iterdir() \ - if x.is_file() \ - and x.name != 'createdb.sql' \ - and x.name != '__init__.py' \ - and int(x.stem) > db_version and not x.is_dir()] + files = [x for x in Path(sql_dir).iterdir() + if x.is_file() + and x.name != 'createdb.sql' + and x.name != '__init__.py' + and int(x.stem) > db_version and not x.is_dir()] files.sort() for file in files: cur.executescript(file.read_text(encoding='utf8')) @@ -88,7 +88,7 @@ def get_codepre_list(connect, marketid, quotations): """获取前缀代码表""" stktype_list = get_stktype_list(quotations) sql = "select codepre, type from coderuletype " \ - "where marketid={marketid} and type in {type_list}"\ + "where marketid={marketid} and type in {type_list} ORDER by length(codepre) DESC"\ .format(marketid=marketid, type_list=stktype_list) cur = connect.cursor() a = cur.execute(sql) @@ -100,7 +100,7 @@ def get_codepre_list(connect, marketid, quotations): def update_last_date(connect, marketid, lastdate): cur = connect.cursor() cur.execute("update Market set lastDate={} where marketid='{}'".format(lastdate, marketid)) - #if marketid == MARKETID.SH: + # if marketid == MARKETID.SH: # cur.execute("update LastDate set date={}".format(lastdate)) connect.commit() cur.close() diff --git a/hikyuu/data/mysql_upgrade/0015.sql b/hikyuu/data/mysql_upgrade/0015.sql index 54be379b..9181e2ba 100644 --- a/hikyuu/data/mysql_upgrade/0015.sql +++ b/hikyuu/data/mysql_upgrade/0015.sql @@ -1,3 +1,4 @@ ALTER TABLE `hku_base`.`coderuletype` modify `id` int(11) auto_increment; INSERT INTO `hku_base`.`coderuletype` (`marketid`,`codepre`,`type`,`description`) VALUES (3,'92',11,'北证A股'); +INSERT INTO `hku_base`.`coderuletype` (`marketid`,`codepre`,`type`,`description`) VALUES (1,'880',2,'通达信板块指数'); UPDATE `hku_base`.`version` set `version` = 15; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0016.sql b/hikyuu/data/sqlite_upgrade/0016.sql index 0c0b1590..c73c3eaa 100644 --- a/hikyuu/data/sqlite_upgrade/0016.sql +++ b/hikyuu/data/sqlite_upgrade/0016.sql @@ -1,6 +1,8 @@ BEGIN TRANSACTION; INSERT INTO `coderuletype` (marketid,codepre,type,description) VALUES (3,'92',11,'北证A股'); +INSERT INTO `coderuletype` (marketid,codepre,type,description) VALUES (1,'880',2,'通达信板块指数'); + UPDATE `version` set `version` = 16; COMMIT; \ No newline at end of file From 1ee0655a0232b4954cd7af9a026d8302b6d701f8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 23 Apr 2024 18:04:05 +0800 Subject: [PATCH 230/601] =?UTF-8?q?Block=E6=94=AF=E6=8C=81=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E5=AF=B9=E5=BA=94=E6=8C=87=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Block.cpp | 17 +++++++++++++++-- hikyuu_cpp/hikyuu/Block.h | 14 ++++++++++++-- .../block_info/mysql/MySQLBlockInfoDriver.cpp | 11 ++++++++--- .../block_info/sqlite/SQLiteBlockInfoDriver.cpp | 10 +++++++--- hikyuu_pywrap/_Block.cpp | 2 ++ 5 files changed, 44 insertions(+), 10 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Block.cpp b/hikyuu_cpp/hikyuu/Block.cpp index 35afec71..90ead0c2 100644 --- a/hikyuu_cpp/hikyuu/Block.cpp +++ b/hikyuu_cpp/hikyuu/Block.cpp @@ -26,13 +26,20 @@ Block::Block(const string& category, const string& name) : m_data(make_sharedm_name = name; } -Block::Block(const Block& block) { +Block::Block(const string& category, const string& name, const string& indexCode) +: Block(category, name) { + if (!indexCode.empty()) { + m_data->m_indexStock = StockManager::instance().getStock(indexCode); + } +} + +Block::Block(const Block& block) noexcept { if (m_data == block.m_data) return; m_data = block.m_data; } -Block& Block::operator=(const Block& block) { +Block& Block::operator=(const Block& block) noexcept { HKU_IF_RETURN(this == &block || m_data == block.m_data, *this); m_data = block.m_data; return *this; @@ -114,4 +121,10 @@ bool Block::remove(const Stock& stock) { return true; } +void Block::setIndexStock(const Stock& stk) { + if (!m_data) + m_data = shared_ptr(new Data); + m_data->m_indexStock = stk; +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/Block.h b/hikyuu_cpp/hikyuu/Block.h index 2c780364..7ce7e282 100644 --- a/hikyuu_cpp/hikyuu/Block.h +++ b/hikyuu_cpp/hikyuu/Block.h @@ -21,8 +21,9 @@ class HKU_API Block { public: Block(); Block(const string& category, const string& name); - Block(const Block&); - Block& operator=(const Block&); + Block(const string& category, const string& name, const string& indexCode); + Block(const Block&) noexcept; + Block& operator=(const Block&) noexcept; virtual ~Block(); typedef StockMapIterator const_iterator; @@ -118,10 +119,19 @@ public: m_data->m_stockDict.clear(); } + /** 获取对应的指数,可能为空 Stock */ + Stock getIndexStock() const { + return m_data ? m_data->m_indexStock : Stock(); + } + + /** 设置对应的指数 */ + void setIndexStock(const Stock& stk); + private: struct HKU_API Data { string m_category; string m_name; + Stock m_indexStock; // 对应指数,可能不存在 StockMapIterator::stock_map_t m_stockDict; }; shared_ptr m_data; diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp index 4ce4c3fa..ee9c5f4e 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp @@ -12,10 +12,11 @@ namespace hku { struct MySQLBlockTable { - TABLE_BIND3(MySQLBlockTable, block, category, name, market_code) + TABLE_BIND4(MySQLBlockTable, block, category, name, market_code, index_code) string category; string name; string market_code; + string index_code; }; MySQLBlockInfoDriver::~MySQLBlockInfoDriver() {} @@ -36,7 +37,11 @@ void MySQLBlockInfoDriver::load() { MySQLConnect connect(connect_param); vector records; - connect.batchLoad(records); + connect.batchLoadView( + records, + "select a.id, a.category, a.name, a.market_code, b.market_code as " + "index_code from `hku_base`.`block` a left " + "join `hku_base`.`BlockIndex` b on a.category=b.category and a.name = b.name"); for (auto& record : records) { auto category_iter = m_buffer.find(record.category); @@ -46,7 +51,7 @@ void MySQLBlockInfoDriver::load() { auto& name_dict = m_buffer[record.category]; auto name_iter = name_dict.find(record.name); if (name_iter == name_dict.end()) { - name_dict[record.name] = {Block(record.category, record.name)}; + name_dict[record.name] = {Block(record.category, record.name, record.index_code)}; } name_dict[record.name].add(record.market_code); } diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp index e3124f77..a4ea2d6c 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp @@ -12,10 +12,11 @@ namespace hku { struct SQLiteBlockTable { - TABLE_BIND3(SQLiteBlockTable, block, category, name, market_code) + TABLE_BIND4(SQLiteBlockTable, block, category, name, market_code, index_code) string category; string name; string market_code; + string index_code; }; SQLiteBlockInfoDriver::~SQLiteBlockInfoDriver() {} @@ -32,7 +33,10 @@ void SQLiteBlockInfoDriver::load() { SQLiteConnect connect(m_params); vector records; - connect.batchLoad(records); + connect.batchLoadView(records, + "select a.id, a.category, a.name, a.market_code, b.market_code as " + "index_code from block a left " + "join BlockIndex b on a.category=b.category and a.name = b.name"); for (auto& record : records) { auto category_iter = m_buffer.find(record.category); @@ -42,7 +46,7 @@ void SQLiteBlockInfoDriver::load() { auto& name_dict = m_buffer[record.category]; auto name_iter = name_dict.find(record.name); if (name_iter == name_dict.end()) { - name_dict[record.name] = {Block(record.category, record.name)}; + name_dict[record.name] = {Block(record.category, record.name, record.index_code)}; } name_dict[record.name].add(record.market_code); } diff --git a/hikyuu_pywrap/_Block.cpp b/hikyuu_pywrap/_Block.cpp index cff69d3d..27198484 100644 --- a/hikyuu_pywrap/_Block.cpp +++ b/hikyuu_pywrap/_Block.cpp @@ -36,6 +36,8 @@ void export_Block(py::module& m) { .def_property("category", setCategory, getCategory, "板块所属分类") .def_property("name", getName, setName, "板块名称") + .def_property("index_stock", &Block::getIndexStock, &Block::setIndexStock, + py::return_value_policy::copy, "对应指数") .def("empty", &Block::empty, R"(empty(self) From 6c97f53c8f6ef330f117e1d1d943aa03bdc81867 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 23 Apr 2024 18:07:30 +0800 Subject: [PATCH 231/601] update docs --- docs/source/stock_manager.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/stock_manager.rst b/docs/source/stock_manager.rst index 0d2bffa5..773be187 100644 --- a/docs/source/stock_manager.rst +++ b/docs/source/stock_manager.rst @@ -426,6 +426,7 @@ StockManager/Block/Stock .. py:attribute:: category : 板块分类 .. py:attribute:: name : 板块名称 + .. py:attribute:: index_stock: 对应指数(可能为空 Stock) .. py:method:: __init__(self, category, name): From 90cc0d93d7d91bcf83eeac5070cf3e23bfd76bab Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 24 Apr 2024 20:02:32 +0800 Subject: [PATCH 232/601] add hku_to_async utils method --- hikyuu/util/__init__.py | 1 + hikyuu/util/check.py | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/hikyuu/util/__init__.py b/hikyuu/util/__init__.py index e4d41361..543108ae 100644 --- a/hikyuu/util/__init__.py +++ b/hikyuu/util/__init__.py @@ -25,6 +25,7 @@ __all__ = [ 'hku_check_throw', 'hku_check_ignore', 'hku_catch', + 'hku_to_async', 'hku_trace', 'hku_debug', 'hku_info', diff --git a/hikyuu/util/check.py b/hikyuu/util/check.py index 1fd9f6f6..f05b5b69 100644 --- a/hikyuu/util/check.py +++ b/hikyuu/util/check.py @@ -10,6 +10,7 @@ import sys import traceback import functools +import asyncio from .mylog import hku_logger @@ -142,3 +143,11 @@ def hku_catch(ret=None, trace=False, callback=None, retry=1, with_msg=False, re_ return wrappedFunc return hku_catch_wrap + + +def hku_to_async(func): + @functools.wraps(func) + async def async_func(*args, **kwargs): + loop = asyncio.get_event_loop() + return await loop.run_in_executor(None, func, *args, **kwargs) + return async_func From 6b2036a8c23cc8c27becee09a2733912444250fe Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 25 Apr 2024 14:50:16 +0800 Subject: [PATCH 233/601] =?UTF-8?q?fixed=20interactive=20=E4=B8=AD=20block?= =?UTF-8?q?bj=20=E4=B8=BA=E7=A9=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 51e48266..957eae27 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -144,7 +144,7 @@ for s in blocka: zsbk_sz = blocksz blockbj = Block("A", "BJ") -for s in blocka: +for s in sm: if s.market == "BJ": blockbj.add(s) zsbk_bj = blockbj From 4b2c6b6c76a7bafab33d1924a363ef4087e9588a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 25 Apr 2024 15:00:41 +0800 Subject: [PATCH 234/601] fixed interactive STOCKTYPE_A_BJ --- hikyuu/interactive.py | 4 ++-- hikyuu_pywrap/_Constant.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 957eae27..d3093d87 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -127,7 +127,7 @@ start_spot_agent(False) # ============================================================================== blocka = Block("A", "ALL") for s in sm: - if s.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_GEM): + if s.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_A_BJ, constant.STOCKTYPE_GEM): blocka.add(s) zsbk_a = blocka @@ -144,7 +144,7 @@ for s in blocka: zsbk_sz = blocksz blockbj = Block("A", "BJ") -for s in sm: +for s in blocka: if s.market == "BJ": blockbj.add(s) zsbk_bj = blockbj diff --git a/hikyuu_pywrap/_Constant.cpp b/hikyuu_pywrap/_Constant.cpp index 2befcb75..4566856d 100644 --- a/hikyuu_pywrap/_Constant.cpp +++ b/hikyuu_pywrap/_Constant.cpp @@ -144,7 +144,7 @@ void export_Constant(py::module& m) { .def_readonly("STOCKTYPE_GEM", &Constant::STOCKTYPE_GEM, "创业板") .def_readonly("STOCKTYPE_START", &Constant::STOCKTYPE_START, "科创板") .def_readonly("STOCKTYPE_CRYPTO", &Constant::STOCKTYPE_START, "数字币") - .def_readonly("STOCKTYPE_A_BJ", &Constant::STOCKTYPE_START, "A股北交所") + .def_readonly("STOCKTYPE_A_BJ", &Constant::STOCKTYPE_A_BJ, "A股北交所") .def_readonly("STOCKTYPE_TMP", &Constant::STOCKTYPE_TMP, "临时Stock"); m.attr("constant") = Constant(); From b6f8b2830d7914478ccf9c403c5d621e2ce46d77 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 25 Apr 2024 16:56:13 +0800 Subject: [PATCH 235/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=BF=AE=E6=94=B9=20?= =?UTF-8?q?pytdx=20ping?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common_pytdx.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu/data/common_pytdx.py b/hikyuu/data/common_pytdx.py index 4a2ddc44..6e78641c 100644 --- a/hikyuu/data/common_pytdx.py +++ b/hikyuu/data/common_pytdx.py @@ -49,21 +49,21 @@ def to_pytdx_market(market): return pytdx_market[market.upper()] -def ping(ip, port=7709, multithread=False): +def ping(ip, port=7709, multithread=False, timeout=1): api = TdxHq_API(multithread=multithread) success = False starttime = time.time() + success = False try: - with api.connect(ip, port, time_out=1): + if api.connect(ip, port, time_out=timeout): # x = api.get_security_count(0) # x = api.get_index_bars(7, 1, '000001', 800, 100) x = api.get_security_bars(7, 0, '000001', 800, 100) if x: success = True - except Exception as e: - success = False - + print(e) + pass endtime = time.time() return (success, endtime - starttime, ip, port) From a96cfbfb5a8236e6a7c9d012094fda97b4431e72 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 25 Apr 2024 23:12:43 +0800 Subject: [PATCH 236/601] release 2.0.3 --- docs/source/release.rst | 11 +++++++++++ xmake.lua | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 2ba0efb7..85942a62 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,17 @@ 版本发布说明 ======================= +2.0.3 - 2024年4月25日 +------------------------- + +1. 增强 FINANCE,增加 only_year_report 和 dynamic 参数,以便进行市盈率等计算 +2. Indicaotr.plot 绘制时,将 x 轴设置为日期 +3. 增加北交所 92 号段 +4. 增加 BlockIndex 表,支持 Block 获取对应指数 +5. fixed 板块信息导入时,如果网络不好,未获取到当前板块信息时,会把之前的板块信息删除 +6. fixed interactive 中 blockbj 为空 + + 2.0.2 - 2024年4月19日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 6e21dd46..52afd59f 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.2", {build = "%Y%m%d%H%M"}) +set_version("2.0.3", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 02a09de0ff8c527ebb0b35ec40e61f959ce16f78 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 26 Apr 2024 18:46:09 +0800 Subject: [PATCH 237/601] =?UTF-8?q?Stock=20=E6=B7=BB=E5=8A=A0=E8=8E=B7?= =?UTF-8?q?=E5=8F=96=E6=89=80=E5=B1=9E=E6=9D=BF=E5=9D=97=E5=88=97=E8=A1=A8?= =?UTF-8?q?=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Block.cpp | 14 +++++++++++++- hikyuu_cpp/hikyuu/Block.h | 2 ++ hikyuu_cpp/hikyuu/Stock.cpp | 4 ++++ hikyuu_cpp/hikyuu/Stock.h | 8 ++++++++ hikyuu_cpp/hikyuu/StockManager.cpp | 4 ++++ hikyuu_cpp/hikyuu/StockManager.h | 9 ++++++++- .../hikyuu/data_driver/BlockInfoDriver.cpp | 11 +++++++++++ hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.h | 8 ++++++++ hikyuu_pywrap/_Stock.cpp | 16 ++++++++++++++++ 9 files changed, 74 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Block.cpp b/hikyuu_cpp/hikyuu/Block.cpp index 90ead0c2..70df2e3d 100644 --- a/hikyuu_cpp/hikyuu/Block.cpp +++ b/hikyuu_cpp/hikyuu/Block.cpp @@ -34,17 +34,29 @@ Block::Block(const string& category, const string& name, const string& indexCode } Block::Block(const Block& block) noexcept { - if (m_data == block.m_data) + if (!block.m_data) return; m_data = block.m_data; } +Block::Block(Block&& block) noexcept { + if (!block.m_data) + return; + m_data = std::move(block.m_data); +} + Block& Block::operator=(const Block& block) noexcept { HKU_IF_RETURN(this == &block || m_data == block.m_data, *this); m_data = block.m_data; return *this; } +Block& Block::operator=(Block&& block) noexcept { + HKU_IF_RETURN(this == &block || m_data == block.m_data, *this); + m_data = std::move(block.m_data); + return *this; +} + bool Block::have(const string& market_code) const { HKU_IF_RETURN(!m_data, false); string query_str = market_code; diff --git a/hikyuu_cpp/hikyuu/Block.h b/hikyuu_cpp/hikyuu/Block.h index 7ce7e282..9db6a31d 100644 --- a/hikyuu_cpp/hikyuu/Block.h +++ b/hikyuu_cpp/hikyuu/Block.h @@ -23,7 +23,9 @@ public: Block(const string& category, const string& name); Block(const string& category, const string& name, const string& indexCode); Block(const Block&) noexcept; + Block(Block&&) noexcept; Block& operator=(const Block&) noexcept; + Block& operator=(Block&&) noexcept; virtual ~Block(); typedef StockMapIterator const_iterator; diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index b1253722..15188008 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -831,6 +831,10 @@ Parameter Stock::getFinanceInfo() const { return result; } +vector Stock::getBelongToBlockList(const string& category) const { + return StockManager::instance().getStockBelongs(*this, category); +} + // 判断是否在交易时间段内(不判断日期) bool Stock::isTransactionTime(Datetime time) { MarketInfo market_info = StockManager::instance().getMarketInfo(market()); diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 5bd1d0c3..1561f88d 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -28,6 +28,7 @@ typedef DriverConnectPool KDataDriverConnectPool; typedef shared_ptr KDataDriverConnectPoolPtr; class HKU_API KData; class HKU_API Parameter; +class HKU_API Block; /** * Stock基类,Application中一般使用StockPtr进行操作 @@ -191,6 +192,13 @@ public: */ Parameter getFinanceInfo() const; + /** + * 获取所属板块列表 + * @param category 指定的板块分类,如果为空,则返回所有板块分类的所属板块 + * @return BlockList + */ + vector getBelongToBlockList(const string& category) const; + /** * 获取历史财务信息 */ diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 072d91dc..fcf32951 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -403,6 +403,10 @@ BlockList StockManager::getBlockList() { return m_blockDriver ? m_blockDriver->getBlockList() : BlockList(); } +BlockList StockManager::getStockBelongs(const Stock& stk, const string& category) { + return m_blockDriver ? m_blockDriver->getStockBelongs(stk, category) : BlockList(); +} + DatetimeList StockManager::getTradingCalendar(const KQuery& query, const string& market) { auto marketinfo = getMarketInfo(market); return getStock(fmt::format("{}{}", marketinfo.market(), marketinfo.code())) diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 366d42be..2cd32999 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -141,7 +141,14 @@ public: */ BlockList getBlockList(); - // 目前支持"SH" + /** + * 获取指定证券所属的板块列表 + * @param stk 指定证券 + * @param category 板块分类,如果为空字符串,返回所有板块分类下的所属板块 + * @return BlockList + */ + BlockList getStockBelongs(const Stock& stk, const string& category); + /** * 获取交易日历,目前支持"SH" * @param query diff --git a/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.cpp index 92a8f5b6..18a7b3ed 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.cpp @@ -55,4 +55,15 @@ bool BlockInfoDriver::init(const Parameter& params) { return _init(); } +BlockList BlockInfoDriver::getStockBelongs(const Stock& stk, const string& category) { + BlockList ret; + auto category_blks = category.empty() ? getBlockList() : getBlockList(category); + for (auto&& blk : category_blks) { + if (blk.have(stk)) { + ret.emplace_back(std::move(blk)); + } + } + return ret; +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.h index 88735fe3..1a4f2d45 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/BlockInfoDriver.h @@ -35,6 +35,14 @@ public: */ bool init(const Parameter& params); + /** + * 获取指定证券所属的板块列表 + * @param stk 指定证券 + * @param category 板块分类,如果为空字符串,返回所有板块分类下的所属板块 + * @return BlockList + */ + BlockList getStockBelongs(const Stock& stk, const string& category); + /** * 子类如果需要缓存,可实现该方法将数据加载至自身的缓存 */ diff --git a/hikyuu_pywrap/_Stock.cpp b/hikyuu_pywrap/_Stock.cpp index 50b25289..b6ab7e21 100644 --- a/hikyuu_pywrap/_Stock.cpp +++ b/hikyuu_pywrap/_Stock.cpp @@ -175,6 +175,22 @@ void export_Stock(py::module& m) { :param Datetime end: 结束时刻 :rtype: StockWeightList)") + .def( + "get_belong_to_block_list", + [](Stock& stk, const py::object& category) { + string c_category; + if (!category.is_none()) { + c_category = category.cast(); + } + return stk.getBelongToBlockList(c_category); + }, + py::arg("category") = py::none(), R"(get_belong_to_block_list(self[, category=None]) + + 获取所属板块列表 + + :param str category: 指定的板块分类,为 None 时,返回所有板块分类下的所属板块 + :rtype: list)") + .def( "get_history_finance", [](const Stock& stk) { From 914f74a54389f43d7f2fd01e5833c9461049ff42 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 26 Apr 2024 18:49:39 +0800 Subject: [PATCH 238/601] update --- docs/source/stock_manager.rst | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/source/stock_manager.rst b/docs/source/stock_manager.rst index 773be187..b8ee171a 100644 --- a/docs/source/stock_manager.rst +++ b/docs/source/stock_manager.rst @@ -416,8 +416,12 @@ StockManager/Block/Stock :param Query.KType ktype: K线类型 - + .. py:method:: get_belong_to_block_list(self[, category=None]) + 获取所属板块列表 + + :param str category: 指定的板块分类,为 None 时,返回所有板块分类下的所属板块 + :rtype: list .. py:class:: Block From cca00a548ca091caf3d6cf7a2b09b6f70d6d3707 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 28 Apr 2024 02:32:31 +0800 Subject: [PATCH 239/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E9=94=99=E8=AF=AF?= =?UTF-8?q?=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/pytdx_weight_to_mysql.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/hikyuu/data/pytdx_weight_to_mysql.py b/hikyuu/data/pytdx_weight_to_mysql.py index ab4a7384..8d92ba2f 100644 --- a/hikyuu/data/pytdx_weight_to_mysql.py +++ b/hikyuu/data/pytdx_weight_to_mysql.py @@ -27,9 +27,10 @@ import mysql.connector from hikyuu.util import * from .common_pytdx import to_pytdx_market + @hku_catch(trace=True) def pytdx_import_weight_to_mysql(pytdx_api, connect, market): - """导入钱龙格式的权息数据""" + """从 pytdx 导入权息数据""" cur = connect.cursor() cur.execute("select marketid from `hku_base`.`market` where market='%s'" % market) marketid = [id[0] for id in cur.fetchall()] @@ -43,7 +44,7 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): for stockrecord in stockid_list: stockid, code = stockrecord - #print("{}{}".format(market, code)) + # print("{}{}".format(market, code)) # 获取当前数据库中最后的一条权息记录的总股本和流通股本 cur = connect.cursor() @@ -100,15 +101,15 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): records[date] = [ stockid, date, - int(10000 * xdxr['songzhuangu']) if xdxr['songzhuangu'] is not None else 0, #countAsGift - int(10000 * xdxr['peigu']) if xdxr['peigu'] is not None else 0, #countForSell - int(1000 * xdxr['peigujia']) if xdxr['peigujia'] is not None else 0, #priceForSell - int(1000 * xdxr['fenhong']) if xdxr['fenhong'] is not None else 0, #bonus - 0, #countOfIncreasement, pytdx 不区分送股和转增股,统一记在送股 + int(10000 * xdxr['songzhuangu']) if xdxr['songzhuangu'] is not None else 0, # countAsGift + int(10000 * xdxr['peigu']) if xdxr['peigu'] is not None else 0, # countForSell + int(1000 * xdxr['peigujia']) if xdxr['peigujia'] is not None else 0, # priceForSell + int(1000 * xdxr['fenhong']) if xdxr['fenhong'] is not None else 0, # bonus + 0, # countOfIncreasement, pytdx 不区分送股和转增股,统一记在送股 round(xdxr['houzongguben']) - if xdxr['houzongguben'] is not None else last_total_count, #totalCount + if xdxr['houzongguben'] is not None else last_total_count, # totalCount round(xdxr['panhouliutong']) - if xdxr['panhouliutong'] is not None else last_free_count #freeCount + if xdxr['panhouliutong'] is not None else last_free_count # freeCount ] else: if xdxr['songzhuangu'] is not None: @@ -187,4 +188,4 @@ if __name__ == '__main__': endtime = time.time() print("\nTotal time:") print("%.2fs" % (endtime - starttime)) - print("%.2fm" % ((endtime - starttime) / 60)) \ No newline at end of file + print("%.2fm" % ((endtime - starttime) / 60)) From 4aae90134cc326eb23fb759bef7c0297ef4b2ad7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 28 Apr 2024 02:33:14 +0800 Subject: [PATCH 240/601] =?UTF-8?q?=E6=94=B9=E8=BF=9B=20sys=5Fperformance?= =?UTF-8?q?=EF=BC=8C=E5=9C=A8query=E6=97=A5=E6=9C=9F=E4=B8=8D=E5=9C=A8stoc?= =?UTF-8?q?k=E7=9A=84=E6=9C=89=E6=95=88=E6=97=A5=E6=9C=9F=E8=8C=83?= =?UTF-8?q?=E5=9B=B4=E5=86=85=E6=97=B6=EF=BC=8C=E6=8A=9B=E5=87=BA=E5=BC=82?= =?UTF-8?q?=E5=B8=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index cd7a747e..8d95910c 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -754,6 +754,7 @@ def sys_performance(sys, ref_stk=None): if ref_stk is None: ref_stk = get_stock('sh000300') ref_k = ref_stk.get_kdata(sys.query) + hku_check(len(ref_k) > 0, "The length of ref_k is zero! Maybe The query date is out of the ref-stock range!\n ref_k: {}", ref_k) query = Query(ref_k[0].datetime.start_of_day(), ref_k[-1].datetime.start_of_day() + TimeDelta(1), Query.DAY) ref_k = ref_stk.get_kdata(query) From 8f6ca8076fb8ac563f45d0708c6a4fa77f84bfbc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 29 Apr 2024 01:58:41 +0800 Subject: [PATCH 241/601] =?UTF-8?q?=E5=8D=95=E5=9B=A0=E5=AD=90=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E4=BD=BF=E7=94=A8=E5=8E=9F=E5=9B=A0=E5=AD=90=E5=80=BC?= =?UTF-8?q?=EF=BC=8C=E8=80=8C=E6=98=AF=E6=94=B9=E4=B8=BA=20MF=20=E5=85=B7?= =?UTF-8?q?=E4=BD=93=E5=AE=9E=E7=8E=B0=E8=87=AA=E8=A1=8C=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/multifactor/MultiFactorBase.cpp | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 19070fca..b308aefc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -484,18 +484,7 @@ void MultiFactorBase::calculate() { vector> all_stk_inds = getAllSrcFactors(); try { - if (m_inds.size() == 1) { - // 直接使用原始因子 - size_t stk_count = m_stks.size(); - m_all_factors.resize(stk_count); - for (size_t i = 0; i < stk_count; i++) { - m_all_factors[i] = std::move(all_stk_inds[i][0]); - } - - } else { - // 计算每支证券调整后的合成因子 - m_all_factors = _calculate(all_stk_inds); - } + m_all_factors = _calculate(all_stk_inds); // 计算完成后创建截面索引 _buildIndex(); From 2471f59106ce6bde40b1566848d3cfd0e4985956 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 29 Apr 2024 02:45:49 +0800 Subject: [PATCH 242/601] update doc.h --- hikyuu_cpp/hikyuu/doc.h | 3 --- 1 file changed, 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/doc.h b/hikyuu_cpp/hikyuu/doc.h index e300405f..42d488ec 100644 --- a/hikyuu_cpp/hikyuu/doc.h +++ b/hikyuu_cpp/hikyuu/doc.h @@ -156,9 +156,6 @@ * * @defgroup ThreadPool Thread Pool 线程池 * @ingroup Utilities - * - * @defgroup TaskGroup Task Group 并行任务组 - * @ingroup Utilities */ /** From 1c8e03fcae941d822deee1d0aedb07caef8a4099 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 30 Apr 2024 01:00:08 +0800 Subject: [PATCH 243/601] =?UTF-8?q?matplotlib=20sysplot=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0=20only=5Fdraw=5Fclose=EF=BC=8C=E9=81=BF=E5=85=8D?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E9=87=8F=E8=BE=83=E5=A4=A7=E6=97=B6,=20matpl?= =?UTF-8?q?oblib=20=E7=BB=98=E5=88=B6=20K=20=E7=BA=BF=E8=BF=87=E6=85=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 8d95910c..8a340af7 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -681,7 +681,7 @@ def cnplot(cn, new=True, axes=None, kdata=None): axes.fill_between(x, y1, y2, where=y2 < y1, facecolor='red', alpha=0.6) -def sysplot(sys, new=True, axes=None, style=1): +def sysplot(sys, new=True, axes=None, style=1, only_draw_close=False): """绘制系统实际买入/卖出信号 :param SystemBase sys: 系统实例 @@ -689,6 +689,7 @@ def sysplot(sys, new=True, axes=None, style=1): 创建新的窗口对象并在其中进行绘制 :param axes: 指定在那个轴对象中进行绘制 :param style: 1 | 2 信号箭头绘制样式 + :param bool only_draw_close: 不绘制K线,仅绘制 close """ kdata = sys.to @@ -698,7 +699,10 @@ def sysplot(sys, new=True, axes=None, style=1): if axes is None: if new: axes = create_figure() - kplot(kdata, axes=axes) + if only_draw_close: + iplot(kdata.close, axes=axes) + else: + kplot(kdata, axes=axes) else: axes = gca() From f5cfd006013ca192b2ca42f227c3a4011d86a80a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 30 Apr 2024 01:15:16 +0800 Subject: [PATCH 244/601] update gh ci --- .github/workflows/ubuntu.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 3dfd085a..793ed365 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -34,7 +34,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: branch@master + xmake-version: 2.9.1 actions-cache-folder: '.xmake-cache' actions-cache-key: 'ubuntu' From ed3fcb559ce624ffde846fc3f32a12311cc7cc1d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 30 Apr 2024 01:18:05 +0800 Subject: [PATCH 245/601] update gh ai, lock xmake version --- .github/workflows/ubuntu_aarch64.yml | 2 +- .github/workflows/ubuntu_python.yml | 2 +- .github/workflows/windows.yml | 2 +- .github/workflows/windows_python.yml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ubuntu_aarch64.yml b/.github/workflows/ubuntu_aarch64.yml index 7342b31b..c5c362f1 100644 --- a/.github/workflows/ubuntu_aarch64.yml +++ b/.github/workflows/ubuntu_aarch64.yml @@ -41,7 +41,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: branch@master + xmake-version: 2.9.1 actions-cache-folder: '.xmake-cache' - name: Installation musl diff --git a/.github/workflows/ubuntu_python.yml b/.github/workflows/ubuntu_python.yml index 20f06646..0bb9b8ed 100644 --- a/.github/workflows/ubuntu_python.yml +++ b/.github/workflows/ubuntu_python.yml @@ -34,7 +34,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: branch@master + xmake-version: 2.9.1 actions-cache-folder: '.xmake-cache' actions-cache-key: 'ubuntu' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 0e508c99..c345f298 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -26,7 +26,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: branch@master + xmake-version: 2.9.1 - name: configure shell: cmd diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index fbd885fb..2d8b6f54 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -39,7 +39,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: branch@master + xmake-version: 2.9.1 actions-cache-folder: '.xmake-cache' - name: configure From 7eda134bdccb6530c22ffb1a33b1de02d6f339e6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 30 Apr 2024 23:26:58 +0800 Subject: [PATCH 246/601] =?UTF-8?q?=E6=81=A2=E5=A4=8DMF=E5=9C=A8=E5=8D=95?= =?UTF-8?q?=E5=9B=A0=E5=AD=90=E6=97=B6=E7=9B=B4=E6=8E=A5=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E5=8E=9F=E5=A7=8B=E5=9B=A0=E5=AD=90=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/multifactor/MultiFactorBase.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index b308aefc..19070fca 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -484,7 +484,18 @@ void MultiFactorBase::calculate() { vector> all_stk_inds = getAllSrcFactors(); try { - m_all_factors = _calculate(all_stk_inds); + if (m_inds.size() == 1) { + // 直接使用原始因子 + size_t stk_count = m_stks.size(); + m_all_factors.resize(stk_count); + for (size_t i = 0; i < stk_count; i++) { + m_all_factors[i] = std::move(all_stk_inds[i][0]); + } + + } else { + // 计算每支证券调整后的合成因子 + m_all_factors = _calculate(all_stk_inds); + } // 计算完成后创建截面索引 _buildIndex(); From 65ced11ad0ff05c09d6fc6f9098a075a80d01ef0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 30 Apr 2024 23:27:36 +0800 Subject: [PATCH 247/601] =?UTF-8?q?=E6=94=B9=E8=BF=9Bmatplot=E7=BB=98?= =?UTF-8?q?=E5=88=B6=E5=9B=BE=E5=BD=A2=E6=97=B6=EF=BC=8Cx=E8=BD=B4?= =?UTF-8?q?=E5=9D=90=E6=A0=87=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 8a340af7..ccdb3309 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -147,7 +147,7 @@ class StockFuncFormatter(object): def getDayLocatorAndFormatter(dates): """获取显示日线时使用的Major Locator和Major Formatter""" - sep = len(dates) / 10 + sep = int(len(dates) / 10) loc = [ (i, str(d) if (i != (len(dates) - 1)) and (i % sep != 0) else "{}-{}-{}".format(d.year, d.month, d.day)) for i, d in enumerate(dates) From ff0094fd5d6bef06fd10798881fbae09dfd1de1a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 1 May 2024 00:02:19 +0800 Subject: [PATCH 248/601] update --- .../hikyuu/trade_manage/TradeManager.cpp | 6 +++--- .../hikyuu/trade_sys/signal/SignalBase.cpp | 18 ++---------------- 2 files changed, 5 insertions(+), 19 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 24573db7..79c9233c 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -905,9 +905,9 @@ TradeRecord TradeManager::sell(const Datetime& datetime, const Stock& stock, pri // 未持仓 position_map_type::iterator pos_iter = m_position.find(stock.id()); - HKU_WARN_IF_RETURN(pos_iter == m_position.end(), result, - "{} {} This stock was not bought never! ({}, {:<.4f}, {}, {})", datetime, - stock.market_code(), datetime, realPrice, number, int(from)); + HKU_TRACE_IF_RETURN(pos_iter == m_position.end(), result, + "{} {} This stock was not bought never! ({}, {:<.4f}, {}, {})", datetime, + stock.market_code(), datetime, realPrice, number, getSystemPartName(from)); // 根据权息调整当前持仓情况 updateWithWeight(datetime); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index bb911c39..2dc88bb5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -76,24 +76,10 @@ void SignalBase::setTO(const KData& kdata) { bool cycle = getParam("cycle"); m_cycle_start = kdata[0].datetime; + HKU_IF_RETURN(cycle, void()); - const KQuery& query = kdata.getQuery(); - if (query.queryType() == KQuery::DATE) { - m_cycle_end = query.endDatetime(); - } else { - size_t last_pos = kdata.lastPos(); - const Stock& stk = kdata.getStock(); - if (last_pos + 1 >= stk.getCount(query.kType())) { - m_cycle_end = Null(); - } else { - KRecord krecord = stk.getKRecord(last_pos + 1, query.kType()); - m_cycle_end = krecord.datetime; - } - } - - KData cycle_kdata = kdata.getKData(m_cycle_start, m_cycle_end); if (!cycle) { - _calculate(cycle_kdata); + _calculate(kdata); } } From 5ef1daf35bbbe22d225b6485433a347119648636 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 1 May 2024 00:11:17 +0800 Subject: [PATCH 249/601] =?UTF-8?q?fixed=20matplotlib=20performance=20?= =?UTF-8?q?=E7=BB=98=E5=88=B6=E6=97=B6=EF=BC=8C=E5=BD=93=E5=89=8D=E6=94=B6?= =?UTF-8?q?=E7=9B=8A=E7=8E=87=E6=98=BE=E7=A4=BA=E6=97=B6=E6=9C=AA=E4=B9=98?= =?UTF-8?q?=E4=BB=A5100?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index ccdb3309..a0aa5b83 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -791,7 +791,7 @@ def sys_performance(sys, ref_stk=None): t1 = '投入总资产: {:<.2f} 当前总资产: {:<.2f} 当前盈利: {:<.2f}'.format( invest_total, cur_fund, cur_fund - invest_total) t2 = '当前策略收益: {:<.2f}% 年化收益率: {:<.2f}% 最大回撤: {:<.2f}%'.format( - funds_return[-1], per["帐户平均年收益率%"], max_pullback) + funds_return[-1]*100, per["帐户平均年收益率%"], max_pullback) t3 = '系统胜率: {:<.2f}% 盈/亏比: 1 : {:<.2f} 夏普比率: {:<.2f}'.format( per['赢利交易比例%'], per['净赢利/亏损比例'], sharp) From a293ebcb3b01077db763c3bec317568d3ca3391c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 1 May 2024 23:26:52 +0800 Subject: [PATCH 250/601] update requirements.txt --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c4326cb4..1c3bde4b 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,4 +16,5 @@ pynng akshare pyecharts pipdeptree -h5py \ No newline at end of file +h5py +tdqm \ No newline at end of file From 912594e81bee459c67e079ccae17bdb63936ae61 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 2 May 2024 15:15:38 +0800 Subject: [PATCH 251/601] =?UTF-8?q?update=20=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index bd049ac7..ffb6f3ef 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -35,7 +35,7 @@ void export_MultiFactor(py::module& m) { .def(py::init()) .def("__str__", to_py_str) .def("__repr__", to_py_str) - .def_readwrite("stock", &ScoreRecord::stock, "时间") + .def_readwrite("stock", &ScoreRecord::stock, "证券") .def_readwrite("value", &ScoreRecord::value, "时间"); py::class_(m, "MultiFactorBase", From 4fc25b01c7d7a071a68cb443721971dacd52d318 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 2 May 2024 15:16:11 +0800 Subject: [PATCH 252/601] =?UTF-8?q?update=20=E5=B8=AE=E5=8A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index ffb6f3ef..b79f1b34 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -36,7 +36,7 @@ void export_MultiFactor(py::module& m) { .def("__str__", to_py_str) .def("__repr__", to_py_str) .def_readwrite("stock", &ScoreRecord::stock, "证券") - .def_readwrite("value", &ScoreRecord::value, "时间"); + .def_readwrite("value", &ScoreRecord::value, "分值"); py::class_(m, "MultiFactorBase", R"(市场环境判定策略基类 From ae29fbb0ef81412800868ecb65f41fd820019b87 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 2 May 2024 16:27:54 +0800 Subject: [PATCH 253/601] =?UTF-8?q?fixed=20performance=20=E7=BB=98?= =?UTF-8?q?=E5=9B=BE=E7=AD=96=E7=95=A5=E6=94=B6=E7=9B=8A=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index a0aa5b83..78396af8 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -791,7 +791,7 @@ def sys_performance(sys, ref_stk=None): t1 = '投入总资产: {:<.2f} 当前总资产: {:<.2f} 当前盈利: {:<.2f}'.format( invest_total, cur_fund, cur_fund - invest_total) t2 = '当前策略收益: {:<.2f}% 年化收益率: {:<.2f}% 最大回撤: {:<.2f}%'.format( - funds_return[-1]*100, per["帐户平均年收益率%"], max_pullback) + funds_return[-1]*100 - 100, per["帐户平均年收益率%"], max_pullback) t3 = '系统胜率: {:<.2f}% 盈/亏比: 1 : {:<.2f} 夏普比率: {:<.2f}'.format( per['赢利交易比例%'], per['净赢利/亏损比例'], sharp) From 635ec2aa68a36966a44368ef17557bfb501d570d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 3 May 2024 23:35:36 +0800 Subject: [PATCH 254/601] =?UTF-8?q?pf=20=E7=B3=BB=E7=BB=9F=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E5=8A=A0=E4=B8=8A=E8=82=A1=E7=A5=A8=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 1c11ef24..9b6b3cb9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -148,7 +148,8 @@ void Portfolio::_readyForRun() { // 为内部实际执行的系统创建初始资金为0的子账户 sys->setTM(pro_tm->clone()); - string sys_name = fmt::format("{}_{}", sys->name(), sys->getStock().market_code()); + string sys_name = fmt::format("{}_{}_{}", sys->name(), sys->getStock().market_code(), + sys->getStock().name()); sys->getTM()->name(fmt::format("TM_SUB_{}", sys_name)); sys->name(fmt::format("PF_{}", sys_name)); @@ -255,10 +256,10 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool 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); - } + // } 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()); From 0a21a5cd999dc9e482d751d5a9cf8464942e4f1f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 3 May 2024 23:45:41 +0800 Subject: [PATCH 255/601] =?UTF-8?q?=E5=A4=84=E7=90=86nng=E5=8D=87=E7=BA=A7?= =?UTF-8?q?=E5=90=8E=E7=9A=84=E7=BC=96=E8=AF=91=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 4 ++-- hikyuu_cpp/hikyuu/global/node/NodeClient.h | 16 ++++++++-------- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 56b18cf4..7558fc13 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -146,10 +146,10 @@ void SpotAgent::work_thread() { int rv = nng_sub0_open(&sock); HKU_ERROR_IF_RETURN(rv != 0, void(), "Can't open nng sub0! {}", nng_strerror(rv)); - rv = nng_setopt(sock, NNG_OPT_SUB_SUBSCRIBE, ms_spotTopic, ms_spotTopicLength); + rv = nng_socket_set(sock, NNG_OPT_SUB_SUBSCRIBE, ms_spotTopic, ms_spotTopicLength); HKU_ERROR_IF_RETURN(rv != 0, void(), "Failed set nng socket option! {}", nng_strerror(rv)); - rv = nng_setopt_ms(sock, NNG_OPT_RECVTIMEO, m_revTimeout); + rv = nng_socket_set_ms(sock, NNG_OPT_RECVTIMEO, m_revTimeout); HKU_ERROR_IF_RETURN(rv != 0, void(), "Failed set receive timeout option!"); rv = -1; diff --git a/hikyuu_cpp/hikyuu/global/node/NodeClient.h b/hikyuu_cpp/hikyuu/global/node/NodeClient.h index c7502e2a..b7512784 100644 --- a/hikyuu_cpp/hikyuu/global/node/NodeClient.h +++ b/hikyuu_cpp/hikyuu/global/node/NodeClient.h @@ -42,17 +42,17 @@ public: try { // 设置发送结果 socket 连接参数 - rv = nng_setopt_ms(m_socket, NNG_OPT_RECONNMINT, 10); - NODE_NNG_CHECK(rv, "Failed nng_setopt_ms!"); + rv = nng_socket_set_ms(m_socket, NNG_OPT_RECONNMINT, 10); + NODE_NNG_CHECK(rv, "Failed nng_socket_set_ms!"); - rv = nng_setopt_ms(m_socket, NNG_OPT_RECONNMAXT, 15000); - NODE_NNG_CHECK(rv, "Failed nng_setopt_ms!"); + rv = nng_socket_set_ms(m_socket, NNG_OPT_RECONNMAXT, 15000); + NODE_NNG_CHECK(rv, "Failed nng_socket_set_ms!"); - rv = nng_setopt_ms(m_socket, NNG_OPT_SENDTIMEO, 10000); - NODE_NNG_CHECK(rv, "Failed nng_setopt_ms!"); + rv = nng_socket_set_ms(m_socket, NNG_OPT_SENDTIMEO, 10000); + NODE_NNG_CHECK(rv, "Failed nng_socket_set_ms!"); - rv = nng_setopt_ms(m_socket, NNG_OPT_RECVTIMEO, 10000); - NODE_NNG_CHECK(rv, "Failed nng_setopt_ms!"); + rv = nng_socket_set_ms(m_socket, NNG_OPT_RECVTIMEO, 10000); + NODE_NNG_CHECK(rv, "Failed nng_socket_set_ms!"); rv = nng_dial(m_socket, m_server_addr.c_str(), NULL, 0); NODE_NNG_CHECK(rv, "Failed dial server: {}!", m_server_addr); From be18d35241b8ca046a74895f3d03a36f7dda432f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 4 May 2024 01:15:41 +0800 Subject: [PATCH 256/601] =?UTF-8?q?fixed=20Portfolio=20=E5=9C=A8=E9=9D=9E?= =?UTF-8?q?=E5=BB=B6=E8=BF=9F=E4=B9=B0=E5=85=A5=E3=80=81=E5=BB=B6=E8=BF=9F?= =?UTF-8?q?=E5=8D=96=E5=87=BA=E7=9A=84=E5=9C=BA=E6=99=AF=E4=B8=8B=E5=AF=B9?= =?UTF-8?q?=E8=B4=A6=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 9b6b3cb9..975f98cb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -256,10 +256,10 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool 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); - // } + } else if (m_tm->currentCash() < sum_cash) { + if (m_cash_tm->currentCash() > diff) { + m_cash_tm->checkout(date, m_cash_tm->currentCash() - 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()); From b0859c0decc6304ff154302ca04008185d1ddc44 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 5 May 2024 20:00:18 +0800 Subject: [PATCH 257/601] =?UTF-8?q?fixed=20ETF=20=E6=9D=83=E6=81=AF?= =?UTF-8?q?=E7=BC=BA=E5=B0=91=E6=89=A9=E7=BC=A9=E8=82=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0017.sql | 5 ++ hikyuu/data/pytdx_weight_to_mysql.py | 50 +++++++++++-------- hikyuu/data/pytdx_weight_to_sqlite.py | 26 ++++++---- hikyuu/data/sqlite_upgrade/0018.sql | 5 ++ .../hikyuu/trade_manage/TradeManager.cpp | 2 +- 5 files changed, 56 insertions(+), 32 deletions(-) create mode 100644 hikyuu/data/mysql_upgrade/0017.sql create mode 100644 hikyuu/data/sqlite_upgrade/0018.sql diff --git a/hikyuu/data/mysql_upgrade/0017.sql b/hikyuu/data/mysql_upgrade/0017.sql new file mode 100644 index 00000000..ebc16c1b --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0017.sql @@ -0,0 +1,5 @@ +delete from `hku_base`.`stkweight` where 1=1; +alter table `hku_base`.`stkweight` AUTO_INCREMENT=1; +alter table `hku_base`.`stkweight` modify column `countAsGift` DOUBLE not null default 0; +alter table `hku_base`.`stkweight` modify column `countForSell` DOUBLE not null default 0; +UPDATE `hku_base`.`version` set `version` = 17; diff --git a/hikyuu/data/pytdx_weight_to_mysql.py b/hikyuu/data/pytdx_weight_to_mysql.py index 8d92ba2f..0f37a59e 100644 --- a/hikyuu/data/pytdx_weight_to_mysql.py +++ b/hikyuu/data/pytdx_weight_to_mysql.py @@ -77,57 +77,63 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): continue if date == db_last_date and new_last_db_weight is not None: if xdxr['songzhuangu'] is not None: - new_last_db_weight[3] = int(10000 * xdxr['songzhuangu']) + new_last_db_weight[3] = 10000 * xdxr['songzhuangu'] + update_last_db_weight = True + if xdxr['suogu'] is not None: + # etf 扩股 + new_last_db_weight[3] = 100000 * (xdxr['suogu']-1) update_last_db_weight = True if xdxr['peigu'] is not None: - new_last_db_weight[4] = int(10000 * xdxr['peigu']) + new_last_db_weight[4] = 10000 * xdxr['peigu'] update_last_db_weight = True if xdxr['peigujia'] is not None: - new_last_db_weight[5] = int(1000 * xdxr['peigujia']) + new_last_db_weight[5] = 1000 * xdxr['peigujia'] update_last_db_weight = True if xdxr['fenhong'] is not None: - new_last_db_weight[6] = int(1000 * xdxr['fenhong']) + new_last_db_weight[6] = 1000 * xdxr['fenhong'] update_last_db_weight = True if xdxr['houzongguben'] is not None: - new_last_db_weight[8] = round(xdxr['houzongguben']) + new_last_db_weight[8] = xdxr['houzongguben'] update_last_db_weight = True last_total_count = new_last_db_weight[8] if xdxr['panhouliutong'] is not None: - new_last_db_weight[9] = round(xdxr['panhouliutong']) + new_last_db_weight[9] = xdxr['panhouliutong'] update_last_db_weight = True last_free_count = new_last_db_weight[9] continue if date not in records: + songzhuangu = 10000 * xdxr['songzhuangu'] if xdxr['songzhuangu'] is not None else 0 + songzhuangu = 100000 * (xdxr['suogu']-1) if xdxr['suogu'] is not None else 0 records[date] = [ stockid, date, - int(10000 * xdxr['songzhuangu']) if xdxr['songzhuangu'] is not None else 0, # countAsGift - int(10000 * xdxr['peigu']) if xdxr['peigu'] is not None else 0, # countForSell - int(1000 * xdxr['peigujia']) if xdxr['peigujia'] is not None else 0, # priceForSell - int(1000 * xdxr['fenhong']) if xdxr['fenhong'] is not None else 0, # bonus + songzhuangu, # countAsGift + 10000 * xdxr['peigu'] if xdxr['peigu'] is not None else 0, # countForSell + 1000 * xdxr['peigujia'] if xdxr['peigujia'] is not None else 0, # priceForSell + 1000 * xdxr['fenhong'] if xdxr['fenhong'] is not None else 0, # bonus 0, # countOfIncreasement, pytdx 不区分送股和转增股,统一记在送股 - round(xdxr['houzongguben']) - if xdxr['houzongguben'] is not None else last_total_count, # totalCount - round(xdxr['panhouliutong']) - if xdxr['panhouliutong'] is not None else last_free_count # freeCount + xdxr['houzongguben'] if xdxr['houzongguben'] is not None else last_total_count, # totalCount + xdxr['panhouliutong'] if xdxr['panhouliutong'] is not None else last_free_count # freeCount ] else: if xdxr['songzhuangu'] is not None: - records[date][2] = int(10000 * xdxr['songzhuangu']) + records[date][2] = 10000 * xdxr['songzhuangu'] + if xdxr['suogu'] is not None: + records[date][2] = 100000 * (xdxr['suogu']-1) if xdxr['peigu'] is not None: - records[date][3] = int(10000 * xdxr['peigu']) + records[date][3] = 10000 * xdxr['peigu'] if xdxr['peigujia'] is not None: - records[date][4] = int(1000 * xdxr['peigujia']) + records[date][4] = 1000 * xdxr['peigujia'] if xdxr['fenhong'] is not None: - records[date][5] = int(1000 * xdxr['fenhong']) + records[date][5] = 1000 * xdxr['fenhong'] if xdxr['houzongguben'] is not None: - records[date][7] = round(xdxr['houzongguben']) + records[date][7] = xdxr['houzongguben'] if xdxr['panhouliutong'] is not None: - records[date][8] = round(xdxr['panhouliutong']) + records[date][8] = xdxr['panhouliutong'] if xdxr['houzongguben'] is not None: - last_total_count = round(xdxr['houzongguben']) + last_total_count = xdxr['houzongguben'] if xdxr['panhouliutong'] is not None: - last_free_count = round(xdxr['panhouliutong']) + last_free_count = xdxr['panhouliutong'] except Exception as e: print(e) print("{} {}{} xdxr: {} last_db_weigth:{}".format(stockid, market, code, xdxr, new_last_db_weight)) diff --git a/hikyuu/data/pytdx_weight_to_sqlite.py b/hikyuu/data/pytdx_weight_to_sqlite.py index 9ca63b61..3ddc4aa9 100644 --- a/hikyuu/data/pytdx_weight_to_sqlite.py +++ b/hikyuu/data/pytdx_weight_to_sqlite.py @@ -41,7 +41,7 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): for stockrecord in stockid_list: stockid, code = stockrecord - #print("{}{}".format(market, code)) + # print("{}{}".format(market, code)) # 获取当前数据库中最后的一条权息记录的总股本和流通股本 cur = connect.cursor() @@ -76,6 +76,10 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): if xdxr['songzhuangu'] is not None: new_last_db_weight[3] = int(10000 * xdxr['songzhuangu']) update_last_db_weight = True + if xdxr['suogu'] is not None: + # etf 扩股 + new_last_db_weight[3] = int(100000 * (xdxr['suogu']-1)) + update_last_db_weight = True if xdxr['peigu'] is not None: new_last_db_weight[4] = int(10000 * xdxr['peigu']) update_last_db_weight = True @@ -95,22 +99,26 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): last_free_count = new_last_db_weight[9] continue if date not in records: + songzhuangu = int(10000 * xdxr['songzhuangu']) if xdxr['songzhuangu'] is not None else 0 + songzhuangu = int(100000 * (xdxr['suogu']-1)) if xdxr['suogu'] is not None else 0 records[date] = [ stockid, date, - int(10000 * xdxr['songzhuangu']) if xdxr['songzhuangu'] is not None else 0, #countAsGift - int(10000 * xdxr['peigu']) if xdxr['peigu'] is not None else 0, #countForSell - int(1000 * xdxr['peigujia']) if xdxr['peigujia'] is not None else 0, #priceForSell - int(1000 * xdxr['fenhong']) if xdxr['fenhong'] is not None else 0, #bonus - 0, #countOfIncreasement, pytdx 不区分送股和转增股,统一记在送股 + songzhuangu, # countForGift + int(10000 * xdxr['peigu']) if xdxr['peigu'] is not None else 0, # countForSell + int(1000 * xdxr['peigujia']) if xdxr['peigujia'] is not None else 0, # priceForSell + int(1000 * xdxr['fenhong']) if xdxr['fenhong'] is not None else 0, # bonus + 0, # countOfIncreasement, pytdx 不区分送股和转增股,统一记在送股 round(xdxr['houzongguben']) - if xdxr['houzongguben'] is not None else last_total_count, #totalCount + if xdxr['houzongguben'] is not None else last_total_count, # totalCount round(xdxr['panhouliutong']) - if xdxr['panhouliutong'] is not None else last_free_count #freeCount + if xdxr['panhouliutong'] is not None else last_free_count # freeCount ] else: if xdxr['songzhuangu'] is not None: records[date][2] = int(10000 * xdxr['songzhuangu']) + if xdxr['suogu'] is not None: + records[date][2] = int(100000 * (xdxr['suogu']-1)) if xdxr['peigu'] is not None: records[date][3] = int(10000 * xdxr['peigu']) if xdxr['peigujia'] is not None: @@ -182,4 +190,4 @@ if __name__ == '__main__': endtime = time.time() print("\nTotal time:") print("%.2fs" % (endtime - starttime)) - print("%.2fm" % ((endtime - starttime) / 60)) \ No newline at end of file + print("%.2fm" % ((endtime - starttime) / 60)) diff --git a/hikyuu/data/sqlite_upgrade/0018.sql b/hikyuu/data/sqlite_upgrade/0018.sql new file mode 100644 index 00000000..cc67831c --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0018.sql @@ -0,0 +1,5 @@ +BEGIN TRANSACTION; +DELETE FROM `stkWeight`; +UPDATE sqlite_sequence SET seq = 0 WHERE name='stkWeight'; +UPDATE `version` set `version` = 18; +COMMIT; \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 79c9233c..613e3907 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -1517,7 +1517,7 @@ void TradeManager::updateWithWeight(const Datetime& datetime) { new_trade_buffer.push_back(record); } - size_t addcount = + price_t addcount = (position.number / 10.0) * (weight_iter->countAsGift() + weight_iter->increasement()); if (addcount != 0.0) { position.number += addcount; From 04cc88f0f877708ac3ad321d64d47c7eec7916b7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 6 May 2024 02:31:01 +0800 Subject: [PATCH 258/601] release 2.0.4 --- docs/source/release.rst | 18 ++++++++++++++++++ sub_setup.py | 2 ++ xmake.lua | 2 +- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 85942a62..7c1d343b 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,24 @@ 版本发布说明 ======================= +2.0.4 - 2024年4月25日 +------------------------- + +1. 缺陷修复 + - fixed ETF 权息缺少扩缩股 + - fixed Portfolio 在非延迟买入、延迟卖出的场景下对账错误 + - fixed matplotlib performance 绘制时,当前收益率显示显示错误 + - fixed requirements.txt 增加tdqm, 缺失可能导致 windows HikyuuTdx 无法直接命令启动 + +2. 其他改进 + - Stock 添加获取所属板块列表方法 get_belong_to_block_list + - 改进 sys_performance,在query日期不在stock的有效日期范围内时,抛出异常 + - matplotlib sysplot 增加 only_draw_close,避免数据量较大时, matploblib 绘制 K 线过慢 + - 改进matplot绘制图形时,x轴坐标显示 + - pf 系统名称加上股票名称 + - 处理nng升级后的编译告警 + + 2.0.3 - 2024年4月25日 ------------------------- diff --git a/sub_setup.py b/sub_setup.py index 1f90ec58..3017d9da 100644 --- a/sub_setup.py +++ b/sub_setup.py @@ -143,6 +143,8 @@ setup( 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', ], entry_points={ 'gui_scripts': [ diff --git a/xmake.lua b/xmake.lua index 52afd59f..5f70665d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.3", {build = "%Y%m%d%H%M"}) +set_version("2.0.4", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 53ee558b6ec2de25721f9fa2d45bda1aa691494a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 7 May 2024 00:40:36 +0800 Subject: [PATCH 259/601] =?UTF-8?q?fixed=20strategy=20=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E6=9D=83=E6=81=AF=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp | 4 ++-- .../data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index 3fde4b3f..975513fb 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -106,14 +106,14 @@ StockWeightList MySQLBaseInfoDriver::getStockWeightList(const string &market, co HKU_CHECK(con, "Failed fetch connect!"); vector table; + Datetime new_start = start.isNull() ? Datetime::min() : start; Datetime new_end = end.isNull() ? Datetime::max() : end; con->batchLoad( table, format( "stockid=(select stockid from stock where marketid=(select marketid from " "market where market='{}') and code='{}') and date>={} and date<{} order by date asc", - market, code, start.year() * 10000 + start.month() * 100 + start.day(), - new_end.year() * 10000 + new_end.month() * 100 + new_end.day())); + market, code, new_start.ymd(), new_end.ymd())); for (auto &w : table) { try { diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp index 06b9bf89..14e52345 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/sqlite/SQLiteBaseInfoDriver.cpp @@ -127,14 +127,14 @@ StockWeightList SQLiteBaseInfoDriver::getStockWeightList(const string& market, c HKU_CHECK(con, "Failed fetch connect!"); vector table; + Datetime new_start = start.isNull() ? Datetime::min() : start; Datetime new_end = end.isNull() ? Datetime::max() : end; con->batchLoad( table, format( "stockid=(select stockid from stock where marketid=(select marketid from " "market where market='{}') and code='{}') and date>={} and date<{} order by date asc", - market, code, start.year() * 10000 + start.month() * 100 + start.day(), - new_end.year() * 10000 + new_end.month() * 100 + new_end.day())); + market, code, new_start.ymd(), new_end.ymd())); for (auto& w : table) { try { From 12ce26e07da60c3c5036d41f334c90823332bac9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 7 May 2024 01:49:38 +0800 Subject: [PATCH 260/601] =?UTF-8?q?StrategyContext=20=E5=9C=A8=E8=AE=BE?= =?UTF-8?q?=E5=AE=9A=20ktypes=20=E6=97=B6=E8=BF=9B=E8=A1=8C=E4=BB=8E?= =?UTF-8?q?=E5=B0=8F=E5=88=B0=E5=A4=A7=E7=9A=84=E6=8E=92=E5=BA=8F=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E4=BE=BF=E5=90=8E=E7=BB=AD=E8=83=BD=E5=A4=9F=E6=8C=89?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F=E8=B0=83=E7=94=A8=20onBar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StrategyContext.cpp | 6 ++++ hikyuu_cpp/hikyuu/StrategyContext.h | 3 +- .../hikyuu/hikyuu/test_StrategyContext.cpp | 31 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp diff --git a/hikyuu_cpp/hikyuu/StrategyContext.cpp b/hikyuu_cpp/hikyuu/StrategyContext.cpp index a58e5f69..97256913 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.cpp +++ b/hikyuu_cpp/hikyuu/StrategyContext.cpp @@ -21,6 +21,12 @@ void StrategyContext::setKTypeList(const vector& ktypeList) { to_upper(ktype); return ktype; }); + + // 对 ktype 按时间长度进行升序排序 + std::sort(m_ktypeList.begin(), m_ktypeList.end(), + [](const KQuery::KType& a, const KQuery::KType& b) { + return KQuery::getKTypeInMin(a) < KQuery::getKTypeInMin(b); + }); } bool StrategyContext::isAll() const { diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index abbfba05..2c55ffe9 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -46,12 +46,13 @@ public: void setKTypeList(const vector& ktypeList); + /** 该返回的 ktype 列表,已经按从小到大进行排序 */ const vector& getKTypeList() const { return m_ktypeList; } private: - Datetime m_startDatetime; + Datetime m_startDatetime{19901219}; vector m_stockCodeList; vector m_ktypeList; }; diff --git a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp new file mode 100644 index 00000000..b36da36c --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-07 + * Author: fasiondog + */ + +#include "../test_config.h" +#include "hikyuu/StrategyContext.h" + +/** + * @defgroup test_hikyuu_StrategyContext test_hikyuu_StrategyContext + * @ingroup test_hikyuu_base_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_StrategyContext") { + StrategyContext sc; + sc.setKTypeList( + {KQuery::MONTH, KQuery::MIN5, KQuery::DAY, KQuery::MIN, KQuery::WEEK, KQuery::MIN60}); + + vector expect{KQuery::MIN, KQuery::MIN5, KQuery::MIN60, + KQuery::DAY, KQuery::WEEK, KQuery::MONTH}; + const auto ktypes = sc.getKTypeList(); + for (size_t i = 0, len = ktypes.size(); i < len; i++) { + CHECK_EQ(ktypes[i], expect[i]); + } +} + +/** @} */ \ No newline at end of file From a766b1419226800d7b0eca81ac6ae8148f4994f1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 7 May 2024 17:48:11 +0800 Subject: [PATCH 261/601] =?UTF-8?q?fixed=20setKRecordList=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20move(ks)=20=E6=97=B6=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 15188008..da851d09 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -936,8 +936,8 @@ void Stock::setKRecordList(KRecordList&& ks, const KQuery::KType& ktype) { m_kdataDriver = DataDriverFactory::getKDataDriverPool(param); m_data->m_valid = true; - m_data->m_startDate = ks.front().datetime; - m_data->m_lastDate = ks.back().datetime; + m_data->m_startDate = (*m_data->pKData[nktype]).front().datetime; + m_data->m_lastDate = (*m_data->pKData[nktype]).back().datetime; } const vector& Stock::getHistoryFinance() const { From c63f9d6360bb3f0190bdeb48ae8329f5e164b418 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 7 May 2024 17:48:46 +0800 Subject: [PATCH 262/601] =?UTF-8?q?=E4=BC=98=E5=8C=96StrategyBase=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E4=BE=BF=E6=94=AF=E6=8C=81strategy=E7=BA=A7=E5=88=AB?= =?UTF-8?q?=E5=9B=9E=E6=B5=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StrategyContext.h | 1 + hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 72 +++++++++++++-------- hikyuu_cpp/hikyuu/strategy/StrategyBase.h | 6 +- hikyuu_pywrap/strategy/_StrategyBase.cpp | 2 +- 4 files changed, 53 insertions(+), 28 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index 2c55ffe9..de34b2af 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -31,6 +31,7 @@ public: } void startDatetime(const Datetime& d) { + HKU_CHECK(!d.isNull(), "Don't use null datetime!"); m_startDatetime = d; } diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index 459ef842..c3102bd0 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -58,7 +58,7 @@ void StrategyBase::_initDefaultParam() { setParam("enable_2hour_clock", false); } -void StrategyBase::run() { +void StrategyBase::_run(bool forTest) { // 调用 strategy 自身的初始化方法 init(); @@ -127,15 +127,10 @@ void StrategyBase::run() { ktype_list.push_back(KQuery::DAY); } + // 不使用默认的预加载模式 for (auto ktype : ktype_list) { to_lower(ktype); - preloadParam.set(ktype, true); - string key(format("{}_max", ktype)); - try { - preloadParam.set(key, config.getInt("preload", key)); - } catch (...) { - preloadParam.set(key, 4096); - } + preloadParam.set(ktype, false); } sm.init(baseParam, blockParam, kdataParam, preloadParam, hkuParam, m_context); @@ -152,29 +147,54 @@ void StrategyBase::run() { } HKU_WARN_IF(m_stock_list.empty(), "[Strategy {}] stock list is empty!", m_name); - if (m_stock_list.size() > 0) { - const Stock& ref_stk = m_stock_list[0]; - for (const auto& ktype : ktype_list) { - // 由于异步初始化,此处不用通过先getCount再getKRecord的方式获取最后的KRecord - KRecordList klist = ref_stk.getKRecordList(KQueryByIndex(0, Null(), ktype)); - size_t count = klist.size(); - if (count > 0) { - m_ref_last_time[ktype] = klist[count - 1].datetime; - } else { - m_ref_last_time[ktype] = Null(); - } + // 借助 Stock.setKRecordList 方法进行预加载(同步方式,不需要异步加载) + // 只从 context 指定起始日期开始加载 + size_t ktype_count = ktype_list.size(); + vector k_buffer(ktype_count); + for (auto& stk : m_stock_list) { + // 保留原始 KDataDriver,因为使用 stock.setKRecordList 将会把 stock 的 KDataDriver 设置为 + // DoNothing + auto old_driver = stk.getKDataDirver(); + + for (size_t i = 0; i < ktype_count; i++) { + k_buffer[i] = std::move(stk.getKRecordList( + KQueryByDate(m_context.startDatetime(), Null(), ktype_list[i]))); } + for (size_t i = 0; i < ktype_count; i++) { + stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]); + } + + // 恢复 KDataDriver + stk.setKDataDriver(old_driver); } - // 启动行情接收代理 - auto& agent = *getGlobalSpotAgent(); - agent.addProcess([this](const SpotRecord& spot) { this->receivedSpot(spot); }); - agent.addPostProcess([this](Datetime revTime) { this->finishReceivedSpot(revTime); }); - startSpotAgent(false); + // 计算每个类型当前最后的日期 + for (const auto& ktype : ktype_list) { + Datetime last_date = Datetime::min(); + for (auto& stk : m_stock_list) { + size_t count = stk.getCount(ktype); + if (count > 1) { + auto kr = stk.getKRecord(count - 1, ktype); + if (kr.datetime > last_date) { + last_date = kr.datetime; + } + } + } + m_ref_last_time[ktype] = last_date == Datetime::min() ? Null() : last_date; + } - _addTimer(); + if (!forTest) { + // 启动行情接收代理 + auto& agent = *getGlobalSpotAgent(); + agent.addProcess([this](const SpotRecord& spot) { this->receivedSpot(spot); }); + agent.addPostProcess([this](Datetime revTime) { this->finishReceivedSpot(revTime); }); + startSpotAgent(false); - _startEventLoop(); + _addTimer(); + + HKU_INFO("start even loop ..."); + _startEventLoop(); + } } void StrategyBase::receivedSpot(const SpotRecord& spot) { diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h index 597dee17..20b99194 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h @@ -84,7 +84,9 @@ public: return m_context.getKTypeList(); } - void run(); + void run() { + _run(false); + } void receivedSpot(const SpotRecord& spot); void finishReceivedSpot(Datetime revTime); @@ -114,6 +116,8 @@ private: void _addClockEvent(const string& enable, TimeDelta delta, TimeDelta openTime, TimeDelta closeTime); + void _run(bool forTest); + private: static std::atomic_bool ms_keep_running; static void sig_handler(int sig); diff --git a/hikyuu_pywrap/strategy/_StrategyBase.cpp b/hikyuu_pywrap/strategy/_StrategyBase.cpp index 02b089c3..27e32487 100644 --- a/hikyuu_pywrap/strategy/_StrategyBase.cpp +++ b/hikyuu_pywrap/strategy/_StrategyBase.cpp @@ -42,7 +42,7 @@ public: }; void export_Strategy(py::module& m) { - py::class_(m, "StrategyBase") + py::class_(m, "StrategyBase") .def(py::init<>()) .def_property("name", py::overload_cast<>(&StrategyBase::name, py::const_), py::overload_cast(&StrategyBase::name), From 2169f3fef9579528dc42e8da17e10c4c1ff130f4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 7 May 2024 23:09:39 +0800 Subject: [PATCH 263/601] =?UTF-8?q?fixed=20=E6=8E=A5=E6=94=B6spot=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E5=88=86=E9=92=9F=E7=BA=A7=E5=88=AB=E7=9A=84=E6=88=90?= =?UTF-8?q?=E4=BA=A4=E9=87=8F=E4=B8=BA=E8=82=A1=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KRecord.h | 2 +- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index 87133d76..8de33a1e 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -25,7 +25,7 @@ public: price_t lowPrice; ///< 最低价 price_t closePrice; ///< 最低价 price_t transAmount; ///< 成交金额(千元) - price_t transCount; ///< 成交量(手) + price_t transCount; ///< 成交量(手),日线以下为股数 KRecord() : datetime(Null()), diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 79aa9a41..f2707b5c 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -156,7 +156,8 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { } price_t amount = spot.amount > sum_amount ? spot.amount - sum_amount : spot.amount; - price_t volume = spot.volume > sum_volume ? spot.volume - sum_volume : spot.volume; + price_t spot_volume = spot.volume * 100; // spot 传过来的是手数 + price_t volume = spot_volume > sum_volume ? spot_volume - sum_volume : spot_volume; KRecord krecord(minute, spot.open, spot.high, spot.low, spot.close, amount, volume); stk.realtimeUpdate(krecord, ktype); } From 9abafb4c10245660175926225b89d9d0ef262ac8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 8 May 2024 03:46:35 +0800 Subject: [PATCH 264/601] fixed CycleSignale --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 6 ++++-- hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp | 5 +++++ hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 10 ++++++++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 975f98cb..6f4fbab3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -296,6 +296,7 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool // 强制卖出失败的情况下,如果当前仍有持仓,则需要下一交易日继续进行处理 PositionRecord position = sys.sys->getTM()->getPosition(date, sys.sys->getStock()); if (position.number > 0.0) { + HKU_INFO_IF("[{}] failed to force sell, delay to next day", name()); tmp_continue_adjust_sys_list.emplace_back(sys); } } @@ -357,17 +358,18 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool 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); + funds.total_assets(), funds.cash, funds.market_value); } //---------------------------------------------------------------------------- // 执行所有运行中的系统,无论是延迟还是非延迟,当天运行中的系统都需要被执行一次 //---------------------------------------------------------------------------- for (auto& sub_sys : m_running_sys_set) { - HKU_TRACE_IF(trace, "run: {}", sub_sys->name()); + HKU_INFO_IF(trace, "[PF] run: {}", sub_sys->name()); if (adjust) { auto sg = sub_sys->getSG(); sg->startCycle(date, nextCycle); + HKU_INFO_IF(trace, "[PF] sg should buy: {}", sg->shouldBuy(date)); } auto tr = sub_sys->runMoment(date); diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp index 3ac99aaa..d3fb2c3f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp @@ -15,16 +15,21 @@ namespace hku { CycleSignal::CycleSignal() : SignalBase("SG_AllwaysBuy") { setParam("cycle", true); + setParam("alternate", false); } void CycleSignal::_checkParam(const string& name) const { if ("cycle" == name) { bool cycle = getParam(name); HKU_CHECK(cycle, "param cycle must be true!"); + } else if ("alternate" == name) { + bool alternate = getParam("alternate"); + HKU_CHECK(!alternate, "param alternate must be false!"); } } void CycleSignal::_calculate(const KData& kdata) { + HKU_INFO("cycle start: {}", getCycleStart()); _addBuySignal(getCycleStart()); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index 1f9a10dd..ee7c9a08 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -452,6 +452,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 如果当前环境无效 if (!current_ev_valid) { + HKU_INFO_IF(trace, "[{}] current EV is invalid", name()); TradeRecord tr; // 如果持有多头仓位,则立即清仓卖出 if (m_tm->have(m_stock)) { @@ -465,6 +466,8 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 环境是从无效变为有效时 if (!m_pre_ev_valid) { + HKU_INFO_IF(trace, "[{}] EV status from invalid to valid", name()); + // 如果使用环境判定策略进行初始建仓 if (getParam("ev_open_position")) { HKU_INFO_IF(trace, "[{}] EV to buy", name()); @@ -484,6 +487,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 如果系统当前无效 if (!current_cn_valid) { + HKU_INFO_IF(trace, "[{}] current CN is invalid", name()); TradeRecord tr; // 如果持有多头仓位,则立即清仓卖出 if (m_tm->have(m_stock)) { @@ -497,6 +501,8 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { // 如果系统从无效变为有效 if (!m_pre_cn_valid) { + HKU_INFO_IF(trace, "[{}] CN status from invalid to valid", name()); + // 如果使用环境判定策略进行初始建仓 if (getParam("cn_open_position")) { HKU_INFO_IF(trace, "[{}] CN to buy", name()); @@ -550,6 +556,7 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { price_t src_current_price = src_today.closePrice; // 未复权的原始价格 PositionRecord position = m_tm->getPosition(today.datetime, m_stock); + HKU_INFO_IF(trace, "[{}] current postion: {}", name(), position.number); if (position.number != 0) { TradeRecord tr; if (src_current_price <= position.stoploss) { @@ -716,6 +723,9 @@ void System::_submitBuyRequest(const KRecord& today, const KRecord& src_today, P } TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool on_open) { + bool trace = getParam("trace"); + HKU_INFO_IF(trace, "[{}] force sell {} by {}", name(), num, getSystemPartName(from)); + TradeRecord record; size_t pos = m_kdata.getPos(date); HKU_TRACE_IF_RETURN(pos == Null(), record, From 246170416053e411dad2bdfe59e0e9a68fba994f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 8 May 2024 14:59:34 +0800 Subject: [PATCH 265/601] update --- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 70 +++++++++++++++------ hikyuu_cpp/hikyuu/strategy/StrategyBase.h | 3 + 2 files changed, 54 insertions(+), 19 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index c3102bd0..e59e3695 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -147,25 +147,11 @@ void StrategyBase::_run(bool forTest) { } HKU_WARN_IF(m_stock_list.empty(), "[Strategy {}] stock list is empty!", m_name); - // 借助 Stock.setKRecordList 方法进行预加载(同步方式,不需要异步加载) - // 只从 context 指定起始日期开始加载 - size_t ktype_count = ktype_list.size(); - vector k_buffer(ktype_count); - for (auto& stk : m_stock_list) { - // 保留原始 KDataDriver,因为使用 stock.setKRecordList 将会把 stock 的 KDataDriver 设置为 - // DoNothing - auto old_driver = stk.getKDataDirver(); - - for (size_t i = 0; i < ktype_count; i++) { - k_buffer[i] = std::move(stk.getKRecordList( - KQueryByDate(m_context.startDatetime(), Null(), ktype_list[i]))); - } - for (size_t i = 0; i < ktype_count; i++) { - stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]); - } - - // 恢复 KDataDriver - stk.setKDataDriver(old_driver); + // 非测试模式下加载上下文起始日期开始的KData + // 测试模式下不需要预加载,由测试灌入 + if (!forTest) { + // 只从 context 指定起始日期开始加载 + _loadKData(m_context.startDatetime(), Null()); } // 计算每个类型当前最后的日期 @@ -197,6 +183,28 @@ void StrategyBase::_run(bool forTest) { } } +void StrategyBase::_loadKData(const Datetime& start, const Datetime& end) { + // 借助 Stock.setKRecordList 方法进行预加载(同步方式,不需要异步加载) + const auto& ktype_list = getKTypeList(); + size_t ktype_count = ktype_list.size(); + vector k_buffer(ktype_count); + for (auto& stk : m_stock_list) { + // 保留原始 KDataDriver,因为使用 stock.setKRecordList 将会把 stock 的 KDataDriver 设置为 + // DoNothing + auto old_driver = stk.getKDataDirver(); + + for (size_t i = 0; i < ktype_count; i++) { + k_buffer[i] = std::move(stk.getKRecordList(KQueryByDate(start, end, ktype_list[i]))); + } + for (size_t i = 0; i < ktype_count; i++) { + stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]); + } + + // 恢复 KDataDriver + stk.setKDataDriver(old_driver); + } +} + void StrategyBase::receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { @@ -297,4 +305,28 @@ void StrategyBase::_startEventLoop() { } } +void StrategyBase::backTest(const Datetime& start, const Datetime& end) { + HKU_CHECK(!start.isNull(), "start date can't be null!"); + HKU_CHECK(start >= m_context.startDatetime(), + "The backtest start date must be greater than the context start date!"); + + const auto& ktypes = getKTypeList(); + HKU_CHECK(!ktypes.empty(), "The ktype list is empty!"); + + _run(true); + + // 加载回测日期之前的相关K线数据 + _loadKData(m_context.startDatetime(), start); + + size_t ktype_count = ktypes.size(); + vector ktype_mintues(ktypes.size()); + for (size_t i = 0; i < ktype_count; i++) { + ktype_mintues[i] = KQuery::getKTypeInMin(ktypes[i]); + } + + auto dates = StockManager::instance().getTradingCalendar(KQueryByDate(start, end)); + + const auto& down_ktype = ktypes[0]; +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h index 20b99194..89779e15 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h @@ -88,6 +88,8 @@ public: _run(false); } + void backTest(const Datetime& start, const Datetime& end); + void receivedSpot(const SpotRecord& spot); void finishReceivedSpot(Datetime revTime); @@ -116,6 +118,7 @@ private: void _addClockEvent(const string& enable, TimeDelta delta, TimeDelta openTime, TimeDelta closeTime); + void _loadKData(const Datetime& start, const Datetime& end); void _run(bool forTest); private: From 7cb32311d610d46001794d4003c258c96360589d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 8 May 2024 15:05:50 +0800 Subject: [PATCH 266/601] release 2.0.5 --- docs/source/release.rst | 15 ++++++++++++++- xmake.lua | 2 +- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 7c1d343b..93b15e29 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,7 +1,20 @@ 版本发布说明 ======================= -2.0.4 - 2024年4月25日 +2.0.5 - 2024年5月8日 +------------------------- + +主要修复 +1. fixed 接收spot时,分钟级别的成交量为股数 +2. fixed SG_Cycle 其 alternate 属性须为 false,影响 PF 示例 + +其他修复 +1. fixed strategy 加载权息失败 +2. StrategyContext 在设定 ktypes 时进行从小到大的排序,以便后续能够按顺序调用 onBar +3. fixed setKRecordList 使用 move(ks) 时错误 + + +2.0.4 - 2024年5月6日 ------------------------- 1. 缺陷修复 diff --git a/xmake.lua b/xmake.lua index 5f70665d..167de3ca 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.4", {build = "%Y%m%d%H%M"}) +set_version("2.0.5", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From b689e4100de3b9c67437bcdbfe24b2aaca872421 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 8 May 2024 15:41:03 +0800 Subject: [PATCH 267/601] update docs --- docs/source/trade_sys/signal.rst | 32 ++++++++++++++++++- .../hikyuu/trade_sys/signal/crt/SG_Cycle.h | 6 +++- hikyuu_pywrap/trade_sys/_Signal.cpp | 10 ++++-- 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/docs/source/trade_sys/signal.rst b/docs/source/trade_sys/signal.rst index 7e50dd6b..fd40bf90 100644 --- a/docs/source/trade_sys/signal.rst +++ b/docs/source/trade_sys/signal.rst @@ -87,7 +87,8 @@ :param Indicator ind: :param int slow_n: 慢线EMA周期 :return: 信号指示器 - + + 布尔信号指示器 ^^^^^^^^^^^^^^^^ @@ -100,6 +101,35 @@ :return: 信号指示器 +区间突破信号指示器 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. py:function:: SG_Band(ind, lower, upper) + + 指标区间指示器, 当指标超过上轨时,买入; + 当指标低于下轨时,卖出。 + + :: + + SG_Band(MA(C, n=10), 100, 200) + + +持续买入信号指示器 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. py:function:: SG_AllwaysBuy() + + 一个特殊的SG,持续每天发出买入信号,通常配合 PF 使用 + + +PF调仓周期买入信号指示器 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +.. py:function:: SG_Cycle() + + 一个特殊的SG,配合PF使用,以 PF 调仓周期为买入信号 + + 自定义信号指示器 ---------------- diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h index 6c4bf843..fa6edabe 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Cycle.h @@ -11,6 +11,10 @@ namespace hku { +/** + * 以 PF 调仓周期为买入信号 + * @return SignalPtr + */ SignalPtr HKU_API SG_Cycle(); -} \ No newline at end of file +} // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index f0530f82..39b9764e 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -205,12 +205,18 @@ void export_Signal(py::module& m) { m.def("SG_Band", SG_Band, py::arg("ind"), py::arg("lower"), py::arg("upper"), R"(SG_Band(ind, lower, upper) + 指标区间指示器, 当指标超过上轨时,买入; 当指标低于下轨时,卖出。:: SG_Band(MA(C, n=10), 100, 200) )"); - m.def("SG_AllwaysBuy", SG_AllwaysBuy); - m.def("SG_Cycle", SG_Cycle); + m.def("SG_AllwaysBuy", SG_AllwaysBuy, R"(SG_AllwaysBuy() + + 一个特殊的SG,持续每天发出买入信号,通常配合 PF 使用)"); + + m.def("SG_Cycle", SG_Cycle, R"(SG_Cycle() + + 一个特殊的SG,配合PF使用,以 PF 调仓周期为买入信号)"); } From ad1b09a05c59c7f2baee8e600071ab76fe45a465 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 8 May 2024 17:41:35 +0800 Subject: [PATCH 268/601] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp index d3fb2c3f..b7e8ce93 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CycleSignal.cpp @@ -29,7 +29,6 @@ void CycleSignal::_checkParam(const string& name) const { } void CycleSignal::_calculate(const KData& kdata) { - HKU_INFO("cycle start: {}", getCycleStart()); _addBuySignal(getCycleStart()); } From d055c4f748397afd995e06ed1deeb3df55936941 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 9 May 2024 09:59:34 +0800 Subject: [PATCH 269/601] update --- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 19 +++++++++++++++++-- hikyuu_cpp/hikyuu/strategy/StrategyBase.h | 6 +++++- 2 files changed, 22 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index e59e3695..e8f30722 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -306,6 +306,7 @@ void StrategyBase::_startEventLoop() { } void StrategyBase::backTest(const Datetime& start, const Datetime& end) { + HKU_CHECK(!m_stock_list.empty(), "The context stock list is empty!"); HKU_CHECK(!start.isNull(), "start date can't be null!"); HKU_CHECK(start >= m_context.startDatetime(), "The backtest start date must be greater than the context start date!"); @@ -324,9 +325,23 @@ void StrategyBase::backTest(const Datetime& start, const Datetime& end) { ktype_mintues[i] = KQuery::getKTypeInMin(ktypes[i]); } - auto dates = StockManager::instance().getTradingCalendar(KQueryByDate(start, end)); + const auto& level_ktype = ktypes[0]; + Stock level_stk = getStock("sh000001"); + KQuery query = KQueryByDate(start, end, level_ktype); + auto dates = level_stk.getDatetimeList(query); - const auto& down_ktype = ktypes[0]; + vector> krecords(m_stock_list.size()); + for (size_t i = 0, len = m_stock_list.size(); i < len; i++) { + auto& stock = m_stock_list[i]; + HKU_CHECK(!stock.isNull(), "The pos: {} stock is Null!", i); + + KRecordList ks = + stock.getKDataDirver()->getConnect()->getKRecordList(stock.market(), stock.code(), query); + krecords[i].resize(ks.size()); + std::copy(ks.begin(), ks.end(), krecords[i].begin()); + } + + vector spots; } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h index 89779e15..c2151c15 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h @@ -84,6 +84,10 @@ public: return m_context.getKTypeList(); } + const StockList& getStockList() const { + return m_stock_list; + } + void run() { _run(false); } @@ -95,7 +99,7 @@ public: virtual void init() {} virtual void onTick() {} - virtual void onBar(const KQuery::KType& ktype){}; + virtual void onBar(const KQuery::KType& ktype) {}; virtual void onMarketOpen() {} virtual void onMarketClose() {} From 7b85c69b9aaabcffb4c754c0ce887fa66e3dc4c2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 9 May 2024 10:51:04 +0800 Subject: [PATCH 270/601] =?UTF-8?q?fixed=20=5FSelector=20=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=E8=B5=8B=E5=80=BC=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=20clone=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_Selector.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 023a5e55..f5747ae7 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -61,6 +61,7 @@ void export_Selector(py::module& m) { - _clone - 【必须】克隆接口)") .def(py::init<>()) + .def(py::init()) .def(py::init(), R"(初始化构造函数 :param str name: 名称)") From 0a81ccdf708f97f01dcf9f6f6586cf48588306e1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 9 May 2024 11:27:19 +0800 Subject: [PATCH 271/601] =?UTF-8?q?fixed=20=5FSelector=20=E7=BC=BA?= =?UTF-8?q?=E5=A4=B1=E8=B5=8B=E5=80=BC=E6=9E=84=E9=80=A0=E5=87=BD=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E5=AF=BC=E8=87=B4=20clone=20=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_Selector.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index f5747ae7..60f2a1d8 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -16,6 +16,7 @@ class PySelectorBase : public SelectorBase { public: using SelectorBase::SelectorBase; + PySelectorBase(const SelectorBase& base) : SelectorBase(base) {} void _reset() override { PYBIND11_OVERLOAD(void, SelectorBase, _reset, ); From 968443141e818a7e2da49c6b16b86c2832209e4f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 10 May 2024 01:33:01 +0800 Subject: [PATCH 272/601] =?UTF-8?q?MF=20=E6=94=AF=E6=8C=81=E5=B1=9E?= =?UTF-8?q?=E6=80=A7=E8=AE=BE=E5=AE=9A=EF=BC=8C=E4=BB=A5=E4=BE=BF=E7=BB=84?= =?UTF-8?q?=E4=BB=B6=E5=8C=96=E6=97=B6=E5=8F=AF=E4=BF=AE=E6=94=B9=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/multifactor/MultiFactorBase.cpp | 47 +++++++++++++++++-- .../trade_sys/multifactor/MultiFactorBase.h | 14 ++++++ .../imp/EqualWeightMultiFactor.cpp | 2 +- .../multifactor/imp/ICIRMultiFactor.cpp | 4 +- .../multifactor/imp/ICMultiFactor.cpp | 4 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 24 +++++++++- 6 files changed, 84 insertions(+), 11 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 19070fca..fa17586f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -20,8 +20,7 @@ namespace hku { HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorBase& mf) { - out << "MultiFactor{" - << "\n name: " << mf.name() << "\n params: " << mf.getParameter() + out << "MultiFactor{" << "\n name: " << mf.name() << "\n params: " << mf.getParameter() << "\n query: " << mf.getQuery() << "\n ref stock: " << mf.m_ref_stk; out << "\n src inds count: " << mf.m_inds.size() << " ["; @@ -171,6 +170,45 @@ MultiFactorPtr MultiFactorBase::clone() { return p; } +void MultiFactorBase::setQuery(const KQuery& query) { + std::lock_guard lock(m_mutex); + m_query = query; + _reset(); + m_calculated = false; +} + +void MultiFactorBase::setRefStock(const Stock& stk) { + HKU_CHECK(!stk.isNull(), "The reference stock must be set!"); + DatetimeList ref_dates = stk.getDatetimeList(m_query); + HKU_CHECK(ref_dates.size() >= 2, "The dates len is insufficient! current len: {}", + ref_dates.size()); + std::lock_guard lock(m_mutex); + m_ref_stk = stk; + m_ref_dates = std::move(ref_dates); + _reset(); + m_calculated = false; +} + +void MultiFactorBase::setStockList(const StockList& stks) { + // 后续计算需要保持对齐,夹杂 Null stock 处理麻烦,直接抛出异常屏蔽 + for (const auto& stk : stks) { + HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); + } + + std::lock_guard lock(m_mutex); + m_stks = stks; + _reset(); + m_calculated = false; +} + +void MultiFactorBase::setRefIndicators(const IndicatorList& inds) { + HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); + std::lock_guard lock(m_mutex); + m_inds = inds; + _reset(); + m_calculated = false; +} + const Indicator& MultiFactorBase::getFactor(const Stock& stk) { calculate(); const auto iter = m_stk_map.find(stk); @@ -349,13 +387,12 @@ Indicator MultiFactorBase::getICIR(int ir_n, int ic_n) { IndicatorList MultiFactorBase::_getAllReturns(int ndays) const { bool fill_null = getParam("fill_null"); -#if 0 +#if !MF_USE_MULTI_THREAD vector all_returns; all_returns.reserve(m_stks.size()); for (const auto& stk : m_stks) { auto k = stk.getKData(m_query); - all_returns.emplace_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, - fill_null)); + all_returns.emplace_back(ALIGN(REF(ROCP(k.close(), ndays), ndays), m_ref_dates, fill_null)); } return all_returns; #else diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h index 199fa881..f7869aa6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h @@ -10,6 +10,8 @@ #include "hikyuu/KData.h" #include "ScoreRecord.h" +#define MF_USE_MULTI_THREAD 1 + namespace hku { /** @@ -51,16 +53,25 @@ public: return m_query; } + /** 设置查询范围 */ + void setQuery(const KQuery& query); + /** 获取参考证券 */ const Stock& getRefStock() const { return m_ref_stk; } + /** 设置参考证券 */ + void setRefStock(const Stock& stk); + /** 获取证券列表 */ const StockList& getStockList() const { return m_stks; } + /** 设置计算范围证券列表 */ + void setStockList(const StockList& stks); + /** 获取证券列表当前证券数量 */ size_t getStockListNumber() const { return m_stks.size(); @@ -71,6 +82,9 @@ public: return m_inds; } + /** 设置因子列表 */ + void setRefIndicators(const IndicatorList& inds); + /** 获取指定证券合成因子 */ const Indicator& getFactor(const Stock&); diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp index e6052cf1..323ad532 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp @@ -30,7 +30,7 @@ vector EqualWeightMultiFactor::_calculate( size_t stk_count = m_stks.size(); size_t ind_count = m_inds.size(); -#if 0 +#if !MF_USE_MULTI_THREAD value_t null_value = Null(); vector sumByDate(days_total); vector countByDate(days_total); diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp index 96c80c94..0f074969 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp @@ -44,7 +44,7 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i int ic_n = getParam("ic_n"); int ir_n = getParam("ic_rolling_n"); -#if 0 +#if !MF_USE_MULTI_THREAD size_t discard = 0; vector icir(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { @@ -67,7 +67,7 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i #endif // 以 ICIR 为权重,计算加权后的合成因子 -#if 0 +#if !MF_USE_MULTI_THREAD IndicatorList all_factors(stk_count); PriceList new_values(days_total, 0.0); PriceList sum_weight(days_total, 0.0); diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp index 264d8912..836378be 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp @@ -44,7 +44,7 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind int ic_rolling_n = getParam("ic_rolling_n"); // 计算每个原始因子的滚动IC值 -#if 0 +#if !MF_USE_MULTI_THREAD size_t discard = 0; IndicatorList ic(ind_count); for (size_t ii = 0; ii < ind_count; ii++) { @@ -66,7 +66,7 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind #endif // 以滚动 IC 为权重,计算加权后的合成因子 -#if 0 +#if !MF_USE_MULTI_THREAD IndicatorList all_factors(stk_count); PriceList new_values(days_total, 0.0); PriceList sum_weight(days_total, 0.0); diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index b79f1b34..57334647 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -55,7 +55,8 @@ void export_MultiFactor(py::module& m) { .def_property("name", py::overload_cast<>(&MultiFactorBase::name, py::const_), py::overload_cast(&MultiFactorBase::name), py::return_value_policy::copy, "名称") - .def("get_query", &MultiFactorBase::getQuery, py::return_value_policy::copy, R"(查询条件)") + .def_property("query", &MultiFactorBase::getQuery, &MultiFactorBase::setQuery, + py::return_value_policy::copy, R"(查询条件)") .def("get_param", &MultiFactorBase::getParam, R"(get_param(self, name) @@ -77,15 +78,36 @@ void export_MultiFactor(py::module& m) { .def("get_ref_stock", &MultiFactorBase::getRefStock, py::return_value_policy::copy, "获取参考证券") + .def("set_ref_stock", &MultiFactorBase::setRefStock, R"(set_ref_stock(self, stk) + + 设置参考证券 + + :param Stock stk: 参考证券)") + .def("get_datetime_list", &MultiFactorBase::getDatetimeList, py::return_value_policy::copy, "获取参考日期列表(由参考证券通过查询条件获得)") + .def("get_stock_list", &MultiFactorBase::getStockList, py::return_value_policy::copy, "获取创建时指定的证券列表") + .def("set_stock_list", &MultiFactorBase::setStockList, R"(set_stock_list(self, stks) + + 设置计算范围指定的证券列表 + + :param list stks: 新的待计算证券列表)") + .def("get_stock_list_num", &MultiFactorBase::getStockListNumber, "获取创建时指定的证券列表中证券数量") + .def("get_ref_indicators", &MultiFactorBase::getRefIndicators, py::return_value_policy::copy, "获取创建时输入的原始因子列表") + .def("set_ref_indicators", &MultiFactorBase::setRefIndicators, + R"(set_ref_indicators(self, inds) + + 设置原始因子列表 + + :param list inds: 新的原始因子列表)") + .def("get_factor", &MultiFactorBase::getFactor, py::return_value_policy::copy, py::arg("stock"), R"(get_factor(self, stock) From 24aef563c1ffde9f6a4e3608184958d39346371d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 10 May 2024 01:44:19 +0800 Subject: [PATCH 273/601] update --- .../trade_sys/multifactor/MultiFactorBase.cpp | 37 +++++++++++-------- .../trade_sys/multifactor/MultiFactorBase.h | 1 + 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index fa17586f..83f9823e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -82,22 +82,7 @@ MultiFactorBase::MultiFactorBase(const IndicatorList& inds, const StockList& stk initParam(); setParam("ic_n", ic_n); checkParam("ic_n"); - - HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); - HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); - - // 后续计算需要保持对齐,夹杂 Null stock 处理麻烦,直接抛出异常屏蔽 - for (const auto& stk : m_stks) { - HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); - } - - // 获取用于对齐的参考日期 - m_ref_dates = m_ref_stk.getDatetimeList(m_query); - HKU_CHECK(m_ref_dates.size() >= 2, "The dates len is insufficient! current len: {}", - m_ref_dates.size()); - - HKU_CHECK(m_stks.size() >= 2, "The number of stock is insufficient! current stock number: {}", - m_stks.size()); + _checkData(); } void MultiFactorBase::initParam() { @@ -123,6 +108,24 @@ void MultiFactorBase::paramChanged() { m_calculated = false; } +void MultiFactorBase::_checkData() { + HKU_CHECK(!m_ref_stk.isNull(), "The reference stock must be set!"); + HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); + + // 后续计算需要保持对齐,夹杂 Null stock 处理麻烦,直接抛出异常屏蔽 + for (const auto& stk : m_stks) { + HKU_CHECK(!stk.isNull(), "Exist null stock in stks!"); + } + + // 获取用于对齐的参考日期 + m_ref_dates = m_ref_stk.getDatetimeList(m_query); + HKU_CHECK(m_ref_dates.size() >= 2, "The dates len is insufficient! current len: {}", + m_ref_dates.size()); + + HKU_CHECK(m_stks.size() >= 2, "The number of stock is insufficient! current stock number: {}", + m_stks.size()); +} + void MultiFactorBase::reset() { std::lock_guard lock(m_mutex); _reset(); @@ -517,6 +520,8 @@ void MultiFactorBase::calculate() { std::lock_guard lock(m_mutex); HKU_IF_RETURN(m_calculated, void()); + _checkData(); + // 获取所有证券所有对齐后的原始因子 vector> all_stk_inds = getAllSrcFactors(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h index f7869aa6..9b0dca7e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.h @@ -155,6 +155,7 @@ private: protected: void _buildIndex(); // 计算完成后创建截面索引 IndicatorList _getAllReturns(int ndays) const; + void _checkData(); protected: string m_name; From 27d6054e39c09e3654688f9151247528d811161d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 10 May 2024 02:14:48 +0800 Subject: [PATCH 274/601] update --- hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp | 2 +- hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h | 2 ++ hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h | 1 + hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h | 2 +- .../trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp | 4 ++++ .../hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp | 4 ++++ hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp | 4 ++++ hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 3 +++ 8 files changed, 20 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 83f9823e..183f21f2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -205,7 +205,7 @@ void MultiFactorBase::setStockList(const StockList& stks) { } void MultiFactorBase::setRefIndicators(const IndicatorList& inds) { - HKU_CHECK(!m_inds.empty(), "Input source factor list is empty!"); + HKU_CHECK(!inds.empty(), "Input source factor list is empty!"); std::lock_guard lock(m_mutex); m_inds = inds; _reset(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h index 27f5548f..a1e41cad 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_EqualWeight.h @@ -22,4 +22,6 @@ namespace hku { MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n = 5); +MultiFactorPtr HKU_API MF_EqualWeight(); + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h index fdda528e..66a3d696 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICIRWeight.h @@ -25,4 +25,5 @@ MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& const KQuery& query, const Stock& ref_stk, int ic_n = 5, int ic_rolling_n = 120); +MultiFactorPtr HKU_API MF_ICIRWeight(); } \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h index aa27b52a..59c88fd9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/crt/MF_ICWeight.h @@ -24,5 +24,5 @@ namespace hku { MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n = 5, int ic_rolling_n = 120); - +MultiFactorPtr HKU_API MF_ICWeight(); } \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp index 323ad532..642cdd54 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp @@ -111,6 +111,10 @@ vector EqualWeightMultiFactor::_calculate( }); } +MultiFactorPtr HKU_API MF_EqualWeight() { + return make_shared(); +} + MultiFactorPtr HKU_API MF_EqualWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n) { return make_shared(inds, stks, query, ref_stk, ic_n); diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp index 0f074969..5b7a8bc8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICIRMultiFactor.cpp @@ -141,6 +141,10 @@ IndicatorList ICIRMultiFactor::_calculate(const vector& all_stk_i #endif } +MultiFactorPtr HKU_API MF_ICIRWeight() { + return make_shared(); +} + MultiFactorPtr HKU_API MF_ICIRWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n, int ic_rolling_n) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp index 836378be..5b3bde35 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/ICMultiFactor.cpp @@ -142,6 +142,10 @@ IndicatorList ICMultiFactor::_calculate(const vector& all_stk_ind #endif } +MultiFactorPtr HKU_API MF_ICWeight() { + return std::make_shared(); +} + MultiFactorPtr HKU_API MF_ICWeight(const IndicatorList& inds, const StockList& stks, const KQuery& query, const Stock& ref_stk, int ic_n, int ic_rolling_n) { diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 57334647..3897c9c1 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -189,6 +189,7 @@ void export_MultiFactor(py::module& m) { DEF_PICKLE(MultiFactorPtr); + m.def("MF_EqualWeight", py::overload_cast<>(MF_EqualWeight)); m.def( "MF_EqualWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, @@ -212,6 +213,7 @@ void export_MultiFactor(py::module& m) { :param int ic_n: 默认 IC 对应的 N 日收益率 :rtype: MultiFactor)"); + m.def("MF_ICWeight", py::overload_cast<>(MF_ICWeight)); m.def( "MF_ICWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, @@ -236,6 +238,7 @@ void export_MultiFactor(py::module& m) { :param int ic_rolling_n: IC 滚动周期 :rtype: MultiFactor)"); + m.def("MF_ICIRWeight", py::overload_cast<>(MF_ICIRWeight)); m.def( "MF_ICIRWeight", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, From dd827a59ef84ecc6689174602081b5067023c392 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 10 May 2024 22:35:09 +0800 Subject: [PATCH 275/601] =?UTF-8?q?PF=E3=80=81SE=20C++=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=8A=A8=E6=80=81=E5=B1=9E=E6=80=A7=E6=94=AF?= =?UTF-8?q?=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_Portfolio.cpp | 3 ++- hikyuu_pywrap/trade_sys/_Selector.cpp | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/hikyuu_pywrap/trade_sys/_Portfolio.cpp b/hikyuu_pywrap/trade_sys/_Portfolio.cpp index 88dc9bcd..b9aa28e4 100644 --- a/hikyuu_pywrap/trade_sys/_Portfolio.cpp +++ b/hikyuu_pywrap/trade_sys/_Portfolio.cpp @@ -14,7 +14,8 @@ namespace py = pybind11; using namespace hku; void export_Portfolio(py::module& m) { - py::class_(m, "Portfolio", R"(实现多标的、多策略的投资组合)") + py::class_(m, "Portfolio", py::dynamic_attr(), + R"(实现多标的、多策略的投资组合)") .def(py::init<>()) .def(py::init()) .def(py::init()) diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 60f2a1d8..b5eddf2f 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -41,7 +41,7 @@ public: }; void export_Selector(py::module& m) { - py::class_(m, "SystemWeight", + py::class_(m, "SystemWeight", py::dynamic_attr(), "系统权重系数结构,在资产分配时,指定对应系统的资产占比系数") .def(py::init<>()) .def(py::init()) From 9fe2ade3f466ef6db39b4bab7fe4c13d299e59d4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 10 May 2024 22:42:40 +0800 Subject: [PATCH 276/601] =?UTF-8?q?=E6=9B=BF=E6=8D=A2=E9=A1=B9=E7=9B=AE?= =?UTF-8?q?=E8=AF=B4=E6=98=8E=E5=9B=BE=E7=89=87=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 82b04d0a..a6277406 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -.. image:: http://fasiondog.gitee.io/hikyuu/images/00000_title.png +.. image:: http://fasiondog.cn/wp-content/uploads/2024/05/00000_title-1.png :target: http://fasiondog.gitee.io/hikyuu :align: left :alt: Hikyuu @@ -53,7 +53,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm) sys.run(sm['sz000001'], Query(-150)) -.. figure:: http://fasiondog.gitee.io/hikyuu/images/10000-overview.png +.. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10000-overview.png :width: 600px 完整示例参见:``_ @@ -64,7 +64,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下: - .. figure:: http://fasiondog.gitee.io/hikyuu/images/10002-function-arc.png + .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10002-function-arc.png :width: 600px - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 @@ -93,7 +93,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 目前知识星球属于前期建设,您的加入将视为对项目的捐赠(200元) - .. figure:: http://fasiondog.gitee.io/hikyuu/images/zhishixingqiu.jpg + .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png 想要更多了解Hikyuu?请使用以下方式联系: @@ -103,12 +103,12 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - 关注公众号: - .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_gongzhonghao.jpg + .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/weixin_gongzhonghao.jpg - QQ交流群:114910869, 或扫码加入: - .. figure:: http://fasiondog.gitee.io/hikyuu/images/10003-qq.png + .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png - 加入微信群(请注明“加入hikyuu”): - .. figure:: http://fasiondog.gitee.io/hikyuu/images/weixin_group.jpg + .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/weixin_group.jpg From 0ee5950be5b2bd66913ae8114c28c6542c240d76 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 10 May 2024 22:49:38 +0800 Subject: [PATCH 277/601] update readme --- README.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index a6277406..cd7f9c1c 100644 --- a/README.rst +++ b/README.rst @@ -88,27 +88,27 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 :alt: Star History Chart -项目捐赠(加入星球) +想要更多了解Hikyuu?请使用以下方式联系: -------------------------------------------------- -目前知识星球属于前期建设,您的加入将视为对项目的捐赠(200元) +**加入知识星球** 获取最新的示例代码等(您的加入将视为对项目的捐赠) .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png -想要更多了解Hikyuu?请使用以下方式联系: --------------------------------------------------- - **项目交流和问题答复将转移至知识星球-【Hikyuu量化】。** - 关注公众号: .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/weixin_gongzhonghao.jpg -- QQ交流群:114910869, 或扫码加入: - - .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png - 加入微信群(请注明“加入hikyuu”): .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/weixin_group.jpg + + +- QQ交流群(逐渐废弃):114910869, 或扫码加入: + + .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png + From 762bc6da72b7dc4f96ded63775d6911507fe308a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 11 May 2024 12:38:26 +0800 Subject: [PATCH 278/601] update --- docs/source/trade_sys/multifactor.rst | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/docs/source/trade_sys/multifactor.rst b/docs/source/trade_sys/multifactor.rst index ea9a4023..85b4275b 100644 --- a/docs/source/trade_sys/multifactor.rst +++ b/docs/source/trade_sys/multifactor.rst @@ -61,6 +61,7 @@ 多因子合成基类 .. py:attribute:: name 名称 + .. py:attribute:: query 查询条件 .. py:method:: __init__(self) @@ -89,18 +90,26 @@ 克隆操作 - .. py:method:: get_query(self) - - 查询条件范围 - .. py:method:: get_ref_stock(self) 获取参考证券 + .. py:method:: set_ref_stock(self, ref_stk) + + 重新设置参考证券 + + :param Stock ref_stk: 新指定的参考证券 + .. py:method:: get_stock_list(self) 获取创建时指定的证券列表 + .. py:method:: set_stock_list(self, stks) + + 重新指定证券列表 + + :param list stks: 指定的证券列表 + .. py:method:: get_stock_list_num(self) 获取创建时指定的证券列表中证券数量 @@ -113,6 +122,12 @@ 获取创建时输入的原始因子列表 + .. py::method:: set_ref_indicators(self, inds) + + 重新设置原始因子列表 + + :param list Indicator: 原始因子列表 + .. py:method:: get_factor(self, stock) 获取指定证券合成后的新因子 From ddfb138bd657904f4e86e0611ab8c9b1fa99288e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 11 May 2024 12:41:25 +0800 Subject: [PATCH 279/601] =?UTF-8?q?update=20jupyter=20=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/quickstart.rst | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 0c7e7a53..75627909 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -98,7 +98,7 @@ Jupyter notebook(此前被称为 IPython notebook)是一个基于web的交 :: - In [1]: from notebook.auth import passwd + In [1]: from jupyter_server.auth import passwd In [2]: passwd() Enter password: Verify password: @@ -112,10 +112,10 @@ Jupyter notebook(此前被称为 IPython notebook)是一个基于web的交 :: - c.NotebookApp.ip='*' - c.NotebookApp.password = u'sha:ce...刚才复制的那个密文' - c.NotebookApp.open_browser = False - c.NotebookApp.port =8888 #随便指定一个端口 + c.ServerApp.ip='0.0.0.0' + c.ServerApp.password = u'sha:ce...刚才复制的那个密文' + c.ServerApp.open_browser = False + c.ServerApp.port =8888 #随便指定一个端口 5. 启动jupyter notebook,在 cmd 下,进入自己希望的工作目录后,键入命令: From 5f7382287bf7bbe371e2323fccfb707b62504eeb Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 12 May 2024 03:04:54 +0800 Subject: [PATCH 280/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index cd7f9c1c..349f8d8c 100644 --- a/README.rst +++ b/README.rst @@ -91,7 +91,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 想要更多了解Hikyuu?请使用以下方式联系: -------------------------------------------------- -**加入知识星球** 获取最新的示例代码等(您的加入将视为对项目的捐赠) +**加入知识星球** 更多示例与策略部件的及时分享(您的加入将视为对项目的捐赠) .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png From ba0d7cf6a02979f94115997cd15a0778d0f0197c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 12 May 2024 16:06:16 +0800 Subject: [PATCH 281/601] =?UTF-8?q?=E5=AE=89=E8=A3=85=E5=8C=85HikyuuTDX?= =?UTF-8?q?=E4=B8=8D=E9=87=87=E7=94=A8gui=E6=A8=A1=E5=BC=8F=EF=BC=8Cwin11?= =?UTF-8?q?=E4=B8=8BGUI=E6=A8=A1=E5=BC=8F=E4=BC=9A=E9=80=A0=E6=88=90?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5timeout?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sub_setup.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/sub_setup.py b/sub_setup.py index 3017d9da..0ebd059b 100644 --- a/sub_setup.py +++ b/sub_setup.py @@ -147,10 +147,12 @@ setup( 'Programming Language :: Python :: 3.12', ], entry_points={ - 'gui_scripts': [ - 'HikyuuTDX=hikyuu.gui.HikyuuTDX:start', - ], + # win11下使用 GUI 方式,会立刻 timeout,导致无法下载 + # 'gui_scripts': [ + # 'HikyuuTDX=hikyuu.gui.HikyuuTDX:start', + # ], 'console_scripts': [ + 'HikyuuTDX=hikyuu.gui.HikyuuTDX:start', 'importdata=hikyuu.gui.importdata:main', ] }, From a17c822d7916fb8e6b55638cd54a956ca3a30f32 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 13 May 2024 05:23:37 +0800 Subject: [PATCH 282/601] =?UTF-8?q?=E6=B7=BB=E5=8A=A0pybind=E5=8A=A8?= =?UTF-8?q?=E6=80=81=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_AllocateFunds.cpp | 2 +- hikyuu_pywrap/trade_sys/_Condition.cpp | 2 +- hikyuu_pywrap/trade_sys/_Environment.cpp | 2 +- hikyuu_pywrap/trade_sys/_MoneyManager.cpp | 1 + hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 1 + hikyuu_pywrap/trade_sys/_ProfitGoal.cpp | 2 +- hikyuu_pywrap/trade_sys/_Signal.cpp | 3 ++- hikyuu_pywrap/trade_sys/_Slippage.cpp | 3 ++- hikyuu_pywrap/trade_sys/_Stoploss.cpp | 2 +- 9 files changed, 11 insertions(+), 7 deletions(-) diff --git a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp index 9ec76db2..faaaf038 100644 --- a/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp +++ b/hikyuu_pywrap/trade_sys/_AllocateFunds.cpp @@ -35,7 +35,7 @@ public: void export_AllocateFunds(py::module& m) { py::class_( - m, "AllocateFundsBase", + m, "AllocateFundsBase", py::dynamic_attr(), R"(资产分配算法基类, 子类接口: - _allocateWeight : 【必须】子类资产分配调整实现 diff --git a/hikyuu_pywrap/trade_sys/_Condition.cpp b/hikyuu_pywrap/trade_sys/_Condition.cpp index 37e7cfa6..4e920ad1 100644 --- a/hikyuu_pywrap/trade_sys/_Condition.cpp +++ b/hikyuu_pywrap/trade_sys/_Condition.cpp @@ -30,7 +30,7 @@ public: void export_Condition(py::module& m) { py::class_( - m, "ConditionBase", + m, "ConditionBase", py::dynamic_attr(), R"(系统有效条件基类自定义系统有效条件接口: - _calculate : 【必须】子类计算接口 diff --git a/hikyuu_pywrap/trade_sys/_Environment.cpp b/hikyuu_pywrap/trade_sys/_Environment.cpp index 88d31ddc..66d3ed43 100644 --- a/hikyuu_pywrap/trade_sys/_Environment.cpp +++ b/hikyuu_pywrap/trade_sys/_Environment.cpp @@ -37,7 +37,7 @@ public: void export_Environment(py::module& m) { py::class_( - m, "EnvironmentBase", + m, "EnvironmentBase", py::dynamic_attr(), R"(市场环境判定策略基类 自定义市场环境判定策略接口: diff --git a/hikyuu_pywrap/trade_sys/_MoneyManager.cpp b/hikyuu_pywrap/trade_sys/_MoneyManager.cpp index f015cadc..8e6dee93 100644 --- a/hikyuu_pywrap/trade_sys/_MoneyManager.cpp +++ b/hikyuu_pywrap/trade_sys/_MoneyManager.cpp @@ -57,6 +57,7 @@ public: void export_MoneyManager(py::module& m) { py::class_(m, "MoneyManagerBase", + py::dynamic_attr(), R"(资金管理策略基类 公共参数: diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 3897c9c1..5e470c50 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -39,6 +39,7 @@ void export_MultiFactor(py::module& m) { .def_readwrite("value", &ScoreRecord::value, "分值"); py::class_(m, "MultiFactorBase", + py::dynamic_attr(), R"(市场环境判定策略基类 自定义市场环境判定策略接口: diff --git a/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp b/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp index 628c57da..baabd0e0 100644 --- a/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp +++ b/hikyuu_pywrap/trade_sys/_ProfitGoal.cpp @@ -45,7 +45,7 @@ public: }; void export_ProfitGoal(py::module& m) { - py::class_(m, "ProfitGoalBase", + py::class_(m, "ProfitGoalBase", py::dynamic_attr(), R"(盈利目标策略基类 自定义盈利目标策略接口: diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 39b9764e..934af06b 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -28,7 +28,8 @@ public: }; void export_Signal(py::module& m) { - py::class_(m, "SignalBase", R"(信号指示器基类 + py::class_(m, "SignalBase", py::dynamic_attr(), + R"(信号指示器基类 信号指示器负责产生买入、卖出信号。 公共参数: diff --git a/hikyuu_pywrap/trade_sys/_Slippage.cpp b/hikyuu_pywrap/trade_sys/_Slippage.cpp index 42ca4f27..37b3cc7e 100644 --- a/hikyuu_pywrap/trade_sys/_Slippage.cpp +++ b/hikyuu_pywrap/trade_sys/_Slippage.cpp @@ -39,7 +39,8 @@ public: }; void export_Slippage(py::module& m) { - py::class_(m, "SlippageBase", R"(移滑价差算法基类 + py::class_(m, "SlippageBase", py::dynamic_attr(), + R"(移滑价差算法基类 自定义移滑价差接口: diff --git a/hikyuu_pywrap/trade_sys/_Stoploss.cpp b/hikyuu_pywrap/trade_sys/_Stoploss.cpp index dc67da79..a2761875 100644 --- a/hikyuu_pywrap/trade_sys/_Stoploss.cpp +++ b/hikyuu_pywrap/trade_sys/_Stoploss.cpp @@ -37,7 +37,7 @@ public: }; void export_Stoploss(py::module& m) { - py::class_(m, "StoplossBase", + py::class_(m, "StoplossBase", py::dynamic_attr(), R"(止损/止赢算法基类 自定义止损/止赢策略接口: From 824b10d6d2bd9574d1c9666cf8ac686e37e1c27f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 13 May 2024 17:19:36 +0800 Subject: [PATCH 283/601] Release 2.0.6 --- docs/source/release.rst | 8 ++++++++ xmake.lua | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 93b15e29..a4cf3bd9 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,14 @@ 版本发布说明 ======================= +2.0.6 - 2024年5月13日 +------------------------- + +1. 安装包HikyuuTDX不采用gui模式,win11下GUI模式会造成直接timeout +2. 策略部件 python 导出时,支持 python 的动态属性,在 hub 中支持 +3. fixed _Selector 缺失赋值构造函数,导致 clone 失败 + + 2.0.5 - 2024年5月8日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 167de3ca..38f252af 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.5", {build = "%Y%m%d%H%M"}) +set_version("2.0.6", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 238d4a715cb88e6cc49968d0446b38614465474f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 14 May 2024 21:20:32 +0800 Subject: [PATCH 284/601] =?UTF-8?q?fixed=20hub=20=E8=8E=B7=E5=8F=96part?= =?UTF-8?q?=E4=B8=8B=E5=AD=90=E9=83=A8=E4=BB=B6=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/hub.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/hub.py b/hikyuu/hub.py index 02b0f1ba..feb3953e 100644 --- a/hikyuu/hub.py +++ b/hikyuu/hub.py @@ -496,7 +496,7 @@ class HubManager(metaclass=SingletonType): """ abs_path = os.path.abspath(filename) # 当前文件的绝对路径 path_parts = pathlib.Path(abs_path).parts - local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[5] + local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[4] hub_model = self._session.query(HubModel.name).filter_by(local_base=local_base).first() checkif(hub_model is None, local_base, HubNotFoundError) return hub_model.name From 14d4ce919049b76deffe6ac217af6fbda517af03 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 14 May 2024 21:28:39 +0800 Subject: [PATCH 285/601] =?UTF-8?q?fixed=20MultiFactorBase=20=E6=9E=84?= =?UTF-8?q?=E9=80=A0=E6=97=B6=E7=A1=AE=E5=AE=9E=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp index 183f21f2..496a5705 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/MultiFactorBase.cpp @@ -59,11 +59,11 @@ HKU_API std::ostream& operator<<(std::ostream& out, const MultiFactorPtr& mf) { return out; } -MultiFactorBase::MultiFactorBase() { +MultiFactorBase::MultiFactorBase() : m_name("MultiFactorBase") { initParam(); } -MultiFactorBase::MultiFactorBase(const string& name) { +MultiFactorBase::MultiFactorBase(const string& name) : m_name(name) { initParam(); } From 2f613cf4f40feb9456f8f91a7e9d9edde98196eb Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 17 May 2024 16:23:44 +0800 Subject: [PATCH 286/601] =?UTF-8?q?sys.performance=20=E7=BB=98=E5=9B=BE?= =?UTF-8?q?=E6=97=B6=E5=8A=A0=E4=B8=8A=E5=AF=B9=E5=BA=94=E7=9A=84=E8=AF=81?= =?UTF-8?q?=E5=88=B8=E5=90=8D=E7=A7=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 78396af8..56b0bdac 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -803,7 +803,11 @@ def sys_performance(sys, ref_stk=None): ax3 = fg.add_subplot(gs[4:, :3]) ref_return.plot(axes=ax1, legend_on=True) funds_return.plot(axes=ax1, legend_on=True) - ax1.set_title(f"{sys.name} 累积收益率") + if isinstance(sys, System): + stk = sys.get_stock() + ax1.set_title(f"{sys.name} {stk.name}({stk.market_code}) 累积收益率") + else: + ax1.set_title(f"{sys.name} 累积收益率") label = t1 + '\n\n' + t2 + '\n\n' + t3 ax2.text(0, 1, From 0035c9d6bea0250909d7b274579f874cd8e43cd1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 18 May 2024 07:27:45 +0800 Subject: [PATCH 287/601] fixed SignalBase reset --- hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp index 2dc88bb5..51dea966 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.cpp @@ -89,6 +89,8 @@ void SignalBase::reset() { m_sellSig.clear(); m_hold_long = false; m_hold_short = false; + m_cycle_start = Null(); + m_cycle_end = Null(); _reset(); } From b346d3d87977f78486f6d96b28e489bb41671770 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 18 May 2024 07:27:54 +0800 Subject: [PATCH 288/601] update --- hikyuu/analysis/__init__.py | 1 + hikyuu/analysis/analysis.py | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/hikyuu/analysis/__init__.py b/hikyuu/analysis/__init__.py index 48834c37..1df495c4 100644 --- a/hikyuu/analysis/__init__.py +++ b/hikyuu/analysis/__init__.py @@ -10,4 +10,5 @@ __all__ = [ 'combinate_ind_analysis', 'combinate_ind_analysis_multi', 'analysis_sys_list', + 'analysis_sys_list_multi', ] diff --git a/hikyuu/analysis/analysis.py b/hikyuu/analysis/analysis.py index bc483184..16b1185b 100644 --- a/hikyuu/analysis/analysis.py +++ b/hikyuu/analysis/analysis.py @@ -95,6 +95,29 @@ def combinate_ind_analysis_multi( def analysis_sys_list(stks, query, sys_proto, keys=["累计投入本金", "当前总资产", "现金余额", "未平仓头寸净值", "赢利交易比例%", "赢利交易数", "亏损交易数"]): + names = ["证券代码", "证券名称"] + names.extend(keys) + ret = {} + for name in names: + ret[name] = [] + + per = Performance() + sys_proto.force_reset_all() + sys_proto.set_param("shared_ev", False) + for stk in stks: + # print(stk) + k = stk.get_kdata(query) + my_sys = sys_proto.clone() + my_sys.run(k, reset_all=True) + per.statistics(my_sys.tm, k[-1].datetime if len(k) > 0 else Datetime()) + ret["证券代码"].append(stk.market_code) + ret["证券名称"].append(stk.name) + for key in keys: + ret[key].append(per[key]) + return pd.DataFrame(ret) + + +def analysis_sys_list_multi(stks, query, sys_proto, keys=["累计投入本金", "当前总资产", "现金余额", "未平仓头寸净值", "赢利交易比例%", "赢利交易数", "亏损交易数"]): out = inner_analysis_sys_list(stks, query, sys_proto) if not keys: ret = out From c1795d988e1aab50bebf371525adc8c723a9c22b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 18 May 2024 10:48:09 +0800 Subject: [PATCH 289/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20TP?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 21 ++++++++++++++----- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 6 +++--- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index ee7c9a08..ebcb7e45 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -570,7 +570,8 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { tr = _sell(today, src_today, PART_PROFITGOAL); } else { - price_t current_take_profile = _getTakeProfitPrice(today.datetime); + price_t current_take_profile = + _getTakeProfitPrice(today.datetime, src_today.closePrice); if (current_take_profile != 0.0) { if (current_take_profile < m_lastTakeProfit) { current_take_profile = m_lastTakeProfit; @@ -582,7 +583,9 @@ TradeRecord System::_runMoment(const KRecord& today, const KRecord& src_today) { size_t pos = m_kdata.getPos(today.datetime); size_t position_pos = m_kdata.getPos(position.takeDatetime); // 如果当前价格小于等于止盈价,且满足止盈延迟条件则卖出 - if (pos - position_pos >= tp_delay_n && current_price <= current_take_profile) { + price_t profit = position.number * src_today.closePrice - position.totalCost; + if (pos - position_pos >= tp_delay_n && current_price <= current_take_profile && + profit > (position.buyMoney - position.sellMoney)) { HKU_INFO_IF( trace, "[{}] TP to sell, current price after restoration: {}, take_profit: {}", @@ -638,7 +641,7 @@ TradeRecord System::_buyNow(const KRecord& today, const KRecord& src_today, Part return result; } - m_lastTakeProfit = _getTakeProfitPrice(record.datetime); + m_lastTakeProfit = record.realPrice; m_trade_list.push_back(record); _buyNotifyAll(record); return record; @@ -691,7 +694,7 @@ TradeRecord System::_buyDelay(const KRecord& today, const KRecord& src_today) { } m_buy_days = 0; - m_lastTakeProfit = 0; + m_lastTakeProfit = record.realPrice; m_trade_list.push_back(record); _buyNotifyAll(record); m_buyRequest.clear(); @@ -752,6 +755,12 @@ TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool record = m_tm->sell(date, m_stock, realPrice, real_sell_num, position.stoploss, position.goalPrice, on_open ? src_krecord.openPrice : src_krecord.closePrice, from); + + // 如果已未持仓,最后的止赢价初始为0 + if (!m_tm->have(m_stock)) { + m_lastTakeProfit = 0.0; + } + m_trade_list.push_back(record); _sellNotifyAll(record); return record; @@ -799,7 +808,7 @@ TradeRecord System::_sellNow(const KRecord& today, const KRecord& src_today, Par if (!m_tm->have(m_stock)) { m_lastTakeProfit = 0.0; } else { - m_lastTakeProfit = _getTakeProfitPrice(today.datetime); + m_lastTakeProfit = src_today.closePrice; } m_trade_list.push_back(record); @@ -854,6 +863,8 @@ TradeRecord System::_sellDelay(const KRecord& today, const KRecord& src_today) { // 如果已未持仓,最后的止赢价初始为0 if (!m_tm->have(m_stock)) { m_lastTakeProfit = 0.0; + } else { + m_lastTakeProfit = src_today.openPrice; } m_trade_list.push_back(record); diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index 7e8a321d..fe949ae9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -248,7 +248,7 @@ private: price_t _getStoplossPrice(const KRecord& today, const KRecord& src_today, price_t price); price_t _getShortStoplossPrice(const KRecord& today, const KRecord& src_today, price_t price); - price_t _getTakeProfitPrice(const Datetime& datetime); + price_t _getTakeProfitPrice(const Datetime& datetime, price_t currentPrice); price_t _getGoalPrice(const Datetime& datetime, price_t price); price_t _getShortGoalPrice(const Datetime&, price_t price); @@ -589,8 +589,8 @@ inline price_t System ::_getRealSellPrice(const Datetime& datetime, price_t plan return m_sp ? m_sp->getRealSellPrice(datetime, planPrice) : planPrice; } -inline price_t System ::_getTakeProfitPrice(const Datetime& datetime) { - return m_tp ? m_tp->getPrice(datetime, 0.0) : 0.0; +inline price_t System ::_getTakeProfitPrice(const Datetime& datetime, price_t currentPrice) { + return m_tp ? m_tp->getPrice(datetime, currentPrice) : 0.0; } inline price_t System ::_getGoalPrice(const Datetime& datetime, price_t price) { From f90fd135dbccc7c35f8900e7adbd176006ab3e0e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 18 May 2024 12:37:38 +0800 Subject: [PATCH 290/601] =?UTF-8?q?=E6=89=A9=E5=B1=95=20SG=5FBand,=20?= =?UTF-8?q?=E6=94=AF=E6=8C=813=E6=8C=87=E6=A0=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/signal/crt/SG_Band.h | 19 +++++- .../trade_sys/signal/imp/BandSignal.cpp | 2 +- .../trade_sys/signal/imp/BandSignal2.cpp | 63 +++++++++++++++++++ .../hikyuu/trade_sys/signal/imp/BandSignal2.h | 47 ++++++++++++++ hikyuu_pywrap/trade_sys/_Signal.cpp | 8 ++- 5 files changed, 135 insertions(+), 4 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Band.h b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Band.h index 69bf4cb9..1005ee58 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Band.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/crt/SG_Band.h @@ -13,7 +13,24 @@ namespace hku { -SignalPtr HKU_API SG_Band(const Indicator& sig, price_t lower, price_t upper); +/** + * 指标区间指示器, 当指标超过上轨时,买入;当指标低于下轨时,卖出。 + * @note 适用于 RSI 类,有绝对值区间的指标 + * @param ind 指标 + * @param lower 下轨 + * @param upper 上轨 + * @return SignalPtr + */ +SignalPtr HKU_API SG_Band(const Indicator& ind, price_t lower, price_t upper); + +/** + * 指标区间指示器, 当指标超过上轨指标时,买入;当指标低于下轨指标时,卖出。 + * @param ind 指标 + * @param lower 下轨指标 + * @param upper 上轨指标 + * @return SignalPtr + */ +SignalPtr HKU_API SG_Band(const Indicator& ind, const Indicator& lower, const Indicator& upper); } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp index 8121f695..275a9c3d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp @@ -16,7 +16,7 @@ namespace hku { BandSignal::BandSignal() : SignalBase("SG_Band") {} BandSignal::BandSignal(const Indicator& ind, price_t lower, price_t upper) -: SignalBase("SG_Band"), m_ind(ind), m_lower(lower), m_upper(upper) { +: SignalBase("SG_Band"), m_ind(ind.clone()), m_lower(lower), m_upper(upper) { HKU_CHECK(lower > upper, "BandSignal: lower track is greater than upper track"); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp new file mode 100644 index 00000000..3fda54c4 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp @@ -0,0 +1,63 @@ +/* + * BandSignal2.cpp + * + * Created on: 2023年09月23日 + * Author: yangrq1018 + */ +#include "../../../indicator/crt/KDATA.h" +#include "BandSignal2.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::BandSignal2) +#endif + +namespace hku { + +BandSignal2::BandSignal2() : SignalBase("SG_Band") {} + +BandSignal2::BandSignal2(const Indicator& ind, const Indicator& lower, const Indicator& upper) +: SignalBase("SG_Band"), m_ind(ind.clone()), m_lower(lower.clone()), m_upper(upper.clone()) {} + +BandSignal2::~BandSignal2() {} + +SignalPtr BandSignal2::_clone() { + BandSignal2* p = new BandSignal2(); + p->m_upper = m_upper.clone(); + p->m_lower = m_lower.clone(); + p->m_ind = m_ind.clone(); + return SignalPtr(p); +} + +void BandSignal2::_calculate(const KData& kdata) { + Indicator ind = m_ind(kdata); + Indicator upper = m_upper(kdata); + Indicator lower = m_lower(kdata); + HKU_ASSERT(ind.size() == upper.size() && ind.size() == lower.size()); + + size_t discard = ind.discard(); + if (discard < upper.discard()) { + discard = upper.discard(); + } + if (discard < lower.discard()) { + discard = lower.discard(); + } + size_t total = ind.size(); + + auto const* inddata = ind.data(); + auto const* upperdata = upper.data(); + auto const* lowerdata = lower.data(); + auto const* ks = kdata.data(); + for (size_t i = discard; i < total; ++i) { + if (inddata[i] > upperdata[i]) { + _addBuySignal(ks[i].datetime); + } else if (inddata[i] < lowerdata[i]) { + _addSellSignal(ks[i].datetime); + } + } +} + +SignalPtr HKU_API SG_Band(const Indicator& sig, const Indicator& lower, const Indicator& upper) { + return SignalPtr(new BandSignal2(sig, lower, upper)); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.h b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.h new file mode 100644 index 00000000..944543c8 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.h @@ -0,0 +1,47 @@ +/* + * BandSignal2.h + * + * Created on: 2023年09月23日 + * Author: yangrq1018 + */ + +#pragma once +#ifndef TRADE_SYS_SIGNAL_IMP_BANDSIGNAL_H_ +#define TRADE_SYS_SIGNAL_IMP_BANDSIGNAL_H_ + +#include "../../../indicator/Indicator.h" +#include "../SignalBase.h" + +namespace hku { + +class BandSignal2 : public SignalBase { +public: + BandSignal2(); + BandSignal2(const Indicator& sig, const Indicator& lower, const Indicator& upper); + virtual ~BandSignal2(); + + virtual SignalPtr _clone() override; + virtual void _calculate(const KData& kdata) override; + +private: + Indicator m_ind; + Indicator m_lower; + Indicator m_upper; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SignalBase); + ar& BOOST_SERIALIZATION_NVP(m_ind); + ar& BOOST_SERIALIZATION_NVP(m_lower); + ar& BOOST_SERIALIZATION_NVP(m_upper); + } +#endif +}; +} // namespace hku + +#endif \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Signal.cpp b/hikyuu_pywrap/trade_sys/_Signal.cpp index 934af06b..e028f778 100644 --- a/hikyuu_pywrap/trade_sys/_Signal.cpp +++ b/hikyuu_pywrap/trade_sys/_Signal.cpp @@ -204,14 +204,18 @@ void export_Signal(py::module& m) { :param int slow_n: 慢线EMA周期 :return: 信号指示器)"); - m.def("SG_Band", SG_Band, py::arg("ind"), py::arg("lower"), py::arg("upper"), + m.def("SG_Band", + py::overload_cast(SG_Band), + py::arg("ind"), py::arg("lower"), py::arg("upper")); + m.def("SG_Band", py::overload_cast(SG_Band), py::arg("ind"), + py::arg("lower"), py::arg("upper"), R"(SG_Band(ind, lower, upper) 指标区间指示器, 当指标超过上轨时,买入; 当指标低于下轨时,卖出。:: SG_Band(MA(C, n=10), 100, 200) - )"); + SG_Band(CLOSE, MA(LOW), MA(HIGH)))"); m.def("SG_AllwaysBuy", SG_AllwaysBuy, R"(SG_AllwaysBuy() From 42f2dac2045824003e3f1fc2d2b83f9fe933a9b6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 18 May 2024 12:40:12 +0800 Subject: [PATCH 291/601] update --- docs/source/trade_sys/signal.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/trade_sys/signal.rst b/docs/source/trade_sys/signal.rst index fd40bf90..e5e079f6 100644 --- a/docs/source/trade_sys/signal.rst +++ b/docs/source/trade_sys/signal.rst @@ -112,6 +112,7 @@ :: SG_Band(MA(C, n=10), 100, 200) + SG_Band(CLOSE, MA(LOW), MA(HIGH)) 持续买入信号指示器 From e63981210c81259dc327cac3f1d7089ec12c838f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 18 May 2024 17:48:28 +0800 Subject: [PATCH 292/601] =?UTF-8?q?fixed=20sys.performance=20=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=E5=9B=9E=E6=B5=8B=E6=88=AA=E6=AD=A2=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 56b0bdac..22cf332f 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -760,7 +760,7 @@ def sys_performance(sys, ref_stk=None): ref_k = ref_stk.get_kdata(sys.query) hku_check(len(ref_k) > 0, "The length of ref_k is zero! Maybe The query date is out of the ref-stock range!\n ref_k: {}", ref_k) - query = Query(ref_k[0].datetime.start_of_day(), ref_k[-1].datetime.start_of_day() + TimeDelta(1), Query.DAY) + query = Query(ref_k[0].datetime.start_of_day(), ref_k[-1].datetime.start_of_day() + Seconds(1), Query.DAY) ref_k = ref_stk.get_kdata(query) ref_dates = ref_k.get_datetime_list() @@ -775,7 +775,7 @@ def sys_performance(sys, ref_stk=None): ref_return.name = ref_stk.name per = Performance() - text = per.report(sys.tm, Datetime(datetime.today())) + text = per.report(sys.tm, query.end_datetime) # 计算最大回撤 max_pullback = min(MDD(funds).to_np()) From bd69ad52877272e04cff96110ce4d9f9724b88a0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 19 May 2024 02:26:48 +0800 Subject: [PATCH 293/601] Release 2.0.7 --- docs/source/release.rst | 11 +++++++++++ .../hikyuu/trade_sys/selector/imp/SignalSelector.cpp | 6 +++--- xmake.lua | 2 +- 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index a4cf3bd9..fff98bc9 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,17 @@ 版本发布说明 ======================= +2.0.7 - 2024年5月18日 +------------------------- + +1. sys.performance 绘图时标题中加上对应的证券名称及标识,以及修正统计范围为回测截止时间 +2. 优化内建信号指示器 SG_Band, 支持使用 3 个指标分别作为参考、下轨、上轨 +3. fixed MultiFactorBase 默认构造时缺失名称 +4. fixed SignalBase reset 中缺失对象成员 +5. fixed System 中止盈实现,保证止盈仅在盈利情况下发生 +6. fixed hub 获取 part 下子部件失败 + + 2.0.6 - 2024年5月13日 ------------------------- diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp index 2be613bd..afc5f7e5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/SignalSelector.cpp @@ -18,9 +18,9 @@ SignalSelector::SignalSelector() : SelectorBase("SE_Sigal") {} SignalSelector::~SignalSelector() {} bool SignalSelector::isMatchAF(const AFPtr& af) { - HKU_WARN_IF_RETURN( - af->getParam("adjust_running_sys"), false, - "AF will adjust running system funds, but this se is not suitable the case!"); + // HKU_WARN_IF_RETURN( + // af->getParam("adjust_running_sys"), false, + // "AF will adjust running system funds, but this se is not suitable the case!"); return true; } diff --git a/xmake.lua b/xmake.lua index 38f252af..4d8c49e1 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.6", {build = "%Y%m%d%H%M"}) +set_version("2.0.7", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From ca889cf23625eaec4b66a75fd70b49f5912d18be Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 20 May 2024 22:39:27 +0800 Subject: [PATCH 294/601] =?UTF-8?q?Block=E6=94=AF=E6=8C=81=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=8A=A0=E5=85=A5=E8=AF=81=E5=88=B8=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Block.cpp | 22 +++++++++++++++--- hikyuu_cpp/hikyuu/Block.h | 22 +++++++++++++++--- hikyuu_pywrap/_Block.cpp | 45 ++++++++++++++++++++++++++----------- 3 files changed, 70 insertions(+), 19 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Block.cpp b/hikyuu_cpp/hikyuu/Block.cpp index 70df2e3d..e3a236ef 100644 --- a/hikyuu_cpp/hikyuu/Block.cpp +++ b/hikyuu_cpp/hikyuu/Block.cpp @@ -17,16 +17,16 @@ HKU_API std::ostream& operator<<(std::ostream& os, const Block& blk) { return os; } -Block::Block() {} +Block::Block() noexcept {} Block::~Block() {} -Block::Block(const string& category, const string& name) : m_data(make_shared()) { +Block::Block(const string& category, const string& name) noexcept : m_data(make_shared()) { m_data->m_category = category; m_data->m_name = name; } -Block::Block(const string& category, const string& name, const string& indexCode) +Block::Block(const string& category, const string& name, const string& indexCode) noexcept : Block(category, name) { if (!indexCode.empty()) { m_data->m_indexStock = StockManager::instance().getStock(indexCode); @@ -119,6 +119,22 @@ bool Block::add(const string& market_code) { return true; } +bool Block::add(const StockList& stocks) { + bool success = true; + for (const auto& stk : stocks) { + success = add(stk); + } + return success; +} + +bool Block::add(const StringList& market_codes) { + bool success = true; + for (const auto& code : market_codes) { + success = add(code); + } + return success; +} + bool Block::remove(const string& market_code) { HKU_IF_RETURN(!have(market_code), false); string query_str = market_code; diff --git a/hikyuu_cpp/hikyuu/Block.h b/hikyuu_cpp/hikyuu/Block.h index 9db6a31d..86a14fa8 100644 --- a/hikyuu_cpp/hikyuu/Block.h +++ b/hikyuu_cpp/hikyuu/Block.h @@ -19,9 +19,9 @@ namespace hku { */ class HKU_API Block { public: - Block(); - Block(const string& category, const string& name); - Block(const string& category, const string& name, const string& indexCode); + Block() noexcept; + Block(const string& category, const string& name) noexcept; + Block(const string& category, const string& name, const string& indexCode) noexcept; Block(const Block&) noexcept; Block(Block&&) noexcept; Block& operator=(const Block&) noexcept; @@ -99,6 +99,22 @@ public: /** 加入指定证券 */ bool add(const string& market_code); + /** + * 加入指定的证券列表 + * @param stocks 证券列表 + * @return true 全部成功 + * @return false 存在失败 + */ + bool add(const StockList& stocks); + + /** + * 加入指定的证券列表 + * @param market_codes 证券标识列表 + * @return true 全部成功 + * @return false 存在失败 + */ + bool add(const StringList& market_codes); + /** 移除指定证券 */ bool remove(const string& market_code); diff --git a/hikyuu_pywrap/_Block.cpp b/hikyuu_pywrap/_Block.cpp index 27198484..ede1b461 100644 --- a/hikyuu_pywrap/_Block.cpp +++ b/hikyuu_pywrap/_Block.cpp @@ -20,11 +20,6 @@ void (Block::*setCategory)(const string&) = &Block::category; string (Block::*getName)() const = &Block::name; void (Block::*setName)(const string&) = &Block::name; -bool (Block::*add_1)(const Stock&) = &Block::add; -bool (Block::*add_2)(const string&) = &Block::add; -bool (Block::*remove_1)(const Stock&) = &Block::remove; -bool (Block::*remove_2)(const string&) = &Block::remove; - void export_Block(py::module& m) { py::class_(m, "Block", "板块类,可视为证券的容器") .def(py::init<>()) @@ -43,25 +38,49 @@ void export_Block(py::module& m) { 是否为空)") - .def("add", add_1, R"(add(self, stock) + .def("add", py::overload_cast(&Block::add), R"(add(self, stock) 加入指定的证券 :param Stock stock: 待加入的证券 :return: 是否成功加入 - :rtype: bool)", - py::keep_alive<1, 2>()) + :rtype: bool)") - .def("add", add_2, R"(add(self, market_code) + .def("add", py::overload_cast(&Block::add), R"(add(self, market_code) 根据"市场简称证券代码"加入指定的证券 :param str market_code: 市场简称证券代码 :return: 是否成功加入 - :rtype: bool)", - py::keep_alive<1, 2>()) + :rtype: bool)") - .def("remove", remove_1, R"(remove(self, stock) + .def( + "add", + [](Block& blk, py::sequence stks) { + auto total = len(stks); + HKU_IF_RETURN(total == 0, true); + + if (py::isinstance(stks[0])) { + StockList cpp_stks = python_list_to_vector(stks); + return blk.add(cpp_stks); + } + + if (py::isinstance(stks[0])) { + StringList codes = python_list_to_vector(stks); + return blk.add(codes); + } + + HKU_ERROR("Not support type!"); + return false; + }, + R"(add(self, sequence) + + 加入定的证券列表 + + :param sequence stks: 全部由 Stock 组成的序列或全部由字符串市场简称证券代码组成的序列 + :return: True 全部成功 | False 存在失败)") + + .def("remove", py::overload_cast(&Block::remove), R"(remove(self, stock) 移除指定证券 @@ -69,7 +88,7 @@ void export_Block(py::module& m) { :return: 是否成功 :rtype: bool)") - .def("remove", remove_2, R"(remove(market_code) + .def("remove", py::overload_cast(&Block::remove), R"(remove(market_code) 移除指定证券 From 0ba0982f41646540106566633b06adc986d3c409 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 01:04:24 +0800 Subject: [PATCH 295/601] =?UTF-8?q?fixed=20=E7=A9=BA=20Block=20=E5=BA=8F?= =?UTF-8?q?=E5=88=97=E5=8C=96=E6=81=A2=E5=A4=8D=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/serialization/Block_serialization.h | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_cpp/hikyuu/serialization/Block_serialization.h b/hikyuu_cpp/hikyuu/serialization/Block_serialization.h index 0760891c..30cf40a9 100644 --- a/hikyuu_cpp/hikyuu/serialization/Block_serialization.h +++ b/hikyuu_cpp/hikyuu/serialization/Block_serialization.h @@ -38,6 +38,7 @@ void load(Archive& ar, hku::Block& blk, unsigned int version) { ar& boost::serialization::make_nvp("name", name); hku::StockList stock_list; ar& boost::serialization::make_nvp("stock_list", stock_list); + HKU_IF_RETURN(category.empty() && name.empty(), void()); blk.category(category); blk.name(name); for (auto iter = stock_list.begin(); iter != stock_list.end(); ++iter) { From 76bb44df38a2b14bcf71d96a956d29869dbd5627 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 01:04:52 +0800 Subject: [PATCH 296/601] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=20getBlock=20?= =?UTF-8?q?=E4=BE=BF=E6=8D=B7=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Block.cpp | 5 +++++ hikyuu_cpp/hikyuu/Block.h | 12 ++++++++++-- hikyuu_pywrap/main.cpp | 8 ++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Block.cpp b/hikyuu_cpp/hikyuu/Block.cpp index e3a236ef..e7569b46 100644 --- a/hikyuu_cpp/hikyuu/Block.cpp +++ b/hikyuu_cpp/hikyuu/Block.cpp @@ -155,4 +155,9 @@ void Block::setIndexStock(const Stock& stk) { m_data->m_indexStock = stk; } +HKU_API Block getBlock(const string& category, const string& name) { + auto& sm = StockManager::instance(); + return sm.getBlock(category, name); +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/Block.h b/hikyuu_cpp/hikyuu/Block.h index 86a14fa8..5b94e3fc 100644 --- a/hikyuu_cpp/hikyuu/Block.h +++ b/hikyuu_cpp/hikyuu/Block.h @@ -43,11 +43,11 @@ public: return iter; } - bool operator==(const Block& blk) { + bool operator==(const Block& blk) const { return m_data == blk.m_data; } - bool operator!=(const Block& blk) { + bool operator!=(const Block& blk) const { return m_data != blk.m_data; } @@ -160,6 +160,14 @@ typedef vector BlockList; HKU_API std::ostream& operator<<(std::ostream& os, const Block&); +/** + * @brief 获取 StockManager 中的 Block + * @param category + * @param name + * @return HKU_API + */ +HKU_API Block getBlock(const string& category, const string& name); + } /* namespace hku */ #endif /* BLOCK_H_ */ diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index 5fbfadf0..d060017d 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -135,6 +135,14 @@ PYBIND11_MODULE(core, m) { int64_t null_int64 = Null(); Datetime null_date = Null(); + m.def("get_block", getBlock, R"(get_block(category: str, name: str) + + 获取预定义板块 + + :param str category: 板块分类 + :param str name: 板块名称 + :rtype: Block)"); + m.def("get_kdata", py::overload_cast(getKData)); m.def( From 992343dd68cc305c239b5b4263308b257b8e70bd Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 01:05:07 +0800 Subject: [PATCH 297/601] =?UTF-8?q?Parameter=E6=94=AF=E6=8C=81=20Block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/Parameter.cpp | 22 ++++++++++++++----- hikyuu_cpp/hikyuu/utilities/Parameter.h | 11 +++++++++- .../hikyuu/utilities/test_Parameter.cpp | 9 ++++++++ hikyuu_pywrap/convert_any.h | 20 +++++++++++++++-- 4 files changed, 54 insertions(+), 8 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp index 747fb501..1b7e1a95 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp @@ -31,6 +31,8 @@ HKU_API std::ostream& operator<<(std::ostream& os, const Parameter& param) { os << "(string): " << boost::any_cast(iter->second) << strip; } else if (iter->second.type() == typeid(Stock)) { os << "(Stock): " << boost::any_cast(iter->second).market_code() << strip; + } else if (iter->second.type() == typeid(Block)) { + os << "(Block): " << boost::any_cast(iter->second) << strip; } else if (iter->second.type() == typeid(KQuery)) { os << "(Query): " << boost::any_cast(iter->second) << strip; } else if (iter->second.type() == typeid(KData)) { @@ -72,8 +74,9 @@ bool Parameter::support(const boost::any& value) { return value.type() == typeid(int) || value.type() == typeid(int64_t) || value.type() == typeid(bool) || value.type() == typeid(double) || value.type() == typeid(string) || value.type() == typeid(Stock) || - value.type() == typeid(KQuery) || value.type() == typeid(KData) || - value.type() == typeid(PriceList) || value.type() == typeid(DatetimeList); + value.type() == typeid(Block) || value.type() == typeid(KQuery) || + value.type() == typeid(KData) || value.type() == typeid(PriceList) || + value.type() == typeid(DatetimeList); } string Parameter::type(const string& name) const { @@ -86,6 +89,7 @@ string Parameter::type(const string& name) const { HKU_IF_RETURN(iter->second.type() == typeid(double), "double"); HKU_IF_RETURN(iter->second.type() == typeid(string), "string"); HKU_IF_RETURN(iter->second.type() == typeid(Stock), "Stock"); + HKU_IF_RETURN(iter->second.type() == typeid(Block), "Block"); HKU_IF_RETURN(iter->second.type() == typeid(KQuery), "KQuery"); HKU_IF_RETURN(iter->second.type() == typeid(KData), "KData"); HKU_IF_RETURN(iter->second.type() == typeid(PriceList), "PriceList"); @@ -120,6 +124,8 @@ string Parameter::getNameValueList() const { os << "\"" << iter->first << "\"" << equal << boost::any_cast(iter->second); } else if (iter->second.type() == typeid(Stock)) { os << iter->first << equal << boost::any_cast(iter->second); + } else if (iter->second.type() == typeid(Block)) { + os << iter->first << equal << boost::any_cast(iter->second); } else if (iter->second.type() == typeid(KQuery)) { os << iter->first << equal << boost::any_cast(iter->second); } else if (iter->second.type() == typeid(KData)) { @@ -173,9 +179,15 @@ HKU_API bool operator==(const Parameter& p1, const Parameter& p2) { } if (iter1->second.type() == typeid(Stock)) { - const Stock* x1 = boost::any_cast(&iter1->second); - const Stock* x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(*x1 != *x2, false); + const Stock& x1 = boost::any_cast(&iter1->second); + const Stock& x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(x1 != x2, false); + } + + if (iter1->second.type() == typeid(Block)) { + const Block& x1 = boost::any_cast(&iter1->second); + const Block& x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(x1 == x2, true); } if (iter1->second.type() == typeid(KQuery)) { diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index 0a0bbf04..c8284ce3 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -24,6 +24,7 @@ #include #include #include +#include "../serialization/Block_serialization.h" #include "../serialization/KData_serialization.h" #include "../serialization/Datetime_serialization.h" #else @@ -133,7 +134,7 @@ public: /** * 获取指定参数的实际类型 * @param name 指定参数名称 - * @return "string" | "int" | "double" | "bool" | "Stock" | + * @return "string" | "int" | "double" | "bool" | "Stock" | "Block" * "KQuery" | "KData" | "PriceList" | "DatetimeList" */ string type(const string& name) const; @@ -213,6 +214,10 @@ private: type = "stock"; value = "stock"; stock = boost::any_cast(arg); + } else if (arg.type() == typeid(Block)) { + type = "block"; + value = "block"; + block = boost::any_cast(arg); } else if (arg.type() == typeid(KQuery)) { type = "query"; value = "query"; @@ -239,6 +244,7 @@ private: string type; string value; Stock stock; + Block block; KQuery query; KData kdata; PriceList price_list; @@ -250,6 +256,7 @@ private: ar& BOOST_SERIALIZATION_NVP(type); ar& BOOST_SERIALIZATION_NVP(value); ar& BOOST_SERIALIZATION_NVP(stock); + ar& BOOST_SERIALIZATION_NVP(block); ar& BOOST_SERIALIZATION_NVP(query); ar& BOOST_SERIALIZATION_NVP(kdata); ar& BOOST_SERIALIZATION_NVP(price_list); @@ -290,6 +297,8 @@ private: m_params[record.name] = record.value; } else if (record.type == "stock") { m_params[record.name] = record.stock; + } else if (record.type == "block") { + m_params[record.name] = record.block; } else if (record.type == "query") { m_params[record.name] = record.query; } else if (record.type == "kdata") { diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp index d61fac97..f28316f2 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp @@ -105,6 +105,13 @@ TEST_CASE("test_Parameter") { Stock stk2 = p.get("stk"); CHECK(stk == stk2); + /** @arg 测试使用 Block 做为参数 */ + Block blk; + p = Parameter(); + p.set("blk", blk); + Block blk2 = p.get("blk"); + CHECK(blk == blk2); + /** @arg 测试使用 KQuery 做为参数 */ KQuery query(10, 20); p = Parameter(); @@ -176,6 +183,7 @@ TEST_CASE("test_Parameter_serialize") { KData k = stk.getKData(q); DatetimeList d = k.getDatetimeList(); p1.set("stk", stk); + p1.set("blk", Block()); p1.set("query", q); p1.set("kdata", k); p1.set("dates", d); @@ -204,6 +212,7 @@ TEST_CASE("test_Parameter_serialize") { CHECK(p2.get("p") == 0.101); CHECK(p2.get("string") == "This is string!"); CHECK(p2.get("stk") == stk); + CHECK(p2.get("blk") == Block()); CHECK(p2.get("query") == q); KData k2 = p2.get("kdata"); CHECK(k.size() == k2.size()); diff --git a/hikyuu_pywrap/convert_any.h b/hikyuu_pywrap/convert_any.h index 74c4a57c..5d4a5238 100644 --- a/hikyuu_pywrap/convert_any.h +++ b/hikyuu_pywrap/convert_any.h @@ -130,6 +130,10 @@ public: value = obj.cast(); return true; + } else if (isinstance(obj)) { + value = obj.cast(); + return true; + } else if (isinstance(obj)) { value = obj.cast(); return true; @@ -203,6 +207,18 @@ public: o.inc_ref(); return o; + } else if (x.type() == typeid(Block)) { + const Block& blk = boost::any_cast(x); + std::stringstream cmd; + if (blk == Null()) { + cmd << "Block()"; + } else { + cmd << "get_block('" << blk.category() << "','" << blk.name() << "')"; + } + object o = eval(cmd.str()); + o.inc_ref(); + return o; + } else if (x.type() == typeid(KQuery)) { const KQuery& query = boost::any_cast(x); std::stringstream cmd; @@ -212,8 +228,8 @@ public: << KQuery::getRecoverTypeName(query.recoverType()) << ")"; } else { cmd << "Query(Datetime(" << query.startDatetime() << "), Datetime(" - << query.endDatetime() << "), " - << "Query." << KQuery::getKTypeName(query.kType()) << "Query." + << query.endDatetime() << "), " << "Query." + << KQuery::getKTypeName(query.kType()) << "Query." << KQuery::getRecoverTypeName(query.recoverType()) << ")"; } object o = eval(cmd.str()); From e81b34f95b44d483ce155fd716dd09fb441f5747 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 06:00:14 +0800 Subject: [PATCH 298/601] add BLOCKSETNUM indicator --- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h | 31 +++++++ .../hikyuu/indicator/imp/IBlockSetNum.cpp | 84 +++++++++++++++++++ .../hikyuu/indicator/imp/IBlockSetNum.h | 27 ++++++ hikyuu_pywrap/indicator/_build_in.cpp | 18 ++++ 5 files changed, 161 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 19c92a07..745e6d57 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -26,6 +26,7 @@ #include "crt/BARSLAST.h" #include "crt/BARSSINCE.h" #include "crt/BETWEEN.h" +#include "crt/BLOCKSETNUM.h" #include "crt/CEILING.h" #include "crt/CORR.h" #include "crt/COS.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h b/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h new file mode 100644 index 00000000..2bb851a1 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-21 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +/** + * 横向统计(返回板块股个数) + * @param blk 待统计的板块 + * @param query 统计范围 + * @return Indicator + */ +Indicator HKU_API BLOCKSETNUM(const Block& blk, const KQuery& query); + +/** + * 横向统计(返回板块股个数) + * @param category 板块分类 + * @param name 板块名称 + * @param query 统计范围 + * @return Indicator + */ +Indicator HKU_API BLOCKSETNUM(const string& category, const string& name, const KQuery& query); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp new file mode 100644 index 00000000..238dc6b6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp @@ -0,0 +1,84 @@ +/* + * IBlockSetNum.cpp + * + * Copyright (c) 2019, hikyuu.org + * + * Created on: 2024-5-21 + * Author: fasiondog + */ + +#include "IBlockSetNum.h" +#include "../Indicator.h" +#include "../crt/KDATA.h" +#include "../crt/REF.h" +#include "../crt/ALIGN.h" +#include "../../StockManager.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IBlockSetNum) +#endif + +namespace hku { + +IBlockSetNum::IBlockSetNum() : IndicatorImp("BLOCKSETNUM", 1) { + setParam("query", KQueryByIndex(-100)); + setParam("block", Block()); + setParam("ignore_context", false); +} + +IBlockSetNum::~IBlockSetNum() {} + +void IBlockSetNum::_calculate(const Indicator& ind) { + Block block = getParam("block"); + bool ignore_context = getParam("ignore_context"); + KData k = getContext(); + KQuery q; + DatetimeList dates; + if (!ignore_context && !k.empty()) { + q = k.getQuery(); + dates = k.getDatetimeList(); + } else { + q = getParam("query"); + dates = StockManager::instance().getTradingCalendar(q); + } + + size_t total = dates.size(); + m_discard = 0; + _readyBuffer(total, 1); + HKU_IF_RETURN(total == 0, void()); + + value_t zero = 0.0; + auto* dst = this->data(); + for (size_t i = 0; i < total; i++) { + dst[i] = zero; + } + + for (auto iter = block.begin(); iter != block.end(); ++iter) { + const Datetime& start_date = iter->startDatetime(); + Datetime last_date = iter->lastDatetime().isNull() ? Datetime::max() : iter->lastDatetime(); + for (size_t i = 0; i < total; i++) { + if (dates[i] >= start_date && dates[i] <= last_date) { + dst[i]++; + } + } + } +} + +Indicator HKU_API BLOCKSETNUM(const Block& block, const KQuery& query) { + IndicatorImpPtr p = make_shared(); + p->setParam("query", query); + p->setParam("block", block); + if (query == Null()) { + p->setParam("ignore_context", true); + } else { + p->calculate(); + } + return Indicator(p); +} + +Indicator HKU_API BLOCKSETNUM(const string& category, const string& name, const KQuery& query) { + Block block = StockManager::instance().getBlock(category, name); + return BLOCKSETNUM(block, query); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h new file mode 100644 index 00000000..7dd0b513 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-21 + * Author: fasiondog + */ + +#pragma once +#ifndef INDICATOR_IMP_IBLOCKSETNUM_H_ +#define INDICATOR_IMP_IBLOCKSETNUM_H_ + +#include "../Indicator.h" + +namespace hku { + +class IBlockSetNum : public IndicatorImp { + INDICATOR_IMP(IBlockSetNum) + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IBlockSetNum(); + virtual ~IBlockSetNum(); +}; + +} // namespace hku + +#endif /* INDICATOR_IMP_IBLOCKSETNUM_H_ */ \ No newline at end of file diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 561f48b6..40f9e36d 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1848,4 +1848,22 @@ void export_Indicator_build_in(py::module& m) { :param KData kdata: K线数据 :param int ix: 历史财务信息字段索引 :param int name: 历史财务信息字段名称)"); + + m.def("BLOCKSETNUM", py::overload_cast(BLOCKSETNUM), + py::arg("block"), py::arg("query"), R"(BLOCKSETNUM(block, query) + + 横向统计(返回板块股个数) + + :param Block block: 待统计的板块 + :param Query query: 统计范围)"); + + m.def( + "BLOCKSETNUM", py::overload_cast(BLOCKSETNUM), + py::arg("category"), py::arg("name"), py::arg("query"), R"(BLOCKSETNUM(category, name, query) + + 横向统计(返回板块股个数) + + :param str category: 板块类别 + :param str name: 板块名称 + :param query 统计范围)"); } From 5cd2a648575e2c2243beb0157559c5a32ed061c6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 06:01:15 +0800 Subject: [PATCH 299/601] add BLOCKSETNUM --- docs/source/indicator/indicator.rst | 12 ++++++++++++ docs/source/indicator/overview.rst | 4 ++++ 2 files changed, 16 insertions(+) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 9c66331e..d03d703a 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -168,6 +168,18 @@ :rtype: Indicator +.. py:function:: BLOCKSETNUM(block, query) + + 横向统计(返回板块股个数), 两种调用方式: + + BLOCKSET(block, query) + + BLOCKSET(category, name, query) + + :param Block block: 待统计的板块 + :param Query query: 统计范围 + + .. py:function:: CLOSE([data]) 获取收盘价,包装KData的收盘价成Indicator diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index 912b0f1c..dc0bab44 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -99,6 +99,10 @@ * :py:func:`CORR` - 样本相关系数与协方差 * :py:func:`SPEARMAN` - Spearman相关系数 +**横向统计** + +* :py:func:`BLOCKSETNUM` - 返回板块股个数 + **技术指标** From 6bcbc47c248a53774e195bd377ac7f4f098047fa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 06:29:00 +0800 Subject: [PATCH 300/601] update BLOCKSETNUM --- hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp index 238dc6b6..a03f2049 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp @@ -9,9 +9,6 @@ #include "IBlockSetNum.h" #include "../Indicator.h" -#include "../crt/KDATA.h" -#include "../crt/REF.h" -#include "../crt/ALIGN.h" #include "../../StockManager.h" #if HKU_SUPPORT_SERIALIZATION @@ -23,6 +20,7 @@ namespace hku { IBlockSetNum::IBlockSetNum() : IndicatorImp("BLOCKSETNUM", 1) { setParam("query", KQueryByIndex(-100)); setParam("block", Block()); + setParam("market", "SH"); setParam("ignore_context", false); } @@ -39,7 +37,7 @@ void IBlockSetNum::_calculate(const Indicator& ind) { dates = k.getDatetimeList(); } else { q = getParam("query"); - dates = StockManager::instance().getTradingCalendar(q); + dates = StockManager::instance().getTradingCalendar(q, getParam("market")); } size_t total = dates.size(); From 0b85ad4c56f47987646ee8b7acbac7bf29c4b5e1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 06:35:50 +0800 Subject: [PATCH 301/601] update IBlockSum --- hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp | 8 ++++++++ hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h | 1 + 2 files changed, 9 insertions(+) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp index a03f2049..fbe7d0d8 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp @@ -26,6 +26,14 @@ IBlockSetNum::IBlockSetNum() : IndicatorImp("BLOCKSETNUM", 1) { IBlockSetNum::~IBlockSetNum() {} +void IBlockSetNum::_checkParam(const string& name) const { + if ("market" == name) { + string market = getParam(name); + auto market_info = StockManager::instance().getMarketInfo(market); + HKU_CHECK(market_info != Null(), "Invalid market: {}", market); + } +} + void IBlockSetNum::_calculate(const Indicator& ind) { Block block = getParam("block"); bool ignore_context = getParam("ignore_context"); diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h index 7dd0b513..974295fd 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.h @@ -20,6 +20,7 @@ class IBlockSetNum : public IndicatorImp { public: IBlockSetNum(); virtual ~IBlockSetNum(); + virtual void _checkParam(const string& name) const override; }; } // namespace hku From 4c0e461ab93144abbb1d5f4287b9071647e69270 Mon Sep 17 00:00:00 2001 From: Jet <344148042@qq.com> Date: Tue, 21 May 2024 19:32:18 +0800 Subject: [PATCH 302/601] =?UTF-8?q?=E9=80=9A=E8=BE=BE=E4=BF=A1=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E7=9A=84=E6=97=B6=E5=80=99=E6=B2=A1=E6=9C=89=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/data/ImportHistoryFinanceTask.py | 5 +++++ hikyuu/gui/data/UseTdxImportToH5Thread.py | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/hikyuu/gui/data/ImportHistoryFinanceTask.py b/hikyuu/gui/data/ImportHistoryFinanceTask.py index 2b54755d..b5d342d2 100644 --- a/hikyuu/gui/data/ImportHistoryFinanceTask.py +++ b/hikyuu/gui/data/ImportHistoryFinanceTask.py @@ -124,6 +124,11 @@ class ImportHistoryFinanceTask: if __name__ == "__main__": + from multiprocessing import Queue + from configparser import ConfigParser + this_dir = os.path.expanduser('~') + '/.hikyuu' + import_config = ConfigParser() + import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8') task = ImportHistoryFinanceTask(None, None, None, "c:\\stock") task() print("over!") diff --git a/hikyuu/gui/data/UseTdxImportToH5Thread.py b/hikyuu/gui/data/UseTdxImportToH5Thread.py index c42616eb..5c147e5f 100644 --- a/hikyuu/gui/data/UseTdxImportToH5Thread.py +++ b/hikyuu/gui/data/UseTdxImportToH5Thread.py @@ -28,6 +28,7 @@ from multiprocessing import Queue, Process from PyQt5.QtCore import QThread, pyqtSignal from hikyuu.gui.data.ImportTdxToH5Task import ImportTdxToH5Task from hikyuu.gui.data.ImportWeightToSqliteTask import ImportWeightToSqliteTask +from hikyuu.gui.data.ImportHistoryFinanceTask import ImportHistoryFinanceTask from hikyuu.data.common_sqlite3 import create_database from hikyuu.data.tdx_to_h5 import tdx_import_stock_name_from_file @@ -68,6 +69,8 @@ class UseTdxImportToH5Thread(QThread): self.tasks = [] if self.config.getboolean('weight', 'enable', fallback=False): self.tasks.append(ImportWeightToSqliteTask(self.log_queue, self.queue, self.config, dest_dir)) + if self.config.getboolean('finance', 'enable', fallback=True): + self.tasks.append(ImportHistoryFinanceTask(self.log_queue, self.queue, self.config, dest_dir)) if self.config.getboolean('ktype', 'day', fallback=False): self.tasks.append( ImportTdxToH5Task(self.log_queue, self.queue, config, 'SH', 'DAY', self.quotations, src_dir, dest_dir) @@ -149,5 +152,7 @@ class UseTdxImportToH5Thread(QThread): hdf5_import_progress[market][ktype] = progress current_progress = (hdf5_import_progress['SH'][ktype] + hdf5_import_progress['SZ'][ktype]) // 2 self.send_message(['IMPORT_KDATA', ktype, current_progress]) + elif taskname == 'IMPORT_KDATA': + self.send_message([taskname, progress]) else: self.logger.error("Unknow task: {}".format(taskname)) From 23d3bf2b273f9570740309807a1d320008a85202 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 21 May 2024 19:47:13 +0800 Subject: [PATCH 303/601] add INSUM --- hikyuu_cpp/hikyuu/indicator/Indicator.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/Indicator.h | 2 +- hikyuu_cpp/hikyuu/indicator/build_in.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/INSUM.h | 19 +++ hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp | 187 +++++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IInSum.h | 28 +++ hikyuu_pywrap/indicator/_build_in.cpp | 5 + 7 files changed, 242 insertions(+), 2 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/crt/INSUM.h create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IInSum.h diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp index 6c1bbfde..4c671416 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.cpp +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.cpp @@ -19,7 +19,7 @@ Indicator::Indicator(const IndicatorImpPtr& imp) : m_imp(imp) {} Indicator::Indicator(const Indicator& indicator) : m_imp(indicator.m_imp) {} -Indicator::Indicator(Indicator&& ind) : m_imp(std::move(ind.m_imp)) {} +Indicator::Indicator(Indicator&& ind) noexcept : m_imp(std::move(ind.m_imp)) {} Indicator::~Indicator() {} diff --git a/hikyuu_cpp/hikyuu/indicator/Indicator.h b/hikyuu_cpp/hikyuu/indicator/Indicator.h index dfbf1074..a65180e9 100644 --- a/hikyuu_cpp/hikyuu/indicator/Indicator.h +++ b/hikyuu_cpp/hikyuu/indicator/Indicator.h @@ -47,7 +47,7 @@ public: Indicator() {} Indicator(const IndicatorImpPtr& imp); Indicator(const Indicator& ind); - Indicator(Indicator&& ind); + Indicator(Indicator&& ind) noexcept; virtual ~Indicator(); Indicator& operator=(const Indicator&); diff --git a/hikyuu_cpp/hikyuu/indicator/build_in.h b/hikyuu_cpp/hikyuu/indicator/build_in.h index 745e6d57..77dc0427 100644 --- a/hikyuu_cpp/hikyuu/indicator/build_in.h +++ b/hikyuu_cpp/hikyuu/indicator/build_in.h @@ -52,6 +52,7 @@ #include "crt/HSL.h" #include "crt/IC.h" #include "crt/ICIR.h" +#include "crt/INSUM.h" #include "crt/IR.h" #include "crt/INTPART.h" #include "crt/LAST.h" diff --git a/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h b/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h new file mode 100644 index 00000000..2df38062 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-21 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +Indicator HKU_API INSUM(const Block& block, const KQuery& query, const Indicator& ind, int mode); + +Indicator HKU_API INSUM(const string& category, const string& name, const KQuery& query, + const Indicator& ind, int mode); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp new file mode 100644 index 00000000..de0d8dd0 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp @@ -0,0 +1,187 @@ +/* + * IInSum.cpp + * + * Copyright (c) 2019, hikyuu.org + * + * Created on: 2024-5-21 + * Author: fasiondog + */ + +#include "hikyuu/utilities/thread/thread.h" +#include "IInSum.h" +#include "../Indicator.h" +#include "../crt/ALIGN.h" +#include "../../StockManager.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IInSum) +#endif + +namespace hku { + +IInSum::IInSum() : IndicatorImp("INSUM", 1) { + setParam("query", KQueryByIndex(-100)); + setParam("block", Block()); + setParam("mode", 0); + setParam("market", "SH"); + setParam("ignore_context", false); +} + +IInSum::~IInSum() {} + +void IInSum::_checkParam(const string& name) const { + if ("market" == name) { + string market = getParam(name); + auto market_info = StockManager::instance().getMarketInfo(market); + HKU_CHECK(market_info != Null(), "Invalid market: {}", market); + } else if ("mode" == name) { + int mode = getParam("mode"); + HKU_ASSERT(mode == 0 || mode == 1 || mode == 2 || mode == 3); + } +} + +static IndicatorList getAllIndicators(const Block& block, const KQuery& query, + const DatetimeList& dates, const Indicator& ind) { +#if 0 + IndicatorList ret; + for (auto iter = block.begin(); iter != block.end(); ++iter) { + auto k = iter->getKData(query); + ret.emplace_back(ALIGN(ind, dates)(k)); + } + return ret; +#else + auto stks = block.getStockList(); + return parallel_for_index(0, stks.size(), + [nind = ind.clone(), &stks, &query, &dates](size_t index) { + auto k = stks[index].getKData(query); + return ALIGN(nind, dates)(k); + }); +#endif +} + +static void insum_cum(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { + for (const auto& value : inds) { + HKU_ASSERT(value.size() == len); + const auto* data = value.data(); + for (size_t i = 0; i < len; i++) { + if (!std::isnan(data[i])) { + if (std::isnan(dst[i])) { + dst[i] = data[i]; + } else { + dst[i] += data[i]; + } + } + } + } +} + +static void insum_mean(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { + vector count(len, 0); + for (const auto& value : inds) { + HKU_ASSERT(value.size() == len); + const auto* data = value.data(); + for (size_t i = 0; i < len; i++) { + if (!std::isnan(data[i])) { + if (std::isnan(dst[i])) { + dst[i] = data[i]; + } else { + dst[i] += data[i]; + } + count[i]++; + } + } + } + + for (size_t i = 0; i < len; i++) { + if (!std::isnan(dst[i])) { + dst[i] = dst[i] / count[i]; + } + } +} + +static void insum_max(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { + for (const auto& value : inds) { + HKU_ASSERT(value.size() == len); + const auto* data = value.data(); + for (size_t i = 0; i < len; i++) { + if (!std::isnan(data[i])) { + if (std::isnan(dst[i])) { + dst[i] = data[i]; + } else if (data[i] > dst[i]) { + dst[i] = data[i]; + } + } + } + } +} + +static void insum_min(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { + for (const auto& value : inds) { + HKU_ASSERT(value.size() == len); + const auto* data = value.data(); + for (size_t i = 0; i < len; i++) { + if (!std::isnan(data[i])) { + if (std::isnan(dst[i])) { + dst[i] = data[i]; + } else if (data[i] < dst[i]) { + dst[i] = data[i]; + } + } + } + } +} + +void IInSum::_calculate(const Indicator& ind) { + Block block = getParam("block"); + bool ignore_context = getParam("ignore_context"); + KData k = getContext(); + KQuery q; + DatetimeList dates; + if (!ignore_context && !k.empty()) { + q = k.getQuery(); + dates = k.getDatetimeList(); + } else { + q = getParam("query"); + dates = StockManager::instance().getTradingCalendar(q, getParam("market")); + } + + size_t total = dates.size(); + m_discard = 0; + _readyBuffer(total, 1); + HKU_IF_RETURN(total == 0, void()); + + int mode = getParam("mode"); + auto inds = getAllIndicators(block, q, dates, ind); + auto* dst = this->data(); + + if (0 == mode) { + insum_cum(inds, dst, total); + } else if (1 == mode) { + insum_mean(inds, dst, total); + } else if (2 == mode) { + insum_max(inds, dst, total); + } else if (3 == mode) { + insum_min(inds, dst, total); + } else { + HKU_ERROR("Not support mode: {}", mode); + } +} + +Indicator HKU_API INSUM(const Block& block, const KQuery& query, const Indicator& ind, int mode) { + IndicatorImpPtr p = make_shared(); + p->setParam("query", query); + p->setParam("block", block); + p->setParam("mode", mode); + if (query == Null()) { + p->setParam("ignore_context", true); + } + return Indicator(p)(ind); +} + +Indicator HKU_API INSUM(const string& category, const string& name, const KQuery& query, + const Indicator& ind, int mode) { + Block block = StockManager::instance().getBlock(category, name); + return INSUM(block, query, ind, mode); +} + +} /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.h b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.h new file mode 100644 index 00000000..a0083e45 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-21 + * Author: fasiondog + */ + +#pragma once +#ifndef INDICATOR_IMP_IINSUM_H_ +#define INDICATOR_IMP_IINSUM_H_ + +#include "../Indicator.h" + +namespace hku { + +class IInSum : public IndicatorImp { + INDICATOR_IMP(IInSum) + INDICATOR_IMP_NO_PRIVATE_MEMBER_SERIALIZATION + +public: + IInSum(); + virtual ~IInSum(); + virtual void _checkParam(const string& name) const override; +}; + +} // namespace hku + +#endif /* INDICATOR_IMP_IINSUM_H_ */ \ No newline at end of file diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 40f9e36d..aa357970 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1866,4 +1866,9 @@ void export_Indicator_build_in(py::module& m) { :param str category: 板块类别 :param str name: 板块名称 :param query 统计范围)"); + + m.def("INSUM", py::overload_cast(INSUM)); + m.def( + "INSUM", + py::overload_cast(INSUM)); } From 580f01d9de89e29533b9edab82d876dbf59a209d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 22 May 2024 15:04:32 +0800 Subject: [PATCH 304/601] add docs for INSUM --- hikyuu_cpp/hikyuu/indicator/crt/INSUM.h | 17 ++++++++++++++++ hikyuu_pywrap/indicator/_build_in.cpp | 26 +++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h b/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h index 2df38062..6b7e4512 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h @@ -11,8 +11,25 @@ namespace hku { +/** + * 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. + * @param block 指定板块 + * @param query 指定范围 + * @param ind 指定指标 + * @param mode 计算类型:0-累加,1-平均数,2-最大值,3-最小值. + * @return Indicator + */ Indicator HKU_API INSUM(const Block& block, const KQuery& query, const Indicator& ind, int mode); +/** + * 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. + * @param category 板块类别 + * @param category 板块名称 + * @param query 指定范围 + * @param ind 指定指标 + * @param mode 计算类型:0-累加,1-平均数,2-最大值,3-最小值. + * @return Indicator + */ Indicator HKU_API INSUM(const string& category, const string& name, const KQuery& query, const Indicator& ind, int mode); diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index aa357970..0d9ac686 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1867,8 +1867,30 @@ void export_Indicator_build_in(py::module& m) { :param str name: 板块名称 :param query 统计范围)"); - m.def("INSUM", py::overload_cast(INSUM)); + m.def("INSUM", py::overload_cast(INSUM), + py::arg("block"), py::arg("query"), py::arg("ind"), py::arg("mode"), + R"(INSUM(block, query, ind, mode) + + 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. + + :param Block block: 指定板块 + :param Query query: 指定范围 + :param Indicator ind: 指定指标 + :param int mode: 计算类型:0-累加,1-平均数,2-最大值,3-最小值. + :rtype: Indicator)"); + m.def( "INSUM", - py::overload_cast(INSUM)); + py::overload_cast(INSUM), + py::arg("category"), py::arg("name"), py::arg("query"), py::arg("ind"), py::arg("mode"), + R"(INSUM(category, name, ind, mode) + + 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. + + :param str category: 板块类别 + :param str name: 板块名称 + :param Query query: 指定范围 + :param Indicator ind: 指定指标 + :param int mode: 计算类型:0-累加,1-平均数,2-最大值,3-最小值. + :rtype: Indicator)"); } From d78a842b56b8e4d6eae2b48923bfa007574d44de Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 22 May 2024 15:04:49 +0800 Subject: [PATCH 305/601] add docs for INSUM --- docs/source/indicator/indicator.rst | 17 +++++++++++++++++ docs/source/indicator/overview.rst | 1 + 2 files changed, 18 insertions(+) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index d03d703a..9619919f 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -530,6 +530,23 @@ :rtype: Indicator +.. py:function:: INSUM(block, query, ind, mode) + + 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. + + 用法: + + INSUM(block, query, ind, mode) + + INSUM(category, name, query, ind, mode) + + :param Block block: 指定板块 + :param Query query: 指定范围 + :param Indicator ind: 指定指标 + :param int mode: 计算类型:0-累加,1-平均数,2-最大值,3-最小值. + :rtype: Indicator + + .. py:function:: INTPART([data]) 取整(绝对值减小取整,即取得数据的整数部分) diff --git a/docs/source/indicator/overview.rst b/docs/source/indicator/overview.rst index dc0bab44..a4517c2e 100644 --- a/docs/source/indicator/overview.rst +++ b/docs/source/indicator/overview.rst @@ -102,6 +102,7 @@ **横向统计** * :py:func:`BLOCKSETNUM` - 返回板块股个数 +* :py:func:`INSUM` - 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. **技术指标** From 32e0f583f85fb544a90e4750314ce343aecc02e1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 22 May 2024 16:40:17 +0800 Subject: [PATCH 306/601] =?UTF-8?q?fixed=20=E6=9D=83=E6=81=AF=E5=AF=BC?= =?UTF-8?q?=E5=85=A5=E6=97=B6=E8=BD=AC=E9=80=81=E8=82=A1=E8=A2=AB=E8=A6=86?= =?UTF-8?q?=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0018.sql | 2 ++ hikyuu/data/pytdx_weight_to_mysql.py | 6 +++--- hikyuu/data/pytdx_weight_to_sqlite.py | 12 ++++++------ hikyuu/data/sqlite_upgrade/0019.sql | 4 ++++ 4 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 hikyuu/data/mysql_upgrade/0018.sql create mode 100644 hikyuu/data/sqlite_upgrade/0019.sql diff --git a/hikyuu/data/mysql_upgrade/0018.sql b/hikyuu/data/mysql_upgrade/0018.sql new file mode 100644 index 00000000..014ccad8 --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0018.sql @@ -0,0 +1,2 @@ +delete from `hku_base`.`stkweight` where 1=1; +UPDATE `hku_base`.`version` set `version` = 18; diff --git a/hikyuu/data/pytdx_weight_to_mysql.py b/hikyuu/data/pytdx_weight_to_mysql.py index 0f37a59e..5e1e1699 100644 --- a/hikyuu/data/pytdx_weight_to_mysql.py +++ b/hikyuu/data/pytdx_weight_to_mysql.py @@ -81,7 +81,7 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): update_last_db_weight = True if xdxr['suogu'] is not None: # etf 扩股 - new_last_db_weight[3] = 100000 * (xdxr['suogu']-1) + new_last_db_weight[3] += 100000 * (xdxr['suogu']-1) update_last_db_weight = True if xdxr['peigu'] is not None: new_last_db_weight[4] = 10000 * xdxr['peigu'] @@ -103,7 +103,7 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): continue if date not in records: songzhuangu = 10000 * xdxr['songzhuangu'] if xdxr['songzhuangu'] is not None else 0 - songzhuangu = 100000 * (xdxr['suogu']-1) if xdxr['suogu'] is not None else 0 + songzhuangu += 100000 * (xdxr['suogu']-1) if xdxr['suogu'] is not None else 0 records[date] = [ stockid, date, @@ -119,7 +119,7 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): if xdxr['songzhuangu'] is not None: records[date][2] = 10000 * xdxr['songzhuangu'] if xdxr['suogu'] is not None: - records[date][2] = 100000 * (xdxr['suogu']-1) + records[date][2] += 100000 * (xdxr['suogu']-1) if xdxr['peigu'] is not None: records[date][3] = 10000 * xdxr['peigu'] if xdxr['peigujia'] is not None: diff --git a/hikyuu/data/pytdx_weight_to_sqlite.py b/hikyuu/data/pytdx_weight_to_sqlite.py index 3ddc4aa9..fb8fb0a8 100644 --- a/hikyuu/data/pytdx_weight_to_sqlite.py +++ b/hikyuu/data/pytdx_weight_to_sqlite.py @@ -23,7 +23,7 @@ # SOFTWARE. from pytdx.hq import TDXParams -from .common_pytdx import to_pytdx_market +from hikyuu.data.common_pytdx import to_pytdx_market def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): @@ -78,7 +78,7 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): update_last_db_weight = True if xdxr['suogu'] is not None: # etf 扩股 - new_last_db_weight[3] = int(100000 * (xdxr['suogu']-1)) + new_last_db_weight[3] += int(100000 * (xdxr['suogu']-1)) update_last_db_weight = True if xdxr['peigu'] is not None: new_last_db_weight[4] = int(10000 * xdxr['peigu']) @@ -100,7 +100,7 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): continue if date not in records: songzhuangu = int(10000 * xdxr['songzhuangu']) if xdxr['songzhuangu'] is not None else 0 - songzhuangu = int(100000 * (xdxr['suogu']-1)) if xdxr['suogu'] is not None else 0 + songzhuangu += int(100000 * (xdxr['suogu']-1)) if xdxr['suogu'] is not None else 0 records[date] = [ stockid, date, @@ -118,7 +118,7 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): if xdxr['songzhuangu'] is not None: records[date][2] = int(10000 * xdxr['songzhuangu']) if xdxr['suogu'] is not None: - records[date][2] = int(100000 * (xdxr['suogu']-1)) + records[date][2] += int(100000 * (xdxr['suogu']-1)) if xdxr['peigu'] is not None: records[date][3] = int(10000 * xdxr['peigu']) if xdxr['peigujia'] is not None: @@ -170,12 +170,12 @@ if __name__ == '__main__': from hikyuu.data.common_sqlite3 import create_database starttime = time.time() - dest_dir = "c:\\stock" + dest_dir = "d:\\stock" tdx_server = '119.147.212.81' tdx_port = 7709 quotations = ['stock', 'fund'] - connect = sqlite3.connect(dest_dir + "\\hikyuu.db") + connect = sqlite3.connect(dest_dir + "\\stock.db") create_database(connect) from pytdx.hq import TdxHq_API, TDXParams diff --git a/hikyuu/data/sqlite_upgrade/0019.sql b/hikyuu/data/sqlite_upgrade/0019.sql new file mode 100644 index 00000000..29cbc05d --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0019.sql @@ -0,0 +1,4 @@ +BEGIN TRANSACTION; +DELETE FROM `stkWeight`; +UPDATE `version` set `version` = 19; +COMMIT; \ No newline at end of file From 2948418dd04c44ef48e7b0d481715e9e215235c2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 22 May 2024 17:50:50 +0800 Subject: [PATCH 307/601] =?UTF-8?q?hikyuu.interactive=20=E4=B8=AD=20blocka?= =?UTF-8?q?=20=E9=87=8C=E5=8E=BB=E9=99=A4=E5=88=9B=E4=B8=9A=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index d3093d87..016a3b1f 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -127,7 +127,7 @@ start_spot_agent(False) # ============================================================================== blocka = Block("A", "ALL") for s in sm: - if s.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_A_BJ, constant.STOCKTYPE_GEM): + if s.type in (constant.STOCKTYPE_A, constant.STOCKTYPE_A_BJ): blocka.add(s) zsbk_a = blocka From 2813e4976ae6ec8a506586a14f25f12dad828708 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 22 May 2024 18:06:37 +0800 Subject: [PATCH 308/601] Release 2.0.8 --- docs/source/release.rst | 10 ++++++++++ xmake.lua | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index fff98bc9..afd2d349 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,16 @@ 版本发布说明 ======================= +2.0.8 - 2024年5月22日 +------------------------- + +1. fixed 权息数据中转送股导入错误 +2. 增加 BLOCKSETNUM、INSUM 横向统计指标 +3. 本地数据导入时未导入历史财务信息 +4. Block 支持直接加入 list 格式的证券列表 +5. fixed 空 Block 序列化后无法加载 + + 2.0.7 - 2024年5月18日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 4d8c49e1..0e9ea3a0 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.7", {build = "%Y%m%d%H%M"}) +set_version("2.0.8", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From 14a47e46d6c4b235b791a381055691f9937a5b9a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 24 May 2024 01:32:11 +0800 Subject: [PATCH 309/601] fixed interactive realtime_update --- hikyuu/interactive.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 016a3b1f..d27b655e 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -278,7 +278,7 @@ def UpdateOneRealtimeRecord_from_sina(tmpstr): stock = sm[stockstr[0][-8:]] record = KRecord() - record.date = d + record.datetime = d record.open = open record.high = high record.low = low @@ -313,7 +313,7 @@ def UpdateOneRealtimeRecord_from_qq(tmpstr): stock = sm[stockstr[0][-8:]] record = KRecord() - record.date = d + record.datetime = d record.open = open record.high = high record.low = low @@ -429,7 +429,7 @@ def realtime_update_from_tushare(): from datetime import date d = date.today() - record.date = Datetime(d) + record.datetime = Datetime(d) stock.realtime_update(record) # 更新指数行情 @@ -462,7 +462,7 @@ def realtime_update_from_tushare(): ): from datetime import date d = date.today() - record.date = Datetime(d) + record.datetime = Datetime(d) stock.realtime_update(record) From d9332aef40ad01d0be98e8a416a1e1d5030e26a0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 25 May 2024 01:16:55 +0800 Subject: [PATCH 310/601] fixed python Block category attr --- hikyuu_pywrap/_Block.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_pywrap/_Block.cpp b/hikyuu_pywrap/_Block.cpp index ede1b461..4a3a6cd3 100644 --- a/hikyuu_pywrap/_Block.cpp +++ b/hikyuu_pywrap/_Block.cpp @@ -29,7 +29,7 @@ void export_Block(py::module& m) { .def("__str__", to_py_str) .def("__repr__", to_py_str) - .def_property("category", setCategory, getCategory, "板块所属分类") + .def_property("category", getCategory, setCategory, "板块所属分类") .def_property("name", getName, setName, "板块名称") .def_property("index_stock", &Block::getIndexStock, &Block::setIndexStock, py::return_value_policy::copy, "对应指数") From ee95cdec02056bcdb4631bed8878c39cdc106b7b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 25 May 2024 02:20:52 +0800 Subject: [PATCH 311/601] =?UTF-8?q?convert=5Fany=20Parameter=20c++->python?= =?UTF-8?q?=20=E6=94=AF=E6=8C=81=20KData=20=E5=92=8C=20Block?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/convert_any.h | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/hikyuu_pywrap/convert_any.h b/hikyuu_pywrap/convert_any.h index 5d4a5238..27759a0a 100644 --- a/hikyuu_pywrap/convert_any.h +++ b/hikyuu_pywrap/convert_any.h @@ -195,6 +195,31 @@ public: std::string s(boost::any_cast(x)); return Py_BuildValue("s", s.c_str()); + } else if (x.type() == typeid(KData)) { + const KData& k = boost::any_cast(x); + std::stringstream cmd; + if (k == Null()) { + cmd << "KData()"; + } else { + auto stk = k.getStock(); + auto query = k.getQuery(); + std::stringstream q_cmd; + if (query.queryType() == KQuery::INDEX) { + q_cmd << "Query(" << query.start() << "," << query.end() << ", Query." + << KQuery::getKTypeName(query.kType()) << ", Query." + << KQuery::getRecoverTypeName(query.recoverType()) << ")"; + } else { + q_cmd << "Query(Datetime(" << query.startDatetime() << "), Datetime(" + << query.endDatetime() << "), " << "Query." + << KQuery::getKTypeName(query.kType()) << "Query." + << KQuery::getRecoverTypeName(query.recoverType()) << ")"; + } + cmd << "KData(get_stock('" << stk.market_code() << "'), " << q_cmd.str() << ")"; + } + object o = eval(cmd.str()); + o.inc_ref(); + return o; + } else if (x.type() == typeid(Stock)) { const Stock& stk = boost::any_cast(x); std::stringstream cmd; @@ -210,13 +235,18 @@ public: } else if (x.type() == typeid(Block)) { const Block& blk = boost::any_cast(x); std::stringstream cmd; + object o; if (blk == Null()) { cmd << "Block()"; + o = eval(cmd.str()); + o.inc_ref(); } else { - cmd << "get_block('" << blk.category() << "','" << blk.name() << "')"; + cmd << "Block('" << blk.category() << "','" << blk.name() << "')"; + o = eval(cmd.str()); + o.inc_ref(); + Block out = o.cast(); + out.add(blk.getStockList()); } - object o = eval(cmd.str()); - o.inc_ref(); return o; } else if (x.type() == typeid(KQuery)) { From 95d73bd2f8682e1daba968510844d1bf4a3769ac Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 25 May 2024 02:35:23 +0800 Subject: [PATCH 312/601] fixed convert_any Parameter c++->python Query --- hikyuu_pywrap/convert_any.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/hikyuu_pywrap/convert_any.h b/hikyuu_pywrap/convert_any.h index 27759a0a..8c4b17cd 100644 --- a/hikyuu_pywrap/convert_any.h +++ b/hikyuu_pywrap/convert_any.h @@ -209,9 +209,9 @@ public: << KQuery::getKTypeName(query.kType()) << ", Query." << KQuery::getRecoverTypeName(query.recoverType()) << ")"; } else { - q_cmd << "Query(Datetime(" << query.startDatetime() << "), Datetime(" - << query.endDatetime() << "), " << "Query." - << KQuery::getKTypeName(query.kType()) << "Query." + q_cmd << "Query(Datetime('" << query.startDatetime() << "'), Datetime('" + << query.endDatetime() << "'), " << "Query." + << KQuery::getKTypeName(query.kType()) << ", Query." << KQuery::getRecoverTypeName(query.recoverType()) << ")"; } cmd << "KData(get_stock('" << stk.market_code() << "'), " << q_cmd.str() << ")"; @@ -257,9 +257,9 @@ public: << KQuery::getKTypeName(query.kType()) << ", Query." << KQuery::getRecoverTypeName(query.recoverType()) << ")"; } else { - cmd << "Query(Datetime(" << query.startDatetime() << "), Datetime(" - << query.endDatetime() << "), " << "Query." - << KQuery::getKTypeName(query.kType()) << "Query." + cmd << "Query(Datetime('" << query.startDatetime() << "'), Datetime('" + << query.endDatetime() << "'), " << "Query." + << KQuery::getKTypeName(query.kType()) << ", Query." << KQuery::getRecoverTypeName(query.recoverType()) << ")"; } object o = eval(cmd.str()); @@ -272,7 +272,7 @@ public: for (auto iter = price_list.begin(); iter != price_list.end(); ++iter) { o.append(*iter); } - o.inc_ref(); + // o.inc_ref(); return o; } else if (x.type() == typeid(DatetimeList)) { @@ -281,7 +281,7 @@ public: for (auto iter = date_list.begin(); iter != date_list.end(); ++iter) { o.append(*iter); } - o.inc_ref(); + // o.inc_ref(); return o; } From 9d05ef530850d48d3151ad7960885aa84aaf7fbc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 25 May 2024 03:12:26 +0800 Subject: [PATCH 313/601] =?UTF-8?q?fixed=20Parameter=3D=3D=20Block=20?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/Parameter.cpp | 126 +++++++++++----------- hikyuu_cpp/hikyuu/utilities/Parameter.h | 7 +- 2 files changed, 71 insertions(+), 62 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp index 1b7e1a95..41482694 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp @@ -154,75 +154,79 @@ HKU_API bool operator==(const Parameter& p1, const Parameter& p2) { auto iter1 = p1.begin(); auto iter2 = p2.begin(); for (; iter1 != p1.end() && iter2 != p2.end(); ++iter1, ++iter2) { - HKU_IF_RETURN(iter1->first != iter2->first || iter1->second.type() != iter2->second.type(), - false); - HKU_IF_RETURN(iter1->second.type() == typeid(int) && - boost::any_cast(iter1->second) != boost::any_cast(iter2->second), - false); - HKU_IF_RETURN( - iter1->second.type() == typeid(int64_t) && - boost::any_cast(iter1->second) != boost::any_cast(iter2->second), - false); - HKU_IF_RETURN( - iter1->second.type() == typeid(bool) && - boost::any_cast(iter1->second) != boost::any_cast(iter2->second), - false); - HKU_IF_RETURN( - iter1->second.type() == typeid(double) && - boost::any_cast(iter1->second) != boost::any_cast(iter2->second), - false); + try { + HKU_IF_RETURN( + iter1->first != iter2->first || iter1->second.type() != iter2->second.type(), false); + HKU_IF_RETURN( + iter1->second.type() == typeid(int) && + boost::any_cast(iter1->second) != boost::any_cast(iter2->second), + false); + HKU_IF_RETURN( + iter1->second.type() == typeid(int64_t) && + boost::any_cast(iter1->second) != boost::any_cast(iter2->second), + false); + HKU_IF_RETURN( + iter1->second.type() == typeid(bool) && + boost::any_cast(iter1->second) != boost::any_cast(iter2->second), + false); + HKU_IF_RETURN( + iter1->second.type() == typeid(double) && + boost::any_cast(iter1->second) != boost::any_cast(iter2->second), + false); - if (iter1->second.type() == typeid(string)) { - const string* x1 = boost::any_cast(&iter1->second); - const string* x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(*x1 != *x2, false); - } - - if (iter1->second.type() == typeid(Stock)) { - const Stock& x1 = boost::any_cast(&iter1->second); - const Stock& x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(x1 != x2, false); - } - - if (iter1->second.type() == typeid(Block)) { - const Block& x1 = boost::any_cast(&iter1->second); - const Block& x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(x1 == x2, true); - } - - if (iter1->second.type() == typeid(KQuery)) { - const KQuery* x1 = boost::any_cast(&iter1->second); - const KQuery* x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(*x1 != *x2, false); - } - - if (iter1->second.type() == typeid(KData)) { - const KData* x1 = boost::any_cast(&iter1->second); - const KData* x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(*x1 != *x2, false); - } - - if (iter1->second.type() == typeid(PriceList)) { - const PriceList* x1 = boost::any_cast(&iter1->second); - const PriceList* x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(x1->size() != x2->size(), false); - for (size_t i = 0, len = x1->size(); i < len; i++) { - HKU_IF_RETURN((*x1)[i] != (*x2)[i], false); + if (iter1->second.type() == typeid(string)) { + const string* x1 = boost::any_cast(&iter1->second); + const string* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(*x1 != *x2, false); } - } - if (iter1->second.type() == typeid(DatetimeList)) { - const DatetimeList* x1 = boost::any_cast(&iter1->second); - const DatetimeList* x2 = boost::any_cast(&iter2->second); - HKU_IF_RETURN(x1->size() != x2->size(), false); - for (size_t i = 0, len = x1->size(); i < len; i++) { - HKU_IF_RETURN((*x1)[i] != (*x2)[i], false); + if (iter1->second.type() == typeid(Stock)) { + const Stock* x1 = boost::any_cast(&iter1->second); + const Stock* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(*x1 != *x2, false); } + + if (iter1->second.type() == typeid(Block)) { + const Block* x1 = boost::any_cast(&iter1->second); + const Block* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(*x1 != *x2, false); + } + + if (iter1->second.type() == typeid(KQuery)) { + const KQuery* x1 = boost::any_cast(&iter1->second); + const KQuery* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(*x1 != *x2, false); + } + + if (iter1->second.type() == typeid(KData)) { + const KData* x1 = boost::any_cast(&iter1->second); + const KData* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(*x1 != *x2, false); + } + + if (iter1->second.type() == typeid(PriceList)) { + const PriceList* x1 = boost::any_cast(&iter1->second); + const PriceList* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(x1->size() != x2->size(), false); + for (size_t i = 0, len = x1->size(); i < len; i++) { + HKU_IF_RETURN((*x1)[i] != (*x2)[i], false); + } + } + + if (iter1->second.type() == typeid(DatetimeList)) { + const DatetimeList* x1 = boost::any_cast(&iter1->second); + const DatetimeList* x2 = boost::any_cast(&iter2->second); + HKU_IF_RETURN(x1->size() != x2->size(), false); + for (size_t i = 0, len = x1->size(); i < len; i++) { + HKU_IF_RETURN((*x1)[i] != (*x2)[i], false); + } + } + } catch (...) { + HKU_ERROR("failed conversion iter1 key: {}, iter2 key: {}", iter1->first, iter2->first); } } return true; - // return p1.size() == p2.size() && p1.getNameValueList() == p2.getNameValueList(); } HKU_API bool operator!=(const Parameter& p1, const Parameter& p2) { diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index c8284ce3..8b8fb569 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -434,7 +434,12 @@ ValueType Parameter::get(const string& name) const { if (iter == m_params.end()) { throw std::out_of_range("out_of_range in Parameter::get : " + name); } - return boost::any_cast(iter->second); + // return boost::any_cast(iter->second); + try { + return boost::any_cast(iter->second); + } catch (...) { + throw std::runtime_error("failed conversion param: " + name); + } } template From 6e3c17e83399f78ea4afbb0808f2b3cb25c1cfad Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 26 May 2024 15:38:03 +0800 Subject: [PATCH 314/601] =?UTF-8?q?Porfolio=E6=B7=BB=E5=8A=A0=E5=AF=B9?= =?UTF-8?q?=E5=BB=B6=E8=BF=9F=E7=B3=BB=E7=BB=9F=E5=8F=AF=E8=83=BD=E5=87=BA?= =?UTF-8?q?=E7=8E=B0=E7=9A=84=E6=9C=AA=E6=9D=A5=E4=BF=A1=E5=8F=B7=E4=BF=9D?= =?UTF-8?q?=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 6f4fbab3..25227021 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -311,6 +311,13 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool // 从选股策略获取选中的系统列表 m_tmp_selected_list = m_se->getSelected(date); + // 如果选中的系统不在已有列表中, 则先清除其延迟操作,防止在调仓日出现未来信号 + for (auto& sys : m_tmp_selected_list) { + if (m_running_sys_set.find(sys.sys) == m_running_sys_set.end()) { + sys.sys->clearDelayRequest(); + } + } + 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); From 34326f8bf44ba7610b8b4a68416dd50fc2bb6162 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 27 May 2024 16:27:23 +0800 Subject: [PATCH 315/601] update --- .../trade_sys/selector/imp/OperatorAndSelector.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h new file mode 100644 index 00000000..25175e2f --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "../SelectorBase.h" + +namespace hku {} \ No newline at end of file From b8662bf90865cf1edf274e754ccef5be37423329 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 27 May 2024 16:43:39 +0800 Subject: [PATCH 316/601] Release 2.0.9 --- docs/source/release.rst | 7 +++++++ xmake.lua | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index afd2d349..b8d5dafe 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,13 @@ 版本发布说明 ======================= +2.0.9 - 2024年5月27日 +------------------------- + +1. fixed Parameter 中对 Block 的支持,造成 INSUM 无法参与其他指标的计算 +2. Porfolio 添加对延迟系统可能出现的未来信号保护 + + 2.0.8 - 2024年5月22日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 0e9ea3a0..f37415bc 100644 --- a/xmake.lua +++ b/xmake.lua @@ -96,7 +96,7 @@ add_rules("mode.debug", "mode.release") if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version -set_version("2.0.8", {build = "%Y%m%d%H%M"}) +set_version("2.0.9", {build = "%Y%m%d%H%M"}) local level = get_config("log_level") if is_mode("debug") then From d506ffb9b0829734f4497e20c5596909937749a0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 27 May 2024 17:21:04 +0800 Subject: [PATCH 317/601] update --- xmake.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index f37415bc..5e9dd5e6 100644 --- a/xmake.lua +++ b/xmake.lua @@ -168,7 +168,8 @@ if is_plat("windows") or (is_plat("linux", "cross") and is_arch("aarch64", "arm6 mysql_version = "8.0.21" end -add_repositories("hikyuu-repo https://github.com/fasiondog/hikyuu_extern_libs.git") +-- add_repositories("hikyuu-repo https://github.com/fasiondog/hikyuu_extern_libs.git") +add_repositories("hikyuu-repo https://gitee.com/fasiondog/hikyuu_extern_libs.git") if is_plat("windows") then if get_config("hdf5") then if is_mode("release") then From 33d3c99d28ca8fea105ed90821b7f7545c19f53a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 27 May 2024 17:25:21 +0800 Subject: [PATCH 318/601] update --- xmake.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xmake.lua b/xmake.lua index 5e9dd5e6..4db17dcd 100644 --- a/xmake.lua +++ b/xmake.lua @@ -168,8 +168,8 @@ if is_plat("windows") or (is_plat("linux", "cross") and is_arch("aarch64", "arm6 mysql_version = "8.0.21" end --- add_repositories("hikyuu-repo https://github.com/fasiondog/hikyuu_extern_libs.git") -add_repositories("hikyuu-repo https://gitee.com/fasiondog/hikyuu_extern_libs.git") +add_repositories("hikyuu-repo https://github.com/fasiondog/hikyuu_extern_libs.git") +-- add_repositories("hikyuu-repo https://gitee.com/fasiondog/hikyuu_extern_libs.git") if is_plat("windows") then if get_config("hdf5") then if is_mode("release") then From 48892babffde6a76ec63626f90f23f4095f8f3e2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 27 May 2024 23:07:26 +0800 Subject: [PATCH 319/601] OperatorAddSelector(continue) --- .../hikyuu/trade_sys/selector/build_in.h | 1 + .../SE_Operator.h} | 6 +- .../selector/imp/MultiFactorSelector.h | 4 +- .../selector/imp/OperatorAddSelector.cpp | 130 ++++++++++++++++++ .../selector/imp/OperatorAddSelector.h | 44 ++++++ .../trade_sys/selector/test_SE_Operator.cpp | 42 ++++++ 6 files changed, 224 insertions(+), 3 deletions(-) rename hikyuu_cpp/hikyuu/trade_sys/selector/{imp/OperatorAndSelector.h => crt/SE_Operator.h} (59%) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h b/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h index dea8a63c..bfac0787 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/build_in.h @@ -11,6 +11,7 @@ #include "crt/SE_Fixed.h" #include "crt/SE_MultiFactor.h" +#include "crt/SE_Operator.h" #include "crt/SE_Signal.h" #endif /* TRADE_SYS_SELECTOR_BUILD_IN_H_ */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h similarity index 59% rename from hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h rename to hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h index 25175e2f..608df048 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAndSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h @@ -9,4 +9,8 @@ #include "../SelectorBase.h" -namespace hku {} \ No newline at end of file +namespace hku { + +HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2); + +} \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h index 1df475df..70d24233 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h @@ -30,8 +30,8 @@ private: unordered_map m_stk_sys_dict; //============================================ -// 序列化支持 -//============================================ + // 序列化支持 + //============================================ #if HKU_SUPPORT_SERIALIZATION friend class boost::serialization::access; template diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp new file mode 100644 index 00000000..622ec9d7 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -0,0 +1,130 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorAddSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorAddSelector) +#endif + +namespace hku { + +OperatorAddSelector::OperatorAddSelector() : SelectorBase("SE_Add") {} + +OperatorAddSelector::OperatorAddSelector(const SelectorPtr& se1, const SelectorPtr& se2) +: SelectorBase("SE_Add") { + if (se1) { + m_se1 = se1->clone(); + m_se1->removeAll(); + } + if (se2) { + m_se2 = se2->clone(); + m_se2->removeAll(); + } +} + +OperatorAddSelector::~OperatorAddSelector() {} + +void OperatorAddSelector::_reset() { + if (m_se1) { + m_se1->reset(); + } + if (m_se2) { + m_se2->reset(); + } +} + +bool OperatorAddSelector::isMatchAF(const AFPtr& af) { + return true; +} + +SelectorPtr OperatorAddSelector::_clone() { + OperatorAddSelector* p = new OperatorAddSelector(); + if (m_se1) { + p->m_se1 = m_se1->clone(); + } + if (m_se2) { + p->m_se2 = m_se2->clone(); + } + return SelectorPtr(p); +} + +void OperatorAddSelector::_calculate() { + if (m_se1) { + m_se1->calculate(m_real_sys_list, m_query); + } + if (m_se2) { + m_se2->calculate(m_real_sys_list, m_query); + } +} + +SystemWeightList OperatorAddSelector::getSelected(Datetime date) { + SystemWeightList ret; + SystemWeightList sws1, sws2; + if (m_se1) { + sws1 = m_se1->getSelected(date); + } + if (m_se2) { + sws2 = m_se2->getSelected(date); + } + + if (sws1.empty()) { + ret = std::move(sws2); + return ret; + } + + if (sws2.empty()) { + ret = std::move(sws1); + return ret; + } + + unordered_map sw_dict1; + for (auto& sw : sws1) { + sw_dict1[sw.sys.get()] = &sw; + } + + SystemWeight tmp; + unordered_map sw_dict2; + unordered_map::iterator iter; + for (auto& sw : sws2) { + iter = sw_dict1.find(sw.sys.get()); + tmp.sys = sw.sys; + if (iter != sw_dict1.end()) { + tmp.weight = sw.weight + iter->second->weight; + } else { + tmp.weight = sw.weight; + } + auto& back = ret.emplace_back(std::move(tmp)); + sw_dict2[back.sys.get()] = &back; + } + + for (auto& sw : sws1) { + iter = sw_dict2.find(sw.sys.get()); + if (iter == sw_dict2.end()) { + ret.emplace_back(std::move(sw)); + } + } + + std::sort(ret.begin(), ret.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; + }); + + return ret; +} + +HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2) { + return make_shared(se1, se2); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h new file mode 100644 index 00000000..61736d07 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "../SelectorBase.h" + +namespace hku { + +class HKU_API OperatorAddSelector : public SelectorBase { +public: + OperatorAddSelector(); + OperatorAddSelector(const SelectorPtr& se1, const SelectorPtr& se2); + virtual ~OperatorAddSelector(); + + virtual void _reset() override; + virtual SelectorPtr _clone() override; + virtual bool isMatchAF(const AFPtr& af) override; + virtual void _calculate() override; + virtual SystemWeightList getSelected(Datetime date) override; + +private: + SelectorPtr m_se1; + SelectorPtr m_se2; + + //============================================ + // 序列化支持 + //============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_se1); + ar& BOOST_SERIALIZATION_NVP(m_se2); + } +#endif +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp new file mode 100644 index 00000000..844e1601 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "../../test_config.h" +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_Selector_operator test_Selector_operator + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_SE_Add") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); +} + +/** @} */ \ No newline at end of file From 9cafcaafafb249c69fd1fa6b2d5050052acb6f08 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 27 May 2024 23:42:50 +0800 Subject: [PATCH 320/601] update --- hikyuu_pywrap/trade_sys/_Selector.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index b5eddf2f..dc5ce7c9 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -143,6 +143,9 @@ void export_Selector(py::module& m) { :return: 选取的系统实例列表 :rtype: SystemList)") + .def("__add__", + [](const SelectorPtr& self, const SelectorPtr& other) { return self + other; }) + DEF_PICKLE(SEPtr); m.def("SE_Fixed", py::overload_cast<>(SE_Fixed)); From d7651a8c2b4bac88fbbb921a69292e1990512ba6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 28 May 2024 00:40:16 +0800 Subject: [PATCH 321/601] =?UTF-8?q?=E4=BC=98=E5=8C=96MM=E5=A4=84=E7=90=86?= =?UTF-8?q?=E6=9C=80=E5=A4=A7=E4=B9=B0=E5=85=A5=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp index fb7c312f..5e54106b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp @@ -139,13 +139,14 @@ double MoneyManagerBase::getBuyNumber(const Datetime& datetime, const Stock& sto m_tm->checkin(datetime, roundUp(money - cash, precision)); } } else { + int precision = m_tm->getParam("precision"); CostRecord cost = m_tm->getBuyCost(datetime, stock, price, n); - price_t need_cash = n * price + cost.total; + price_t need_cash = roundUp(n * price + cost.total, precision); price_t current_cash = m_tm->cash(datetime, m_query.kType()); while (n > min_trade && need_cash > current_cash) { n = n - min_trade; cost = m_tm->getBuyCost(datetime, stock, price, n); - need_cash = n * price + cost.total; + need_cash = roundUp(n * price + cost.total, precision); } n = need_cash > current_cash ? 0 : n; } From 1dd79744cb73c5bc532a79fb0b8978b5bbe96cba Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 28 May 2024 08:31:16 +0800 Subject: [PATCH 322/601] update --- .../trade_sys/selector/crt/SE_Operator.h | 8 ++- .../selector/imp/OperatorAddValueSelector.cpp | 68 +++++++++++++++++++ .../selector/imp/OperatorAddValueSelector.h | 44 ++++++++++++ .../trade_sys/selector/test_SE_Operator.cpp | 62 +++++++++++++++++ hikyuu_pywrap/trade_sys/_Selector.cpp | 3 + 5 files changed, 184 insertions(+), 1 deletion(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h index 608df048..cbd13bdf 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h @@ -13,4 +13,10 @@ namespace hku { HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2); -} \ No newline at end of file +HKU_API SelectorPtr operator+(const SelectorPtr& se, double value); + +inline SelectorPtr operator+(double value, const SelectorPtr& se) { + return se + value; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp new file mode 100644 index 00000000..9ac01c6d --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorAddValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorAddValueSelector) +#endif + +namespace hku { + +OperatorAddValueSelector::OperatorAddValueSelector() : SelectorBase("SE_Add") {} + +OperatorAddValueSelector::OperatorAddValueSelector(const SelectorPtr& se, double value) +: SelectorBase("SE_Add"), m_value(value) { + if (se) { + m_se = se->clone(); + m_se->removeAll(); + } +} + +OperatorAddValueSelector::~OperatorAddValueSelector() {} + +void OperatorAddValueSelector::_reset() { + if (m_se) { + m_se->reset(); + } +} + +bool OperatorAddValueSelector::isMatchAF(const AFPtr& af) { + return true; +} + +SelectorPtr OperatorAddValueSelector::_clone() { + OperatorAddValueSelector* p = new OperatorAddValueSelector(); + if (m_se) { + p->m_se = m_se->clone(); + } + return SelectorPtr(p); +} + +void OperatorAddValueSelector::_calculate() { + if (m_se) { + m_se->calculate(m_real_sys_list, m_query); + } +} + +SystemWeightList OperatorAddValueSelector::getSelected(Datetime date) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se, ret); + + ret = m_se->getSelected(date); + for (auto& sw : ret) { + sw.weight += m_value; + } + + return ret; +} + +HKU_API SelectorPtr operator+(const SelectorPtr& se, double value) { + return make_shared(se, value); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h new file mode 100644 index 00000000..1db34bf6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "../SelectorBase.h" + +namespace hku { + +class HKU_API OperatorAddValueSelector : public SelectorBase { +public: + OperatorAddValueSelector(); + OperatorAddValueSelector(const SelectorPtr& se1, double value); + virtual ~OperatorAddValueSelector(); + + virtual void _reset() override; + virtual SelectorPtr _clone() override; + virtual bool isMatchAF(const AFPtr& af) override; + virtual void _calculate() override; + virtual SystemWeightList getSelected(Datetime date) override; + +private: + SelectorPtr m_se; + double m_value{0.0}; + + //============================================ + // 序列化支持 + //============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_se); + ar& BOOST_SERIALIZATION_NVP(m_value); + } +#endif +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index 844e1601..a050467a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -37,6 +37,68 @@ TEST_CASE("test_SE_Add") { /** @arg 试图加入一个空的系统策略原型 */ CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + sys->setSG(sg); + sys->setMM(mm); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 2.0); + CHECK_EQ(result[1].weight, 2.0); + CHECK_EQ(result[2].weight, 2.0); + + se = se1 + SEPtr(); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 1.0); + CHECK_EQ(result[1].weight, 1.0); + CHECK_EQ(result[2].weight, 1.0); + + se = se1 + 2.0; + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 3.0); + CHECK_EQ(result[1].weight, 3.0); + CHECK_EQ(result[2].weight, 3.0); + + se = 3.0 + se1; + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 4.0); + CHECK_EQ(result[1].weight, 4.0); + CHECK_EQ(result[2].weight, 4.0); } /** @} */ \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index dc5ce7c9..c7fab0a3 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -146,6 +146,9 @@ void export_Selector(py::module& m) { .def("__add__", [](const SelectorPtr& self, const SelectorPtr& other) { return self + other; }) + .def("__add__", [](const SelectorPtr& self, double other) { return self + other; }) + .def("__radd__", [](const SelectorPtr& self, double other) { return self + other; }) + DEF_PICKLE(SEPtr); m.def("SE_Fixed", py::overload_cast<>(SE_Fixed)); From 534a642eb9fdc155dce8d42e5e70a600dab1e042 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 28 May 2024 08:32:31 +0800 Subject: [PATCH 323/601] =?UTF-8?q?=E8=BF=98=E5=8E=9FMM=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp index 5e54106b..fb7c312f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.cpp @@ -139,14 +139,13 @@ double MoneyManagerBase::getBuyNumber(const Datetime& datetime, const Stock& sto m_tm->checkin(datetime, roundUp(money - cash, precision)); } } else { - int precision = m_tm->getParam("precision"); CostRecord cost = m_tm->getBuyCost(datetime, stock, price, n); - price_t need_cash = roundUp(n * price + cost.total, precision); + price_t need_cash = n * price + cost.total; price_t current_cash = m_tm->cash(datetime, m_query.kType()); while (n > min_trade && need_cash > current_cash) { n = n - min_trade; cost = m_tm->getBuyCost(datetime, stock, price, n); - need_cash = roundUp(n * price + cost.total, precision); + need_cash = n * price + cost.total; } n = need_cash > current_cash ? 0 : n; } From 21421acbcf0eaeb82f89ae1eddecf3bd29e2de1b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 28 May 2024 10:58:43 +0800 Subject: [PATCH 324/601] update --- .../trade_sys/selector/test_SE_Operator.cpp | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index a050467a..372bbdd1 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -28,6 +28,9 @@ TEST_CASE("test_SE_Add") { StockManager& sm = StockManager::instance(); SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + SEPtr se1 = SE_Fixed(); SEPtr se2 = SE_Fixed(); SEPtr se = se1 + se2; @@ -38,10 +41,10 @@ TEST_CASE("test_SE_Add") { /** @arg 试图加入一个空的系统策略原型 */ CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); - SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); - MMPtr mm = MM_FixedCount(100); sys->setSG(sg); sys->setMM(mm); + + se = SEPtr() + SEPtr(); se->addStock(sm["sh600000"], sys); se->addStock(sm["sz000001"], sys); se->addStock(sm["sz000002"], sys); @@ -50,6 +53,19 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(proto_sys_list.size(), 3); se->calculate(proto_sys_list, KQuery(-20)); auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + se = se1 + se2; + sys->setSG(sg); + sys->setMM(mm); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); CHECK_EQ(result.size(), 3); CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); @@ -58,6 +74,14 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[1].weight, 2.0); CHECK_EQ(result[2].weight, 2.0); + se = 3.0 + SEPtr(); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + se = se1 + SEPtr(); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); From 09a80680ff4f892b3635f68f73dc55b22da0b229 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 28 May 2024 18:31:37 +0800 Subject: [PATCH 325/601] update --- .../hikyuu/trade_sys/selector/SelectorBase.cpp | 2 ++ .../hikyuu/trade_sys/selector/SelectorBase.h | 3 +++ .../selector/imp/OperatorAddSelector.cpp | 17 +++++++++++++++-- .../selector/imp/OperatorAddSelector.h | 1 + .../selector/imp/OperatorAddValueSelector.cpp | 9 ++++++++- .../selector/imp/OperatorAddValueSelector.h | 1 + hikyuu_pywrap/trade_sys/_Selector.cpp | 4 ++++ 7 files changed, 34 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 8aef8d67..2b216813 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -140,6 +140,8 @@ void SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { "Scenarios that depend on prototype systems need to specify a TM!"); } + _addStock(stock, protoSys); + auto proto = protoSys; proto->forceResetAll(); SYSPtr sys = proto->clone(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 1531359c..f55f0092 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -101,6 +101,9 @@ public: virtual bool isMatchAF(const AFPtr& af) = 0; + /** 用于逻辑运算的子类中添加原型系统,一般不需要子类实现 */ + virtual void _addStock(const Stock& stock, const SystemPtr& protoSys) {} + /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ void calculate(const SystemList& pf_realSysList, const KQuery& query); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp index 622ec9d7..159ce3f9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -19,11 +19,15 @@ OperatorAddSelector::OperatorAddSelector(const SelectorPtr& se1, const SelectorP : SelectorBase("SE_Add") { if (se1) { m_se1 = se1->clone(); - m_se1->removeAll(); + auto sys_list = m_se1->getProtoSystemList(); + m_pro_sys_list = std::move(sys_list); } if (se2) { m_se2 = se2->clone(); - m_se2->removeAll(); + auto sys_list = m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + m_pro_sys_list.emplace_back(std::move(sys)); + } } } @@ -42,6 +46,15 @@ bool OperatorAddSelector::isMatchAF(const AFPtr& af) { return true; } +void OperatorAddSelector::_addStock(const Stock& stock, const SystemPtr& protoSys) { + if (m_se1) { + m_se1->addStock(stock, protoSys); + } + if (m_se2) { + m_se2->addStock(stock, protoSys); + } +} + SelectorPtr OperatorAddSelector::_clone() { OperatorAddSelector* p = new OperatorAddSelector(); if (m_se1) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h index 61736d07..a3755878 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h @@ -19,6 +19,7 @@ public: virtual void _reset() override; virtual SelectorPtr _clone() override; + virtual void _addStock(const Stock& stock, const SystemPtr& protoSys) override; virtual bool isMatchAF(const AFPtr& af) override; virtual void _calculate() override; virtual SystemWeightList getSelected(Datetime date) override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp index 9ac01c6d..fcb35179 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp @@ -19,7 +19,7 @@ OperatorAddValueSelector::OperatorAddValueSelector(const SelectorPtr& se, double : SelectorBase("SE_Add"), m_value(value) { if (se) { m_se = se->clone(); - m_se->removeAll(); + m_pro_sys_list = m_se->getProtoSystemList(); } } @@ -31,6 +31,12 @@ void OperatorAddValueSelector::_reset() { } } +void OperatorAddValueSelector::_addStock(const Stock& stock, const SystemPtr& protoSys) { + if (m_se) { + m_se->addStock(stock, protoSys); + } +} + bool OperatorAddValueSelector::isMatchAF(const AFPtr& af) { return true; } @@ -40,6 +46,7 @@ SelectorPtr OperatorAddValueSelector::_clone() { if (m_se) { p->m_se = m_se->clone(); } + p->m_value = m_value; return SelectorPtr(p); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h index 1db34bf6..fe0ffae8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h @@ -19,6 +19,7 @@ public: virtual void _reset() override; virtual SelectorPtr _clone() override; + virtual void _addStock(const Stock& stock, const SystemPtr& protoSys) override; virtual bool isMatchAF(const AFPtr& af) override; virtual void _calculate() override; virtual SystemWeightList getSelected(Datetime date) override; diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index c7fab0a3..e8d86d83 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -26,6 +26,10 @@ public: PYBIND11_OVERLOAD_PURE(void, SelectorBase, _calculate, ); } + void _addStock(const Stock& stock, const SystemPtr& protoSys) override { + PYBIND11_OVERLOAD(void, SelectorBase, _addStock, stock, protoSys); + } + SystemWeightList getSelected(Datetime date) override { // PYBIND11_OVERLOAD_PURE_NAME(SystemWeightList, SelectorBase, "get_selected", getSelected, // date); From de1c7e1eaaca4509a6ae897e8e7208f2293048df Mon Sep 17 00:00:00 2001 From: Jet <344148042@qq.com> Date: Tue, 28 May 2024 18:32:18 +0800 Subject: [PATCH 326/601] =?UTF-8?q?fix:=20=E9=80=9A=E8=BE=BE=E4=BF=A1?= =?UTF-8?q?=E5=AF=BC=E5=85=A5=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E7=9A=84=E8=BF=9B=E5=BA=A6=E9=80=9A=E7=9F=A5=E6=B6=88?= =?UTF-8?q?=E6=81=AFtypo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/data/UseTdxImportToH5Thread.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/gui/data/UseTdxImportToH5Thread.py b/hikyuu/gui/data/UseTdxImportToH5Thread.py index 5c147e5f..3498164f 100644 --- a/hikyuu/gui/data/UseTdxImportToH5Thread.py +++ b/hikyuu/gui/data/UseTdxImportToH5Thread.py @@ -152,7 +152,7 @@ class UseTdxImportToH5Thread(QThread): hdf5_import_progress[market][ktype] = progress current_progress = (hdf5_import_progress['SH'][ktype] + hdf5_import_progress['SZ'][ktype]) // 2 self.send_message(['IMPORT_KDATA', ktype, current_progress]) - elif taskname == 'IMPORT_KDATA': + elif taskname == 'IMPORT_FINANCE': self.send_message([taskname, progress]) else: self.logger.error("Unknow task: {}".format(taskname)) From 331475333c2184aa9af606f3a7139fd2d60f2c18 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 29 May 2024 13:52:15 +0800 Subject: [PATCH 327/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 349f8d8c..ce61ed7c 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 - - 百万级别 K 线数据,2~3秒内完成 A 股全市场回测 + - AMD 7950x 实测:A股全市场(1913万日K线)计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒 - C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 From accd236fe1bf64dc9231590b74a21b63aac25165 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 29 May 2024 13:57:17 +0800 Subject: [PATCH 328/601] update --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ce61ed7c..b4d1fa04 100644 --- a/README.rst +++ b/README.rst @@ -69,7 +69,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 - - AMD 7950x 实测:A股全市场(1913万日K线)计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒 + - AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒 - C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 From c4debd793282c6c3a9925397e579b5dfc9ba5985 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 29 May 2024 15:46:32 +0800 Subject: [PATCH 329/601] update reademe --- README.rst | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/README.rst b/README.rst index b4d1fa04..939d732e 100644 --- a/README.rst +++ b/README.rst @@ -1,5 +1,5 @@ -.. image:: http://fasiondog.cn/wp-content/uploads/2024/05/00000_title-1.png - :target: http://fasiondog.gitee.io/hikyuu +.. image:: https://img2.imgtp.com/2024/05/29/mSBrbO7R.png + :target: https://gitee.com/fasiondog/hikyuu :align: left :alt: Hikyuu @@ -31,7 +31,6 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 详细文档: ``_ -如果上述网站无法访问,请戳这里: ``_ 感谢网友提供的 Hikyuu Ubuntu虚拟机环境, 百度网盘下载(提取码: ht8j): ``_ @@ -53,7 +52,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm) sys.run(sm['sz000001'], Query(-150)) -.. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10000-overview.png +.. figure:: https://img2.imgtp.com/2024/05/29/xTEvXesP.png :width: 600px 完整示例参见:``_ @@ -64,7 +63,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下: - .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10002-function-arc.png + .. figure:: hhttps://img2.imgtp.com/2024/05/29/9SrY9vI1.png :width: 600px - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 @@ -93,22 +92,22 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 **加入知识星球** 更多示例与策略部件的及时分享(您的加入将视为对项目的捐赠) - .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png + .. figure:: https://img2.imgtp.com/2024/05/29/3sEP6Re0.png **项目交流和问题答复将转移至知识星球-【Hikyuu量化】。** - 关注公众号: - .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/weixin_gongzhonghao.jpg + .. figure:: https://img2.imgtp.com/2024/05/29/1NQztICj.jpg - 加入微信群(请注明“加入hikyuu”): - .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/weixin_group.jpg + .. figure:: https://img2.imgtp.com/2024/05/29/HD0dAgbn.jpg - QQ交流群(逐渐废弃):114910869, 或扫码加入: - .. figure:: http://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png + .. figure:: https://img2.imgtp.com/2024/05/29/xAH2PesY.png From b99bc6dc95b1cce0def8cd046bef05366004dabf Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 29 May 2024 15:48:22 +0800 Subject: [PATCH 330/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 939d732e..3663096c 100644 --- a/README.rst +++ b/README.rst @@ -63,7 +63,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下: - .. figure:: hhttps://img2.imgtp.com/2024/05/29/9SrY9vI1.png + .. figure:: https://img2.imgtp.com/2024/05/29/9SrY9vI1.png :width: 600px - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 From 4d8a3b63a2e1a05437b10db798cdea964d40ed0c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 29 May 2024 18:28:26 +0800 Subject: [PATCH 331/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 3663096c..ab8b36dd 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 - - AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒 + - AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒,详见: ``_ - C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 From 2a9b233cdaaad927f31fc2bee808751825590f6b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 29 May 2024 18:32:34 +0800 Subject: [PATCH 332/601] update readme --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index ab8b36dd..5ac522e9 100644 --- a/README.rst +++ b/README.rst @@ -68,7 +68,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 - - AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒,详见: ``_ + - AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒,详见: `性能实测 `_ - C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 From 43b03cb0c530acfee2b392933589539478012f50 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 30 May 2024 15:03:51 +0800 Subject: [PATCH 333/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=B3=84=E9=9C=B2?= =?UTF-8?q?=E6=A3=80=E6=B5=8B=EF=BC=9B=E4=BC=98=E5=8C=96clang=E7=BC=96?= =?UTF-8?q?=E8=AF=91=E5=91=8A=E8=AD=A6=EF=BC=9B=E4=BC=98=E5=8C=96shared=5F?= =?UTF-8?q?ptr=E5=88=9B=E5=BB=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Block.cpp | 6 ++-- hikyuu_cpp/hikyuu/Block.h | 4 +-- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 2 +- hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp | 4 +-- hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp | 10 ++++-- hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp | 4 +-- hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp | 4 +-- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 6 ++-- .../hikyuu/trade_manage/Performance.cpp | 1 - .../hikyuu/trade_manage/TradeManager.cpp | 31 ++++++++----------- .../hikyuu/trade_manage/crt/TC_TestStub.cpp | 2 +- hikyuu_cpp/hikyuu/trade_manage/crt/crtTM.cpp | 2 +- .../trade_manage/imp/FixedA2015TradeCost.cpp | 6 ++-- .../trade_manage/imp/FixedA2017TradeCost.cpp | 6 ++-- .../trade_manage/imp/FixedATradeCost.cpp | 6 ++-- .../hikyuu/trade_manage/imp/TradeCostStub.cpp | 2 +- .../hikyuu/trade_manage/imp/ZeroTradeCost.cpp | 4 +-- .../allocatefunds/AllocateFundsBase.h | 10 +++--- .../trade_sys/condition/ConditionBase.h | 2 +- .../trade_sys/condition/imp/AddCondition.cpp | 4 +-- .../trade_sys/condition/imp/AndCondition.cpp | 4 +-- .../trade_sys/condition/imp/DivCondition.cpp | 4 +-- .../condition/imp/MultiCondition.cpp | 4 +-- .../trade_sys/condition/imp/OrCondition.cpp | 4 +-- .../trade_sys/condition/imp/SubCondition.cpp | 4 +-- .../trade_sys/environment/EnvironmentBase.h | 10 +++--- .../environment/imp/BoolEnvironment.cpp | 4 +-- .../environment/imp/TwoLineEnvironment.cpp | 8 ++--- .../trade_sys/moneymanager/MoneyManagerBase.h | 2 +- .../imp/FixedCapitalMoneyManager.cpp | 4 +-- .../imp/FixedCountMoneyManager.cpp | 4 +-- .../imp/FixedPercentMoneyManager.cpp | 4 +-- .../imp/FixedRiskMoneyManager.cpp | 4 +-- .../imp/FixedUnitsMoneyManager.cpp | 4 +-- .../moneymanager/imp/NotMoneyManager.cpp | 3 +- .../imp/WilliamsFixedRiskMoneyManager.cpp | 4 +-- .../imp/EqualWeightMultiFactor.cpp | 2 +- .../trade_sys/profitgoal/ProfitGoalBase.h | 2 +- .../profitgoal/imp/NoGoalProfitGoal.cpp | 2 +- .../hikyuu/trade_sys/selector/SelectorBase.h | 2 +- .../selector/imp/MultiFactorSelector.cpp | 4 +-- .../hikyuu/trade_sys/signal/SignalBase.h | 10 +++--- .../trade_sys/signal/imp/AllwaysBuySignal.cpp | 2 +- .../trade_sys/signal/imp/BandSignal.cpp | 6 ++-- .../trade_sys/signal/imp/BandSignal2.cpp | 6 ++-- .../trade_sys/signal/imp/BoolSignal.cpp | 6 ++-- .../trade_sys/signal/imp/CrossGoldSignal.cpp | 6 ++-- .../trade_sys/signal/imp/CrossSignal.cpp | 6 ++-- .../trade_sys/signal/imp/SingleSignal.cpp | 8 ++--- .../trade_sys/signal/imp/SingleSignal2.cpp | 8 ++--- .../hikyuu/trade_sys/slippage/SlippageBase.h | 2 +- .../slippage/imp/FixedPercentSlippage.cpp | 4 +-- .../slippage/imp/FixedValueSlippage.cpp | 4 +-- .../hikyuu/trade_sys/stoploss/StoplossBase.h | 12 +++---- .../trade_sys/stoploss/crt/ST_Saftyloss.cpp | 4 +-- .../stoploss/imp/FixedPercentStoploss.cpp | 2 +- .../stoploss/imp/IndicatorStoploss.cpp | 6 ++-- hikyuu_cpp/hikyuu/utilities/IniParser.cpp | 4 +-- hikyuu_cpp/hikyuu/utilities/Parameter.cpp | 1 + .../utilities/thread/MQStealThreadPool.h | 4 +-- .../hikyuu/utilities/thread/MQThreadPool.h | 4 +-- .../hikyuu/utilities/thread/StealThreadPool.h | 4 +-- .../hikyuu/utilities/thread/ThreadPool.h | 4 +-- .../unit_test/hikyuu/indicator/test_IF.cpp | 2 +- .../hikyuu/trade_sys/signal/test_Signal.cpp | 4 +-- hikyuu_pywrap/_analysis.cpp | 6 ++-- hikyuu_pywrap/data_driver/_BlockInfoDriver.h | 4 +-- hikyuu_pywrap/data_driver/_KDataDriver.h | 18 ++++++----- hikyuu_pywrap/trade_manage/_TradeManager.cpp | 2 +- hikyuu_pywrap/trade_sys/_MultiFactor.cpp | 2 +- xmake.lua | 15 ++++++++- 71 files changed, 189 insertions(+), 176 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Block.cpp b/hikyuu_cpp/hikyuu/Block.cpp index e7569b46..3e524430 100644 --- a/hikyuu_cpp/hikyuu/Block.cpp +++ b/hikyuu_cpp/hikyuu/Block.cpp @@ -102,7 +102,7 @@ StockList Block::getStockList(std::function&& filter) const bool Block::add(const Stock& stock) { HKU_IF_RETURN(stock.isNull() || have(stock), false); if (!m_data) - m_data = shared_ptr(new Data); + m_data = make_shared(); m_data->m_stockDict[stock.market_code()] = stock; return true; @@ -113,7 +113,7 @@ bool Block::add(const string& market_code) { Stock stock = sm.getStock(market_code); HKU_IF_RETURN(stock.isNull() || have(stock), false); if (!m_data) - m_data = shared_ptr(new Data); + m_data = make_shared(); m_data->m_stockDict[stock.market_code()] = stock; return true; @@ -151,7 +151,7 @@ bool Block::remove(const Stock& stock) { void Block::setIndexStock(const Stock& stk) { if (!m_data) - m_data = shared_ptr(new Data); + m_data = make_shared(); m_data->m_indexStock = stk; } diff --git a/hikyuu_cpp/hikyuu/Block.h b/hikyuu_cpp/hikyuu/Block.h index 5b94e3fc..ef558cd2 100644 --- a/hikyuu_cpp/hikyuu/Block.h +++ b/hikyuu_cpp/hikyuu/Block.h @@ -64,14 +64,14 @@ public: /** 设置板块类别 */ void category(const string& category) { if (!m_data) - m_data = shared_ptr(new Data); + m_data = make_shared(); m_data->m_category = category; } /** 设置名称 */ void name(const string& name) { if (!m_data) - m_data = shared_ptr(new Data); + m_data = make_shared(); m_data->m_name = name; } diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 7558fc13..86325e79 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -35,7 +35,7 @@ SpotAgent::~SpotAgent() { void SpotAgent::start() { if (m_stop) { m_stop = false; - m_receiveThread = std::move(std::thread([this]() { work_thread(); })); + m_receiveThread = std::thread([this]() { work_thread(); }); } } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp index f1b2c05d..eb857657 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ICorr.cpp @@ -36,9 +36,9 @@ void ICorr::_checkParam(const string& name) const { } IndicatorImpPtr ICorr::_clone() { - ICorr* p = new ICorr(); + auto p = make_shared(); p->m_ref_ind = m_ref_ind.clone(); - return IndicatorImpPtr(p); + return p; } void ICorr::_calculate(const Indicator& ind) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp index f295b099..6019e88a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp @@ -33,7 +33,13 @@ void IDropna::_calculate(const Indicator& ind) { m_result_num = ind.getResultNumber(); size_t row_len = total - ind.discard(); - price_t* buf = new price_t[m_result_num * row_len]; + +#if CPP_STANDARD >= CPP_STANDARD_17 + std::unique_ptr buf = std::make_unique(m_result_num * row_len); +#else + std::unique_ptr buf(new price_t[m_result_num * row_len]); +#endif + if (!buf) { HKU_ERROR("Memory allocation failed!"); return; @@ -69,8 +75,6 @@ void IDropna::_calculate(const Indicator& ind) { } } - delete[] buf; - m_discard = 0; setParam("align_date_list", dates); } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp index aba8a0e3..92a4a7de 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IIc.cpp @@ -40,11 +40,11 @@ void IIc::_checkParam(const string& name) const { } IndicatorImpPtr IIc::_clone() { - IIc* p = new IIc(); + auto p = make_shared(); p->m_stks = m_stks; p->m_query = m_query; p->m_ref_stk = m_ref_stk; - return IndicatorImpPtr(p); + return p; } void IIc::_calculate(const Indicator& inputInd) { diff --git a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp index 28a044e5..69030225 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/ISpearman.cpp @@ -37,9 +37,9 @@ void ISpearman::_checkParam(const string &name) const { } IndicatorImpPtr ISpearman::_clone() { - ISpearman *p = new ISpearman(); + auto p = make_shared(); p->m_ref_ind = m_ref_ind.clone(); - return IndicatorImpPtr(p); + return p; } static void spearmanLevel(const IndicatorImp::value_t *data, IndicatorImp::value_t *level, diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index c3102bd0..0175d8c2 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -157,8 +157,8 @@ void StrategyBase::_run(bool forTest) { auto old_driver = stk.getKDataDirver(); for (size_t i = 0; i < ktype_count; i++) { - k_buffer[i] = std::move(stk.getKRecordList( - KQueryByDate(m_context.startDatetime(), Null(), ktype_list[i]))); + k_buffer[i] = stk.getKRecordList( + KQueryByDate(m_context.startDatetime(), Null(), ktype_list[i])); } for (size_t i = 0; i < ktype_count; i++) { stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]); @@ -278,7 +278,7 @@ void StrategyBase::_addClockEvent(const string& enable, TimeDelta delta, TimeDel if (getParam(enable)) { int repeat = static_cast((closeTime - openTime) / delta); scheduler->addFunc(Datetime::min(), Datetime::max(), openTime, closeTime, repeat, delta, - [this, delta]() { [this, delta]() { this->onClock(delta); }; }); + [this, delta]() { this->onClock(delta); }); } } diff --git a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp index 6ae45c5c..8490b8ce 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp @@ -397,7 +397,6 @@ void Performance ::statistics(const TradeManagerPtr& tm, const Datetime& datetim } PositionRecordList cur_position = tm->getPositionList(); - PositionRecordList::const_iterator cur_iter; int total_short_days = 0; if (tm->firstDatetime() != Null()) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 613e3907..bc2212b2 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -128,7 +128,7 @@ void TradeManager::_reset() { } TradeManagerPtr TradeManager::_clone() { - TradeManager* p = new TradeManager(m_init_datetime, m_init_cash, m_costfunc, m_name); + auto p = make_shared(m_init_datetime, m_init_cash, m_costfunc, m_name); p->m_params = m_params; p->m_name = m_name; p->m_init_datetime = m_init_datetime; @@ -154,7 +154,7 @@ TradeManagerPtr TradeManager::_clone() { p->m_actions = m_actions; - return TradeManagerPtr(p); + return p; } double TradeManager::getMarginRate(const Datetime& datetime, const Stock& stock) { @@ -1485,8 +1485,6 @@ void TradeManager::updateWithWeight(const Datetime& datetime) { Datetime end_date(datetime.date() + bd::days(1)); int precision = getParam("precision"); - price_t total_bonus = 0.0; - price_t last_cash = m_cash; TradeRecordList new_trade_buffer; // 更新持仓信息,并缓存新增的交易记录 @@ -1508,8 +1506,6 @@ void TradeManager::updateWithWeight(const Datetime& datetime) { if (weight_iter->bonus() != 0.0) { price_t bonus = roundEx(position.number * weight_iter->bonus() * 0.1, precision); position.sellMoney += bonus; - last_cash += bonus; - total_bonus += bonus; m_cash += bonus; TradeRecord record(stock, weight_iter->datetime(), BUSINESS_BONUS, bonus, bonus, @@ -1527,7 +1523,7 @@ void TradeManager::updateWithWeight(const Datetime& datetime) { new_trade_buffer.push_back(record); } } /* for weight */ - } /* for position */ + } /* for position */ std::sort( new_trade_buffer.begin(), new_trade_buffer.end(), @@ -1560,9 +1556,8 @@ void TradeManager::_saveAction(const TradeRecord& record) { case BUSINESS_INIT: buf << "my_tm = crtTM(datetime=Datetime('" << record.datetime.str() << "'), " << "initCash=" << record.cash << sep << "costFunc=" << m_costfunc->name() << "(" - << m_costfunc->getParameter().getNameValueList() << "), " - << "name='" << m_name << "'" - << ")"; + << m_costfunc->getParameter().getNameValueList() << "), " << "name='" << m_name + << "'" << ")"; break; case BUSINESS_CHECKIN: @@ -1576,17 +1571,17 @@ void TradeManager::_saveAction(const TradeRecord& record) { break; case BUSINESS_BUY: - buf << my_tm << "buy(Datetime('" << record.datetime.str() << "'), " - << "sm['" << record.stock.market_code() << "'], " << record.realPrice << sep - << record.number << sep << record.stoploss << sep << record.goalPrice << sep - << record.planPrice << sep << record.from << ")"; + buf << my_tm << "buy(Datetime('" << record.datetime.str() << "'), " << "sm['" + << record.stock.market_code() << "'], " << record.realPrice << sep << record.number + << sep << record.stoploss << sep << record.goalPrice << sep << record.planPrice + << sep << record.from << ")"; break; case BUSINESS_SELL: - buf << my_tm << "sell(Datetime('" << record.datetime.str() << "')," - << "sm['" << record.stock.market_code() << "'], " << record.realPrice << sep - << record.number << sep << record.stoploss << sep << record.goalPrice << sep - << record.planPrice << sep << record.from << ")"; + buf << my_tm << "sell(Datetime('" << record.datetime.str() << "')," << "sm['" + << record.stock.market_code() << "'], " << record.realPrice << sep << record.number + << sep << record.stoploss << sep << record.goalPrice << sep << record.planPrice + << sep << record.from << ")"; break; default: diff --git a/hikyuu_cpp/hikyuu/trade_manage/crt/TC_TestStub.cpp b/hikyuu_cpp/hikyuu/trade_manage/crt/TC_TestStub.cpp index b672b027..adb7c1fd 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/crt/TC_TestStub.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/crt/TC_TestStub.cpp @@ -10,7 +10,7 @@ namespace hku { HKU_API TradeCostPtr TC_TestStub() { - return TradeCostPtr(new TradeCostStub()); + return make_shared(); } } // namespace hku diff --git a/hikyuu_cpp/hikyuu/trade_manage/crt/crtTM.cpp b/hikyuu_cpp/hikyuu/trade_manage/crt/crtTM.cpp index 9e417913..e7cfce65 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/crt/crtTM.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/crt/crtTM.cpp @@ -11,7 +11,7 @@ namespace hku { TradeManagerPtr HKU_API crtTM(const Datetime& datetime, price_t initcash, const TradeCostPtr& costfunc, const string& name) { - return TradeManagerPtr(new TradeManager(datetime, initcash, costfunc, name)); + return make_shared(datetime, initcash, costfunc, name); } } // namespace hku diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp index d997ef5d..27db2250 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2015TradeCost.cpp @@ -86,17 +86,17 @@ CostRecord FixedA2015TradeCost::getSellCost(const Datetime& datetime, const Stoc } TradeCostPtr FixedA2015TradeCost::_clone() { - return TradeCostPtr(new FixedA2015TradeCost()); + return make_shared(); } TradeCostPtr HKU_API TC_FixedA2015(price_t commission, price_t lowestCommission, price_t stamptax, price_t transferfee) { - FixedA2015TradeCost* p = new FixedA2015TradeCost(); + TradeCostPtr p = make_shared(); p->setParam("commission", commission); p->setParam("lowest_commission", lowestCommission); p->setParam("stamptax", stamptax); p->setParam("transferfee", transferfee); - return TradeCostPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp index df347cf5..28658dc5 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedA2017TradeCost.cpp @@ -85,17 +85,17 @@ CostRecord FixedA2017TradeCost::getSellCost(const Datetime& datetime, const Stoc } TradeCostPtr FixedA2017TradeCost::_clone() { - return TradeCostPtr(new FixedA2017TradeCost()); + return make_shared(); } TradeCostPtr HKU_API TC_FixedA2017(price_t commission, price_t lowestCommission, price_t stamptax, price_t transferfee) { - FixedA2017TradeCost* p = new FixedA2017TradeCost(); + TradeCostPtr p = make_shared(); p->setParam("commission", commission); p->setParam("lowest_commission", lowestCommission); p->setParam("stamptax", stamptax); p->setParam("transferfee", transferfee); - return TradeCostPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp index 51212fb3..0d874ade 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp @@ -103,13 +103,13 @@ CostRecord FixedATradeCost::getSellCost(const Datetime& datetime, const Stock& s } TradeCostPtr FixedATradeCost::_clone() { - return TradeCostPtr(new FixedATradeCost()); + return make_shared(); } TradeCostPtr HKU_API TC_FixedA(price_t commission, price_t lowestCommission, price_t stamptax, price_t transferfee, price_t lowestTransferfee) { - return TradeCostPtr( - new FixedATradeCost(commission, lowestCommission, stamptax, transferfee, lowestTransferfee)); + return make_shared(commission, lowestCommission, stamptax, transferfee, + lowestTransferfee); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/TradeCostStub.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/TradeCostStub.cpp index c58a722c..f3aa6548 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/TradeCostStub.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/TradeCostStub.cpp @@ -48,7 +48,7 @@ CostRecord TradeCostStub::getReturnStockCost(const Datetime& borrow_datetime, } TradeCostPtr TradeCostStub::_clone() { - return TradeCostPtr(new TradeCostStub); + return make_shared(); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/ZeroTradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/ZeroTradeCost.cpp index 4d4e5dab..493d055b 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/ZeroTradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/ZeroTradeCost.cpp @@ -28,11 +28,11 @@ CostRecord ZeroTradeCost ::getSellCost(const Datetime& datetime, const Stock& st } TradeCostPtr ZeroTradeCost::_clone() { - return TradeCostPtr(new ZeroTradeCost()); + return make_shared(); } TradeCostPtr HKU_API TC_Zero() { - return TradeCostPtr(new ZeroTradeCost()); + return make_shared(); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index bf93d129..fcaf48dc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -175,11 +175,11 @@ private: \ #define ALLOCATEFUNDS_NO_PRIVATE_MEMBER_SERIALIZATION #endif -#define ALLOCATEFUNDS_IMP(classname) \ -public: \ - virtual AFPtr _clone() override { \ - return AFPtr(new classname()); \ - } \ +#define ALLOCATEFUNDS_IMP(classname) \ +public: \ + virtual AFPtr _clone() override { \ + return std::make_shared(); \ + } \ virtual SystemWeightList _allocateWeight(const Datetime&, const SystemWeightList&) override; typedef shared_ptr AllocateFundsPtr; diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h index b88c8734..ef0849a4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h @@ -180,7 +180,7 @@ typedef shared_ptr CNPtr; #define CONDITION_IMP(classname) \ public: \ virtual ConditionPtr _clone() { \ - return ConditionPtr(new classname()); \ + return std::make_shared(); \ } \ virtual void _calculate(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AddCondition.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AddCondition.cpp index 3134ad95..97fe8f14 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AddCondition.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AddCondition.cpp @@ -67,14 +67,14 @@ void AddCondition::_reset() { } ConditionPtr AddCondition::_clone() { - AddCondition* p = new AddCondition(); + auto p = make_shared(); if (m_cond1) { p->m_cond1 = m_cond1->clone(); } if (m_cond2) { p->m_cond2 = m_cond2->clone(); } - return ConditionPtr(p); + return p; } HKU_API ConditionPtr operator+(const ConditionPtr& cond1, const ConditionPtr& cond2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AndCondition.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AndCondition.cpp index 4445c4d9..7e379d49 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AndCondition.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/AndCondition.cpp @@ -49,14 +49,14 @@ void AndCondition::_reset() { } ConditionPtr AndCondition::_clone() { - AndCondition* p = new AndCondition(); + auto p = make_shared(); if (m_cond1) { p->m_cond1 = m_cond1->clone(); } if (m_cond2) { p->m_cond2 = m_cond2->clone(); } - return ConditionPtr(p); + return p; } HKU_API ConditionPtr operator&(const ConditionPtr& cond1, const ConditionPtr& cond2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/DivCondition.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/DivCondition.cpp index afa0c727..a724c519 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/DivCondition.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/DivCondition.cpp @@ -55,14 +55,14 @@ void DivCondition::_reset() { } ConditionPtr DivCondition::_clone() { - DivCondition* p = new DivCondition(); + auto p = make_shared(); if (m_cond1) { p->m_cond1 = m_cond1->clone(); } if (m_cond2) { p->m_cond2 = m_cond2->clone(); } - return ConditionPtr(p); + return p; } HKU_API ConditionPtr operator/(const ConditionPtr& cond1, const ConditionPtr& cond2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/MultiCondition.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/MultiCondition.cpp index 40dc297f..6732f985 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/MultiCondition.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/MultiCondition.cpp @@ -46,14 +46,14 @@ void MultiCondition::_reset() { } ConditionPtr MultiCondition::_clone() { - MultiCondition* p = new MultiCondition(); + auto p = make_shared(); if (m_cond1) { p->m_cond1 = m_cond1->clone(); } if (m_cond2) { p->m_cond2 = m_cond2->clone(); } - return ConditionPtr(p); + return p; } HKU_API ConditionPtr operator*(const ConditionPtr& cond1, const ConditionPtr& cond2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/OrCondition.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/OrCondition.cpp index 7bfad1aa..cd23d9e0 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/OrCondition.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/OrCondition.cpp @@ -73,14 +73,14 @@ void OrCondition::_reset() { } ConditionPtr OrCondition::_clone() { - OrCondition* p = new OrCondition(); + auto p = make_shared(); if (m_cond1) { p->m_cond1 = m_cond1->clone(); } if (m_cond2) { p->m_cond2 = m_cond2->clone(); } - return ConditionPtr(p); + return p; } HKU_API ConditionPtr operator|(const ConditionPtr& cond1, const ConditionPtr& cond2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/SubCondition.cpp b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/SubCondition.cpp index 32ddda1a..70a0daac 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/imp/SubCondition.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/imp/SubCondition.cpp @@ -67,14 +67,14 @@ void SubCondition::_reset() { } ConditionPtr SubCondition::_clone() { - SubCondition* p = new SubCondition(); + auto p = make_shared(); if (m_cond1) { p->m_cond1 = m_cond1->clone(); } if (m_cond2) { p->m_cond2 = m_cond2->clone(); } - return ConditionPtr(p); + return p; } HKU_API ConditionPtr operator-(const ConditionPtr& cond1, const ConditionPtr& cond2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h index 62e88855..b55a32ee 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/EnvironmentBase.h @@ -151,11 +151,11 @@ private: \ typedef shared_ptr EnvironmentPtr; typedef shared_ptr EVPtr; -#define ENVIRONMENT_IMP(classname) \ -public: \ - virtual EnvironmentPtr _clone() { \ - return EnvironmentPtr(new classname()); \ - } \ +#define ENVIRONMENT_IMP(classname) \ +public: \ + virtual EnvironmentPtr _clone() { \ + return std::make_shared(); \ + } \ virtual void _calculate(); /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp index 24061570..3086b1fb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/BoolEnvironment.cpp @@ -57,9 +57,9 @@ void BoolEnvironment::_calculate() { } EVPtr HKU_API EV_Bool(const Indicator& ind, const string& market) { - BoolEnvironment* p = new BoolEnvironment(ind); + EVPtr p = make_shared(ind); p->setParam("market", market); - return EVPtr(p); + return p; } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp index e0364a7c..0d442147 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/environment/imp/TwoLineEnvironment.cpp @@ -35,10 +35,10 @@ void TwoLineEnvironment::_checkParam(const string& name) const { } EnvironmentPtr TwoLineEnvironment::_clone() { - TwoLineEnvironment* ptr = new TwoLineEnvironment; + auto ptr = make_shared(); ptr->m_fast = m_fast.clone(); ptr->m_slow = m_slow.clone(); - return EnvironmentPtr(ptr); + return ptr; } void TwoLineEnvironment::_calculate() { @@ -66,9 +66,9 @@ void TwoLineEnvironment::_calculate() { } EVPtr HKU_API EV_TwoLine(const Indicator& fast, const Indicator& slow, const string& market) { - TwoLineEnvironment* ptr = new TwoLineEnvironment(fast, slow); + EVPtr ptr = make_shared(fast, slow); ptr->setParam("market", market); - return EVPtr(ptr); + return ptr; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h index 2b31b7dd..6466a875 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/MoneyManagerBase.h @@ -209,7 +209,7 @@ typedef shared_ptr MMPtr; #define MONEY_MANAGER_IMP(classname) \ public: \ virtual MoneyManagerPtr _clone() override { \ - return MoneyManagerPtr(new classname()); \ + return std::make_shared(); \ } \ virtual double _getBuyNumber(const Datetime& datetime, const Stock& stock, price_t price, \ price_t risk, SystemPart from) override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp index a87471ab..d2f3c439 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCapitalMoneyManager.cpp @@ -33,9 +33,9 @@ double FixedCapitalMoneyManager ::_getBuyNumber(const Datetime& datetime, const } MoneyManagerPtr HKU_API MM_FixedCapital(double capital) { - FixedCapitalMoneyManager* p = new FixedCapitalMoneyManager(); + MoneyManagerPtr p = make_shared(); p->setParam("capital", capital); - return MoneyManagerPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp index 12d3dd57..1e26481b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedCountMoneyManager.cpp @@ -37,9 +37,9 @@ double FixedCountMoneyManager::_getSellShortNumber(const Datetime& datetime, con } MoneyManagerPtr HKU_API MM_FixedCount(double n) { - FixedCountMoneyManager* p = new FixedCountMoneyManager(); + MoneyManagerPtr p = make_shared(); p->setParam("n", n); - return MoneyManagerPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp index eecc9fde..73fc9890 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedPercentMoneyManager.cpp @@ -33,9 +33,9 @@ double FixedPercentMoneyManager ::_getBuyNumber(const Datetime& datetime, const } MoneyManagerPtr HKU_API MM_FixedPercent(double p) { - FixedPercentMoneyManager* ptr = new FixedPercentMoneyManager(); + MoneyManagerPtr ptr = make_shared(); ptr->setParam("p", p); - return MoneyManagerPtr(ptr); + return ptr; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp index 385b6341..242537e2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedRiskMoneyManager.cpp @@ -32,9 +32,9 @@ double FixedRiskMoneyManager ::_getBuyNumber(const Datetime& datetime, const Sto } MoneyManagerPtr HKU_API MM_FixedRisk(double risk) { - FixedRiskMoneyManager* p = new FixedRiskMoneyManager(); + MoneyManagerPtr p = make_shared(); p->setParam("risk", risk); - return MoneyManagerPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp index 630e9520..4c8c5df3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/FixedUnitsMoneyManager.cpp @@ -37,9 +37,9 @@ double FixedUnitsMoneyManager ::_getBuyNumber(const Datetime& datetime, const St } MoneyManagerPtr HKU_API MM_FixedUnits(int n) { - FixedUnitsMoneyManager* p = new FixedUnitsMoneyManager(); + MoneyManagerPtr p = make_shared(); p->setParam("n", n); - return MoneyManagerPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp index bbbd86e7..fbefa05b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/NotMoneyManager.cpp @@ -30,8 +30,7 @@ double NotMoneyManager ::_getBuyNumber(const Datetime& datetime, const Stock& st } MoneyManagerPtr HKU_API MM_Nothing() { - NotMoneyManager* p = new NotMoneyManager(); - return MoneyManagerPtr(p); + return make_shared(); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp index fd514d14..b718d02e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/moneymanager/imp/WilliamsFixedRiskMoneyManager.cpp @@ -39,10 +39,10 @@ double WilliamsFixedRiskMoneyManager::_getBuyNumber(const Datetime& datetime, co } MoneyManagerPtr HKU_API MM_WilliamsFixedRisk(double p, price_t max_loss) { - WilliamsFixedRiskMoneyManager* ptr = new WilliamsFixedRiskMoneyManager(); + MoneyManagerPtr ptr = make_shared(); ptr->setParam("p", p); ptr->setParam("max_loss", max_loss); - return MoneyManagerPtr(ptr); + return ptr; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp index 642cdd54..c5b45966 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/multifactor/imp/EqualWeightMultiFactor.cpp @@ -73,7 +73,7 @@ vector EqualWeightMultiFactor::_calculate( return all_factors; #endif - return parallel_for_index(0, stk_count, [&, this](size_t si) { + return parallel_for_index(0, stk_count, [&](size_t si) { vector sumByDate(days_total); vector countByDate(days_total); diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h index af4efd35..0bbe6dad 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/ProfitGoalBase.h @@ -141,7 +141,7 @@ private: \ #define PROFITGOAL_IMP(classname) \ public: \ virtual ProfitGoalPtr _clone() override { \ - return ProfitGoalPtr(new classname()); \ + return std::make_shared(); \ } \ virtual price_t getGoal(const Datetime&, price_t) override; \ virtual void _calculate() override; diff --git a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/NoGoalProfitGoal.cpp b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/NoGoalProfitGoal.cpp index 60e36351..6156bbd7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/NoGoalProfitGoal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/profitgoal/imp/NoGoalProfitGoal.cpp @@ -24,7 +24,7 @@ price_t NoGoalProfitGoal::getGoal(const Datetime& datetime, price_t price) { } ProfitGoalPtr HKU_API PG_NoGoal() { - return ProfitGoalPtr(new NoGoalProfitGoal); + return make_shared(); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 1531359c..12ebf01f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -175,7 +175,7 @@ private: \ #define SELECTOR_IMP(classname) \ public: \ virtual SelectorPtr _clone() override { \ - return SelectorPtr(new classname()); \ + return std::make_shared(); \ } \ virtual SystemWeightList getSelected(Datetime date) override; \ virtual bool isMatchAF(const AFPtr& af) override; \ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index 651ad8b5..f4d55ce8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -45,10 +45,10 @@ void MultiFactorSelector::_reset() { } SelectorPtr MultiFactorSelector::_clone() { - MultiFactorSelector* p = new MultiFactorSelector(); + auto p = make_shared(); p->m_mf = m_mf->clone(); p->m_stk_sys_dict = m_stk_sys_dict; - return SelectorPtr(p); + return p; } bool MultiFactorSelector::isMatchAF(const AFPtr& af) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h index 7b15a51f..6cab8428 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/SignalBase.h @@ -191,11 +191,11 @@ private: \ #define SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION #endif -#define SIGNAL_IMP(classname) \ -public: \ - virtual SignalPtr _clone() override { \ - return SignalPtr(new classname()); \ - } \ +#define SIGNAL_IMP(classname) \ +public: \ + virtual SignalPtr _clone() override { \ + return std::make_shared(); \ + } \ virtual void _calculate(const KData&) override; /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp index 8a4bb311..a17bbe86 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/AllwaysBuySignal.cpp @@ -31,7 +31,7 @@ void AllwaysBuySignal::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_AllwaysBuy() { - return SignalPtr(new AllwaysBuySignal); + return make_shared(); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp index 275a9c3d..8f305985 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal.cpp @@ -23,11 +23,11 @@ BandSignal::BandSignal(const Indicator& ind, price_t lower, price_t upper) BandSignal::~BandSignal() {} SignalPtr BandSignal::_clone() { - BandSignal* p = new BandSignal(); + auto p = make_shared(); p->m_upper = m_upper; p->m_lower = m_lower; p->m_ind = m_ind.clone(); - return SignalPtr(p); + return p; } void BandSignal::_calculate(const KData& kdata) { @@ -47,7 +47,7 @@ void BandSignal::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_Band(const Indicator& sig, price_t lower, price_t upper) { - return SignalPtr(new BandSignal(sig, lower, upper)); + return make_shared(sig, lower, upper); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp index 3fda54c4..0c4ff5a2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BandSignal2.cpp @@ -21,11 +21,11 @@ BandSignal2::BandSignal2(const Indicator& ind, const Indicator& lower, const Ind BandSignal2::~BandSignal2() {} SignalPtr BandSignal2::_clone() { - BandSignal2* p = new BandSignal2(); + auto p = make_shared(); p->m_upper = m_upper.clone(); p->m_lower = m_lower.clone(); p->m_ind = m_ind.clone(); - return SignalPtr(p); + return p; } void BandSignal2::_calculate(const KData& kdata) { @@ -57,7 +57,7 @@ void BandSignal2::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_Band(const Indicator& sig, const Indicator& lower, const Indicator& upper) { - return SignalPtr(new BandSignal2(sig, lower, upper)); + return make_shared(sig, lower, upper); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp index 0aae59c1..7e220e9b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/BoolSignal.cpp @@ -22,10 +22,10 @@ BoolSignal::BoolSignal(const Indicator& buy, const Indicator& sell) BoolSignal::~BoolSignal() {} SignalPtr BoolSignal::_clone() { - BoolSignal* p = new BoolSignal(); + auto p = make_shared(); p->m_bool_buy = m_bool_buy.clone(); p->m_bool_sell = m_bool_sell.clone(); - return SignalPtr(p); + return p; } void BoolSignal::_calculate(const KData& kdata) { @@ -47,7 +47,7 @@ void BoolSignal::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_Bool(const Indicator& buy, const Indicator& sell) { - return SignalPtr(new BoolSignal(buy, sell)); + return make_shared(buy, sell); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp index 41a3f264..008665b1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossGoldSignal.cpp @@ -22,10 +22,10 @@ CrossGoldSignal::CrossGoldSignal(const Indicator& fast, const Indicator& slow) CrossGoldSignal::~CrossGoldSignal() {} SignalPtr CrossGoldSignal::_clone() { - CrossGoldSignal* p = new CrossGoldSignal(); + auto p = make_shared(); p->m_fast = m_fast.clone(); p->m_slow = m_slow.clone(); - return SignalPtr(p); + return p; } void CrossGoldSignal::_calculate(const KData& kdata) { @@ -50,7 +50,7 @@ void CrossGoldSignal::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_CrossGold(const Indicator& fast, const Indicator& slow) { - return SignalPtr(new CrossGoldSignal(fast, slow)); + return make_shared(fast, slow); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp index d1b2ee20..e29dd2c6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/CrossSignal.cpp @@ -22,10 +22,10 @@ CrossSignal::CrossSignal(const Indicator& fast, const Indicator& slow) CrossSignal::~CrossSignal() {} SignalPtr CrossSignal::_clone() { - CrossSignal* p = new CrossSignal(); + auto p = make_shared(); p->m_fast = m_fast.clone(); p->m_slow = m_slow.clone(); - return SignalPtr(p); + return p; } void CrossSignal::_calculate(const KData& kdata) { @@ -48,7 +48,7 @@ void CrossSignal::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_Cross(const Indicator& fast, const Indicator& slow) { - return SignalPtr(new CrossSignal(fast, slow)); + return make_shared(fast, slow); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp index e03772ad..e5719660 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal.cpp @@ -38,9 +38,9 @@ void SingleSignal::_checkParam(const string& name) const { } SignalPtr SingleSignal::_clone() { - SingleSignal* p = new SingleSignal(); + auto p = make_shared(); p->m_ind = m_ind.clone(); - return SignalPtr(p); + return p; } void SingleSignal::_calculate(const KData& kdata) { @@ -71,10 +71,10 @@ void SingleSignal::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_Single(const Indicator& ind, int filter_n, double filter_p) { - SingleSignal* p = new SingleSignal(ind); + SignalPtr p = make_shared(ind); p->setParam("filter_n", filter_n); p->setParam("filter_p", filter_p); - return SignalPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp index 8629833f..7dc61824 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/signal/imp/SingleSignal2.cpp @@ -41,9 +41,9 @@ void SingleSignal2::_checkParam(const string& name) const { } SignalPtr SingleSignal2::_clone() { - SingleSignal2* p = new SingleSignal2(); + auto p = make_shared(); p->m_ind = m_ind.clone(); - return SignalPtr(p); + return p; } void SingleSignal2::_calculate(const KData& kdata) { @@ -74,10 +74,10 @@ void SingleSignal2::_calculate(const KData& kdata) { } SignalPtr HKU_API SG_Single2(const Indicator& ind, int filter_n, double filter_p) { - SingleSignal2* p = new SingleSignal2(ind); + SignalPtr p = make_shared(ind); p->setParam("filter_n", filter_n); p->setParam("filter_p", filter_p); - return SignalPtr(p); + return p; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h index 8ea3aea2..33730d9f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/SlippageBase.h @@ -129,7 +129,7 @@ private: \ #define SLIPPAGE_IMP(classname) \ public: \ virtual SlippagePtr _clone() override { \ - return SlippagePtr(new classname()); \ + return std::make_shared(); \ } \ virtual price_t getRealBuyPrice(const Datetime&, price_t) override; \ virtual price_t getRealSellPrice(const Datetime&, price_t) override; \ diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp index 7f597b53..ac218e27 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedPercentSlippage.cpp @@ -37,9 +37,9 @@ price_t FixedPercentSlippage ::getRealSellPrice(const Datetime& datetime, price_ void FixedPercentSlippage::_calculate() {} SlippagePtr HKU_API SP_FixedPercent(double p) { - FixedPercentSlippage* ptr = new FixedPercentSlippage; + SlippagePtr ptr = make_shared(); ptr->setParam("p", p); - return SlippagePtr(ptr); + return ptr; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp index be1c6def..9dff2e39 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/slippage/imp/FixedValueSlippage.cpp @@ -36,9 +36,9 @@ price_t FixedValueSlippage ::getRealSellPrice(const Datetime& datetime, price_t void FixedValueSlippage::_calculate() {} SlippagePtr HKU_API SP_FixedValue(double value) { - FixedValueSlippage* ptr = new FixedValueSlippage; + SlippagePtr ptr = make_shared(); ptr->setParam("value", value); - return SlippagePtr(ptr); + return ptr; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h index cf8f1b69..a5ab75bb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/StoplossBase.h @@ -146,12 +146,12 @@ private: \ #define STOPLOSS_NO_PRIVATE_MEMBER_SERIALIZATION #endif -#define STOPLOSS_IMP(classname, str_name) \ -public: \ - virtual StoplossPtr _clone() override { \ - return StoplossPtr(new classname()); \ - } \ - virtual void _calculate() override; \ +#define STOPLOSS_IMP(classname, str_name) \ +public: \ + virtual StoplossPtr _clone() override { \ + return std::make_shared(); \ + } \ + virtual void _calculate() override; \ virtual price_t getPrice(const Datetime&, price_t) override; /** diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/crt/ST_Saftyloss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/crt/ST_Saftyloss.cpp index 1dc10fab..b54c8cc3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/crt/ST_Saftyloss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/crt/ST_Saftyloss.cpp @@ -12,9 +12,9 @@ namespace hku { StoplossPtr HKU_API ST_Saftyloss(int n1, int n2, double p) { Indicator op = SAFTYLOSS(n1, n2, p); - IndicatorStoploss *result = new IndicatorStoploss(op, "CLOSE"); + auto result = make_shared(op, "CLOSE"); result->name("Saftyloss_ST"); - return StoplossPtr(result); + return result; } } // namespace hku diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp index b372691d..8e038593 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/FixedPercentStoploss.cpp @@ -35,7 +35,7 @@ price_t FixedPercentStoploss ::getPrice(const Datetime& datetime, price_t price) void FixedPercentStoploss::_calculate() {} StoplossPtr HKU_API ST_FixedPercent(double p) { - StoplossPtr result(new FixedPercentStoploss()); + StoplossPtr result = make_shared(); result->setParam("p", p); return result; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp index fa1a1a80..a23c7b60 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/stoploss/imp/IndicatorStoploss.cpp @@ -43,9 +43,9 @@ void IndicatorStoploss::_reset() { } StoplossPtr IndicatorStoploss::_clone() { - IndicatorStoploss* p = new IndicatorStoploss(m_op.clone(), getParam("kpart")); + auto p = make_shared(m_op.clone(), getParam("kpart")); p->m_result = m_result; - return StoplossPtr(p); + return p; } void IndicatorStoploss::_calculate() { @@ -59,7 +59,7 @@ void IndicatorStoploss::_calculate() { } StoplossPtr HKU_API ST_Indicator(const Indicator& op, const string& kpart) { - return StoplossPtr(new IndicatorStoploss(op, kpart)); + return make_shared(op, kpart); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/utilities/IniParser.cpp b/hikyuu_cpp/hikyuu/utilities/IniParser.cpp index b2518fff..1e6d69bc 100644 --- a/hikyuu_cpp/hikyuu/utilities/IniParser.cpp +++ b/hikyuu_cpp/hikyuu/utilities/IniParser.cpp @@ -173,7 +173,7 @@ bool IniParser::hasOption(const std::string& section, const std::string& option) * @return 所有的section列表 */ IniParser::StringListPtr IniParser::getSectionList() const { - StringListPtr result(new std::list); + StringListPtr result = std::make_shared>(); section_map_type::const_iterator iter = m_sections.begin(); for (; iter != m_sections.end(); ++iter) { result->push_back(iter->first); @@ -192,7 +192,7 @@ IniParser::StringListPtr IniParser::getOptionList(const std::string& section) co throw(std::invalid_argument("No section: " + section)); } - StringListPtr result(new std::list); + StringListPtr result = std::make_shared>(); item_map_type option_map = m_sections.find(section)->second; item_map_type::const_iterator iter = option_map.begin(); for (; iter != option_map.end(); ++iter) { diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp index 41482694..2aff1f3d 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp @@ -6,6 +6,7 @@ */ #include +#include "hikyuu/Block.h" #include "../Log.h" #include "Parameter.h" diff --git a/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h index eac44db0..c943f440 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h @@ -165,7 +165,7 @@ public: if (m_interrupt_flags[i]) { m_interrupt_flags[i]->set(); } - m_queues[i]->push(std::move(FuncWrapper())); + m_queues[i]->push(FuncWrapper()); } for (size_t i = 0; i < m_worker_num; i++) { @@ -215,7 +215,7 @@ public: } for (size_t i = 0; i < m_worker_num; i++) { - m_queues[i]->push(std::move(FuncWrapper())); + m_queues[i]->push(FuncWrapper()); } // 等待线程结束 diff --git a/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h index 24cf00ac..fb016d14 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h @@ -157,7 +157,7 @@ public: if (m_interrupt_flags[i]) { m_interrupt_flags[i]->set(); } - m_queues[i]->push(std::move(FuncWrapper())); + m_queues[i]->push(FuncWrapper()); } for (size_t i = 0; i < m_worker_num; i++) { @@ -207,7 +207,7 @@ public: } for (size_t i = 0; i < m_worker_num; i++) { - m_queues[i]->push(std::move(FuncWrapper())); + m_queues[i]->push(FuncWrapper()); } // 等待线程结束 diff --git a/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h index d1c1c8e2..b1682002 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h @@ -149,7 +149,7 @@ public: if (m_interrupt_flags[i]) { m_interrupt_flags[i]->set(); } - m_queues[i]->push_front(std::move(FuncWrapper())); + m_queues[i]->push_front(FuncWrapper()); } m_cv.notify_all(); // 唤醒所有工作线程 @@ -204,7 +204,7 @@ public: } for (size_t i = 0; i < m_worker_num; i++) { - m_master_work_queue.push(std::move(FuncWrapper())); + m_master_work_queue.push(FuncWrapper()); } // 唤醒所有工作线程 diff --git a/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h index 7fddc422..401f4524 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h @@ -133,7 +133,7 @@ public: if (m_interrupt_flags[i]) { m_interrupt_flags[i]->set(); } - m_master_work_queue.push(std::move(FuncWrapper())); + m_master_work_queue.push(FuncWrapper()); } for (size_t i = 0; i < m_worker_num; i++) { @@ -168,7 +168,7 @@ public: } for (size_t i = 0; i < m_worker_num; i++) { - m_master_work_queue.push(std::move(FuncWrapper())); + m_master_work_queue.push(FuncWrapper()); } // 等待线程结束 diff --git a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IF.cpp b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IF.cpp index 8ef2ac6b..72ce09ce 100644 --- a/hikyuu_cpp/unit_test/hikyuu/indicator/test_IF.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/indicator/test_IF.cpp @@ -45,7 +45,7 @@ TEST_CASE("test_IF") { if (i == 0) CHECK(std::isnan(x[i])); else - CHECK_EQ(x[i], c[i] > c[i-1] ? 1 : 0); + CHECK_EQ(x[i], c[i] > c[i - 1] ? 1 : 0); } /** @arg 参数其中之一为数字 */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp index 12360cde..87967fea 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/signal/test_Signal.cpp @@ -23,11 +23,11 @@ public: return m_x; } - virtual void _reset() { + virtual void _reset() override { m_x = 0; } - virtual SignalPtr _clone() { + virtual SignalPtr _clone() override { SignalTest *p = new SignalTest; p->m_x = m_x; return SignalPtr(p); diff --git a/hikyuu_pywrap/_analysis.cpp b/hikyuu_pywrap/_analysis.cpp index c67b309c..79d79a7c 100644 --- a/hikyuu_pywrap/_analysis.cpp +++ b/hikyuu_pywrap/_analysis.cpp @@ -132,19 +132,19 @@ static py::dict analysis_sys_list(const py::object& pystk_list, const KQuery& qu if (py::isinstance(pystk_list)) { const auto& blk = pystk_list.cast(); for (const auto& stk : blk) { - sys_list.emplace_back(std::move(sys_proto->clone())); + sys_list.emplace_back(sys_proto->clone()); stk_list.emplace_back(stk); } } else if (py::isinstance(pystk_list)) { const auto& blk = pystk_list.cast(); for (const auto& stk : blk) { - sys_list.emplace_back(std::move(sys_proto->clone())); + sys_list.emplace_back(sys_proto->clone()); stk_list.emplace_back(stk); } } else if (py::isinstance(pystk_list)) { auto pyseq = pystk_list.cast(); for (const auto& obj : pyseq) { - sys_list.emplace_back(std::move(sys_proto->clone())); + sys_list.emplace_back(sys_proto->clone()); stk_list.emplace_back(obj.cast()); } } diff --git a/hikyuu_pywrap/data_driver/_BlockInfoDriver.h b/hikyuu_pywrap/data_driver/_BlockInfoDriver.h index 092bb5a9..b4dbeb74 100644 --- a/hikyuu_pywrap/data_driver/_BlockInfoDriver.h +++ b/hikyuu_pywrap/data_driver/_BlockInfoDriver.h @@ -26,13 +26,13 @@ public: PYBIND11_OVERLOAD_PURE(Block, BlockInfoDriver, getBlock, category, name); } - BlockList getBlockList(const string& category) { + BlockList getBlockList(const string& category) override { auto self = py::cast(this); auto py_list = self.attr("_getBlockList")(category); return python_list_to_vector(py_list); } - BlockList getBlockList() { + BlockList getBlockList() override { auto self = py::cast(this); auto py_list = self.attr("_getBlockList")(py::none()); return python_list_to_vector(py_list); diff --git a/hikyuu_pywrap/data_driver/_KDataDriver.h b/hikyuu_pywrap/data_driver/_KDataDriver.h index dc462be9..599e1787 100644 --- a/hikyuu_pywrap/data_driver/_KDataDriver.h +++ b/hikyuu_pywrap/data_driver/_KDataDriver.h @@ -20,24 +20,24 @@ class PyKDataDriver : public KDataDriver { public: using KDataDriver::KDataDriver; - bool _init() { + bool _init() override { PYBIND11_OVERLOAD(bool, KDataDriver, _init, ); } - bool isIndexFirst() { + bool isIndexFirst() override { PYBIND11_OVERLOAD_PURE(bool, KDataDriver, isIndexFirst, ); } - bool canParallelLoad() { + bool canParallelLoad() override { PYBIND11_OVERLOAD_PURE(bool, KDataDriver, canParallelLoad, ); } - size_t getCount(const string& market, const string& code, KQuery::KType kType) { + size_t getCount(const string& market, const string& code, const KQuery::KType& kType) override { PYBIND11_OVERLOAD(size_t, KDataDriver, getCount, market, code, kType); } bool getIndexRangeByDate(const string& market, const string& code, const KQuery& query, - size_t& out_start, size_t& out_end) { + size_t& out_start, size_t& out_end) override { auto self = py::cast(this); py::tuple t = self.attr("_getIndexRangeByDate")(market, code, query); if (len(t) != 2) { @@ -65,19 +65,21 @@ public: return true; } - KRecordList getKRecordList(const string& market, const string& code, const KQuery& query) { + KRecordList getKRecordList(const string& market, const string& code, + const KQuery& query) override { auto self = py::cast(this); py::list py_list = self.attr("_getKRecordList")(market, code, query); return python_list_to_vector(py_list); } - TimeLineList getTimeLineList(const string& market, const string& code, const KQuery& query) { + TimeLineList getTimeLineList(const string& market, const string& code, + const KQuery& query) override { auto self = py::cast(this); py::list py_list = self.attr("_getTimeLineList")(market, code, query); return python_list_to_vector(py_list); } - TransList getTransList(const string& market, const string& code, const KQuery& query) { + TransList getTransList(const string& market, const string& code, const KQuery& query) override { auto self = py::cast(this); py::list py_list = self.attr("_getTransList")(market, code, query); return python_list_to_vector(py_list); diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index 0551cba6..b24d730f 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -59,7 +59,7 @@ public: PYBIND11_OVERLOAD(bool, TradeManagerBase, have, stock); } - bool haveShort(const Stock& stock) const { + bool haveShort(const Stock& stock) const override { PYBIND11_OVERRIDE_NAME(bool, TradeManagerBase, "have_short", haveShort, stock); } diff --git a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp index 5e470c50..17df8d00 100644 --- a/hikyuu_pywrap/trade_sys/_MultiFactor.cpp +++ b/hikyuu_pywrap/trade_sys/_MultiFactor.cpp @@ -18,7 +18,7 @@ public: using MultiFactorBase::MultiFactorBase; PyMultiFactor(const MultiFactorBase& base) : MultiFactorBase(base) {} - IndicatorList _calculate(const vector& all_stk_inds) { + IndicatorList _calculate(const vector& all_stk_inds) override { // PYBIND11_OVERLOAD_PURE_NAME(IndicatorList, MultiFactorBase, "_calculate", _calculate, // all_stk_inds); auto self = py::cast(this); diff --git a/xmake.lua b/xmake.lua index 4db17dcd..c88e2d4d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -89,15 +89,28 @@ option("log_level") set_description("set log level") option_end() +option("leak_check") + set_default(false) + set_showmenu(true) + set_category("hikyuu") + set_description("Enable leak check for test") +option_end() + -- project set_project("hikyuu") add_rules("mode.debug", "mode.release") -if not is_plat("windows") then add_rules("mode.coverage", "mode.asan", "mode.msan", "mode.tsan", "mode.lsan") end -- version set_version("2.0.9", {build = "%Y%m%d%H%M"}) +if get_config("leak_check") then + set_policy("build.sanitizer.address", true) + set_policy("build.sanitizer.leak", true) + -- set_policy("build.sanitizer.memory", true) + -- set_policy("build.sanitizer.thread", true) +end + local level = get_config("log_level") if is_mode("debug") then level = "trace" From 4b60b8a359c0c83ee6e486e4d332cd8e479d91d7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 30 May 2024 19:05:52 +0800 Subject: [PATCH 334/601] add nng_closeall --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 8802ee32..f2c12c22 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -92,6 +92,8 @@ void GlobalInitializer::clean() { StockManager::quit(); DataDriverFactory::release(); + nng_closeall(); + #if HKU_ENABLE_HDF5_KDATA H5close(); #endif From 1706bd505e4f286c61c85840e5cd2a24aa7d5517 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 30 May 2024 22:37:05 +0800 Subject: [PATCH 335/601] =?UTF-8?q?=E6=B8=85=E7=90=86=20cppcheck=20?= =?UTF-8?q?=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp | 4 +--- hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp | 1 - 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp index fbe7d0d8..fab90646 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp @@ -38,13 +38,11 @@ void IBlockSetNum::_calculate(const Indicator& ind) { Block block = getParam("block"); bool ignore_context = getParam("ignore_context"); KData k = getContext(); - KQuery q; DatetimeList dates; if (!ignore_context && !k.empty()) { - q = k.getQuery(); dates = k.getDatetimeList(); } else { - q = getParam("query"); + KQuery q = getParam("query"); dates = StockManager::instance().getTradingCalendar(q, getParam("market")); } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp index 82003551..b2c4268a 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IFinance.cpp @@ -73,7 +73,6 @@ void IFinance::_calculate(const Indicator& data) { bool dynamic = getParam("dynamic"); auto* dst = this->data(); - auto dates = kdata.getDatetimeList(); auto* k = kdata.data(); size_t finances_total = finances.size(); From 718fdc9a353a1217b2bc7e80633fc52033d131ab Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 31 May 2024 22:01:34 +0800 Subject: [PATCH 336/601] =?UTF-8?q?=E5=A4=9A=E4=BD=99=E8=AF=AD=E5=8F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp index 275b957c..8b3652cc 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.cpp @@ -316,14 +316,12 @@ SystemWeightList AllocateFundsBase::_adjust_with_running( HKU_INFO_IF(trace, "[AF] Clean position sell: {}, recycle cash: {:<.2f}", sys->name(), sub_cash); } - } else { + } else // 清仓卖出失败情况,也加入到延迟卖出列表中,以便下一交易日可执行 - PositionRecord position = sys->getTM()->getPosition(date, sys->getStock()); if (position.number > 0.0) { delay_list.emplace_back(sys, position.number); HKU_INFO_IF(trace, "[AF] Clean delay {}", sys->name()); } - } } } From 2b55dabe39046cf732e1fef14b4a861bcd5f8c0d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 1 Jun 2024 14:41:32 +0800 Subject: [PATCH 337/601] =?UTF-8?q?=E9=9D=9E=E6=B3=84=E6=BC=8F=E6=A3=80?= =?UTF-8?q?=E6=B5=8B=E6=A8=A1=E5=BC=8F=E4=B8=8B=E7=9B=B4=E6=8E=A5=E9=80=80?= =?UTF-8?q?=E5=87=BA=EF=BC=8C=E8=AE=A9=E7=B3=BB=E7=BB=9F=E9=87=8A=E6=94=BE?= =?UTF-8?q?=E8=B5=84=E6=BA=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.h.in | 3 +++ hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 8 ++++++-- xmake.lua | 1 + 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/config.h.in b/config.h.in index a4ec7bef..798234db 100644 --- a/config.h.in +++ b/config.h.in @@ -34,6 +34,9 @@ // 启用MSVC内存泄漏检查 #define ENABLE_MSVC_LEAK_DETECT ${ENABLE_MSVC_LEAK_DETECT} +// 启用内存泄漏检测,用于 linux 系统 +#define HKU_ENABLE_LEAK_DETECT ${HKU_ENABLE_LEAK_DETECT} + // 启用发送用户使用信息 #define HKU_ENABLE_SEND_FEEDBACK ${HKU_ENABLE_SEND_FEEDBACK} diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index f2c12c22..08420018 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -84,6 +84,12 @@ void GlobalInitializer::clean() { getLatestVersion(), getLatestVersion()); } +#if !HKU_ENABLE_LEAK_DETECT && not defined(MSVC_LEAKER_DETECT) + // 未启用内存泄漏检测时,直接退出,让系统自行释放全部资源 + fmt::print("Quit Hikyuu system!\n\n"); + return; +#endif + releaseGlobalTaskGroup(); releaseScheduler(); releaseGlobalSpotAgent(); @@ -92,8 +98,6 @@ void GlobalInitializer::clean() { StockManager::quit(); DataDriverFactory::release(); - nng_closeall(); - #if HKU_ENABLE_HDF5_KDATA H5close(); #endif diff --git a/xmake.lua b/xmake.lua index c88e2d4d..29bbc0d2 100644 --- a/xmake.lua +++ b/xmake.lua @@ -148,6 +148,7 @@ set_configvar("SUPPORT_TEXT_ARCHIVE", 0) set_configvar("SUPPORT_XML_ARCHIVE", 1) set_configvar("SUPPORT_BINARY_ARCHIVE", 1) set_configvar("ENABLE_MSVC_LEAK_DETECT", 0) +set_configvar("HKU_ENABLE_LEAK_DETECT", get_config("leak_check") and 1 or 0) set_configvar("HKU_ENABLE_SEND_FEEDBACK", get_config("feedback") and 1 or 0) set_configvar("HKU_ENABLE_HDF5_KDATA", get_config("hdf5") and 1 or 0) From 49731ecf561d99a61e8e643ab69a3263dcd8fba1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 1 Jun 2024 14:44:25 +0800 Subject: [PATCH 338/601] update --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 08420018..1ce19261 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -84,7 +84,7 @@ void GlobalInitializer::clean() { getLatestVersion(), getLatestVersion()); } -#if !HKU_ENABLE_LEAK_DETECT && not defined(MSVC_LEAKER_DETECT) +#if !HKU_ENABLE_LEAK_DETECT && !defined(MSVC_LEAKER_DETECT) // 未启用内存泄漏检测时,直接退出,让系统自行释放全部资源 fmt::print("Quit Hikyuu system!\n\n"); return; From 99f1a12b448edc7c34534432dec783b6e9542442 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 1 Jun 2024 15:39:38 +0800 Subject: [PATCH 339/601] =?UTF-8?q?fixed=20=E5=8C=97=E4=BA=A4=E6=89=8092?= =?UTF-8?q?=E5=8F=B7=E6=AE=B5=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1=E4=BF=A1?= =?UTF-8?q?=E6=81=AF=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index fe82abca..b86a54f9 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -203,7 +203,7 @@ def get_china_bond10_rate(start_date="19901219"): def modifiy_code(code): if code.startswith(('0', '3')): return 'SZ' + code - if code.startswith(('4', '8')): + if code.startswith(('4', '8', '92')): return 'BJ' + code if code.startswith('6'): return 'SH' + code From 77bde1922f176881e40025219a51a468d4970c07 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 2 Jun 2024 22:40:40 +0800 Subject: [PATCH 340/601] update --- hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 2b216813..80cdd71d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -52,9 +52,7 @@ void SelectorBase::paramChanged() { void SelectorBase::removeAll() { m_pro_sys_list.clear(); - m_real_sys_list.clear(); - m_calculated = false; - m_proto_calculated = false; + reset(); } void SelectorBase::reset() { From ec0e225055a928529469c140293f3141bb4fc988 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 3 Jun 2024 02:40:46 +0800 Subject: [PATCH 341/601] =?UTF-8?q?SE=20operator=20=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E6=93=8D=E4=BD=9C=E6=95=B0=20se=20=E5=AE=9E=E4=BE=8B=E8=87=AA?= =?UTF-8?q?=E5=B8=A6=E4=B8=8D=E5=90=8C=E7=9A=84=20sys?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 1 + .../trade_sys/selector/SelectorBase.cpp | 29 ++++++++-- .../hikyuu/trade_sys/selector/SelectorBase.h | 25 ++++++++- .../selector/imp/OperatorAddSelector.cpp | 53 ++++++++++++++++--- .../selector/imp/OperatorAddSelector.h | 12 ++++- .../selector/imp/OperatorAddValueSelector.cpp | 10 ++-- .../selector/imp/OperatorAddValueSelector.h | 1 - .../trade_sys/selector/test_SE_Operator.cpp | 9 ++++ hikyuu_pywrap/trade_sys/_Selector.cpp | 8 ++- 9 files changed, 128 insertions(+), 20 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 25227021..54fdbe66 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -144,6 +144,7 @@ void Portfolio::_readyForRun() { for (size_t i = 0; i < total; i++) { SystemPtr& pro_sys = pro_sys_list[i]; SystemPtr sys = pro_sys->clone(); + m_se->bindRealToProto(sys, pro_sys); m_real_sys_list.emplace_back(sys); // 为内部实际执行的系统创建初始资金为0的子账户 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 80cdd71d..6731d39b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -52,6 +52,7 @@ void SelectorBase::paramChanged() { void SelectorBase::removeAll() { m_pro_sys_list.clear(); + _removeAll(); reset(); } @@ -127,9 +128,32 @@ void SelectorBase::calculate_proto(const KQuery& query) { } } +void SelectorBase::addSystem(const SYSPtr& sys) { + HKU_CHECK(sys, "The input sys is null!"); + HKU_CHECK(sys->getMM(), "protoSys missing MoneyManager!"); + HKU_CHECK(sys->getSG(), "protoSys missing Siganl!"); + HKU_CHECK(!sys->getParam("shared_tm"), "Unsupport shared TM for sys!"); + if (getParam("depend_on_proto_sys")) { + HKU_CHECK(sys->getTM(), "Scenarios that depend on prototype systems need to specify a TM!"); + } + + sys->reset(); + _addSystem(sys); + + m_pro_sys_list.emplace_back(sys); + m_calculated = false; + m_proto_calculated = false; +} + +void SelectorBase::addSystemList(const SystemList& sysList) { + for (const auto& sys : sysList) { + addSystem(sys); + } +} + void SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { HKU_CHECK(!stock.isNull(), "The input stock is null!"); - HKU_CHECK(protoSys, "The input stock is null!"); + HKU_CHECK(protoSys, "The input protoSys is null!"); HKU_CHECK(protoSys->getMM(), "protoSys missing MoneyManager!"); HKU_CHECK(protoSys->getSG(), "protoSys missing Siganl!"); HKU_CHECK(!protoSys->getParam("shared_tm"), "Unsupport shared TM for protoSys!"); @@ -138,13 +162,12 @@ void SelectorBase::addStock(const Stock& stock, const SystemPtr& protoSys) { "Scenarios that depend on prototype systems need to specify a TM!"); } - _addStock(stock, protoSys); - auto proto = protoSys; proto->forceResetAll(); SYSPtr sys = proto->clone(); sys->reset(); sys->setStock(stock); + _addSystem(sys); m_pro_sys_list.emplace_back(sys); m_calculated = false; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 83015ef6..c0b4a277 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -17,12 +17,15 @@ namespace hku { +class HKU_API Portfolio; + /** * 交易对象选择模块 * @ingroup Selector */ class HKU_API SelectorBase : public enable_shared_from_this { PARAMETER_SUPPORT_WITH_CHECK + friend class HKU_API Portfolio; public: /** 默认构造函数 */ @@ -61,6 +64,20 @@ public: */ void addStockList(const StockList& stkList, const SystemPtr& protoSys); + /** + * 直接加入已有系统策略示例 + * @note 应该已经绑定 stock + * @param sys + */ + void addSystem(const SYSPtr& sys); + + /** + * 直接加入已有系统策略示例 + * @note 应该已经绑定 stock + * @param sys + */ + void addSystemList(const SystemList& sys); + /** * @brief 获取原型系统列表 * @return const SystemList& @@ -102,16 +119,22 @@ public: virtual bool isMatchAF(const AFPtr& af) = 0; /** 用于逻辑运算的子类中添加原型系统,一般不需要子类实现 */ - virtual void _addStock(const Stock& stock, const SystemPtr& protoSys) {} + virtual void _addSystem(const SYSPtr& sys) {} + + /** 用于逻辑运算的子类中添加原型系统,一般不需要子类实现 */ + virtual void _removeAll() {} /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ void calculate(const SystemList& pf_realSysList, const KQuery& query); + virtual void bindRealToProto(const SYSPtr& real, const SYSPtr& proto) {} + void calculate_proto(const KQuery& query); private: void initParam(); +protected: protected: string m_name; bool m_calculated{false}; // 是否已计算过 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp index 159ce3f9..3bdcc2bf 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -20,12 +20,16 @@ OperatorAddSelector::OperatorAddSelector(const SelectorPtr& se1, const SelectorP if (se1) { m_se1 = se1->clone(); auto sys_list = m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se1_set.insert(sys); + } m_pro_sys_list = std::move(sys_list); } if (se2) { m_se2 = se2->clone(); auto sys_list = m_se2->getProtoSystemList(); for (auto& sys : sys_list) { + m_se2_set.insert(sys); m_pro_sys_list.emplace_back(std::move(sys)); } } @@ -36,9 +40,19 @@ OperatorAddSelector::~OperatorAddSelector() {} void OperatorAddSelector::_reset() { if (m_se1) { m_se1->reset(); + m_se1_set.clear(); + auto sys_list = m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se1_set.insert(sys); + } } if (m_se2) { m_se2->reset(); + m_se2_set.clear(); + auto sys_list = m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se2_set.insert(sys); + } } } @@ -46,32 +60,57 @@ bool OperatorAddSelector::isMatchAF(const AFPtr& af) { return true; } -void OperatorAddSelector::_addStock(const Stock& stock, const SystemPtr& protoSys) { +void OperatorAddSelector::_addSystem(const SYSPtr& sys) { if (m_se1) { - m_se1->addStock(stock, protoSys); + m_se1->addSystem(sys); + m_se1_set.insert(sys); } if (m_se2) { - m_se2->addStock(stock, protoSys); + m_se2->addSystem(sys); + m_se2_set.insert(sys); } } +void OperatorAddSelector::_removeAll() { + m_se1_set.clear(); + m_se2_set.clear(); +} + SelectorPtr OperatorAddSelector::_clone() { - OperatorAddSelector* p = new OperatorAddSelector(); + auto p = make_shared(); if (m_se1) { p->m_se1 = m_se1->clone(); + auto sys_list = p->m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + p->m_se1_set.insert(sys); + } } if (m_se2) { p->m_se2 = m_se2->clone(); + auto sys_list = p->m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + p->m_se2_set.insert(sys); + } } - return SelectorPtr(p); + return p; } void OperatorAddSelector::_calculate() { + SystemList se1_list, se2_list; + for (const auto& sys : m_real_sys_list) { + const auto& protoSys = m_real_to_proto[sys]; + if (m_se1_set.find(protoSys) != m_se1_set.end()) { + se1_list.emplace_back(sys); + } + if (m_se2_set.find(protoSys) != m_se2_set.end()) { + se2_list.emplace_back(sys); + } + } if (m_se1) { - m_se1->calculate(m_real_sys_list, m_query); + m_se1->calculate(se1_list, m_query); } if (m_se2) { - m_se2->calculate(m_real_sys_list, m_query); + m_se2->calculate(se2_list, m_query); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h index a3755878..42281be8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h @@ -19,14 +19,24 @@ public: virtual void _reset() override; virtual SelectorPtr _clone() override; - virtual void _addStock(const Stock& stock, const SystemPtr& protoSys) override; virtual bool isMatchAF(const AFPtr& af) override; virtual void _calculate() override; virtual SystemWeightList getSelected(Datetime date) override; + virtual void _addSystem(const SYSPtr& sys) override; + virtual void _removeAll() override; + +protected: + virtual void bindRealToProto(const SYSPtr& real, const SYSPtr& proto) override { + m_real_to_proto[real] = proto; + } + private: SelectorPtr m_se1; SelectorPtr m_se2; + std::unordered_set m_se1_set; + std::unordered_set m_se2_set; + std::unordered_map m_real_to_proto; //============================================ // 序列化支持 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp index fcb35179..9f20d302 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp @@ -31,11 +31,11 @@ void OperatorAddValueSelector::_reset() { } } -void OperatorAddValueSelector::_addStock(const Stock& stock, const SystemPtr& protoSys) { - if (m_se) { - m_se->addStock(stock, protoSys); - } -} +// void OperatorAddValueSelector::_addStock(const Stock& stock, const SystemPtr& protoSys) { +// if (m_se) { +// m_se->addStock(stock, protoSys); +// } +// } bool OperatorAddValueSelector::isMatchAF(const AFPtr& af) { return true; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h index fe0ffae8..1db34bf6 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h @@ -19,7 +19,6 @@ public: virtual void _reset() override; virtual SelectorPtr _clone() override; - virtual void _addStock(const Stock& stock, const SystemPtr& protoSys) override; virtual bool isMatchAF(const AFPtr& af) override; virtual void _calculate() override; virtual SystemWeightList getSelected(Datetime date) override; diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index 372bbdd1..0e074faf 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -64,6 +64,9 @@ TEST_CASE("test_SE_Add") { proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } se->calculate(proto_sys_list, KQuery(-20)); result = se->getSelected(Datetime(200001010000L)); CHECK_EQ(result.size(), 3); @@ -78,6 +81,9 @@ TEST_CASE("test_SE_Add") { se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } se->calculate(proto_sys_list, KQuery(-20)); result = se->getSelected(Datetime(200001010000L)); CHECK_UNARY(result.empty()); @@ -86,6 +92,9 @@ TEST_CASE("test_SE_Add") { se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } se->calculate(proto_sys_list, KQuery(-20)); result = se->getSelected(Datetime(200001010000L)); CHECK_EQ(result.size(), 3); diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index e8d86d83..0ec845f0 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -26,8 +26,12 @@ public: PYBIND11_OVERLOAD_PURE(void, SelectorBase, _calculate, ); } - void _addStock(const Stock& stock, const SystemPtr& protoSys) override { - PYBIND11_OVERLOAD(void, SelectorBase, _addStock, stock, protoSys); + void _addSystem(const SystemPtr& sys) override { + PYBIND11_OVERLOAD(void, SelectorBase, _addSystem, sys); + } + + void _removeAll() override { + PYBIND11_OVERLOAD(void, SelectorBase, _removeAll, ); } SystemWeightList getSelected(Datetime date) override { From f4d754140d991b9b7832f96a1e592b6f6bc92ec0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 3 Jun 2024 18:04:23 +0800 Subject: [PATCH 342/601] =?UTF-8?q?add=20SE=20=E5=92=8C=20=E6=95=B0?= =?UTF-8?q?=E5=AD=97=E7=9A=84=E5=8A=A0=E5=87=8F=E4=B9=98=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/crt/SE_Operator.h | 12 +- .../selector/imp/OperatorAddSelector.cpp | 101 --------------- .../selector/imp/OperatorAddSelector.h | 44 +------ .../selector/imp/OperatorAddValueSelector.cpp | 43 ------- .../selector/imp/OperatorAddValueSelector.h | 33 +---- .../selector/imp/OperatorDivValueSelector.cpp | 32 +++++ .../selector/imp/OperatorDivValueSelector.h | 19 +++ .../imp/OperatorInvertDivValueSelector.cpp | 32 +++++ .../imp/OperatorInvertDivValueSelector.h | 19 +++ .../imp/OperatorInvertSubValueSelector.cpp | 32 +++++ .../imp/OperatorInvertSubValueSelector.h | 19 +++ .../selector/imp/OperatorMulValueSelector.cpp | 32 +++++ .../selector/imp/OperatorMulValueSelector.h | 19 +++ .../selector/imp/OperatorSelector.cpp | 120 ++++++++++++++++++ .../trade_sys/selector/imp/OperatorSelector.h | 99 +++++++++++++++ .../selector/imp/OperatorSubValueSelector.cpp | 32 +++++ .../selector/imp/OperatorSubValueSelector.h | 19 +++ .../selector/imp/OperatorValueSelector.cpp | 56 ++++++++ .../selector/imp/OperatorValueSelector.h | 77 +++++++++++ hikyuu_pywrap/trade_sys/_Selector.cpp | 9 ++ 20 files changed, 635 insertions(+), 214 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h index cbd13bdf..ad8cc68e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h @@ -14,9 +14,19 @@ namespace hku { HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2); HKU_API SelectorPtr operator+(const SelectorPtr& se, double value); - inline SelectorPtr operator+(double value, const SelectorPtr& se) { return se + value; } +HKU_API SelectorPtr operator-(const SelectorPtr& se, double value); +HKU_API SelectorPtr operator-(double value, const SelectorPtr& se); + +HKU_API SelectorPtr operator*(const SelectorPtr& se, double value); +inline SelectorPtr operator*(double value, const SelectorPtr& se) { + return se * value; +} + +HKU_API SelectorPtr operator/(const SelectorPtr& se, double value); +HKU_API SelectorPtr operator/(double value, const SelectorPtr& se); + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp index 3bdcc2bf..c96201e2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -13,107 +13,6 @@ BOOST_CLASS_EXPORT(hku::OperatorAddSelector) namespace hku { -OperatorAddSelector::OperatorAddSelector() : SelectorBase("SE_Add") {} - -OperatorAddSelector::OperatorAddSelector(const SelectorPtr& se1, const SelectorPtr& se2) -: SelectorBase("SE_Add") { - if (se1) { - m_se1 = se1->clone(); - auto sys_list = m_se1->getProtoSystemList(); - for (auto& sys : sys_list) { - m_se1_set.insert(sys); - } - m_pro_sys_list = std::move(sys_list); - } - if (se2) { - m_se2 = se2->clone(); - auto sys_list = m_se2->getProtoSystemList(); - for (auto& sys : sys_list) { - m_se2_set.insert(sys); - m_pro_sys_list.emplace_back(std::move(sys)); - } - } -} - -OperatorAddSelector::~OperatorAddSelector() {} - -void OperatorAddSelector::_reset() { - if (m_se1) { - m_se1->reset(); - m_se1_set.clear(); - auto sys_list = m_se1->getProtoSystemList(); - for (auto& sys : sys_list) { - m_se1_set.insert(sys); - } - } - if (m_se2) { - m_se2->reset(); - m_se2_set.clear(); - auto sys_list = m_se2->getProtoSystemList(); - for (auto& sys : sys_list) { - m_se2_set.insert(sys); - } - } -} - -bool OperatorAddSelector::isMatchAF(const AFPtr& af) { - return true; -} - -void OperatorAddSelector::_addSystem(const SYSPtr& sys) { - if (m_se1) { - m_se1->addSystem(sys); - m_se1_set.insert(sys); - } - if (m_se2) { - m_se2->addSystem(sys); - m_se2_set.insert(sys); - } -} - -void OperatorAddSelector::_removeAll() { - m_se1_set.clear(); - m_se2_set.clear(); -} - -SelectorPtr OperatorAddSelector::_clone() { - auto p = make_shared(); - if (m_se1) { - p->m_se1 = m_se1->clone(); - auto sys_list = p->m_se1->getProtoSystemList(); - for (auto& sys : sys_list) { - p->m_se1_set.insert(sys); - } - } - if (m_se2) { - p->m_se2 = m_se2->clone(); - auto sys_list = p->m_se2->getProtoSystemList(); - for (auto& sys : sys_list) { - p->m_se2_set.insert(sys); - } - } - return p; -} - -void OperatorAddSelector::_calculate() { - SystemList se1_list, se2_list; - for (const auto& sys : m_real_sys_list) { - const auto& protoSys = m_real_to_proto[sys]; - if (m_se1_set.find(protoSys) != m_se1_set.end()) { - se1_list.emplace_back(sys); - } - if (m_se2_set.find(protoSys) != m_se2_set.end()) { - se2_list.emplace_back(sys); - } - } - if (m_se1) { - m_se1->calculate(se1_list, m_query); - } - if (m_se2) { - m_se2->calculate(se2_list, m_query); - } -} - SystemWeightList OperatorAddSelector::getSelected(Datetime date) { SystemWeightList ret; SystemWeightList sws1, sws2; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h index 42281be8..9c0049c3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.h @@ -7,49 +7,13 @@ #pragma once -#include "../SelectorBase.h" +#include "OperatorSelector.h" namespace hku { -class HKU_API OperatorAddSelector : public SelectorBase { -public: - OperatorAddSelector(); - OperatorAddSelector(const SelectorPtr& se1, const SelectorPtr& se2); - virtual ~OperatorAddSelector(); - - virtual void _reset() override; - virtual SelectorPtr _clone() override; - virtual bool isMatchAF(const AFPtr& af) override; - virtual void _calculate() override; - virtual SystemWeightList getSelected(Datetime date) override; - - virtual void _addSystem(const SYSPtr& sys) override; - virtual void _removeAll() override; - -protected: - virtual void bindRealToProto(const SYSPtr& real, const SYSPtr& proto) override { - m_real_to_proto[real] = proto; - } - -private: - SelectorPtr m_se1; - SelectorPtr m_se2; - std::unordered_set m_se1_set; - std::unordered_set m_se2_set; - std::unordered_map m_real_to_proto; - - //============================================ - // 序列化支持 - //============================================ -#if HKU_SUPPORT_SERIALIZATION - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { - ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); - ar& BOOST_SERIALIZATION_NVP(m_se1); - ar& BOOST_SERIALIZATION_NVP(m_se2); - } -#endif +class HKU_API OperatorAddSelector : public OperatorSelector { + OPERATOR_SELECTOR_IMP(OperatorAddSelector, "SE_Add") + OPERATOR_SELECTOR_SERIALIZATION }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp index 9f20d302..5719fea0 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.cpp @@ -13,49 +13,6 @@ BOOST_CLASS_EXPORT(hku::OperatorAddValueSelector) namespace hku { -OperatorAddValueSelector::OperatorAddValueSelector() : SelectorBase("SE_Add") {} - -OperatorAddValueSelector::OperatorAddValueSelector(const SelectorPtr& se, double value) -: SelectorBase("SE_Add"), m_value(value) { - if (se) { - m_se = se->clone(); - m_pro_sys_list = m_se->getProtoSystemList(); - } -} - -OperatorAddValueSelector::~OperatorAddValueSelector() {} - -void OperatorAddValueSelector::_reset() { - if (m_se) { - m_se->reset(); - } -} - -// void OperatorAddValueSelector::_addStock(const Stock& stock, const SystemPtr& protoSys) { -// if (m_se) { -// m_se->addStock(stock, protoSys); -// } -// } - -bool OperatorAddValueSelector::isMatchAF(const AFPtr& af) { - return true; -} - -SelectorPtr OperatorAddValueSelector::_clone() { - OperatorAddValueSelector* p = new OperatorAddValueSelector(); - if (m_se) { - p->m_se = m_se->clone(); - } - p->m_value = m_value; - return SelectorPtr(p); -} - -void OperatorAddValueSelector::_calculate() { - if (m_se) { - m_se->calculate(m_real_sys_list, m_query); - } -} - SystemWeightList OperatorAddValueSelector::getSelected(Datetime date) { SystemWeightList ret; HKU_IF_RETURN(!m_se, ret); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h index 1db34bf6..74bf682e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddValueSelector.h @@ -7,38 +7,13 @@ #pragma once -#include "../SelectorBase.h" +#include "OperatorValueSelector.h" namespace hku { -class HKU_API OperatorAddValueSelector : public SelectorBase { -public: - OperatorAddValueSelector(); - OperatorAddValueSelector(const SelectorPtr& se1, double value); - virtual ~OperatorAddValueSelector(); - - virtual void _reset() override; - virtual SelectorPtr _clone() override; - virtual bool isMatchAF(const AFPtr& af) override; - virtual void _calculate() override; - virtual SystemWeightList getSelected(Datetime date) override; - -private: - SelectorPtr m_se; - double m_value{0.0}; - - //============================================ - // 序列化支持 - //============================================ -#if HKU_SUPPORT_SERIALIZATION - friend class boost::serialization::access; - template - void serialize(Archive& ar, const unsigned int version) { - ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); - ar& BOOST_SERIALIZATION_NVP(m_se); - ar& BOOST_SERIALIZATION_NVP(m_value); - } -#endif +class HKU_API OperatorAddValueSelector : public OperatorValueSelector { + OPERATOR_VALUE_SELECTOR_IMP(OperatorAddValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_SERIALIZATION }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.cpp new file mode 100644 index 00000000..7ca019ee --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorDivValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorDivValueSelector) +#endif + +namespace hku { + +SystemWeightList OperatorDivValueSelector::getSelected(Datetime date) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se, ret); + + ret = m_se->getSelected(date); + for (auto& sw : ret) { + sw.weight /= m_value; + } + + return ret; +} + +HKU_API SelectorPtr operator/(const SelectorPtr& se, double value) { + return make_shared(se, value); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h new file mode 100644 index 00000000..b58b7ed0 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSelector.h" + +namespace hku { + +class HKU_API OperatorDivValueSelector : public OperatorValueSelector { + OPERATOR_VALUE_SELECTOR_IMP(OperatorDivValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp new file mode 100644 index 00000000..fe978962 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorInvertDivValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorInvertDivValueSelector) +#endif + +namespace hku { + +SystemWeightList OperatorInvertDivValueSelector::getSelected(Datetime date) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se, ret); + + ret = m_se->getSelected(date); + for (auto& sw : ret) { + sw.weight += m_value; + } + + return ret; +} + +HKU_API SelectorPtr operator/(double value, const SelectorPtr& se) { + return make_shared(se, value); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h new file mode 100644 index 00000000..1558b749 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSelector.h" + +namespace hku { + +class HKU_API OperatorInvertDivValueSelector : public OperatorValueSelector { + OPERATOR_VALUE_SELECTOR_IMP(OperatorInvertDivValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.cpp new file mode 100644 index 00000000..a62b2097 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorInvertSubValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorInvertSubValueSelector) +#endif + +namespace hku { + +SystemWeightList OperatorInvertSubValueSelector::getSelected(Datetime date) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se, ret); + + ret = m_se->getSelected(date); + for (auto& sw : ret) { + sw.weight = m_value - sw.weight; + } + + return ret; +} + +HKU_API SelectorPtr operator-(double value, const SelectorPtr& se) { + return make_shared(se, value); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.h new file mode 100644 index 00000000..6d380330 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertSubValueSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSelector.h" + +namespace hku { + +class HKU_API OperatorInvertSubValueSelector : public OperatorValueSelector { + OPERATOR_VALUE_SELECTOR_IMP(OperatorInvertSubValueSelector, "SE_SubValue") + OPERATOR_VALUE_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.cpp new file mode 100644 index 00000000..8f6562b7 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorMulValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorMulValueSelector) +#endif + +namespace hku { + +SystemWeightList OperatorMulValueSelector::getSelected(Datetime date) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se, ret); + + ret = m_se->getSelected(date); + for (auto& sw : ret) { + sw.weight *= m_value; + } + + return ret; +} + +HKU_API SelectorPtr operator*(const SelectorPtr& se, double value) { + return make_shared(se, value); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h new file mode 100644 index 00000000..c01b4c04 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSelector.h" + +namespace hku { + +class HKU_API OperatorMulValueSelector : public OperatorValueSelector { + OPERATOR_VALUE_SELECTOR_IMP(OperatorMulValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp new file mode 100644 index 00000000..0a024be9 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorSelector) +#endif + +namespace hku { + +OperatorSelector::OperatorSelector() : SelectorBase("SE_Operator") {} + +OperatorSelector::OperatorSelector(const string& name) : SelectorBase(name) {} + +OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, + const SelectorPtr& se2) +: SelectorBase(name) { + if (se1) { + m_se1 = se1->clone(); + auto sys_list = m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se1_set.insert(sys); + } + m_pro_sys_list = std::move(sys_list); + } + if (se2) { + m_se2 = se2->clone(); + auto sys_list = m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se2_set.insert(sys); + m_pro_sys_list.emplace_back(std::move(sys)); + } + } +} + +OperatorSelector::~OperatorSelector() {} + +void OperatorSelector::_reset() { + if (m_se1) { + m_se1->reset(); + m_se1_set.clear(); + auto sys_list = m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se1_set.insert(sys); + } + } + if (m_se2) { + m_se2->reset(); + m_se2_set.clear(); + auto sys_list = m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se2_set.insert(sys); + } + } +} + +bool OperatorSelector::isMatchAF(const AFPtr& af) { + return true; +} + +void OperatorSelector::_addSystem(const SYSPtr& sys) { + if (m_se1) { + m_se1->addSystem(sys); + m_se1_set.insert(sys); + } + if (m_se2) { + m_se2->addSystem(sys); + m_se2_set.insert(sys); + } +} + +void OperatorSelector::_removeAll() { + m_se1_set.clear(); + m_se2_set.clear(); +} + +SelectorPtr OperatorSelector::_clone() { + auto p = make_shared(); + if (m_se1) { + p->m_se1 = m_se1->clone(); + auto sys_list = p->m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + p->m_se1_set.insert(sys); + } + } + if (m_se2) { + p->m_se2 = m_se2->clone(); + auto sys_list = p->m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + p->m_se2_set.insert(sys); + } + } + return p; +} + +void OperatorSelector::_calculate() { + SystemList se1_list, se2_list; + for (const auto& sys : m_real_sys_list) { + const auto& protoSys = m_real_to_proto[sys]; + if (m_se1_set.find(protoSys) != m_se1_set.end()) { + se1_list.emplace_back(sys); + } + if (m_se2_set.find(protoSys) != m_se2_set.end()) { + se2_list.emplace_back(sys); + } + } + if (m_se1) { + m_se1->calculate(se1_list, m_query); + } + if (m_se2) { + m_se2->calculate(se2_list, m_query); + } +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h new file mode 100644 index 00000000..1e8c640e --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "../SelectorBase.h" + +namespace hku { + +class HKU_API OperatorSelector : public SelectorBase { +public: + OperatorSelector(); + OperatorSelector(const string& name); + OperatorSelector(const string& name, const SelectorPtr& se1, const SelectorPtr& se2); + virtual ~OperatorSelector(); + + virtual void _reset() override; + virtual SelectorPtr _clone() override; + virtual bool isMatchAF(const AFPtr& af) override; + virtual void _calculate() override; + virtual SystemWeightList getSelected(Datetime date) override { + return SystemWeightList(); + } + + virtual void _addSystem(const SYSPtr& sys) override; + virtual void _removeAll() override; + +protected: + virtual void bindRealToProto(const SYSPtr& real, const SYSPtr& proto) override { + m_real_to_proto[real] = proto; + } + +protected: + SelectorPtr m_se1; + SelectorPtr m_se2; + std::unordered_set m_se1_set; + std::unordered_set m_se2_set; + std::unordered_map m_real_to_proto; + +private: + //============================================ + // 序列化支持 + //============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_se1); + ar& BOOST_SERIALIZATION_NVP(m_se2); + } +#endif +}; + +#define OPERATOR_SELECTOR_IMP(classname, name) \ +public: \ + classname() : OperatorSelector(name) {} \ + classname(const SelectorPtr& se1, const SelectorPtr& se2) \ + : OperatorSelector(name, se1, se2) {} \ + virtual ~classname() {} \ + \ + virtual SystemWeightList getSelected(Datetime date) override; \ + \ + virtual SelectorPtr _clone() override { \ + auto p = std::make_shared(); \ + if (m_se1) { \ + p->m_se1 = m_se1->clone(); \ + auto sys_list = p->m_se1->getProtoSystemList(); \ + for (auto& sys : sys_list) { \ + p->m_se1_set.insert(sys); \ + } \ + } \ + if (m_se2) { \ + p->m_se2 = m_se2->clone(); \ + auto sys_list = p->m_se2->getProtoSystemList(); \ + for (auto& sys : sys_list) { \ + p->m_se2_set.insert(sys); \ + } \ + } \ + return p; \ + } + +#if HKU_SUPPORT_SERIALIZATION +#define OPERATOR_SELECTOR_SERIALIZATION \ +private: \ + friend class boost::serialization::access; \ + template \ + void serialize(Archive& ar, const unsigned int version) { \ + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(OperatorSelector); \ + } +#else +#define OPERATOR_SELECTOR_SERIALIZATION +#endif + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.cpp new file mode 100644 index 00000000..226b0b79 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorSubValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorSubValueSelector) +#endif + +namespace hku { + +SystemWeightList OperatorSubValueSelector::getSelected(Datetime date) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se, ret); + + ret = m_se->getSelected(date); + for (auto& sw : ret) { + sw.weight -= m_value; + } + + return ret; +} + +HKU_API SelectorPtr operator-(const SelectorPtr& se, double value) { + return make_shared(se, value); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.h new file mode 100644 index 00000000..de24c109 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubValueSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorValueSelector.h" + +namespace hku { + +class HKU_API OperatorSubValueSelector : public OperatorValueSelector { + OPERATOR_VALUE_SELECTOR_IMP(OperatorSubValueSelector, "SE_SubValue") + OPERATOR_VALUE_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp new file mode 100644 index 00000000..ba281065 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorValueSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorValueSelector) +#endif + +namespace hku { + +OperatorValueSelector::OperatorValueSelector() : SelectorBase("SE_OpearatorValue") {} + +OperatorValueSelector::OperatorValueSelector(const string& name) : SelectorBase(name) {} + +OperatorValueSelector::OperatorValueSelector(const string& name, const SelectorPtr& se, + double value) +: SelectorBase(name), m_value(value) { + if (se) { + m_se = se->clone(); + m_pro_sys_list = m_se->getProtoSystemList(); + } +} + +OperatorValueSelector::~OperatorValueSelector() {} + +void OperatorValueSelector::_reset() { + if (m_se) { + m_se->reset(); + } +} + +bool OperatorValueSelector::isMatchAF(const AFPtr& af) { + return true; +} + +SelectorPtr OperatorValueSelector::_clone() { + auto p = make_shared(); + if (m_se) { + p->m_se = m_se->clone(); + } + p->m_value = m_value; + return p; +} + +void OperatorValueSelector::_calculate() { + if (m_se) { + m_se->calculate(m_real_sys_list, m_query); + } +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h new file mode 100644 index 00000000..004213cb --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "../SelectorBase.h" + +namespace hku { + +class HKU_API OperatorValueSelector : public SelectorBase { +public: + OperatorValueSelector(); + OperatorValueSelector(const string& name); + OperatorValueSelector(const string& name, const SelectorPtr& se, double value); + virtual ~OperatorValueSelector(); + + virtual void _reset() override; + virtual SelectorPtr _clone() override; + virtual bool isMatchAF(const AFPtr& af) override; + virtual void _calculate() override; + virtual SystemWeightList getSelected(Datetime date) override { + return SystemWeightList(); + } + +protected: + SelectorPtr m_se; + double m_value{0.0}; + +private: + //============================================ + // 序列化支持 + //============================================ +#if HKU_SUPPORT_SERIALIZATION + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_se); + ar& BOOST_SERIALIZATION_NVP(m_value); + } +#endif +}; + +#define OPERATOR_VALUE_SELECTOR_IMP(classname, name) \ +public: \ + classname() : OperatorValueSelector(name) {} \ + classname(const SelectorPtr& se, double value) : OperatorValueSelector(name, se, value) {} \ + virtual ~classname() {} \ + \ + virtual SystemWeightList getSelected(Datetime date) override; \ + \ + virtual SelectorPtr _clone() override { \ + auto p = std::make_shared(); \ + if (m_se) { \ + p->m_se = m_se->clone(); \ + } \ + p->m_value = m_value; \ + return p; \ + } + +#if HKU_SUPPORT_SERIALIZATION +#define OPERATOR_VALUE_SELECTOR_SERIALIZATION \ +private: \ + friend class boost::serialization::access; \ + template \ + void serialize(Archive& ar, const unsigned int version) { \ + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(OperatorValueSelector); \ + } +#else +#define OPERATOR_VALUE_SELECTOR_SERIALIZATION +#endif + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 0ec845f0..4c7ee35f 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -157,6 +157,15 @@ void export_Selector(py::module& m) { .def("__add__", [](const SelectorPtr& self, double other) { return self + other; }) .def("__radd__", [](const SelectorPtr& self, double other) { return self + other; }) + .def("__sub__", [](const SelectorPtr& self, double other) { return self - other; }) + .def("__rsub__", [](const SelectorPtr& self, double other) { return other - self; }) + + .def("__mul__", [](const SelectorPtr& self, double other) { return self * other; }) + .def("__rmul__", [](const SelectorPtr& self, double other) { return self * other; }) + + .def("__truediv__", [](const SelectorPtr& self, double other) { return self / other; }) + .def("__rtruediv__", [](const SelectorPtr& self, double other) { return other / self; }) + DEF_PICKLE(SEPtr); m.def("SE_Fixed", py::overload_cast<>(SE_Fixed)); From 451cb69a68724372ee564df3c14867de2af9b0d4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 3 Jun 2024 18:30:13 +0800 Subject: [PATCH 343/601] add multi SE --- .../trade_sys/selector/crt/SE_Operator.h | 1 + .../selector/imp/OperatorMulSelector.cpp | 81 +++++++++++++++++++ .../selector/imp/OperatorMulSelector.h | 19 +++++ hikyuu_pywrap/trade_sys/_Selector.cpp | 3 +- 4 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h index ad8cc68e..199ad1d7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h @@ -12,6 +12,7 @@ namespace hku { HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2); +HKU_API SelectorPtr operator*(const SelectorPtr& se1, const SelectorPtr& se2); HKU_API SelectorPtr operator+(const SelectorPtr& se, double value); inline SelectorPtr operator+(double value, const SelectorPtr& se) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp new file mode 100644 index 00000000..96e195cd --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorMulSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorMulSelector) +#endif + +namespace hku { + +SystemWeightList OperatorMulSelector::getSelected(Datetime date) { + SystemWeightList ret; + SystemWeightList sws1, sws2; + if (m_se1) { + sws1 = m_se1->getSelected(date); + } + if (m_se2) { + sws2 = m_se2->getSelected(date); + } + + if (sws1.empty()) { + ret = std::move(sws2); + return ret; + } + + if (sws2.empty()) { + ret = std::move(sws1); + return ret; + } + + unordered_map sw_dict1; + for (auto& sw : sws1) { + sw_dict1[sw.sys.get()] = &sw; + } + + SystemWeight tmp; + unordered_map sw_dict2; + unordered_map::iterator iter; + for (auto& sw : sws2) { + iter = sw_dict1.find(sw.sys.get()); + tmp.sys = sw.sys; + if (iter != sw_dict1.end()) { + tmp.weight = sw.weight * iter->second->weight; + } else { + tmp.weight = sw.weight; + } + auto& back = ret.emplace_back(std::move(tmp)); + sw_dict2[back.sys.get()] = &back; + } + + for (auto& sw : sws1) { + iter = sw_dict2.find(sw.sys.get()); + if (iter == sw_dict2.end()) { + ret.emplace_back(std::move(sw)); + } + } + + std::sort(ret.begin(), ret.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; + }); + + return ret; +} + +HKU_API SelectorPtr operator*(const SelectorPtr& se1, const SelectorPtr& se2) { + return make_shared(se1, se2); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h new file mode 100644 index 00000000..937b1e6d --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorSelector.h" + +namespace hku { + +class HKU_API OperatorMulSelector : public OperatorSelector { + OPERATOR_SELECTOR_IMP(OperatorMulSelector, "SE_Add") + OPERATOR_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 4c7ee35f..e3075166 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -153,13 +153,14 @@ void export_Selector(py::module& m) { .def("__add__", [](const SelectorPtr& self, const SelectorPtr& other) { return self + other; }) - .def("__add__", [](const SelectorPtr& self, double other) { return self + other; }) .def("__radd__", [](const SelectorPtr& self, double other) { return self + other; }) .def("__sub__", [](const SelectorPtr& self, double other) { return self - other; }) .def("__rsub__", [](const SelectorPtr& self, double other) { return other - self; }) + .def("__mul__", + [](const SelectorPtr& self, const SelectorPtr& other) { return self * other; }) .def("__mul__", [](const SelectorPtr& self, double other) { return self * other; }) .def("__rmul__", [](const SelectorPtr& self, double other) { return self * other; }) From b0449c41dd91ddcf57a21664c6db4a4d56dc3dfa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 4 Jun 2024 01:56:52 +0800 Subject: [PATCH 344/601] update --- .../hikyuu/trade_sys/selector/SelectorBase.h | 4 +- .../trade_sys/selector/crt/SE_Operator.h | 2 + .../selector/imp/OperatorAddSelector.cpp | 2 +- .../selector/imp/OperatorDivSelector.cpp | 81 +++++++++++++++++++ .../selector/imp/OperatorDivSelector.h | 19 +++++ .../selector/imp/OperatorDivValueSelector.h | 2 +- .../imp/OperatorInvertDivValueSelector.h | 2 +- .../selector/imp/OperatorMulSelector.h | 2 +- .../selector/imp/OperatorMulValueSelector.h | 2 +- .../trade_sys/selector/imp/OperatorSelector.h | 4 +- .../selector/imp/OperatorSubSelector.cpp | 81 +++++++++++++++++++ .../selector/imp/OperatorSubSelector.h | 19 +++++ .../trade_sys/selector/test_SE_Operator.cpp | 78 ++++++++++++++++++ hikyuu_pywrap/trade_sys/_Selector.cpp | 4 + 14 files changed, 292 insertions(+), 10 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp create mode 100644 hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.h diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index c0b4a277..53fb61a2 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -17,15 +17,12 @@ namespace hku { -class HKU_API Portfolio; - /** * 交易对象选择模块 * @ingroup Selector */ class HKU_API SelectorBase : public enable_shared_from_this { PARAMETER_SUPPORT_WITH_CHECK - friend class HKU_API Portfolio; public: /** 默认构造函数 */ @@ -127,6 +124,7 @@ public: /* 仅供PF调用,由PF通知其实际运行的系统列表,并启动计算 */ void calculate(const SystemList& pf_realSysList, const KQuery& query); + /* 仅供PF调用,建立实际系统到原型系统映射 */ virtual void bindRealToProto(const SYSPtr& real, const SYSPtr& proto) {} void calculate_proto(const KQuery& query); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h index 199ad1d7..f1789460 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h @@ -12,7 +12,9 @@ namespace hku { HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2); +HKU_API SelectorPtr operator-(const SelectorPtr& se1, const SelectorPtr& se2); HKU_API SelectorPtr operator*(const SelectorPtr& se1, const SelectorPtr& se2); +HKU_API SelectorPtr operator/(const SelectorPtr& se1, const SelectorPtr& se2); HKU_API SelectorPtr operator+(const SelectorPtr& se, double value); inline SelectorPtr operator+(double value, const SelectorPtr& se) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp index c96201e2..bad5155b 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -45,7 +45,7 @@ SystemWeightList OperatorAddSelector::getSelected(Datetime date) { iter = sw_dict1.find(sw.sys.get()); tmp.sys = sw.sys; if (iter != sw_dict1.end()) { - tmp.weight = sw.weight + iter->second->weight; + tmp.weight = std::isnan(sw.weight) ? sw.weight : sw.weight + iter->second->weight; } else { tmp.weight = sw.weight; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp new file mode 100644 index 00000000..e1685244 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorDivSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorDivSelector) +#endif + +namespace hku { + +SystemWeightList OperatorDivSelector::getSelected(Datetime date) { + SystemWeightList ret; + SystemWeightList sws1, sws2; + if (m_se1) { + sws1 = m_se1->getSelected(date); + } + if (m_se2) { + sws2 = m_se2->getSelected(date); + } + + if (sws1.empty()) { + ret = std::move(sws2); + return ret; + } + + if (sws2.empty()) { + ret = std::move(sws1); + return ret; + } + + unordered_map sw_dict1; + for (auto& sw : sws1) { + sw_dict1[sw.sys.get()] = &sw; + } + + SystemWeight tmp; + unordered_map sw_dict2; + unordered_map::iterator iter; + for (auto& sw : sws2) { + iter = sw_dict1.find(sw.sys.get()); + tmp.sys = sw.sys; + if (iter != sw_dict1.end()) { + tmp.weight = std::isnan(sw.weight) ? sw.weight : iter->second->weight / sw.weight; + } else { + tmp.weight = 1.0 / sw.weight; + } + auto& back = ret.emplace_back(std::move(tmp)); + sw_dict2[back.sys.get()] = &back; + } + + for (auto& sw : sws1) { + iter = sw_dict2.find(sw.sys.get()); + if (iter == sw_dict2.end()) { + ret.emplace_back(std::move(sw)); + } + } + + std::sort(ret.begin(), ret.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; + }); + + return ret; +} + +HKU_API SelectorPtr operator/(const SelectorPtr& se1, const SelectorPtr& se2) { + return make_shared(se1, se2); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h new file mode 100644 index 00000000..c17c3512 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorSelector.h" + +namespace hku { + +class HKU_API OperatorDivSelector : public OperatorSelector { + OPERATOR_SELECTOR_IMP(OperatorDivSelector, "SE_Add") + OPERATOR_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h index b58b7ed0..945504f3 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivValueSelector.h @@ -12,7 +12,7 @@ namespace hku { class HKU_API OperatorDivValueSelector : public OperatorValueSelector { - OPERATOR_VALUE_SELECTOR_IMP(OperatorDivValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_IMP(OperatorDivValueSelector, "SE_DivValue") OPERATOR_VALUE_SELECTOR_SERIALIZATION }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h index 1558b749..5475ce77 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.h @@ -12,7 +12,7 @@ namespace hku { class HKU_API OperatorInvertDivValueSelector : public OperatorValueSelector { - OPERATOR_VALUE_SELECTOR_IMP(OperatorInvertDivValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_IMP(OperatorInvertDivValueSelector, "SE_DivValue") OPERATOR_VALUE_SELECTOR_SERIALIZATION }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h index 937b1e6d..831bd085 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.h @@ -12,7 +12,7 @@ namespace hku { class HKU_API OperatorMulSelector : public OperatorSelector { - OPERATOR_SELECTOR_IMP(OperatorMulSelector, "SE_Add") + OPERATOR_SELECTOR_IMP(OperatorMulSelector, "SE_Multi") OPERATOR_SELECTOR_SERIALIZATION }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h index c01b4c04..29d8e81c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h @@ -12,7 +12,7 @@ namespace hku { class HKU_API OperatorMulValueSelector : public OperatorValueSelector { - OPERATOR_VALUE_SELECTOR_IMP(OperatorMulValueSelector, "SE_AddValue") + OPERATOR_VALUE_SELECTOR_IMP(OperatorMulValueSelector, "SE_MulValue") OPERATOR_VALUE_SELECTOR_SERIALIZATION }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index 1e8c640e..bd8a51b8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -37,8 +37,8 @@ protected: protected: SelectorPtr m_se1; SelectorPtr m_se2; - std::unordered_set m_se1_set; - std::unordered_set m_se2_set; + std::unordered_set m_se1_set; // se1 的原型系统实例集合 + std::unordered_set m_se2_set; // se2 的原型系统实例集合 std::unordered_map m_real_to_proto; private: diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp new file mode 100644 index 00000000..783c0221 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#include "OperatorSubSelector.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::OperatorSubSelector) +#endif + +namespace hku { + +SystemWeightList OperatorSubSelector::getSelected(Datetime date) { + SystemWeightList ret; + SystemWeightList sws1, sws2; + if (m_se1) { + sws1 = m_se1->getSelected(date); + } + if (m_se2) { + sws2 = m_se2->getSelected(date); + } + + if (sws1.empty()) { + ret = std::move(sws2); + return ret; + } + + if (sws2.empty()) { + ret = std::move(sws1); + return ret; + } + + unordered_map sw_dict1; + for (auto& sw : sws1) { + sw_dict1[sw.sys.get()] = &sw; + } + + SystemWeight tmp; + unordered_map sw_dict2; + unordered_map::iterator iter; + for (auto& sw : sws2) { + iter = sw_dict1.find(sw.sys.get()); + tmp.sys = sw.sys; + if (iter != sw_dict1.end()) { + tmp.weight = std::isnan(sw.weight) ? -sw.weight : iter->second->weight - sw.weight; + } else { + tmp.weight = -sw.weight; + } + auto& back = ret.emplace_back(std::move(tmp)); + sw_dict2[back.sys.get()] = &back; + } + + for (auto& sw : sws1) { + iter = sw_dict2.find(sw.sys.get()); + if (iter == sw_dict2.end()) { + ret.emplace_back(std::move(sw)); + } + } + + std::sort(ret.begin(), ret.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; + }); + + return ret; +} + +HKU_API SelectorPtr operator-(const SelectorPtr& se1, const SelectorPtr& se2) { + return make_shared(se1, se2); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.h new file mode 100644 index 00000000..53de6d08 --- /dev/null +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-05-27 + * Author: fasiondog + */ + +#pragma once + +#include "OperatorSelector.h" + +namespace hku { + +class HKU_API OperatorSubSelector : public OperatorSelector { + OPERATOR_SELECTOR_IMP(OperatorSubSelector, "SE_Sub") + OPERATOR_SELECTOR_SERIALIZATION +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index 0e074faf..82fe9462 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -23,6 +23,84 @@ using namespace hku; * @{ */ +/** @par 检测点 */ +TEST_CASE("test_SE_AddValue") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + se = 3.0 + SEPtr(); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + se = se1 + SEPtr(); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 1.0); + CHECK_EQ(result[1].weight, 1.0); + CHECK_EQ(result[2].weight, 1.0); + + se = se1 + 2.0; + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 3.0); + CHECK_EQ(result[1].weight, 3.0); + CHECK_EQ(result[2].weight, 3.0); + + se = 3.0 + se1; + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 4.0); + CHECK_EQ(result[1].weight, 4.0); + CHECK_EQ(result[2].weight, 4.0); +} + /** @par 检测点 */ TEST_CASE("test_SE_Add") { StockManager& sm = StockManager::instance(); diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index e3075166..c7407681 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -156,6 +156,8 @@ void export_Selector(py::module& m) { .def("__add__", [](const SelectorPtr& self, double other) { return self + other; }) .def("__radd__", [](const SelectorPtr& self, double other) { return self + other; }) + .def("__sub__", + [](const SelectorPtr& self, const SelectorPtr& other) { return self - other; }) .def("__sub__", [](const SelectorPtr& self, double other) { return self - other; }) .def("__rsub__", [](const SelectorPtr& self, double other) { return other - self; }) @@ -164,6 +166,8 @@ void export_Selector(py::module& m) { .def("__mul__", [](const SelectorPtr& self, double other) { return self * other; }) .def("__rmul__", [](const SelectorPtr& self, double other) { return self * other; }) + .def("__truediv__", + [](const SelectorPtr& self, const SelectorPtr& other) { return self / other; }) .def("__truediv__", [](const SelectorPtr& self, double other) { return self / other; }) .def("__rtruediv__", [](const SelectorPtr& self, double other) { return other / self; }) From f30f15133df7d9020c2273b826dc24204e8668c9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 4 Jun 2024 16:00:11 +0800 Subject: [PATCH 345/601] =?UTF-8?q?add=20SE=20and=EF=BC=8Cor=20operator?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h | 8 ++++++++ hikyuu_pywrap/trade_sys/_Selector.cpp | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h index f1789460..4aa9a593 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Operator.h @@ -16,6 +16,14 @@ HKU_API SelectorPtr operator-(const SelectorPtr& se1, const SelectorPtr& se2); HKU_API SelectorPtr operator*(const SelectorPtr& se1, const SelectorPtr& se2); HKU_API SelectorPtr operator/(const SelectorPtr& se1, const SelectorPtr& se2); +inline SelectorPtr operator&(const SelectorPtr& se1, const SelectorPtr& se2) { + return se1 * se2; +} + +inline SelectorPtr operator|(const SelectorPtr& se1, const SelectorPtr& se2) { + return se1 + se2; +} + HKU_API SelectorPtr operator+(const SelectorPtr& se, double value); inline SelectorPtr operator+(double value, const SelectorPtr& se) { return se + value; diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index c7407681..9ae0dba9 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -171,6 +171,10 @@ void export_Selector(py::module& m) { .def("__truediv__", [](const SelectorPtr& self, double other) { return self / other; }) .def("__rtruediv__", [](const SelectorPtr& self, double other) { return other / self; }) + .def("__and__", + [](const SelectorPtr& self, const SelectorPtr& other) { return self & other; }) + .def("__or__", [](const SelectorPtr& self, const SelectorPtr& other) { return self | other; }) + DEF_PICKLE(SEPtr); m.def("SE_Fixed", py::overload_cast<>(SE_Fixed)); From 9609fecb8be893965df358212c56f6b887797c7f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 6 Jun 2024 12:45:31 +0800 Subject: [PATCH 346/601] =?UTF-8?q?fixed=20getSystemPartName/getSystemPart?= =?UTF-8?q?Enum=20=E7=BC=BA=E5=A4=B1=20PF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/system/SystemPart.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/SystemPart.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/SystemPart.cpp index 5c6668e2..57ed092f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/SystemPart.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/SystemPart.cpp @@ -30,6 +30,8 @@ string HKU_API getSystemPartName(int part) { return "SP"; case PART_ALLOCATEFUNDS: return "AF"; + case PART_PORTFOLIO: + return "PF"; default: return "--"; } @@ -47,6 +49,7 @@ SystemPart HKU_API getSystemPartEnum(const string& arg) { HKU_IF_RETURN("SP" == name, PART_SLIPPAGE); HKU_IF_RETURN("MM" == name, PART_MONEYMANAGER); HKU_IF_RETURN("AF" == name, PART_ALLOCATEFUNDS); + HKU_IF_RETURN("PF" == name, PART_PORTFOLIO); return PART_INVALID; } From 0d71d9af10ae502209fb4a934ec5a47325413626 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 6 Jun 2024 18:21:00 +0800 Subject: [PATCH 347/601] =?UTF-8?q?fixed=20PF=20=E5=A4=84=E7=90=86?= =?UTF-8?q?=E7=AB=8B=E5=8D=B3=E4=B9=B0=E5=85=A5/=E5=BB=B6=E8=BF=9F?= =?UTF-8?q?=E5=8D=96=E5=87=BA=E7=9A=84=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/portfolio/Portfolio.cpp | 15 ++++++++-- hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 27 +++++++++++++---- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 29 ++++++++++++------- 3 files changed, 54 insertions(+), 17 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index 25227021..87bdaf23 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -304,6 +304,17 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list); + //--------------------------------------------------- + // 检测当前运行中的系统是否存在延迟卖出信号,并在开盘时有效处理 + //--------------------------------------------------- + for (auto& sys : m_running_sys_set) { + auto tr = sys->pfProcessDelaySellRequest(date); + if (!tr.isNull()) { + HKU_INFO_IF(trace, "[PF] sell delay {}", tr); + m_tm->addTradeRecord(tr); + } + } + //--------------------------------------------------- // 调仓日,进行资金分配调整 //--------------------------------------------------- @@ -311,10 +322,10 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool // 从选股策略获取选中的系统列表 m_tmp_selected_list = m_se->getSelected(date); - // 如果选中的系统不在已有列表中, 则先清除其延迟操作,防止在调仓日出现未来信号 + // 如果选中的系统不在已有列表中, 则先清除其延迟买入操作,防止在调仓日出现未来信号 for (auto& sys : m_tmp_selected_list) { if (m_running_sys_set.find(sys.sys) == m_running_sys_set.end()) { - sys.sys->clearDelayRequest(); + sys.sys->clearDelayBuyRequest(); } } diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index ebcb7e45..bef25c41 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -406,11 +406,12 @@ void System::run(const KData& kdata, bool reset, bool resetAll) { m_calculated = true; } -void System::clearDelayRequest() { +void System::clearDelayBuyRequest() { m_buyRequest.clear(); - m_sellRequest.clear(); - m_sellShortRequest.clear(); - m_buyShortRequest.clear(); +} + +bool System::haveDelaySellRequest() const { + return m_sellRequest.valid; } TradeRecord System::runMoment(const Datetime& datetime) { @@ -767,12 +768,16 @@ TradeRecord System::_sellForce(const Datetime& date, double num, Part from, bool } TradeRecord System::_sell(const KRecord& today, const KRecord& src_today, Part from) { + bool trace = getParam("trace"); TradeRecord result; if (getParam("sell_delay")) { _submitSellRequest(today, src_today, from); + HKU_INFO_IF(trace, "[{}] will be delay to sell", name()); return result; } else { - return _sellNow(today, src_today, from); + result = _sellNow(today, src_today, from); + HKU_INFO_IF(trace, "[{}] sell now: {}", name(), result); + return result; } } @@ -817,6 +822,9 @@ TradeRecord System::_sellNow(const KRecord& today, const KRecord& src_today, Par } TradeRecord System::_sellDelay(const KRecord& today, const KRecord& src_today) { + bool trace = getParam("trace"); + HKU_INFO_IF(trace, "[{}] process _sellDelay request", name()); + TradeRecord result; if (today.highPrice == today.lowPrice && !getParam("can_trade_when_high_eq_low")) { // 无法执行,保留卖出请求,继续延迟至下一时刻 @@ -1175,6 +1183,15 @@ TradeRecord System::_processRequest(const KRecord& today, const KRecord& src_tod return TradeRecord(); } +TradeRecord System::pfProcessDelaySellRequest(const Datetime& date) { + HKU_IF_RETURN(!m_sellRequest.valid, TradeRecord()); + size_t pos = m_kdata.getPos(date); + HKU_IF_RETURN(pos == Null(), TradeRecord()); + KRecord today = m_kdata.getKRecord(pos); + KRecord src_today = m_src_kdata.getKRecord(pos); + return _sellDelay(today, src_today); +} + price_t System::_getStoplossPrice(const KRecord& today, const KRecord& src_today, price_t price) { HKU_IF_RETURN(!m_st, 0.0); HKU_IF_RETURN(today.highPrice == today.lowPrice, src_today.lowPrice); diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index fe949ae9..3ff20618 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -25,12 +25,17 @@ namespace hku { +class HKU_API Portfolio; +class HKU_API AllocateFundsBase; + /** * 交易系统基类 * @ingroup System */ class HKU_API System { PARAMETER_SUPPORT_WITH_CHECK + friend class HKU_API Portfolio; + friend class HKU_API AllocateFundsBase; public: /** 默认构造函数 */ @@ -200,12 +205,6 @@ public: */ TradeRecord runMoment(const Datetime& datetime); - // 清除已有的交易请求,供Portfolio使用 - void clearDelayRequest(); - - // 当前是否存在延迟的操作请求,供Portfolio - bool haveDelayRequest() const; - // 运行前准备工作, 失败将抛出异常 void readyForRun(); @@ -213,6 +212,12 @@ public: return _sell(today, src_today, from); } + // 由各个相关组件调用,用于组件参数变化时通知 sys,以便重算 + void partChangedNotify() { + m_calculated = false; + } + +private: // 强制以开盘价卖出,仅供 PF/AF 内部调用 TradeRecord sellForceOnOpen(const Datetime& date, double num, Part from) { HKU_ASSERT(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO); @@ -225,10 +230,14 @@ public: return _sellForce(date, num, from, false); } - // 由各个相关组件调用,用于组件参数变化时通知 sys,以便重算 - void partChangedNotify() { - m_calculated = false; - } + // 清除已有的交易请求,供Portfolio使用 + void clearDelayBuyRequest(); + + // 当前是否存在延迟的操作请求,供Portfolio + bool haveDelaySellRequest() const; + + // 处理延迟买入请求,仅供 PF 调用 + TradeRecord pfProcessDelaySellRequest(const Datetime& date); private: bool _environmentIsValid(const Datetime& datetime); From 0530ef0eab2625f10bf1a5229979f3577f4b9785 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 6 Jun 2024 23:10:38 +0800 Subject: [PATCH 348/601] update github ci --- .github/workflows/windows_python.yml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 2d8b6f54..403b5b95 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -24,23 +24,9 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Cache windows packages - id: cache-xmake-windows - uses: actions/cache@v4 - env: - cache-name: cache-windows-modules - with: - path: /Users/%USERNAME/.xmake - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- - ${{ runner.os }}-build- - ${{ runner.os }}- - - uses: xmake-io/github-action-setup-xmake@v1 with: xmake-version: 2.9.1 - actions-cache-folder: '.xmake-cache' - name: configure shell: cmd From fb25e00dee45b49d6fe9592cc2052d7b8e1b6b5b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 7 Jun 2024 00:39:51 +0800 Subject: [PATCH 349/601] =?UTF-8?q?fixed=20analysis=20=E5=9C=A8=20k=20?= =?UTF-8?q?=E7=BA=BF=E6=97=A0=E6=95=B0=E6=8D=AE=E6=97=B6=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/analysis/analysis.py | 11 ++++++----- hikyuu_cpp/hikyuu/trade_manage/Performance.cpp | 15 +++++---------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/hikyuu/analysis/analysis.py b/hikyuu/analysis/analysis.py index 16b1185b..c4b813f1 100644 --- a/hikyuu/analysis/analysis.py +++ b/hikyuu/analysis/analysis.py @@ -109,11 +109,12 @@ def analysis_sys_list(stks, query, sys_proto, keys=["累计投入本金", "当 k = stk.get_kdata(query) my_sys = sys_proto.clone() my_sys.run(k, reset_all=True) - per.statistics(my_sys.tm, k[-1].datetime if len(k) > 0 else Datetime()) - ret["证券代码"].append(stk.market_code) - ret["证券名称"].append(stk.name) - for key in keys: - ret[key].append(per[key]) + if len(k) > 0: + per.statistics(my_sys.tm, k[-1].datetime) + ret["证券代码"].append(stk.market_code) + ret["证券名称"].append(stk.name) + for key in keys: + ret[key].append(per[key]) return pd.DataFrame(ret) diff --git a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp index 8490b8ce..73af5181 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/Performance.cpp @@ -128,19 +128,14 @@ string Performance::report(const TradeManagerPtr& tm, const Datetime& datetime) return buf.str(); } -void Performance ::statistics(const TradeManagerPtr& tm, const Datetime& datetime) { +void Performance::statistics(const TradeManagerPtr& tm, const Datetime& datetime) { // 清除上次统计结果 reset(); - if (!tm) { - HKU_INFO("TradeManagerPtr is Null!"); - return; - } - - if (datetime < tm->lastDatetime()) { - HKU_ERROR("datetime must >= tm->lastDatetime !"); - return; - } + HKU_INFO_IF_RETURN(!tm, void(), "TradeManagerPtr is Null!"); + HKU_ERROR_IF_RETURN(datetime.isNull(), void(), "Invalid input datetime"); + HKU_ERROR_IF_RETURN(datetime < tm->lastDatetime(), void(), + "datetime must >= tm->lastDatetime !"); int precision = tm->precision(); m_result["帐户初始金额"] = tm->initCash(); From be42ff19020e6990a88236403617255e25d88fe6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 7 Jun 2024 12:21:46 +0800 Subject: [PATCH 350/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20HikyuuTDX=EF=BC=8C?= =?UTF-8?q?=E9=81=BF=E5=85=8D=E7=9B=AE=E5=BD=95=E4=B8=8D=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E6=97=B6=E5=AF=BC=E5=85=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/HikyuuTDX.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index b6c63e2d..18770211 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -675,6 +675,12 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): @pyqtSlot() def on_start_import_pushButton_clicked(self): + try: + self.saveConfig() + except Exception as e: + QMessageBox.about(self, "保存配置信息失败", str(e)) + return + config = self.getCurrentConfig() if config.getboolean('hdf5', 'enable') \ and (not os.path.lexists(config['hdf5']['dir']) or not os.path.isdir(config['hdf5']['dir'])): @@ -686,11 +692,6 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): or not os.path.isdir(config['tdx']['dir'])): QMessageBox.about(self, "错误", "请确认通达信安装目录是否正确!") return - try: - self.saveConfig() - except Exception as e: - QMessageBox.about(self, "保存配置信息失败", str(e)) - return self.import_running = True self.start_import_pushButton.setEnabled(False) From b718810d1ec1681baf3225acca5acd2170c20b1c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 8 Jun 2024 02:20:38 +0800 Subject: [PATCH 351/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20SE=5FMultiFactor?= =?UTF-8?q?=20=E4=BB=A5=E6=9B=B4=E5=A5=BD=E7=9A=84=E9=80=82=E5=BA=94=20PF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/crt/SE_MultiFactor.h | 4 ++ .../selector/imp/MultiFactorSelector.cpp | 70 ++++++++++++++++++- .../selector/imp/MultiFactorSelector.h | 6 ++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h index 77f209ce..a83fe7f9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h @@ -39,4 +39,8 @@ SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, const StockLis int ic_rolling_n = 120, const Stock& ref_stk = Stock(), const string& mode = "MF_ICIRWeight"); +SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, int topn = 10, int ic_n = 5, + int ic_rolling_n = 120, const Stock& ref_stk = Stock(), + const string& mode = "MF_ICIRWeight"); + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index f4d55ce8..34b2e350 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -18,6 +18,11 @@ MultiFactorSelector::MultiFactorSelector() : SelectorBase("SE_MultiFactor") { // 只选择发出买入信号的系统,此时选中的系统会变成资产平均分配,参考 AF 参数:ignore_zero_weight setParam("only_should_buy", false); setParam("topn", 10); + setParam("ic_n", 5); + setParam("ic_rolling_n", 120); + setParam("query", KQuery()); + setParam("ref_stk", Stock()); + setParam("mode", "MF_ICIRWeight"); } MultiFactorSelector::MultiFactorSelector(const MFPtr& mf, int topn) @@ -25,7 +30,14 @@ MultiFactorSelector::MultiFactorSelector(const MFPtr& mf, int topn) HKU_CHECK(mf, "mf is null!"); setParam("only_should_buy", false); setParam("topn", topn); - checkParam("topn"); + + setParam("ic_n", mf->getParam("ic_n")); + setParam("query", mf->getQuery()); + setParam("ref_stk", mf->getRefStock()); + if (mf->haveParam("ic_rolling_n")) { + setParam("ic_rolling_n", mf->getParam("ic_rolling_n")); + } + setParam("mode", mf->name()); } MultiFactorSelector::~MultiFactorSelector() {} @@ -34,6 +46,10 @@ void MultiFactorSelector::_checkParam(const string& name) const { if ("topn" == name) { int topn = getParam("topn"); HKU_ASSERT(topn > 0); + } else if ("ic_n" == name) { + HKU_ASSERT(getParam("ic_n") >= 1); + } else if ("ic_rolling_n" == name) { + HKU_ASSERT(getParam("ic_rolling_n") >= 1); } } @@ -48,6 +64,9 @@ SelectorPtr MultiFactorSelector::_clone() { auto p = make_shared(); p->m_mf = m_mf->clone(); p->m_stk_sys_dict = m_stk_sys_dict; + for (const auto& ind : m_inds) { + p->m_inds.emplace_back(ind.clone()); + } return p; } @@ -76,6 +95,42 @@ SystemWeightList MultiFactorSelector::getSelected(Datetime date) { } void MultiFactorSelector::_calculate() { + Stock ref_stk = getParam("ref_stk"); + if (ref_stk.isNull()) { + ref_stk = getStock("sh000300"); + } + + StockList stks; + for (const auto& sys : m_pro_sys_list) { + stks.emplace_back(sys->getStock()); + } + + auto query = getParam("query"); + auto ic_n = getParam("ic_n"); + auto ic_rolling_n = getParam("ic_rolling_n"); + auto mode = getParam("mode"); + + if (!m_mf) { + if ("MF_ICIRWeight" == mode) { + m_mf = MF_ICIRWeight(m_inds, stks, query, ref_stk, ic_n, ic_rolling_n); + } else if ("MF_ICWeight" == mode) { + m_mf = MF_ICWeight(m_inds, stks, query, ref_stk, ic_n, ic_rolling_n); + } else if ("MF_EqualWeight" == mode) { + m_mf = MF_EqualWeight(m_inds, stks, query, ref_stk, ic_n); + } else { + HKU_THROW("Invalid mode: {}", mode); + } + } else { + m_mf->setRefIndicators(m_inds); + m_mf->setRefStock(ref_stk); + m_mf->setStockList(stks); + m_mf->setQuery(query); + m_mf->setParam("ic_n", ic_n); + if (m_mf->haveParam("ic_rolling_n")) { + m_mf->setParam("ic_rolling_n", ic_rolling_n); + } + } + for (const auto& sys : m_real_sys_list) { m_stk_sys_dict[sys->getStock()] = sys; } @@ -85,6 +140,19 @@ SelectorPtr HKU_API SE_MultiFactor(const MFPtr& mf, int topn) { return make_shared(mf, topn); } +SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, int topn = 10, int ic_n = 5, + int ic_rolling_n = 120, const Stock& ref_stk = Stock(), + const string& mode = "MF_ICIRWeight") { + auto p = make_shared(); + p->setIndicators(src_inds); + p->setParam("topn", topn); + p->setParam("ic_n", ic_n); + p->setParam("ic_rolling_n", ic_rolling_n); + p->setParam("ref_stock", ref_stk); + p->setParam("mode", mode); + return p; +} + SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, const StockList& stks, const KQuery& query, int topn, int ic_n, int ic_rolling_n, const Stock& ref_stk, const string& mode) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h index 1df475df..77144171 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h @@ -25,7 +25,12 @@ public: virtual bool isMatchAF(const AFPtr& af) override; virtual void _calculate() override; + void setIndicators(const IndicatorList& inds) { + m_inds = inds; + } + private: + IndicatorList m_inds; MFPtr m_mf; unordered_map m_stk_sys_dict; @@ -37,6 +42,7 @@ private: template void serialize(Archive& ar, const unsigned int version) { ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_inds); ar& BOOST_SERIALIZATION_NVP(m_mf); } #endif From 8e09ef541fe7ce092713fd23697c873d21ddac5f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 8 Jun 2024 11:00:08 +0800 Subject: [PATCH 352/601] update --- .../trade_sys/selector/imp/MultiFactorSelector.cpp | 3 +++ hikyuu_pywrap/trade_sys/_Selector.cpp | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index 34b2e350..a8fad58e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -50,6 +50,9 @@ void MultiFactorSelector::_checkParam(const string& name) const { HKU_ASSERT(getParam("ic_n") >= 1); } else if ("ic_rolling_n" == name) { HKU_ASSERT(getParam("ic_rolling_n") >= 1); + } else if ("mode" == name) { + auto mode = getParam("mode"); + HKU_ASSERT("MF_ICIRWeight" == mode || "MF_ICWeight" == mode || "MF_EqualWeight" == mode); } } diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index b5eddf2f..b3006f99 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -167,6 +167,16 @@ void export_Selector(py::module& m) { m.def("SE_MultiFactor", py::overload_cast(SE_MultiFactor), py::arg("mf"), py::arg("topn") = 10); + m.def( + "SE_MultiFactor", + [](const py::sequence& inds, int topn, int ic_n, int ic_rolling_n, const py::object& ref_stk, + const string& mode) { + IndicatorList c_inds = python_list_to_vector(inds); + Stock c_ref_stk = ref_stk.is_none() ? getStock("sh000300") : ref_stk.cast(); + return SE_MultiFactor(c_inds, topn, ic_n, ic_rolling_n, c_ref_stk, mode); + }, + py::arg("inds"), py::arg("topn") = 10, py::arg("ic_n") = 5, py::arg("ic_rolling_n") = 120, + py::arg("ref_stk") = py::none(), py::arg("mode") = "MF_ICIRWeight"); m.def( "SE_MultiFactor", [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, int topn, From 04e8b3a846fa2415a51f90f33ed9cbda9d0fdd0d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Jun 2024 01:34:53 +0800 Subject: [PATCH 353/601] =?UTF-8?q?=E4=BC=98=E5=8C=96SE=5FMultiFactor?= =?UTF-8?q?=E6=9B=B4=E9=80=82=E5=BA=94=20pf=20=E4=B8=8E=20hub=20=E6=96=B9?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/crt/SE_MultiFactor.h | 7 ------- .../selector/imp/MultiFactorSelector.cpp | 18 ------------------ hikyuu_pywrap/trade_sys/_Selector.cpp | 19 ++----------------- 3 files changed, 2 insertions(+), 42 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h index a83fe7f9..5194ce97 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h @@ -24,8 +24,6 @@ SelectorPtr HKU_API SE_MultiFactor(const MFPtr& mf, int topn = 10); /** * 基于 MultiFactor 选股算法 * @param src_inds 原始因子公式 - * @param stks 证券列表 - * @param query 查询条件 * @param topn 只选取时间截面中前 topn 个系统 * @param ic_n ic 对应的 ic_n 日收益率 * @param ic_rolling_n 计算滚动 IC (即 IC 的 n 日移动平均)周期 @@ -34,11 +32,6 @@ SelectorPtr HKU_API SE_MultiFactor(const MFPtr& mf, int topn = 10); * @return SelectorPtr * @ingroup Selector */ -SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, const StockList& stks, - const KQuery& query, int topn = 10, int ic_n = 5, - int ic_rolling_n = 120, const Stock& ref_stk = Stock(), - const string& mode = "MF_ICIRWeight"); - SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, int topn = 10, int ic_n = 5, int ic_rolling_n = 120, const Stock& ref_stk = Stock(), const string& mode = "MF_ICIRWeight"); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index a8fad58e..3b9b82f4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -156,22 +156,4 @@ SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, int topn = 10, return p; } -SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, const StockList& stks, - const KQuery& query, int topn, int ic_n, int ic_rolling_n, - const Stock& ref_stk, const string& mode) { - Stock n_ref_stk = ref_stk.isNull() ? getStock("sh000300") : ref_stk; - MFPtr mf; - if ("MF_ICIRWeight" == mode) { - mf = MF_ICIRWeight(src_inds, stks, query, n_ref_stk, ic_n, ic_rolling_n); - } else if ("MF_ICWeight" == mode) { - mf = MF_ICWeight(src_inds, stks, query, n_ref_stk, ic_n, ic_rolling_n); - } else if ("MF_EqualWeight" == mode) { - mf = MF_EqualWeight(src_inds, stks, query, n_ref_stk, ic_n); - } else { - HKU_THROW("Invalid mode: {}", mode); - } - - return make_shared(mf, topn); -} - } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index b3006f99..dca2d247 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -176,19 +176,7 @@ void export_Selector(py::module& m) { return SE_MultiFactor(c_inds, topn, ic_n, ic_rolling_n, c_ref_stk, mode); }, py::arg("inds"), py::arg("topn") = 10, py::arg("ic_n") = 5, py::arg("ic_rolling_n") = 120, - py::arg("ref_stk") = py::none(), py::arg("mode") = "MF_ICIRWeight"); - m.def( - "SE_MultiFactor", - [](const py::sequence& inds, const py::sequence& stks, const KQuery& query, int topn, - int ic_n, int ic_rolling_n, const py::object& ref_stk, const string& mode) { - IndicatorList c_inds = python_list_to_vector(inds); - StockList c_stks = python_list_to_vector(stks); - Stock c_ref_stk = ref_stk.is_none() ? getStock("sh000300") : ref_stk.cast(); - return SE_MultiFactor(c_inds, c_stks, query, topn, ic_n, ic_rolling_n, c_ref_stk, mode); - }, - py::arg("inds"), py::arg("stks"), py::arg("query"), py::arg("topn") = 10, py::arg("ic_n") = 5, - py::arg("ic_rolling_n") = 120, py::arg("ref_stk") = py::none(), - py::arg("mode") = "MF_ICIRWeight", + py::arg("ref_stk") = py::none(), py::arg("mode") = "MF_ICIRWeight", R"(SE_MultiFactor 创建基于多因子评分的选择器,两种创建方式 @@ -199,11 +187,8 @@ void export_Selector(py::module& m) { - 参数直接创建: :param sequense(Indicator) inds: 原始因子列表 - :param sequense(stock) stks: 计算证券列表 - :param Query query: 日期范围 :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 - :param str mode: "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称 - )"); + :param str mode: "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称)"); } From 1b906f468d7b8f8d6265284c3c1cedffdc60db46 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Jun 2024 01:45:31 +0800 Subject: [PATCH 354/601] update --- docs/source/trade_portfolio/selector.rst | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/source/trade_portfolio/selector.rst b/docs/source/trade_portfolio/selector.rst index ef5bcfab..a11a09eb 100644 --- a/docs/source/trade_portfolio/selector.rst +++ b/docs/source/trade_portfolio/selector.rst @@ -1,8 +1,8 @@ .. py:currentmodule:: hikyuu.trade_sys .. highlight:: python -选择器策略 -============= +选择器算法组件 +================ 实现标的、系统策略的评估和选取算法。 @@ -29,6 +29,20 @@ :param System sys: 系统策略原型 :return: SE选择器实例 +.. py:function:: SE_MultiFactor(inds[, topn=10, ic_n=5, ic_rolling_n=120, ref_stk=None, mode="MF_ICIRWeight"]) + + 创建基于多因子评分的选择器,两种创建方式: + + - 直接指定 MF: SE_MultiFactor(mf, topn=10) + - 参数直接创建: SE_MultiFactor(inds, topn=10, ic_n=5, ic_rolling_n=120, ref_stk=None, mode="MF_ICIRWeight") + + :param sequense(Indicator) inds: 原始因子列表 + :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) + :param int ic_n: 默认 IC 对应的 N 日收益率 + :param int ic_rolling_n: IC 滚动周期 + :param str mode: "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称 + :return: SE选择器实例 + 自定义选择器策略 -------------------- From 917171c3a0d5cc33be86c9ddc267dda6b4bcefa0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Jun 2024 17:07:18 +0800 Subject: [PATCH 355/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20MultiFactorSelecto?= =?UTF-8?q?r,=20=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/crt/SE_MultiFactor.h | 2 +- .../selector/imp/MultiFactorSelector.cpp | 26 ++++--- .../selector/imp/MultiFactorSelector.h | 1 + .../trade_sys/selector/test_SE_Fixed.cpp | 43 +++++++++++- .../selector/test_SE_MultiFactor.cpp | 66 ++++++++++++++++++ .../hikyuu/trade_sys/selector/test_export.cpp | 68 ------------------- hikyuu_pywrap/trade_sys/_Selector.cpp | 3 +- 7 files changed, 130 insertions(+), 79 deletions(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp delete mode 100644 hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h index 5194ce97..e75ee61d 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_MultiFactor.h @@ -24,7 +24,7 @@ SelectorPtr HKU_API SE_MultiFactor(const MFPtr& mf, int topn = 10); /** * 基于 MultiFactor 选股算法 * @param src_inds 原始因子公式 - * @param topn 只选取时间截面中前 topn 个系统 + * @param topn 只选取时间截面中前 topn 个系统,小于等于0时代表不限制 * @param ic_n ic 对应的 ic_n 日收益率 * @param ic_rolling_n 计算滚动 IC (即 IC 的 n 日移动平均)周期 * @param ref_stk 参照对比证券,未指定时,默认使用 sh000300 沪深300指数 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index 3b9b82f4..59710ccb 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -17,6 +17,7 @@ namespace hku { MultiFactorSelector::MultiFactorSelector() : SelectorBase("SE_MultiFactor") { // 只选择发出买入信号的系统,此时选中的系统会变成资产平均分配,参考 AF 参数:ignore_zero_weight setParam("only_should_buy", false); + setParam("ignore_null", true); // 是否忽略 MF 中 score 值为 nan 的证券 setParam("topn", 10); setParam("ic_n", 5); setParam("ic_rolling_n", 120); @@ -29,6 +30,7 @@ MultiFactorSelector::MultiFactorSelector(const MFPtr& mf, int topn) : SelectorBase("SE_MultiFactor"), m_mf(mf) { HKU_CHECK(mf, "mf is null!"); setParam("only_should_buy", false); + setParam("ignore_null", true); setParam("topn", topn); setParam("ic_n", mf->getParam("ic_n")); @@ -43,10 +45,7 @@ MultiFactorSelector::MultiFactorSelector(const MFPtr& mf, int topn) MultiFactorSelector::~MultiFactorSelector() {} void MultiFactorSelector::_checkParam(const string& name) const { - if ("topn" == name) { - int topn = getParam("topn"); - HKU_ASSERT(topn > 0); - } else if ("ic_n" == name) { + if ("ic_n" == name) { HKU_ASSERT(getParam("ic_n") >= 1); } else if ("ic_rolling_n" == name) { HKU_ASSERT(getParam("ic_rolling_n") >= 1); @@ -79,8 +78,19 @@ bool MultiFactorSelector::isMatchAF(const AFPtr& af) { SystemWeightList MultiFactorSelector::getSelected(Datetime date) { SystemWeightList ret; - auto scores = m_mf->getScores(date, 0, getParam("topn"), - [](const ScoreRecord& sc) { return !std::isnan(sc.value); }); + int topn = getParam("topn"); + if (topn <= 0) { + topn = std::numeric_limits::max(); + } + + ScoreRecordList scores; + if (getParam("ignore_null")) { + scores = m_mf->getScores(date, 0, getParam("topn"), + [](const ScoreRecord& sc) { return !std::isnan(sc.value); }); + } else { + scores = m_mf->getScores(date, 0, getParam("topn")); + } + if (getParam("only_should_buy")) { for (const auto& sc : scores) { auto sys = m_stk_sys_dict[sc.stock]; @@ -124,10 +134,10 @@ void MultiFactorSelector::_calculate() { HKU_THROW("Invalid mode: {}", mode); } } else { + m_mf->setQuery(query); m_mf->setRefIndicators(m_inds); m_mf->setRefStock(ref_stk); m_mf->setStockList(stks); - m_mf->setQuery(query); m_mf->setParam("ic_n", ic_n); if (m_mf->haveParam("ic_rolling_n")) { m_mf->setParam("ic_rolling_n", ic_rolling_n); @@ -151,7 +161,7 @@ SelectorPtr HKU_API SE_MultiFactor(const IndicatorList& src_inds, int topn = 10, p->setParam("topn", topn); p->setParam("ic_n", ic_n); p->setParam("ic_rolling_n", ic_rolling_n); - p->setParam("ref_stock", ref_stk); + p->setParam("ref_stk", ref_stk); p->setParam("mode", mode); return p; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h index 77144171..27b18dc4 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.h @@ -26,6 +26,7 @@ public: virtual void _calculate() override; void setIndicators(const IndicatorList& inds) { + HKU_ASSERT(!inds.empty()); m_inds = inds; } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp index a29a478a..b07e230c 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Fixed.cpp @@ -7,6 +7,7 @@ #include "doctest/doctest.h" #include +#include #include #include #include @@ -17,7 +18,7 @@ using namespace hku; /** - * @defgroup test_Selector test_Selector + * @defgroup test_SE_Fixed test_SE_Fixed * @ingroup test_hikyuu_trade_sys_suite * @{ */ @@ -80,4 +81,44 @@ TEST_CASE("test_SE_Fixed") { CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); } +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_SE_Fixed_export") { + StockManager& sm = StockManager::instance(); + string filename(sm.tmpdir()); + filename += "/SE_FIXED.xml"; + + TMPtr tm = crtTM(Datetime(20010101), 100000); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + SYSPtr sys = SYS_Simple(); + sys->setTM(tm); + sys->setSG(sg); + sys->setMM(mm); + StockList stkList; + stkList.push_back(sm["sh600000"]); + stkList.push_back(sm["sz000001"]); + + SEPtr se1 = SE_Fixed(stkList, sys); + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(se1); + } + + SEPtr se2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(se2); + } + + CHECK_EQ(se1->name(), se2->name()); +} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + /** @} */ diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp new file mode 100644 index 00000000..c51fca05 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-06-09 + * Author: fasiondog + */ + +#include "doctest/doctest.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace hku; + +/** + * @defgroup test_SE_MultiFactor test_SE_MultiFactor + * @ingroup test_hikyuu_trade_sys_suite + * @{ + */ + +/** @par 检测点 */ +TEST_CASE("test_SE_MultiFactor") { + StockManager& sm = StockManager::instance(); + StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + Stock ref_stk = sm["sh000001"]; + KQuery query = KQuery(-100); + IndicatorList src_inds{MA(CLOSE()), EMA(CLOSE())}; + + auto sys = SYS_Simple(crtTM(), MM_Nothing()); + sys->setSG(SG_Cycle()); + sys->setParam("buy_delay", false); + + /** @arg 测试试图修改参数值为非法值 */ + auto ret = SE_MultiFactor(src_inds); + CHECK_THROWS(ret->setParam("ic_n", 0)); + CHECK_THROWS(ret->setParam("ic_rolling_n", 0)); + CHECK_THROWS(ret->setParam("mode", "MF")); + + /** @arg src_inds 为空,其余为默认参数 */ + CHECK_THROWS(SE_MultiFactor(IndicatorList{})); + + /** @arg 默认参数 */ + ret = SE_MultiFactor(src_inds, 10, 5, 120, ref_stk); + ret->addStockList(stks, sys); + auto proto_list = ret->getProtoSystemList(); + ret->calculate(proto_list, query); +} + +//----------------------------------------------------------------------------- +// test export +//----------------------------------------------------------------------------- +#if HKU_SUPPORT_SERIALIZATION + +/** @par 检测点 */ +TEST_CASE("test_SE_MultiFactor_export") {} +#endif /* #if HKU_SUPPORT_SERIALIZATION */ + +/** @} */ \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp deleted file mode 100644 index f2287fc4..00000000 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_export.cpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - * test_export.cpp - * - * Created on: 2018-2-10 - * Author: fasiondog - */ -#include "doctest/doctest.h" -#include - -#if HKU_SUPPORT_SERIALIZATION - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace hku; - -/** - * @defgroup test_selector_serialization test_selector_serialization - * @ingroup test_hikyuu_trade_sys_suite - * @{ - */ - -/** @par 检测点 */ -TEST_CASE("test_SE_FIXED_export") { - StockManager& sm = StockManager::instance(); - string filename(sm.tmpdir()); - filename += "/SE_FIXED.xml"; - - TMPtr tm = crtTM(Datetime(20010101), 100000); - SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); - MMPtr mm = MM_FixedCount(100); - SYSPtr sys = SYS_Simple(); - sys->setTM(tm); - sys->setSG(sg); - sys->setMM(mm); - StockList stkList; - stkList.push_back(sm["sh600000"]); - stkList.push_back(sm["sz000001"]); - - SEPtr se1 = SE_Fixed(stkList, sys); - { - std::ofstream ofs(filename); - boost::archive::xml_oarchive oa(ofs); - oa << BOOST_SERIALIZATION_NVP(se1); - } - - SEPtr se2; - { - std::ifstream ifs(filename); - boost::archive::xml_iarchive ia(ifs); - ia >> BOOST_SERIALIZATION_NVP(se2); - } - - CHECK_EQ(se1->name(), se2->name()); -} - -/** @} */ - -#endif /* HKU_SUPPORT_SERIALIZATION */ diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index dca2d247..8c61b3b9 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -187,8 +187,9 @@ void export_Selector(py::module& m) { - 参数直接创建: :param sequense(Indicator) inds: 原始因子列表 - :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) + :param int topn: 只选取时间截面中前 topn 个系统,小于等于0时代表不限制 :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 + :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) :param str mode: "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称)"); } From 74acf9bc8c6f3d61374634db3301a4bf04c591a3 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Jun 2024 17:39:43 +0800 Subject: [PATCH 356/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/test_SE_MultiFactor.cpp | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp index c51fca05..e613b78e 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp @@ -31,7 +31,7 @@ TEST_CASE("test_SE_MultiFactor") { StockManager& sm = StockManager::instance(); StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; Stock ref_stk = sm["sh000001"]; - KQuery query = KQuery(-100); + KQuery query = KQueryByDate(Datetime(20110712), Datetime(20111206)); IndicatorList src_inds{MA(CLOSE()), EMA(CLOSE())}; auto sys = SYS_Simple(crtTM(), MM_Nothing()); @@ -52,6 +52,20 @@ TEST_CASE("test_SE_MultiFactor") { ret->addStockList(stks, sys); auto proto_list = ret->getProtoSystemList(); ret->calculate(proto_list, query); + auto sw_list = ret->getSelected(Datetime(20110712)); + CHECK_EQ(sw_list.size(), 4); + + /** @arg topn = 2 */ + ret = SE_MultiFactor(src_inds, 2, 5, 120, ref_stk); + ret->addStockList(stks, sys); + proto_list = ret->getProtoSystemList(); + ret->calculate(proto_list, query); + sw_list = ret->getSelected(Datetime(20110712)); + CHECK_EQ(sw_list.size(), 2); + + // for (const auto& sw : sw_list) { + // HKU_INFO("{} {}", sw.sys->name(), sw.weight); + // } } //----------------------------------------------------------------------------- From 4b9057e44f0e76e71748ae1d36ece68711785519 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 9 Jun 2024 17:56:23 +0800 Subject: [PATCH 357/601] update --- docs/source/trade_portfolio/selector.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/source/trade_portfolio/selector.rst b/docs/source/trade_portfolio/selector.rst index a11a09eb..2e26938a 100644 --- a/docs/source/trade_portfolio/selector.rst +++ b/docs/source/trade_portfolio/selector.rst @@ -37,9 +37,10 @@ - 参数直接创建: SE_MultiFactor(inds, topn=10, ic_n=5, ic_rolling_n=120, ref_stk=None, mode="MF_ICIRWeight") :param sequense(Indicator) inds: 原始因子列表 - :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) + :param int topn: 只选取时间截面中前 topn 个系统, 小于等于0时代表不限制 :param int ic_n: 默认 IC 对应的 N 日收益率 :param int ic_rolling_n: IC 滚动周期 + :param Stock ref_stk: 参考证券 (未指定时,默认为 sh000300 沪深300) :param str mode: "MF_ICIRWeight" | "MF_ICWeight" | "MF_EqualWeight" 因子合成算法名称 :return: SE选择器实例 From e008cea5d859600c5e48193e719c06356c67e33f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 10 Jun 2024 01:20:51 +0800 Subject: [PATCH 358/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=EF=BC=8Cfixed=20System=20=E5=BA=8F=E5=88=97=E5=8C=96=E5=AD=98?= =?UTF-8?q?=E5=82=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/system/System.h | 5 +- .../selector/test_SE_MultiFactor.cpp | 55 ++++++++++++++++++- 2 files changed, 54 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.h b/hikyuu_cpp/hikyuu/trade_sys/system/System.h index 3ff20618..ea3a4eb9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.h +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.h @@ -347,8 +347,8 @@ private: ar& BOOST_SERIALIZATION_NVP(m_pg); ar& BOOST_SERIALIZATION_NVP(m_sp); - // m_kdata中包含了stock和query的信息,不用保存m_stock ar& BOOST_SERIALIZATION_NVP(m_kdata); + ar& BOOST_SERIALIZATION_NVP(m_stock); ar& BOOST_SERIALIZATION_NVP(m_calculated); ar& BOOST_SERIALIZATION_NVP(m_pre_ev_valid); @@ -381,9 +381,8 @@ private: ar& BOOST_SERIALIZATION_NVP(m_pg); ar& BOOST_SERIALIZATION_NVP(m_sp); - // m_kdata中包含了stock和query的信息,不用保存m_stock ar& BOOST_SERIALIZATION_NVP(m_kdata); - m_stock = m_kdata.getStock(); + ar& BOOST_SERIALIZATION_NVP(m_stock); ar& BOOST_SERIALIZATION_NVP(m_calculated); ar& BOOST_SERIALIZATION_NVP(m_pre_ev_valid); diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp index e613b78e..db0218ff 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_MultiFactor.cpp @@ -32,7 +32,7 @@ TEST_CASE("test_SE_MultiFactor") { StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; Stock ref_stk = sm["sh000001"]; KQuery query = KQueryByDate(Datetime(20110712), Datetime(20111206)); - IndicatorList src_inds{MA(CLOSE()), EMA(CLOSE())}; + IndicatorList src_inds{MA(CLOSE())}; auto sys = SYS_Simple(crtTM(), MM_Nothing()); sys->setSG(SG_Cycle()); @@ -48,13 +48,29 @@ TEST_CASE("test_SE_MultiFactor") { CHECK_THROWS(SE_MultiFactor(IndicatorList{})); /** @arg 默认参数 */ - ret = SE_MultiFactor(src_inds, 10, 5, 120, ref_stk); + ret = SE_MultiFactor(src_inds, 10, 5, 120, ref_stk, "MF_ICIRWeight"); ret->addStockList(stks, sys); auto proto_list = ret->getProtoSystemList(); ret->calculate(proto_list, query); auto sw_list = ret->getSelected(Datetime(20110712)); CHECK_EQ(sw_list.size(), 4); +#if 0 + ret = SE_MultiFactor(src_inds, 10, 5, 120, ref_stk, "MF_EqualWeight"); + ret->addStockList(stks, sys); + proto_list = ret->getProtoSystemList(); + ret->calculate(proto_list, query); + sw_list = ret->getSelected(Datetime(20110712)); + CHECK_EQ(sw_list.size(), 4); + + ret = SE_MultiFactor(src_inds, 10, 5, 120, ref_stk, "MF_ICWeight"); + ret->addStockList(stks, sys); + proto_list = ret->getProtoSystemList(); + ret->calculate(proto_list, query); + sw_list = ret->getSelected(Datetime(20110712)); + CHECK_EQ(sw_list.size(), 4); +#endif + /** @arg topn = 2 */ ret = SE_MultiFactor(src_inds, 2, 5, 120, ref_stk); ret->addStockList(stks, sys); @@ -74,7 +90,40 @@ TEST_CASE("test_SE_MultiFactor") { #if HKU_SUPPORT_SERIALIZATION /** @par 检测点 */ -TEST_CASE("test_SE_MultiFactor_export") {} +TEST_CASE("test_SE_MultiFactor_export") { + StockManager& sm = StockManager::instance(); + string filename(fmt::format("{}/SE_MultiFactor.xml", sm.tmpdir())); + + StockList stks{sm["sh600004"], sm["sh600005"], sm["sz000001"], sm["sz000002"]}; + Stock ref_stk = sm["sh000001"]; + KQuery query = KQueryByDate(Datetime(20110712), Datetime(20111206)); + IndicatorList src_inds{MA(CLOSE())}; + + auto sys = SYS_Simple(crtTM(), MM_Nothing()); + sys->setSG(SG_Cycle()); + sys->setParam("buy_delay", false); + + auto x1 = SE_MultiFactor(src_inds, 10, 5, 120, ref_stk); + x1->addStockList(stks, sys); + + { + std::ofstream ofs(filename); + boost::archive::xml_oarchive oa(ofs); + oa << BOOST_SERIALIZATION_NVP(x1); + } + + SelectorPtr x2; + { + std::ifstream ifs(filename); + boost::archive::xml_iarchive ia(ifs); + ia >> BOOST_SERIALIZATION_NVP(x2); + } + + auto proto_list = x2->getProtoSystemList(); + x2->calculate(proto_list, query); + auto sw_list = x2->getSelected(Datetime(20110712)); + CHECK_EQ(sw_list.size(), 4); +} #endif /* #if HKU_SUPPORT_SERIALIZATION */ /** @} */ \ No newline at end of file From b5c75307993eca98cd2c087872aef67c34aebaab Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 10 Jun 2024 10:06:12 +0800 Subject: [PATCH 359/601] update flatbuffers_version --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index 29bbc0d2..cae77db4 100644 --- a/xmake.lua +++ b/xmake.lua @@ -174,7 +174,7 @@ end local boost_version = "1.84.0" local hdf5_version = "1.12.2" local fmt_version = "10.2.1" -local flatbuffers_version = "23.5.26" +local flatbuffers_version = "24.3.25" local cpp_httplib_version = "0.14.3" local sqlite_version = "3.43.0+200" local mysql_version = "8.0.31" From f56dcb3705da626f1b999b10a1697966758bd080 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 10 Jun 2024 10:20:25 +0800 Subject: [PATCH 360/601] upgrade flatbuffers --- hikyuu/flat/Spot.py | 139 ------ hikyuu/flat/SpotList.py | 60 +-- .../hikyuu/global/agent/hikyuu/__init__.py | 0 .../hikyuu/global/agent/hikyuu/flat/Spot.py | 440 ++++++++++++++++++ .../global/agent/hikyuu/flat/SpotList.py | 74 +++ .../global/agent/hikyuu/flat/__init__.py | 0 .../hikyuu/global/agent/spot_generated.h | 228 +-------- requirements.txt | 2 +- 8 files changed, 519 insertions(+), 424 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/global/agent/hikyuu/__init__.py create mode 100644 hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/Spot.py create mode 100644 hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/SpotList.py create mode 100644 hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/__init__.py diff --git a/hikyuu/flat/Spot.py b/hikyuu/flat/Spot.py index 6af64016..d671b2a4 100644 --- a/hikyuu/flat/Spot.py +++ b/hikyuu/flat/Spot.py @@ -438,142 +438,3 @@ def SpotEnd(builder): def End(builder): return SpotEnd(builder) - - -class SpotT(object): - - # SpotT - def __init__(self): - self.market = None # type: str - self.code = None # type: str - self.name = None # type: str - self.datetime = None # type: str - self.yesterdayClose = 0.0 # type: float - self.open = 0.0 # type: float - self.high = 0.0 # type: float - self.low = 0.0 # type: float - self.close = 0.0 # type: float - self.amount = 0.0 # type: float - self.volume = 0.0 # type: float - self.bid1 = 0.0 # type: float - self.bid1Amount = 0.0 # type: float - self.bid2 = 0.0 # type: float - self.bid2Amount = 0.0 # type: float - self.bid3 = 0.0 # type: float - self.bid3Amount = 0.0 # type: float - self.bid4 = 0.0 # type: float - self.bid4Amount = 0.0 # type: float - self.bid5 = 0.0 # type: float - self.bid5Amount = 0.0 # type: float - self.ask1 = 0.0 # type: float - self.ask1Amount = 0.0 # type: float - self.ask2 = 0.0 # type: float - self.ask2Amount = 0.0 # type: float - self.ask3 = 0.0 # type: float - self.ask3Amount = 0.0 # type: float - self.ask4 = 0.0 # type: float - self.ask4Amount = 0.0 # type: float - self.ask5 = 0.0 # type: float - self.ask5Amount = 0.0 # type: float - - @classmethod - def InitFromBuf(cls, buf, pos): - spot = Spot() - spot.Init(buf, pos) - return cls.InitFromObj(spot) - - @classmethod - def InitFromPackedBuf(cls, buf, pos=0): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, pos) - return cls.InitFromBuf(buf, pos+n) - - @classmethod - def InitFromObj(cls, spot): - x = SpotT() - x._UnPack(spot) - return x - - # SpotT - def _UnPack(self, spot): - if spot is None: - return - self.market = spot.Market() - self.code = spot.Code() - self.name = spot.Name() - self.datetime = spot.Datetime() - self.yesterdayClose = spot.YesterdayClose() - self.open = spot.Open() - self.high = spot.High() - self.low = spot.Low() - self.close = spot.Close() - self.amount = spot.Amount() - self.volume = spot.Volume() - self.bid1 = spot.Bid1() - self.bid1Amount = spot.Bid1Amount() - self.bid2 = spot.Bid2() - self.bid2Amount = spot.Bid2Amount() - self.bid3 = spot.Bid3() - self.bid3Amount = spot.Bid3Amount() - self.bid4 = spot.Bid4() - self.bid4Amount = spot.Bid4Amount() - self.bid5 = spot.Bid5() - self.bid5Amount = spot.Bid5Amount() - self.ask1 = spot.Ask1() - self.ask1Amount = spot.Ask1Amount() - self.ask2 = spot.Ask2() - self.ask2Amount = spot.Ask2Amount() - self.ask3 = spot.Ask3() - self.ask3Amount = spot.Ask3Amount() - self.ask4 = spot.Ask4() - self.ask4Amount = spot.Ask4Amount() - self.ask5 = spot.Ask5() - self.ask5Amount = spot.Ask5Amount() - - # SpotT - def Pack(self, builder): - if self.market is not None: - market = builder.CreateString(self.market) - if self.code is not None: - code = builder.CreateString(self.code) - if self.name is not None: - name = builder.CreateString(self.name) - if self.datetime is not None: - datetime = builder.CreateString(self.datetime) - SpotStart(builder) - if self.market is not None: - SpotAddMarket(builder, market) - if self.code is not None: - SpotAddCode(builder, code) - if self.name is not None: - SpotAddName(builder, name) - if self.datetime is not None: - SpotAddDatetime(builder, datetime) - SpotAddYesterdayClose(builder, self.yesterdayClose) - SpotAddOpen(builder, self.open) - SpotAddHigh(builder, self.high) - SpotAddLow(builder, self.low) - SpotAddClose(builder, self.close) - SpotAddAmount(builder, self.amount) - SpotAddVolume(builder, self.volume) - SpotAddBid1(builder, self.bid1) - SpotAddBid1Amount(builder, self.bid1Amount) - SpotAddBid2(builder, self.bid2) - SpotAddBid2Amount(builder, self.bid2Amount) - SpotAddBid3(builder, self.bid3) - SpotAddBid3Amount(builder, self.bid3Amount) - SpotAddBid4(builder, self.bid4) - SpotAddBid4Amount(builder, self.bid4Amount) - SpotAddBid5(builder, self.bid5) - SpotAddBid5Amount(builder, self.bid5Amount) - SpotAddAsk1(builder, self.ask1) - SpotAddAsk1Amount(builder, self.ask1Amount) - SpotAddAsk2(builder, self.ask2) - SpotAddAsk2Amount(builder, self.ask2Amount) - SpotAddAsk3(builder, self.ask3) - SpotAddAsk3Amount(builder, self.ask3Amount) - SpotAddAsk4(builder, self.ask4) - SpotAddAsk4Amount(builder, self.ask4Amount) - SpotAddAsk5(builder, self.ask5) - SpotAddAsk5Amount(builder, self.ask5Amount) - spot = SpotEnd(builder) - return spot diff --git a/hikyuu/flat/SpotList.py b/hikyuu/flat/SpotList.py index d4486395..401d7cfa 100644 --- a/hikyuu/flat/SpotList.py +++ b/hikyuu/flat/SpotList.py @@ -64,7 +64,7 @@ def AddSpot(builder, spot): def SpotListStartSpotVector(builder, numElems): return builder.StartVector(4, numElems, 4) -def StartSpotVector(builder, numElems: int) -> int: +def StartSpotVector(builder, numElems): return SpotListStartSpotVector(builder, numElems) def SpotListEnd(builder): @@ -72,61 +72,3 @@ def SpotListEnd(builder): def End(builder): return SpotListEnd(builder) - -import hikyuu.flat.Spot -try: - from typing import List -except: - pass - -class SpotListT(object): - - # SpotListT - def __init__(self): - self.spot = None # type: List[hikyuu.flat.Spot.SpotT] - - @classmethod - def InitFromBuf(cls, buf, pos): - spotList = SpotList() - spotList.Init(buf, pos) - return cls.InitFromObj(spotList) - - @classmethod - def InitFromPackedBuf(cls, buf, pos=0): - n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, pos) - return cls.InitFromBuf(buf, pos+n) - - @classmethod - def InitFromObj(cls, spotList): - x = SpotListT() - x._UnPack(spotList) - return x - - # SpotListT - def _UnPack(self, spotList): - if spotList is None: - return - if not spotList.SpotIsNone(): - self.spot = [] - for i in range(spotList.SpotLength()): - if spotList.Spot(i) is None: - self.spot.append(None) - else: - spot_ = hikyuu.flat.Spot.SpotT.InitFromObj(spotList.Spot(i)) - self.spot.append(spot_) - - # SpotListT - def Pack(self, builder): - if self.spot is not None: - spotlist = [] - for i in range(len(self.spot)): - spotlist.append(self.spot[i].Pack(builder)) - SpotListStartSpotVector(builder, len(self.spot)) - for i in reversed(range(len(self.spot))): - builder.PrependUOffsetTRelative(spotlist[i]) - spot = builder.EndVector() - SpotListStart(builder) - if self.spot is not None: - SpotListAddSpot(builder, spot) - spotList = SpotListEnd(builder) - return spotList diff --git a/hikyuu_cpp/hikyuu/global/agent/hikyuu/__init__.py b/hikyuu_cpp/hikyuu/global/agent/hikyuu/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/Spot.py b/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/Spot.py new file mode 100644 index 00000000..d671b2a4 --- /dev/null +++ b/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/Spot.py @@ -0,0 +1,440 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: flat + +import flatbuffers +from flatbuffers.compat import import_numpy +np = import_numpy() + +class Spot(object): + __slots__ = ['_tab'] + + @classmethod + def GetRootAs(cls, buf, offset=0): + n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) + x = Spot() + x.Init(buf, n + offset) + return x + + @classmethod + def GetRootAsSpot(cls, buf, offset=0): + """This method is deprecated. Please switch to GetRootAs.""" + return cls.GetRootAs(buf, offset) + # Spot + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # Spot + def Market(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + if o != 0: + return self._tab.String(o + self._tab.Pos) + return None + + # Spot + def Code(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6)) + if o != 0: + return self._tab.String(o + self._tab.Pos) + return None + + # Spot + def Name(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(8)) + if o != 0: + return self._tab.String(o + self._tab.Pos) + return None + + # Spot + def Datetime(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(10)) + if o != 0: + return self._tab.String(o + self._tab.Pos) + return None + + # Spot + def YesterdayClose(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(12)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Open(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(14)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def High(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(16)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Low(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(18)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Close(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(20)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(22)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Volume(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(24)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid1(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(26)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid1Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(28)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid2(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(30)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid2Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(32)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid3(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(34)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid3Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(36)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid4(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(38)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid4Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(40)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid5(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(42)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Bid5Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(44)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask1(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(46)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask1Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(48)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask2(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(50)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask2Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(52)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask3(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(54)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask3Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(56)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask4(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(58)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask4Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(60)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask5(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(62)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + + # Spot + def Ask5Amount(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(64)) + if o != 0: + return self._tab.Get(flatbuffers.number_types.Float64Flags, o + self._tab.Pos) + return 0.0 + +def SpotStart(builder): + builder.StartObject(31) + +def Start(builder): + SpotStart(builder) + +def SpotAddMarket(builder, market): + builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(market), 0) + +def AddMarket(builder, market): + SpotAddMarket(builder, market) + +def SpotAddCode(builder, code): + builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(code), 0) + +def AddCode(builder, code): + SpotAddCode(builder, code) + +def SpotAddName(builder, name): + builder.PrependUOffsetTRelativeSlot(2, flatbuffers.number_types.UOffsetTFlags.py_type(name), 0) + +def AddName(builder, name): + SpotAddName(builder, name) + +def SpotAddDatetime(builder, datetime): + builder.PrependUOffsetTRelativeSlot(3, flatbuffers.number_types.UOffsetTFlags.py_type(datetime), 0) + +def AddDatetime(builder, datetime): + SpotAddDatetime(builder, datetime) + +def SpotAddYesterdayClose(builder, yesterdayClose): + builder.PrependFloat64Slot(4, yesterdayClose, 0.0) + +def AddYesterdayClose(builder, yesterdayClose): + SpotAddYesterdayClose(builder, yesterdayClose) + +def SpotAddOpen(builder, open): + builder.PrependFloat64Slot(5, open, 0.0) + +def AddOpen(builder, open): + SpotAddOpen(builder, open) + +def SpotAddHigh(builder, high): + builder.PrependFloat64Slot(6, high, 0.0) + +def AddHigh(builder, high): + SpotAddHigh(builder, high) + +def SpotAddLow(builder, low): + builder.PrependFloat64Slot(7, low, 0.0) + +def AddLow(builder, low): + SpotAddLow(builder, low) + +def SpotAddClose(builder, close): + builder.PrependFloat64Slot(8, close, 0.0) + +def AddClose(builder, close): + SpotAddClose(builder, close) + +def SpotAddAmount(builder, amount): + builder.PrependFloat64Slot(9, amount, 0.0) + +def AddAmount(builder, amount): + SpotAddAmount(builder, amount) + +def SpotAddVolume(builder, volume): + builder.PrependFloat64Slot(10, volume, 0.0) + +def AddVolume(builder, volume): + SpotAddVolume(builder, volume) + +def SpotAddBid1(builder, bid1): + builder.PrependFloat64Slot(11, bid1, 0.0) + +def AddBid1(builder, bid1): + SpotAddBid1(builder, bid1) + +def SpotAddBid1Amount(builder, bid1Amount): + builder.PrependFloat64Slot(12, bid1Amount, 0.0) + +def AddBid1Amount(builder, bid1Amount): + SpotAddBid1Amount(builder, bid1Amount) + +def SpotAddBid2(builder, bid2): + builder.PrependFloat64Slot(13, bid2, 0.0) + +def AddBid2(builder, bid2): + SpotAddBid2(builder, bid2) + +def SpotAddBid2Amount(builder, bid2Amount): + builder.PrependFloat64Slot(14, bid2Amount, 0.0) + +def AddBid2Amount(builder, bid2Amount): + SpotAddBid2Amount(builder, bid2Amount) + +def SpotAddBid3(builder, bid3): + builder.PrependFloat64Slot(15, bid3, 0.0) + +def AddBid3(builder, bid3): + SpotAddBid3(builder, bid3) + +def SpotAddBid3Amount(builder, bid3Amount): + builder.PrependFloat64Slot(16, bid3Amount, 0.0) + +def AddBid3Amount(builder, bid3Amount): + SpotAddBid3Amount(builder, bid3Amount) + +def SpotAddBid4(builder, bid4): + builder.PrependFloat64Slot(17, bid4, 0.0) + +def AddBid4(builder, bid4): + SpotAddBid4(builder, bid4) + +def SpotAddBid4Amount(builder, bid4Amount): + builder.PrependFloat64Slot(18, bid4Amount, 0.0) + +def AddBid4Amount(builder, bid4Amount): + SpotAddBid4Amount(builder, bid4Amount) + +def SpotAddBid5(builder, bid5): + builder.PrependFloat64Slot(19, bid5, 0.0) + +def AddBid5(builder, bid5): + SpotAddBid5(builder, bid5) + +def SpotAddBid5Amount(builder, bid5Amount): + builder.PrependFloat64Slot(20, bid5Amount, 0.0) + +def AddBid5Amount(builder, bid5Amount): + SpotAddBid5Amount(builder, bid5Amount) + +def SpotAddAsk1(builder, ask1): + builder.PrependFloat64Slot(21, ask1, 0.0) + +def AddAsk1(builder, ask1): + SpotAddAsk1(builder, ask1) + +def SpotAddAsk1Amount(builder, ask1Amount): + builder.PrependFloat64Slot(22, ask1Amount, 0.0) + +def AddAsk1Amount(builder, ask1Amount): + SpotAddAsk1Amount(builder, ask1Amount) + +def SpotAddAsk2(builder, ask2): + builder.PrependFloat64Slot(23, ask2, 0.0) + +def AddAsk2(builder, ask2): + SpotAddAsk2(builder, ask2) + +def SpotAddAsk2Amount(builder, ask2Amount): + builder.PrependFloat64Slot(24, ask2Amount, 0.0) + +def AddAsk2Amount(builder, ask2Amount): + SpotAddAsk2Amount(builder, ask2Amount) + +def SpotAddAsk3(builder, ask3): + builder.PrependFloat64Slot(25, ask3, 0.0) + +def AddAsk3(builder, ask3): + SpotAddAsk3(builder, ask3) + +def SpotAddAsk3Amount(builder, ask3Amount): + builder.PrependFloat64Slot(26, ask3Amount, 0.0) + +def AddAsk3Amount(builder, ask3Amount): + SpotAddAsk3Amount(builder, ask3Amount) + +def SpotAddAsk4(builder, ask4): + builder.PrependFloat64Slot(27, ask4, 0.0) + +def AddAsk4(builder, ask4): + SpotAddAsk4(builder, ask4) + +def SpotAddAsk4Amount(builder, ask4Amount): + builder.PrependFloat64Slot(28, ask4Amount, 0.0) + +def AddAsk4Amount(builder, ask4Amount): + SpotAddAsk4Amount(builder, ask4Amount) + +def SpotAddAsk5(builder, ask5): + builder.PrependFloat64Slot(29, ask5, 0.0) + +def AddAsk5(builder, ask5): + SpotAddAsk5(builder, ask5) + +def SpotAddAsk5Amount(builder, ask5Amount): + builder.PrependFloat64Slot(30, ask5Amount, 0.0) + +def AddAsk5Amount(builder, ask5Amount): + SpotAddAsk5Amount(builder, ask5Amount) + +def SpotEnd(builder): + return builder.EndObject() + +def End(builder): + return SpotEnd(builder) diff --git a/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/SpotList.py b/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/SpotList.py new file mode 100644 index 00000000..401d7cfa --- /dev/null +++ b/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/SpotList.py @@ -0,0 +1,74 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: flat + +import flatbuffers +from flatbuffers.compat import import_numpy +np = import_numpy() + +class SpotList(object): + __slots__ = ['_tab'] + + @classmethod + def GetRootAs(cls, buf, offset=0): + n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) + x = SpotList() + x.Init(buf, n + offset) + return x + + @classmethod + def GetRootAsSpotList(cls, buf, offset=0): + """This method is deprecated. Please switch to GetRootAs.""" + return cls.GetRootAs(buf, offset) + # SpotList + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # SpotList + def Spot(self, j): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + if o != 0: + x = self._tab.Vector(o) + x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4 + x = self._tab.Indirect(x) + from hikyuu.flat.Spot import Spot + obj = Spot() + obj.Init(self._tab.Bytes, x) + return obj + return None + + # SpotList + def SpotLength(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + if o != 0: + return self._tab.VectorLen(o) + return 0 + + # SpotList + def SpotIsNone(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + return o == 0 + +def SpotListStart(builder): + builder.StartObject(1) + +def Start(builder): + SpotListStart(builder) + +def SpotListAddSpot(builder, spot): + builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(spot), 0) + +def AddSpot(builder, spot): + SpotListAddSpot(builder, spot) + +def SpotListStartSpotVector(builder, numElems): + return builder.StartVector(4, numElems, 4) + +def StartSpotVector(builder, numElems): + return SpotListStartSpotVector(builder, numElems) + +def SpotListEnd(builder): + return builder.EndObject() + +def End(builder): + return SpotListEnd(builder) diff --git a/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/__init__.py b/hikyuu_cpp/hikyuu/global/agent/hikyuu/flat/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/hikyuu_cpp/hikyuu/global/agent/spot_generated.h b/hikyuu_cpp/hikyuu/global/agent/spot_generated.h index 9880a1bc..1e2c2748 100644 --- a/hikyuu_cpp/hikyuu/global/agent/spot_generated.h +++ b/hikyuu_cpp/hikyuu/global/agent/spot_generated.h @@ -8,9 +8,9 @@ // Ensure the included flatbuffers.h is the same version as when this file was // generated, otherwise it may not be compatible. -static_assert(FLATBUFFERS_VERSION_MAJOR == 23 && - FLATBUFFERS_VERSION_MINOR == 5 && - FLATBUFFERS_VERSION_REVISION == 26, +static_assert(FLATBUFFERS_VERSION_MAJOR == 24 && + FLATBUFFERS_VERSION_MINOR == 3 && + FLATBUFFERS_VERSION_REVISION == 25, "Non-compatible flatbuffers version included"); namespace hikyuu { @@ -18,49 +18,11 @@ namespace flat { struct Spot; struct SpotBuilder; -struct SpotT; struct SpotList; struct SpotListBuilder; -struct SpotListT; - -struct SpotT : public ::flatbuffers::NativeTable { - typedef Spot TableType; - std::string market{}; - std::string code{}; - std::string name{}; - std::string datetime{}; - double yesterday_close = 0.0; - double open = 0.0; - double high = 0.0; - double low = 0.0; - double close = 0.0; - double amount = 0.0; - double volume = 0.0; - double bid1 = 0.0; - double bid1_amount = 0.0; - double bid2 = 0.0; - double bid2_amount = 0.0; - double bid3 = 0.0; - double bid3_amount = 0.0; - double bid4 = 0.0; - double bid4_amount = 0.0; - double bid5 = 0.0; - double bid5_amount = 0.0; - double ask1 = 0.0; - double ask1_amount = 0.0; - double ask2 = 0.0; - double ask2_amount = 0.0; - double ask3 = 0.0; - double ask3_amount = 0.0; - double ask4 = 0.0; - double ask4_amount = 0.0; - double ask5 = 0.0; - double ask5_amount = 0.0; -}; struct Spot FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef SpotT NativeTableType; typedef SpotBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_MARKET = 4, @@ -227,9 +189,6 @@ struct Spot FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { VerifyField(verifier, VT_ASK5_AMOUNT, 8) && verifier.EndTable(); } - SpotT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; - void UnPackTo(SpotT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; - static ::flatbuffers::Offset Pack(::flatbuffers::FlatBufferBuilder &_fbb, const SpotT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); }; struct SpotBuilder { @@ -480,19 +439,7 @@ inline ::flatbuffers::Offset CreateSpotDirect( ask5_amount); } -::flatbuffers::Offset CreateSpot(::flatbuffers::FlatBufferBuilder &_fbb, const SpotT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); - -struct SpotListT : public ::flatbuffers::NativeTable { - typedef SpotList TableType; - std::vector> spot{}; - SpotListT() = default; - SpotListT(const SpotListT &o); - SpotListT(SpotListT&&) FLATBUFFERS_NOEXCEPT = default; - SpotListT &operator=(SpotListT o) FLATBUFFERS_NOEXCEPT; -}; - struct SpotList FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { - typedef SpotListT NativeTableType; typedef SpotListBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { VT_SPOT = 4 @@ -507,9 +454,6 @@ struct SpotList FLATBUFFERS_FINAL_CLASS : private ::flatbuffers::Table { verifier.VerifyVectorOfTables(spot()) && verifier.EndTable(); } - SpotListT *UnPack(const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; - void UnPackTo(SpotListT *_o, const ::flatbuffers::resolver_function_t *_resolver = nullptr) const; - static ::flatbuffers::Offset Pack(::flatbuffers::FlatBufferBuilder &_fbb, const SpotListT* _o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); }; struct SpotListBuilder { @@ -547,160 +491,6 @@ inline ::flatbuffers::Offset CreateSpotListDirect( spot__); } -::flatbuffers::Offset CreateSpotList(::flatbuffers::FlatBufferBuilder &_fbb, const SpotListT *_o, const ::flatbuffers::rehasher_function_t *_rehasher = nullptr); - -inline SpotT *Spot::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const { - auto _o = std::unique_ptr(new SpotT()); - UnPackTo(_o.get(), _resolver); - return _o.release(); -} - -inline void Spot::UnPackTo(SpotT *_o, const ::flatbuffers::resolver_function_t *_resolver) const { - (void)_o; - (void)_resolver; - { auto _e = market(); if (_e) _o->market = _e->str(); } - { auto _e = code(); if (_e) _o->code = _e->str(); } - { auto _e = name(); if (_e) _o->name = _e->str(); } - { auto _e = datetime(); if (_e) _o->datetime = _e->str(); } - { auto _e = yesterday_close(); _o->yesterday_close = _e; } - { auto _e = open(); _o->open = _e; } - { auto _e = high(); _o->high = _e; } - { auto _e = low(); _o->low = _e; } - { auto _e = close(); _o->close = _e; } - { auto _e = amount(); _o->amount = _e; } - { auto _e = volume(); _o->volume = _e; } - { auto _e = bid1(); _o->bid1 = _e; } - { auto _e = bid1_amount(); _o->bid1_amount = _e; } - { auto _e = bid2(); _o->bid2 = _e; } - { auto _e = bid2_amount(); _o->bid2_amount = _e; } - { auto _e = bid3(); _o->bid3 = _e; } - { auto _e = bid3_amount(); _o->bid3_amount = _e; } - { auto _e = bid4(); _o->bid4 = _e; } - { auto _e = bid4_amount(); _o->bid4_amount = _e; } - { auto _e = bid5(); _o->bid5 = _e; } - { auto _e = bid5_amount(); _o->bid5_amount = _e; } - { auto _e = ask1(); _o->ask1 = _e; } - { auto _e = ask1_amount(); _o->ask1_amount = _e; } - { auto _e = ask2(); _o->ask2 = _e; } - { auto _e = ask2_amount(); _o->ask2_amount = _e; } - { auto _e = ask3(); _o->ask3 = _e; } - { auto _e = ask3_amount(); _o->ask3_amount = _e; } - { auto _e = ask4(); _o->ask4 = _e; } - { auto _e = ask4_amount(); _o->ask4_amount = _e; } - { auto _e = ask5(); _o->ask5 = _e; } - { auto _e = ask5_amount(); _o->ask5_amount = _e; } -} - -inline ::flatbuffers::Offset Spot::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const SpotT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) { - return CreateSpot(_fbb, _o, _rehasher); -} - -inline ::flatbuffers::Offset CreateSpot(::flatbuffers::FlatBufferBuilder &_fbb, const SpotT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) { - (void)_rehasher; - (void)_o; - struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const SpotT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; - auto _market = _o->market.empty() ? 0 : _fbb.CreateString(_o->market); - auto _code = _o->code.empty() ? 0 : _fbb.CreateString(_o->code); - auto _name = _o->name.empty() ? 0 : _fbb.CreateString(_o->name); - auto _datetime = _o->datetime.empty() ? 0 : _fbb.CreateString(_o->datetime); - auto _yesterday_close = _o->yesterday_close; - auto _open = _o->open; - auto _high = _o->high; - auto _low = _o->low; - auto _close = _o->close; - auto _amount = _o->amount; - auto _volume = _o->volume; - auto _bid1 = _o->bid1; - auto _bid1_amount = _o->bid1_amount; - auto _bid2 = _o->bid2; - auto _bid2_amount = _o->bid2_amount; - auto _bid3 = _o->bid3; - auto _bid3_amount = _o->bid3_amount; - auto _bid4 = _o->bid4; - auto _bid4_amount = _o->bid4_amount; - auto _bid5 = _o->bid5; - auto _bid5_amount = _o->bid5_amount; - auto _ask1 = _o->ask1; - auto _ask1_amount = _o->ask1_amount; - auto _ask2 = _o->ask2; - auto _ask2_amount = _o->ask2_amount; - auto _ask3 = _o->ask3; - auto _ask3_amount = _o->ask3_amount; - auto _ask4 = _o->ask4; - auto _ask4_amount = _o->ask4_amount; - auto _ask5 = _o->ask5; - auto _ask5_amount = _o->ask5_amount; - return hikyuu::flat::CreateSpot( - _fbb, - _market, - _code, - _name, - _datetime, - _yesterday_close, - _open, - _high, - _low, - _close, - _amount, - _volume, - _bid1, - _bid1_amount, - _bid2, - _bid2_amount, - _bid3, - _bid3_amount, - _bid4, - _bid4_amount, - _bid5, - _bid5_amount, - _ask1, - _ask1_amount, - _ask2, - _ask2_amount, - _ask3, - _ask3_amount, - _ask4, - _ask4_amount, - _ask5, - _ask5_amount); -} - -inline SpotListT::SpotListT(const SpotListT &o) { - spot.reserve(o.spot.size()); - for (const auto &spot_ : o.spot) { spot.emplace_back((spot_) ? new hikyuu::flat::SpotT(*spot_) : nullptr); } -} - -inline SpotListT &SpotListT::operator=(SpotListT o) FLATBUFFERS_NOEXCEPT { - std::swap(spot, o.spot); - return *this; -} - -inline SpotListT *SpotList::UnPack(const ::flatbuffers::resolver_function_t *_resolver) const { - auto _o = std::unique_ptr(new SpotListT()); - UnPackTo(_o.get(), _resolver); - return _o.release(); -} - -inline void SpotList::UnPackTo(SpotListT *_o, const ::flatbuffers::resolver_function_t *_resolver) const { - (void)_o; - (void)_resolver; - { auto _e = spot(); if (_e) { _o->spot.resize(_e->size()); for (::flatbuffers::uoffset_t _i = 0; _i < _e->size(); _i++) { if(_o->spot[_i]) { _e->Get(_i)->UnPackTo(_o->spot[_i].get(), _resolver); } else { _o->spot[_i] = std::unique_ptr(_e->Get(_i)->UnPack(_resolver)); }; } } else { _o->spot.resize(0); } } -} - -inline ::flatbuffers::Offset SpotList::Pack(::flatbuffers::FlatBufferBuilder &_fbb, const SpotListT* _o, const ::flatbuffers::rehasher_function_t *_rehasher) { - return CreateSpotList(_fbb, _o, _rehasher); -} - -inline ::flatbuffers::Offset CreateSpotList(::flatbuffers::FlatBufferBuilder &_fbb, const SpotListT *_o, const ::flatbuffers::rehasher_function_t *_rehasher) { - (void)_rehasher; - (void)_o; - struct _VectorArgs { ::flatbuffers::FlatBufferBuilder *__fbb; const SpotListT* __o; const ::flatbuffers::rehasher_function_t *__rehasher; } _va = { &_fbb, _o, _rehasher}; (void)_va; - auto _spot = _o->spot.size() ? _fbb.CreateVector<::flatbuffers::Offset> (_o->spot.size(), [](size_t i, _VectorArgs *__va) { return CreateSpot(*__va->__fbb, __va->__o->spot[i].get(), __va->__rehasher); }, &_va ) : 0; - return hikyuu::flat::CreateSpotList( - _fbb, - _spot); -} - inline const hikyuu::flat::SpotList *GetSpotList(const void *buf) { return ::flatbuffers::GetRoot(buf); } @@ -731,18 +521,6 @@ inline void FinishSizePrefixedSpotListBuffer( fbb.FinishSizePrefixed(root); } -inline std::unique_ptr UnPackSpotList( - const void *buf, - const ::flatbuffers::resolver_function_t *res = nullptr) { - return std::unique_ptr(GetSpotList(buf)->UnPack(res)); -} - -inline std::unique_ptr UnPackSizePrefixedSpotList( - const void *buf, - const ::flatbuffers::resolver_function_t *res = nullptr) { - return std::unique_ptr(GetSizePrefixedSpotList(buf)->UnPack(res)); -} - } // namespace flat } // namespace hikyuu diff --git a/requirements.txt b/requirements.txt index 1c3bde4b..777ed1bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,7 +11,7 @@ mysql-connector-python pyperclip requests qdarkstyle -flatbuffers>=23.5.6 +flatbuffers>=24.3.25 pynng akshare pyecharts From 6a02c6cf8f3422507f576ebffd3a33223b23eb8e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 10 Jun 2024 10:22:38 +0800 Subject: [PATCH 361/601] update github ci --- .github/workflows/ubuntu.yml | 2 +- .github/workflows/ubuntu_aarch64.yml | 2 +- .github/workflows/ubuntu_python.yml | 2 +- .github/workflows/windows.yml | 2 +- .github/workflows/windows_python.yml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 793ed365..592eda61 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -34,7 +34,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.1 + xmake-version: 2.9.2 actions-cache-folder: '.xmake-cache' actions-cache-key: 'ubuntu' diff --git a/.github/workflows/ubuntu_aarch64.yml b/.github/workflows/ubuntu_aarch64.yml index c5c362f1..0184d18c 100644 --- a/.github/workflows/ubuntu_aarch64.yml +++ b/.github/workflows/ubuntu_aarch64.yml @@ -41,7 +41,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.1 + xmake-version: 2.9.2 actions-cache-folder: '.xmake-cache' - name: Installation musl diff --git a/.github/workflows/ubuntu_python.yml b/.github/workflows/ubuntu_python.yml index 0bb9b8ed..7e90c977 100644 --- a/.github/workflows/ubuntu_python.yml +++ b/.github/workflows/ubuntu_python.yml @@ -34,7 +34,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.1 + xmake-version: 2.9.2 actions-cache-folder: '.xmake-cache' actions-cache-key: 'ubuntu' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index c345f298..330cc1ae 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -26,7 +26,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.1 + xmake-version: 2.9.2 - name: configure shell: cmd diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 403b5b95..cba1625f 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -26,7 +26,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.1 + xmake-version: 2.9.2 - name: configure shell: cmd From a6e8bb1e4a6a31156a7349125b5d63e37aeefcd9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 11 Jun 2024 00:03:38 +0800 Subject: [PATCH 362/601] update --- hikyuu_cpp/hikyuu/xmake.lua | 2 +- xmake.lua | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 5c60919b..dd5b20da 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -43,7 +43,7 @@ target("hikyuu") if is_mode("release") then add_packages("hdf5") else - add_packages("hdf5_D") + add_packages("hdf5_d") end end if get_config("mysql") then diff --git a/xmake.lua b/xmake.lua index cae77db4..ca3480d9 100644 --- a/xmake.lua +++ b/xmake.lua @@ -189,7 +189,7 @@ if is_plat("windows") then if is_mode("release") then add_requires("hdf5 " .. hdf5_version) else - add_requires("hdf5_D " .. hdf5_version) + add_requires("hdf5_d " .. hdf5_version) end end if get_config("mysql") then From c4e6e8c5d1a35f649488ed3d4d9b93200fd05121 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 11 Jun 2024 09:50:55 +0800 Subject: [PATCH 363/601] upgrade boost, sqlite --- xmake.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xmake.lua b/xmake.lua index ca3480d9..ebfd6899 100644 --- a/xmake.lua +++ b/xmake.lua @@ -171,12 +171,12 @@ if is_plat("windows") then end end -local boost_version = "1.84.0" +local boost_version = "1.85.0" local hdf5_version = "1.12.2" local fmt_version = "10.2.1" local flatbuffers_version = "24.3.25" local cpp_httplib_version = "0.14.3" -local sqlite_version = "3.43.0+200" +local sqlite_version = "3.46.0+0" local mysql_version = "8.0.31" if is_plat("windows") or (is_plat("linux", "cross") and is_arch("aarch64", "arm64.*")) then mysql_version = "8.0.21" From d5751a76cd36d5be162eddd9eb8fac6c2b4c83ba Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 11 Jun 2024 10:22:08 +0800 Subject: [PATCH 364/601] update nng to 1.8.0 --- xmake.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index ebfd6899..a00ea028 100644 --- a/xmake.lua +++ b/xmake.lua @@ -175,6 +175,7 @@ local boost_version = "1.85.0" local hdf5_version = "1.12.2" local fmt_version = "10.2.1" local flatbuffers_version = "24.3.25" +local nng_version = "1.8.0" local cpp_httplib_version = "0.14.3" local sqlite_version = "3.46.0+0" local mysql_version = "8.0.31" @@ -231,7 +232,7 @@ add_requires("spdlog", {system = false, configs = {header_only = true, fmt_exter add_requireconfs("spdlog.fmt", {override = true, version = fmt_version, configs = {header_only = true}}) add_requires("sqlite3 " .. sqlite_version, {system = false, configs = {shared = true, cxflags = "-fPIC"}}) add_requires("flatbuffers v" .. flatbuffers_version, {system = false}) -add_requires("nng", {system = false, configs = {cxflags = "-fPIC"}}) +add_requires("nng " .. nng_version, {system = false, configs = {cxflags = "-fPIC"}}) add_requires("nlohmann_json", {system = false}) add_requires("cpp-httplib " .. cpp_httplib_version, {system = false, configs = {zlib = true, ssl = true}}) add_requires("zlib", {system = false}) From a15a37b6c317137e11077003ce77833056be7aeb Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 11 Jun 2024 20:52:18 +0800 Subject: [PATCH 365/601] fixed get_current_hub --- hikyuu/hub.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/hikyuu/hub.py b/hikyuu/hub.py index feb3953e..910a076e 100644 --- a/hikyuu/hub.py +++ b/hikyuu/hub.py @@ -496,7 +496,8 @@ class HubManager(metaclass=SingletonType): """ abs_path = os.path.abspath(filename) # 当前文件的绝对路径 path_parts = pathlib.Path(abs_path).parts - local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[4] + local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[-5] + print(path_parts) hub_model = self._session.query(HubModel.name).filter_by(local_base=local_base).first() checkif(hub_model is None, local_base, HubNotFoundError) return hub_model.name From 038b3bd4231bd45451ba0d3befec7b96659a5c43 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 11 Jun 2024 20:57:57 +0800 Subject: [PATCH 366/601] remove print --- hikyuu/hub.py | 1 - 1 file changed, 1 deletion(-) diff --git a/hikyuu/hub.py b/hikyuu/hub.py index 910a076e..803fcbd8 100644 --- a/hikyuu/hub.py +++ b/hikyuu/hub.py @@ -497,7 +497,6 @@ class HubManager(metaclass=SingletonType): abs_path = os.path.abspath(filename) # 当前文件的绝对路径 path_parts = pathlib.Path(abs_path).parts local_base = path_parts[-4] if path_parts[-3] in ('pf', 'sys', 'ind', 'other') else path_parts[-5] - print(path_parts) hub_model = self._session.query(HubModel.name).filter_by(local_base=local_base).first() checkif(hub_model is None, local_base, HubNotFoundError) return hub_model.name From 197514fe7bdfe492c349766c7ae0d169b62ed553 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 12 Jun 2024 03:02:18 +0800 Subject: [PATCH 367/601] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E4=B8=8D=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/common.py | 3 --- hikyuu_pywrap/main.cpp | 9 --------- 2 files changed, 12 deletions(-) diff --git a/hikyuu/data/common.py b/hikyuu/data/common.py index b86a54f9..8a9663c2 100644 --- a/hikyuu/data/common.py +++ b/hikyuu/data/common.py @@ -27,7 +27,6 @@ import re import akshare as ak import pandas as pd import datetime -from hikyuu.core import cpp_bytes_to_vector_float_blob from hikyuu.util import * @@ -244,8 +243,6 @@ def historyfinancialreader(filepath): cw_info = list(struct.unpack(report_pack_format, info_data)) report_date = int(cw_info[313]) # 财务公告日期 report_date = 19000000 + report_date if report_date > 800000 else 20000000 + report_date - # results.append((modifiy_code(code), report_date, cw_info)) - # results.append((file_date, modifiy_code(code), report_date, cpp_bytes_to_vector_float_blob(info_data))) results.append((file_date, modifiy_code(code), report_date, info_data)) cw_file.close() return results diff --git a/hikyuu_pywrap/main.cpp b/hikyuu_pywrap/main.cpp index d060017d..7b311e94 100644 --- a/hikyuu_pywrap/main.cpp +++ b/hikyuu_pywrap/main.cpp @@ -171,13 +171,4 @@ PYBIND11_MODULE(core, m) { :param int end: 结束日期 :param Query.KType ktype: K 线类型, 'DAY'|'WEEK'|'MONTH'|'QUARTER'|'HALFYEAR'|'YEAR'|'MIN'|'MIN5'|'MIN15'|'MIN30'|'MIN60' :param Query.RecoverType recover_type: 复权类型)"); - - // 仅供导入历史财务数据时将其转成 cpp 的 blob 格式 - m.def("cpp_bytes_to_vector_float_blob", [](const py::bytes& obj) { - vector c_vector = python_bytes_to_vector(obj); - std::ostringstream sout; - boost::archive::binary_oarchive oa(sout); - oa << BOOST_SERIALIZATION_NVP(c_vector); - return py::bytes(sout.str()); - }); } From 272459e8a11f886d4818930aa2b9b8224045abe8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 12 Jun 2024 03:03:25 +0800 Subject: [PATCH 368/601] =?UTF-8?q?=E6=B3=84=E9=9C=B2=E6=A3=80=E6=B5=8B?= =?UTF-8?q?=E6=97=B6=E4=B8=8D=E4=BD=BF=E7=94=A8=E5=BA=8F=E5=88=97=E5=8C=96?= =?UTF-8?q?=EF=BC=8Cboost=E5=BA=8F=E5=88=97=E5=8C=96=E5=AD=98=E5=9C=A8?= =?UTF-8?q?=E5=85=A8=E5=B1=80=E9=9D=99=E6=80=81=E5=8F=98=E9=87=8F=E4=BC=9A?= =?UTF-8?q?=E5=BC=95=E8=B5=B7=E8=AF=AF=E6=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xmake.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index a00ea028..f45bae26 100644 --- a/xmake.lua +++ b/xmake.lua @@ -105,6 +105,7 @@ add_rules("mode.debug", "mode.release") set_version("2.0.9", {build = "%Y%m%d%H%M"}) if get_config("leak_check") then + -- 需要 export LD_PRELOAD=libasan.so set_policy("build.sanitizer.address", true) set_policy("build.sanitizer.leak", true) -- set_policy("build.sanitizer.memory", true) @@ -139,7 +140,7 @@ end set_configvar("USE_SPDLOG_LOGGER", 1) -- 是否使用spdlog作为日志输出 set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) -if is_plat("macosx") then +if is_plat("macosx") or get_config("leak_check") then set_configvar("SUPPORT_SERIALIZATION", 0) else set_configvar("SUPPORT_SERIALIZATION", is_mode("release") and 1 or 0) From ab1af02f5974788ff58fb6e279e93250f3abbf22 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 12 Jun 2024 18:13:35 +0800 Subject: [PATCH 369/601] =?UTF-8?q?=E5=8E=BB=E9=99=A4=20boost::filesystem?= =?UTF-8?q?=20=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/os.cpp | 20 ++++++++++ hikyuu_cpp/hikyuu/utilities/os.h | 5 +++ .../hikyuu/hikyuu/test_StockManager.cpp | 9 +---- hikyuu_cpp/unit_test/hikyuu/test_main.cpp | 38 ++++++------------- xmake.lua | 2 +- 5 files changed, 40 insertions(+), 34 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/os.cpp b/hikyuu_cpp/hikyuu/utilities/os.cpp index a16b9066..c17e528f 100644 --- a/hikyuu_cpp/hikyuu/utilities/os.cpp +++ b/hikyuu_cpp/hikyuu/utilities/os.cpp @@ -241,6 +241,26 @@ std::string HKU_API getUserDir() { #endif } +std::string HKU_API getCurrentDir() { + std::string ret; + char* buffer = NULL; +#if HKU_OS_WINSOWS + buffer = _getcwd(buffer, 0); +#else + buffer = getcwd(buffer, 0); +#endif + + if (buffer) { +#if HKU_OS_WINDOWS + ret = GBToUTF8(std::string(buffer)); +#else + ret = std::string(buffer); +#endif + free(buffer); + } + return ret; +} + uint64_t HKU_API getDiskFreeSpace(const char* path) { #if HKU_OS_WINDOWS uint64_t freespace = Null(); diff --git a/hikyuu_cpp/hikyuu/utilities/os.h b/hikyuu_cpp/hikyuu/utilities/os.h index a322f38d..9f4e30bf 100644 --- a/hikyuu_cpp/hikyuu/utilities/os.h +++ b/hikyuu_cpp/hikyuu/utilities/os.h @@ -73,6 +73,11 @@ bool HKU_API renameFile(const std::string& oldname, const std::string& newname, */ std::string HKU_API getUserDir(); +/** + * 获取程序当前所在路径 + */ +std::string HKU_API getCurrentDir(); + /** * 输出终端是否支持彩色控制字符 */ diff --git a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp index d84aa48b..11c9c1a0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp @@ -6,13 +6,11 @@ */ #include "doctest/doctest.h" -#include #include #include #include using namespace hku; -using namespace boost::filesystem; /** * @defgroup test_hikyuu_StockManager test_hikyuu_StockManager @@ -165,11 +163,8 @@ TEST_CASE("test_StockManager_getBlock") { TEST_CASE("test_StockManager_TempCsvStock") { StockManager& sm = StockManager::instance(); - path tmp_dir(sm.tmpdir()); - tmp_dir = tmp_dir.parent_path(); - - string day_filename(tmp_dir.string() + "/test_day_data.csv"); - string min_filename(tmp_dir.string() + "/test_min_data.csv"); + string day_filename(fmt::format("{}/test_day_data.csv", sm.datadir())); + string min_filename(fmt::format("{}/test_min_data.csv", sm.datadir())); /** @arg 增加临时增加返还的Stock的基本属性 */ Stock stk = sm.addTempCsvStock("test", day_filename, min_filename); diff --git a/hikyuu_cpp/unit_test/hikyuu/test_main.cpp b/hikyuu_cpp/unit_test/hikyuu/test_main.cpp index 4fff8e60..6cfd8819 100644 --- a/hikyuu_cpp/unit_test/hikyuu/test_main.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/test_main.cpp @@ -13,46 +13,32 @@ #error test program only for hdf5 engine! You must config --hdf5=y #endif -#if __GNUC__ <= 8 || __clang_major__ <= 6 -#include -using namespace boost::filesystem; -#else -#include -using namespace std::filesystem; -#endif - #if defined(_WIN32) #include #endif #include +#include #include using namespace hku; void init_hikyuu_test() { - path current = current_path(); - if (current.stem() == path("test")) { - current /= path("data"); - } else { - current /= "test_data"; - } - set_log_level(LOG_LEVEL::LOG_TRACE); - std::cout << "current path : " << current << std::endl; -#if defined(_MSC_VER) - std::cout << "configure file: " << current.string() << "\\hikyuu_win.ini" << std::endl; - hku::hikyuu_init(current.string() + "\\hikyuu_win.ini"); + auto current = fmt::format("{}/test_data", getCurrentDir()); + fmt::print("current path: {}\n", current); + +#if HKU_OS_WINDOWS + std::string config_file(fmt::format("{}\\hikyuu_win.ini", current)); #else - std::cout << "configure file: " << current.string() << "/hikyuu_linux.ini" << std::endl; - hikyuu_init(current.string() + "/hikyuu_linux.ini"); + std::string config_file(fmt::format("{}/hikyuu_linux.ini", current)); #endif - path tmp_dir = current; - tmp_dir /= "tmp"; - if (!exists(tmp_dir)) { - create_directory(tmp_dir); - } + fmt::print("configure file: {}\n", config_file); + hikyuu_init(config_file); + + std::string tmp_dir(fmt::format("{}/tmp", current)); + createDir(tmp_dir); } int main(int argc, char** argv) { diff --git a/xmake.lua b/xmake.lua index f45bae26..7ece021c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -222,7 +222,7 @@ add_requires("boost " .. boost_version, { shared = is_plat("windows"), multi = true, date_time = true, - filesystem = true, + filesystem = false, serialization = true, system = false, python = false, From 8c4a824e11b3731c45a3bc10306746d2bde57426 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 12 Jun 2024 18:40:42 +0800 Subject: [PATCH 370/601] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E6=95=B0=E6=8D=AE=E6=B5=8B=E8=AF=95=E7=9A=84?= =?UTF-8?q?=E8=84=9A=E6=89=8B=E6=9E=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../unit_test/hikyuu/real_data/test_Null.cpp | 22 ++++++++ hikyuu_cpp/unit_test/hikyuu/test_main.cpp | 11 ++++ hikyuu_cpp/unit_test/xmake.lua | 51 ++++++++++++++++++- 3 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 hikyuu_cpp/unit_test/hikyuu/real_data/test_Null.cpp diff --git a/hikyuu_cpp/unit_test/hikyuu/real_data/test_Null.cpp b/hikyuu_cpp/unit_test/hikyuu/real_data/test_Null.cpp new file mode 100644 index 00000000..d986f6e7 --- /dev/null +++ b/hikyuu_cpp/unit_test/hikyuu/real_data/test_Null.cpp @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2019 hikyuu.org + * + * Created on: 2022-02-27 + * Author: fasiondog + */ + +#include +#include +#include +#include +#include + +using namespace hku; + +TEST_CASE("test_Null_size_t") { + size_t null_size = Null(); + CHECK_EQ(std::numeric_limits::max(), null_size); + + CHECK_UNARY(std::isnan(Null())); + CHECK_UNARY(std::isnan(Null())); +} diff --git a/hikyuu_cpp/unit_test/hikyuu/test_main.cpp b/hikyuu_cpp/unit_test/hikyuu/test_main.cpp index 6cfd8819..2e5feb27 100644 --- a/hikyuu_cpp/unit_test/hikyuu/test_main.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/test_main.cpp @@ -22,6 +22,16 @@ #include using namespace hku; +#ifdef HKU_USE_REAL_DATA_TEST +void init_hikyuu_test() { + set_log_level(LOG_LEVEL::LOG_TRACE); + std::string config_file(fmt::format("{}/.hikyuu/hikyuu.ini", getUserDir())); + fmt::print("configure file: {}\n", config_file); + hikyuu_init(config_file); + StockManager& sm = StockManager::instance(); + createDir(sm.tmpdir()); +} +#else void init_hikyuu_test() { set_log_level(LOG_LEVEL::LOG_TRACE); @@ -40,6 +50,7 @@ void init_hikyuu_test() { std::string tmp_dir(fmt::format("{}/tmp", current)); createDir(tmp_dir); } +#endif int main(int argc, char** argv) { #if defined(_WIN32) diff --git a/hikyuu_cpp/unit_test/xmake.lua b/hikyuu_cpp/unit_test/xmake.lua index 6d0b11b6..25f95c12 100644 --- a/hikyuu_cpp/unit_test/xmake.lua +++ b/hikyuu_cpp/unit_test/xmake.lua @@ -96,7 +96,7 @@ target("unit-test") end -- add files - add_files("**.cpp") + add_files("**.cpp|hikyuu/real_data/**") before_run(prepare_run) after_run(coverage_report) @@ -147,3 +147,52 @@ target("small-test") before_run(prepare_run) after_run(coverage_report) target_end() + +target("real-test") + set_kind("binary") + set_default(false) + + add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level") + + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") + if get_config("mysql") then + if is_plat("macosx") then + add_packages("mysqlclient") + else + add_packages("mysql") + end + end + + add_includedirs("..") + + if is_plat("windows") then + add_cxflags("-wd4267", "-wd4996", "-wd4251", "-wd4244", "-wd4805", "-wd4566") + else + add_cxflags("-Wno-unused-variable", "-Wno-missing-braces") + add_cxflags("-Wno-sign-compare") + end + + if is_plat("windows") and get_config("kind") == "shared" then + add_defines("HKU_API=__declspec(dllimport)") + end + + add_defines("HKU_USE_REAL_DATA_TEST") + add_deps("hikyuu") + + if is_plat("linux") or is_plat("macosx") then + add_links("sqlite3") + add_shflags("-Wl,-rpath=$ORIGIN", "-Wl,-rpath=$ORIGIN/../lib") + end + + if is_plat("macosx") then + add_includedirs("/usr/local/opt/mysql-client/include") + add_linkdirs("/usr/local/opt/mysql-client/lib") + end + + -- add files + add_files("./hikyuu/real_data/**.cpp"); + add_files("./hikyuu/test_main.cpp") + + before_run(prepare_run) + after_run(coverage_report) +target_end() From f4b2850619c78dba559139a718e8d9d33ceec735 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 13 Jun 2024 01:37:23 +0800 Subject: [PATCH 371/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../selector/imp/OperatorDivSelector.cpp | 12 +- .../selector/imp/OperatorDivSelector.h | 2 +- .../imp/OperatorInvertDivValueSelector.cpp | 2 +- .../selector/imp/OperatorMulSelector.cpp | 12 +- .../selector/imp/OperatorMulValueSelector.h | 2 +- .../selector/imp/OperatorSubSelector.cpp | 4 +- .../trade_sys/selector/test_SE_Operator.cpp | 545 +++++++++++++++++- 7 files changed, 532 insertions(+), 47 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp index e1685244..1dafd190 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp @@ -15,6 +15,8 @@ namespace hku { SystemWeightList OperatorDivSelector::getSelected(Datetime date) { SystemWeightList ret; + HKU_IF_RETURN(!m_se1 || !m_se2, ret); + SystemWeightList sws1, sws2; if (m_se1) { sws1 = m_se1->getSelected(date); @@ -23,15 +25,7 @@ SystemWeightList OperatorDivSelector::getSelected(Datetime date) { sws2 = m_se2->getSelected(date); } - if (sws1.empty()) { - ret = std::move(sws2); - return ret; - } - - if (sws2.empty()) { - ret = std::move(sws1); - return ret; - } + HKU_IF_RETURN(sws1.empty() || sws2.empty(), ret); unordered_map sw_dict1; for (auto& sw : sws1) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h index c17c3512..9a443c9e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.h @@ -12,7 +12,7 @@ namespace hku { class HKU_API OperatorDivSelector : public OperatorSelector { - OPERATOR_SELECTOR_IMP(OperatorDivSelector, "SE_Add") + OPERATOR_SELECTOR_IMP(OperatorDivSelector, "SE_Div") OPERATOR_SELECTOR_SERIALIZATION }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp index fe978962..6c06442a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorInvertDivValueSelector.cpp @@ -19,7 +19,7 @@ SystemWeightList OperatorInvertDivValueSelector::getSelected(Datetime date) { ret = m_se->getSelected(date); for (auto& sw : ret) { - sw.weight += m_value; + sw.weight = m_value / sw.weight; } return ret; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp index 96e195cd..d426bb8a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp @@ -15,6 +15,8 @@ namespace hku { SystemWeightList OperatorMulSelector::getSelected(Datetime date) { SystemWeightList ret; + HKU_IF_RETURN(!m_se1 || !m_se2, ret); + SystemWeightList sws1, sws2; if (m_se1) { sws1 = m_se1->getSelected(date); @@ -23,15 +25,7 @@ SystemWeightList OperatorMulSelector::getSelected(Datetime date) { sws2 = m_se2->getSelected(date); } - if (sws1.empty()) { - ret = std::move(sws2); - return ret; - } - - if (sws2.empty()) { - ret = std::move(sws1); - return ret; - } + HKU_IF_RETURN(sws1.empty() || sws2.empty(), ret); unordered_map sw_dict1; for (auto& sw : sws1) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h index 29d8e81c..b7d25971 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulValueSelector.h @@ -12,7 +12,7 @@ namespace hku { class HKU_API OperatorMulValueSelector : public OperatorValueSelector { - OPERATOR_VALUE_SELECTOR_IMP(OperatorMulValueSelector, "SE_MulValue") + OPERATOR_VALUE_SELECTOR_IMP(OperatorMulValueSelector, "SE_MultiValue") OPERATOR_VALUE_SELECTOR_SERIALIZATION }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp index 783c0221..b1b89628 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp @@ -24,7 +24,9 @@ SystemWeightList OperatorSubSelector::getSelected(Datetime date) { } if (sws1.empty()) { - ret = std::move(sws2); + for (const auto& sw : sws2) { + ret.emplace_back(SystemWeight(sw.sys, -sw.weight)); + } return ret; } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index 82fe9462..d4db3992 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -44,7 +44,9 @@ TEST_CASE("test_SE_AddValue") { sys->setSG(sg); sys->setMM(mm); + /** @arg 数字 + 空指针 */ se = 3.0 + SEPtr(); + CHECK_EQ(se->name(), "SE_AddValue"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); auto proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); @@ -55,7 +57,9 @@ TEST_CASE("test_SE_AddValue") { auto result = se->getSelected(Datetime(200001010000L)); CHECK_UNARY(result.empty()); - se = se1 + SEPtr(); + /** @arg 空指针 + 数字 */ + se = SEPtr() + 3.0; + CHECK_EQ(se->name(), "SE_AddValue"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); @@ -64,15 +68,11 @@ TEST_CASE("test_SE_AddValue") { } se->calculate(proto_sys_list, KQuery(-20)); result = se->getSelected(Datetime(200001010000L)); - CHECK_EQ(result.size(), 3); - CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); - CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); - CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); - CHECK_EQ(result[0].weight, 1.0); - CHECK_EQ(result[1].weight, 1.0); - CHECK_EQ(result[2].weight, 1.0); + CHECK_UNARY(result.empty()); + /** @arg 选择器 + 数字 */ se = se1 + 2.0; + CHECK_EQ(se->name(), "SE_AddValue"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); @@ -86,7 +86,9 @@ TEST_CASE("test_SE_AddValue") { CHECK_EQ(result[1].weight, 3.0); CHECK_EQ(result[2].weight, 3.0); + /** @arg 数字 + 选择器 */ se = 3.0 + se1; + CHECK_EQ(se->name(), "SE_AddValue"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); @@ -101,6 +103,246 @@ TEST_CASE("test_SE_AddValue") { CHECK_EQ(result[2].weight, 4.0); } +/** @par 检测点 */ +TEST_CASE("test_SE_SubValue") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + /** @arg 数字 - 空指针 */ + se = 3.0 - SEPtr(); + CHECK_EQ(se->name(), "SE_SubValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 空指针 - 数字 */ + se = SEPtr() - 3.0; + CHECK_EQ(se->name(), "SE_SubValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 选择器 - 数字 */ + se = se1 - 2.0; + CHECK_EQ(se->name(), "SE_SubValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, -1.0); + CHECK_EQ(result[1].weight, -1.0); + CHECK_EQ(result[2].weight, -1.0); + + /** @arg 数字 - 选择器 */ + se = 3.0 - se1; + CHECK_EQ(se->name(), "SE_SubValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 2.0); + CHECK_EQ(result[1].weight, 2.0); + CHECK_EQ(result[2].weight, 2.0); +} + +/** @par 检测点 */ +TEST_CASE("test_SE_MultiValue") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + /** @arg 数字 * 空指针 */ + se = 3.0 * SEPtr(); + CHECK_EQ(se->name(), "SE_MultiValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 空指针 * 数字 */ + se = SEPtr() * 3.0; + CHECK_EQ(se->name(), "SE_MultiValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 选择器 * 数字 */ + se = se1 * 2.0; + CHECK_EQ(se->name(), "SE_MultiValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 2.0); + CHECK_EQ(result[1].weight, 2.0); + CHECK_EQ(result[2].weight, 2.0); + + /** @arg 数字 * 选择器 */ + se = 3.0 * se1; + CHECK_EQ(se->name(), "SE_MultiValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 3.0); + CHECK_EQ(result[1].weight, 3.0); + CHECK_EQ(result[2].weight, 3.0); +} + +/** @par 检测点 */ +TEST_CASE("test_SE_DivValue") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + /** @arg 数字 / 空指针 */ + se = 3.0 / SEPtr(); + CHECK_EQ(se->name(), "SE_DivValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 空指针 / 数字 */ + se = SEPtr() / 3.0; + CHECK_EQ(se->name(), "SE_DivValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 选择器 * 数字 */ + se = se1 / 2.0; + CHECK_EQ(se->name(), "SE_DivValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 0.5); + CHECK_EQ(result[1].weight, 0.5); + CHECK_EQ(result[2].weight, 0.5); + + /** @arg 数字 / 选择器 */ + se = 3.0 / se1; + CHECK_EQ(se->name(), "SE_DivValue"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 3.0); + CHECK_EQ(result[1].weight, 3.0); + CHECK_EQ(result[2].weight, 3.0); +} + /** @par 检测点 */ TEST_CASE("test_SE_Add") { StockManager& sm = StockManager::instance(); @@ -122,7 +364,9 @@ TEST_CASE("test_SE_Add") { sys->setSG(sg); sys->setMM(mm); + /** @arg 空指针 + 空指针 */ se = SEPtr() + SEPtr(); + CHECK_EQ(se->name(), "SE_Add"); se->addStock(sm["sh600000"], sys); se->addStock(sm["sz000001"], sys); se->addStock(sm["sz000002"], sys); @@ -133,7 +377,47 @@ TEST_CASE("test_SE_Add") { auto result = se->getSelected(Datetime(200001010000L)); CHECK_UNARY(result.empty()); + /** @arg 选择器 + 空指针 */ + se = se1 + SEPtr(); + CHECK_EQ(se->name(), "SE_Add"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 1.0); + CHECK_EQ(result[1].weight, 1.0); + CHECK_EQ(result[2].weight, 1.0); + + /** @arg 空指针 + 选择器 */ + se = SEPtr() + se1; + CHECK_EQ(se->name(), "SE_Add"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 1.0); + CHECK_EQ(result[1].weight, 1.0); + CHECK_EQ(result[2].weight, 1.0); + + /** @arg 正常两个选择器相加 */ se = se1 + se2; + CHECK_EQ(se->name(), "SE_Add"); sys->setSG(sg); sys->setMM(mm); se->addStock(sm["sh600000"], sys); @@ -154,19 +438,45 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[0].weight, 2.0); CHECK_EQ(result[1].weight, 2.0); CHECK_EQ(result[2].weight, 2.0); +} - se = 3.0 + SEPtr(); - se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); - proto_sys_list = se->getProtoSystemList(); +/** @par 检测点 */ +TEST_CASE("test_SE_Sub") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + /** @arg 空指针 - 空指针 */ + se = SEPtr() - SEPtr(); + CHECK_EQ(se->name(), "SE_Sub"); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + auto proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); - for (const auto& sys : proto_sys_list) { - se->bindRealToProto(sys, sys); - } se->calculate(proto_sys_list, KQuery(-20)); - result = se->getSelected(Datetime(200001010000L)); + auto result = se->getSelected(Datetime(200001010000L)); CHECK_UNARY(result.empty()); - se = se1 + SEPtr(); + /** @arg 选择器 - 空指针 */ + se = se1 - SEPtr(); + CHECK_EQ(se->name(), "SE_Sub"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); @@ -183,33 +493,218 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[1].weight, 1.0); CHECK_EQ(result[2].weight, 1.0); - se = se1 + 2.0; + /** @arg 空指针 - 选择器 */ + se = SEPtr() - se1; + CHECK_EQ(se->name(), "SE_Sub"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } se->calculate(proto_sys_list, KQuery(-20)); result = se->getSelected(Datetime(200001010000L)); CHECK_EQ(result.size(), 3); CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); - CHECK_EQ(result[0].weight, 3.0); - CHECK_EQ(result[1].weight, 3.0); - CHECK_EQ(result[2].weight, 3.0); + CHECK_EQ(result[0].weight, -1.0); + CHECK_EQ(result[1].weight, -1.0); + CHECK_EQ(result[2].weight, -1.0); + + /** @arg 正常两个选择器相- */ + se = se1 - se2; + CHECK_EQ(se->name(), "SE_Sub"); + sys->setSG(sg); + sys->setMM(mm); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); - se = 3.0 + se1; - se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); proto_sys_list = se->getProtoSystemList(); CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } se->calculate(proto_sys_list, KQuery(-20)); result = se->getSelected(Datetime(200001010000L)); CHECK_EQ(result.size(), 3); CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); - CHECK_EQ(result[0].weight, 4.0); - CHECK_EQ(result[1].weight, 4.0); - CHECK_EQ(result[2].weight, 4.0); + CHECK_EQ(result[0].weight, 0.0); + CHECK_EQ(result[1].weight, 0.0); + CHECK_EQ(result[2].weight, 0.0); +} + +/** @par 检测点 */ +TEST_CASE("test_SE_Multi") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + /** @arg 空指针 * 空指针 */ + se = SEPtr() * SEPtr(); + CHECK_EQ(se->name(), "SE_Multi"); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 选择器 * 空指针 */ + se = se1 * SEPtr(); + CHECK_EQ(se->name(), "SE_Multi"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 空指针 * 选择器 */ + se = SEPtr() * se1; + CHECK_EQ(se->name(), "SE_Multi"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 正常两个选择器相* */ + se = se1 * se2; + CHECK_EQ(se->name(), "SE_Multi"); + sys->setSG(sg); + sys->setMM(mm); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 1.0); + CHECK_EQ(result[1].weight, 1.0); + CHECK_EQ(result[2].weight, 1.0); +} + +/** @par 检测点 */ +TEST_CASE("test_SE_Div") { + StockManager& sm = StockManager::instance(); + + SYSPtr sys = SYS_Simple(); + SGPtr sg = SG_Cross(MA(CLOSE(), 5), MA(CLOSE(), 10)); + MMPtr mm = MM_FixedCount(100); + + SEPtr se1 = SE_Fixed(); + SEPtr se2 = SE_Fixed(); + SEPtr se = se1 + se2; + + /** @arg 试图加入一个不存在的stock */ + CHECK_THROWS_AS(se->addStock(Stock(), sys), std::exception); + + /** @arg 试图加入一个空的系统策略原型 */ + CHECK_THROWS_AS(se->addStock(sm["sh600000"], SYSPtr()), std::exception); + + sys->setSG(sg); + sys->setMM(mm); + + /** @arg 空指针 / 空指针 */ + se = SEPtr() / SEPtr(); + CHECK_EQ(se->name(), "SE_Div"); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + auto proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + se->calculate(proto_sys_list, KQuery(-20)); + auto result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 选择器 / 空指针 */ + se = se1 / SEPtr(); + CHECK_EQ(se->name(), "SE_Div"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 空指针 / 选择器 */ + se = SEPtr() / se1; + CHECK_EQ(se->name(), "SE_Div"); + se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_UNARY(result.empty()); + + /** @arg 正常两个选择器相* */ + se = se1 / se2; + CHECK_EQ(se->name(), "SE_Div"); + sys->setSG(sg); + sys->setMM(mm); + se->addStock(sm["sh600000"], sys); + se->addStock(sm["sz000001"], sys); + se->addStock(sm["sz000002"], sys); + + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 1.0); + CHECK_EQ(result[1].weight, 1.0); + CHECK_EQ(result[2].weight, 1.0); } /** @} */ \ No newline at end of file From 6795ad6ffeb6aec9a92b4f2379c44e23cae8ef9e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 13 Jun 2024 02:11:29 +0800 Subject: [PATCH 372/601] =?UTF-8?q?SE=5FFixed=E5=8F=AF=E4=BB=A5=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E9=BB=98=E8=AE=A4=E6=9D=83=E9=87=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/selector/crt/SE_Fixed.h | 7 +++++-- .../trade_sys/selector/imp/FixedSelector.cpp | 19 +++++++++++++------ .../trade_sys/selector/imp/FixedSelector.h | 1 + hikyuu_pywrap/trade_sys/_Selector.cpp | 13 ++++++++++--- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Fixed.h b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Fixed.h index 35cae0a3..9404ead8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Fixed.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/crt/SE_Fixed.h @@ -15,20 +15,23 @@ namespace hku { /** * @brief 固定选择器,每天都选择指定的交易系统 + * @param weight 固定权重 * @return SelectorPtr * @ingroup Selector */ -SelectorPtr HKU_API SE_Fixed(); +SelectorPtr HKU_API SE_Fixed(double weight = 1.0); /** * @brief 固定选择器,每天都选择指定的交易系统 * @details 对指定的每个股票以原型系统创建相应的交易系统 * @param stock_list 指定的股票列表 * @param sys 原型系统 + * @param weight 固定权重 * @return SelectorPtr * @ingroup Selector */ -SelectorPtr HKU_API SE_Fixed(const StockList& stock_list, const SystemPtr& sys); +SelectorPtr HKU_API SE_Fixed(const StockList& stock_list, const SystemPtr& sys, + double weight = 1.0); } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp index 9865670f..ba834cd7 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.cpp @@ -13,7 +13,13 @@ BOOST_CLASS_EXPORT(hku::FixedSelector) namespace hku { -FixedSelector::FixedSelector() : SelectorBase("SE_Fixed") {} +FixedSelector::FixedSelector() : SelectorBase("SE_Fixed") { + setParam("weight", 1.0); +} + +FixedSelector::FixedSelector(double weight) : SelectorBase("SE_Fixed") { + setParam("weight", weight); +} FixedSelector::~FixedSelector() {} @@ -22,21 +28,22 @@ bool FixedSelector::isMatchAF(const AFPtr& af) { } SystemWeightList FixedSelector::getSelected(Datetime date) { + auto weight = getParam("weight"); SystemWeightList result; for (auto& sys : m_real_sys_list) { - result.emplace_back(sys, 1.0); + result.emplace_back(sys, weight); } return result; } void FixedSelector::_calculate() {} -SelectorPtr HKU_API SE_Fixed() { - return make_shared(); +SelectorPtr HKU_API SE_Fixed(double weight) { + return make_shared(weight); } -SelectorPtr HKU_API SE_Fixed(const StockList& stock_list, const SystemPtr& sys) { - SelectorPtr p = make_shared(); +SelectorPtr HKU_API SE_Fixed(const StockList& stock_list, const SystemPtr& sys, double weight) { + SelectorPtr p = make_shared(weight); p->addStockList(stock_list, sys); return p; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h index 55c5c581..f04332ae 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h @@ -19,6 +19,7 @@ class FixedSelector : public SelectorBase { public: FixedSelector(); + FixedSelector(double weight); virtual ~FixedSelector(); }; diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 1f5fc210..3fe3f189 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -177,14 +177,21 @@ void export_Selector(py::module& m) { DEF_PICKLE(SEPtr); - m.def("SE_Fixed", py::overload_cast<>(SE_Fixed)); - m.def("SE_Fixed", py::overload_cast(SE_Fixed), - R"(SE_Fixed([stk_list, sys]) + m.def("SE_Fixed", [](double weight) { return SE_Fixed(weight); }, py::arg("weight") = 1.0); + m.def( + "SE_Fixed", + [](const py::sequence& pystks, const SystemPtr& sys, double weight) { + StockList stks = python_list_to_vector(pystks); + return SE_Fixed(stks, sys, weight); + }, + py::arg("stk_list"), py::arg("sys"), py::arg("weight") = 1.0, + R"(SE_Fixed([stk_list, sys]) 固定选择器,即始终选择初始划定的标的及其系统策略原型 :param list stk_list: 初始划定的标的 :param System sys: 系统策略原型 + :param float weight: 默认权重 :return: SE选择器实例)"); m.def("SE_Signal", py::overload_cast<>(SE_Signal)); From 6affe70d4c5640ca36cc4d9a06308191294f55cc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 13 Jun 2024 13:20:24 +0800 Subject: [PATCH 373/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/test_SE_Operator.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index d4db3992..530afbce 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -546,7 +546,7 @@ TEST_CASE("test_SE_Multi") { MMPtr mm = MM_FixedCount(100); SEPtr se1 = SE_Fixed(); - SEPtr se2 = SE_Fixed(); + SEPtr se2 = SE_Fixed(0.5); SEPtr se = se1 + se2; /** @arg 试图加入一个不存在的stock */ @@ -617,9 +617,9 @@ TEST_CASE("test_SE_Multi") { CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); - CHECK_EQ(result[0].weight, 1.0); - CHECK_EQ(result[1].weight, 1.0); - CHECK_EQ(result[2].weight, 1.0); + CHECK_EQ(result[0].weight, 0.5); + CHECK_EQ(result[1].weight, 0.5); + CHECK_EQ(result[2].weight, 0.5); } /** @par 检测点 */ @@ -631,7 +631,7 @@ TEST_CASE("test_SE_Div") { MMPtr mm = MM_FixedCount(100); SEPtr se1 = SE_Fixed(); - SEPtr se2 = SE_Fixed(); + SEPtr se2 = SE_Fixed(0.5); SEPtr se = se1 + se2; /** @arg 试图加入一个不存在的stock */ @@ -682,7 +682,7 @@ TEST_CASE("test_SE_Div") { result = se->getSelected(Datetime(200001010000L)); CHECK_UNARY(result.empty()); - /** @arg 正常两个选择器相* */ + /** @arg 正常两个选择器相除 */ se = se1 / se2; CHECK_EQ(se->name(), "SE_Div"); sys->setSG(sg); @@ -702,9 +702,9 @@ TEST_CASE("test_SE_Div") { CHECK_EQ(sm["sh600000"], result[0].sys->getStock()); CHECK_EQ(sm["sz000001"], result[1].sys->getStock()); CHECK_EQ(sm["sz000002"], result[2].sys->getStock()); - CHECK_EQ(result[0].weight, 1.0); - CHECK_EQ(result[1].weight, 1.0); - CHECK_EQ(result[2].weight, 1.0); + CHECK_EQ(result[0].weight, 2.0); + CHECK_EQ(result[1].weight, 2.0); + CHECK_EQ(result[2].weight, 2.0); } /** @} */ \ No newline at end of file From a9d80818103325e28389cdb48da04ea067384b98 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 13 Jun 2024 15:10:23 +0800 Subject: [PATCH 374/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20performance=20?= =?UTF-8?q?=E7=BB=98=E5=9B=BE=EF=BC=8C=E5=8F=82=E8=80=83=E6=A0=87=E7=9A=84?= =?UTF-8?q?=E7=B4=AF=E7=A7=AF=E6=94=B6=E7=9B=8A=E7=8E=87=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=E7=AD=89=E6=AF=94=E5=90=8E=E5=A4=8D=E6=9D=83=E8=AE=A1=E7=AE=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/draw/drawplot/matplotlib_draw.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 22cf332f..ebfcd628 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -760,7 +760,8 @@ def sys_performance(sys, ref_stk=None): ref_k = ref_stk.get_kdata(sys.query) hku_check(len(ref_k) > 0, "The length of ref_k is zero! Maybe The query date is out of the ref-stock range!\n ref_k: {}", ref_k) - query = Query(ref_k[0].datetime.start_of_day(), ref_k[-1].datetime.start_of_day() + Seconds(1), Query.DAY) + query = Query(ref_k[0].datetime.start_of_day(), ref_k[-1].datetime.start_of_day() + + Seconds(1), Query.DAY, recover_type=Query.EQUAL_BACKWARD) ref_k = ref_stk.get_kdata(query) ref_dates = ref_k.get_datetime_list() @@ -772,7 +773,7 @@ def sys_performance(sys, ref_stk=None): funds_return = VALUE(funds_return) funds_return.name = "系统累积收益率" ref_return = ROCR(ref_k.close, 0) - ref_return.name = ref_stk.name + ref_return.name = f"{ref_stk.name}({ref_stk.market_code})" per = Performance() text = per.report(sys.tm, query.end_datetime) From 1bc9e0dbc24409d728bbe12783ab6920cdde2a19 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 13 Jun 2024 17:39:09 +0800 Subject: [PATCH 375/601] fixed SelectorBase clone --- hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 6731d39b..6bccbb59 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -96,7 +96,7 @@ SelectorPtr SelectorBase::clone() { } p->m_pro_sys_list.reserve(m_pro_sys_list.size()); - for (const auto& sys : m_real_sys_list) { + for (const auto& sys : m_pro_sys_list) { p->m_pro_sys_list.emplace_back(sys->clone()); } return p; From 91daa8eb837ba4367e8de44deea8873a74ca384c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 14 Jun 2024 02:05:07 +0800 Subject: [PATCH 376/601] =?UTF-8?q?=E5=BC=82=E6=9E=84SE=E7=BB=84=E5=90=88?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_sys/selector/SelectorBase.h | 1 - .../selector/imp/OperatorAddSelector.cpp | 12 +--- .../selector/imp/OperatorSelector.cpp | 69 ++++++++++++++++++- .../trade_sys/selector/imp/OperatorSelector.h | 22 ++---- .../trade_sys/selector/test_SE_Operator.cpp | 35 ++++++++++ 5 files changed, 109 insertions(+), 30 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h index 53fb61a2..b4cb5f5a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.h @@ -132,7 +132,6 @@ public: private: void initParam(); -protected: protected: string m_name; bool m_calculated{false}; // 是否已计算过 diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp index bad5155b..fdf9ba95 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -60,17 +60,7 @@ SystemWeightList OperatorAddSelector::getSelected(Datetime date) { } } - std::sort(ret.begin(), ret.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; - }); - + sortSystemWeightList(ret); return ret; } diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index 0a024be9..dcb50ff1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -13,6 +13,36 @@ BOOST_CLASS_EXPORT(hku::OperatorSelector) namespace hku { +std::unordered_set OperatorSelector::findIntersection(const SelectorPtr& se1, + const SelectorPtr& se2) { + std::unordered_set ret; + if (se1 && se2) { + const auto& sys_list1 = se1->getProtoSystemList(); + const auto& sys_list2 = se2->getProtoSystemList(); + for (const auto& sys1 : sys_list1) { + for (const auto sys2 : sys_list2) { + if (sys1 == sys2) { + ret.insert(sys1.get()); + } + } + } + } + return ret; +} + +void OperatorSelector::sortSystemWeightList(SystemWeightList& swlist) { + std::sort(swlist.begin(), swlist.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; + }); +} + OperatorSelector::OperatorSelector() : SelectorBase("SE_Operator") {} OperatorSelector::OperatorSelector(const string& name) : SelectorBase(name) {} @@ -20,15 +50,48 @@ OperatorSelector::OperatorSelector(const string& name) : SelectorBase(name) {} OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, const SelectorPtr& se2) : SelectorBase(name) { - if (se1) { + auto inter = findIntersection(se1, se2); + if (se1 && se2) { + m_se1 = se1->clone(); + m_se1->removeAll(); + m_se2 = se2->clone(); + m_se2->removeAll(); + + std::map tmpdict; + const auto& raw_sys_list1 = se1->getProtoSystemList(); + for (const auto& sys : raw_sys_list1) { + auto tmpsys = sys->clone(); + m_pro_sys_list.emplace_back(tmpsys); + m_se1->addSystem(tmpsys); + m_se1_set.insert(tmpsys); + if (inter.find(sys.get()) != inter.end()) { + tmpdict[sys.get()] = tmpsys; + } + } + + const auto& raw_sys_list2 = se2->getProtoSystemList(); + for (size_t i = 0, total = raw_sys_list2.size(); i < total; i++) { + const auto& sys = raw_sys_list2[i]; + auto tmpsys = sys->clone(); + auto iter = inter.find(sys.get()); + if (iter == inter.end()) { + m_pro_sys_list.emplace_back(tmpsys); + m_se2_set.insert(tmpsys); + } else { + m_se2_set.insert(tmpdict[*iter]); + } + m_se2->addSystem(tmpsys); + } + + } else if (se1) { m_se1 = se1->clone(); auto sys_list = m_se1->getProtoSystemList(); for (auto& sys : sys_list) { m_se1_set.insert(sys); } m_pro_sys_list = std::move(sys_list); - } - if (se2) { + + } else if (se2) { m_se2 = se2->clone(); auto sys_list = m_se2->getProtoSystemList(); for (auto& sys : sys_list) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index bd8a51b8..0d1a2ab9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -34,6 +34,8 @@ protected: m_real_to_proto[real] = proto; } + static void sortSystemWeightList(SystemWeightList& swlist); + protected: SelectorPtr m_se1; SelectorPtr m_se2; @@ -41,6 +43,10 @@ protected: std::unordered_set m_se2_set; // se2 的原型系统实例集合 std::unordered_map m_real_to_proto; +private: + static std::unordered_set findIntersection(const SelectorPtr& se1, + const SelectorPtr& se2); + private: //============================================ // 序列化支持 @@ -66,21 +72,7 @@ public: \ virtual SystemWeightList getSelected(Datetime date) override; \ \ virtual SelectorPtr _clone() override { \ - auto p = std::make_shared(); \ - if (m_se1) { \ - p->m_se1 = m_se1->clone(); \ - auto sys_list = p->m_se1->getProtoSystemList(); \ - for (auto& sys : sys_list) { \ - p->m_se1_set.insert(sys); \ - } \ - } \ - if (m_se2) { \ - p->m_se2 = m_se2->clone(); \ - auto sys_list = p->m_se2->getProtoSystemList(); \ - for (auto& sys : sys_list) { \ - p->m_se2_set.insert(sys); \ - } \ - } \ + auto p = std::make_shared(m_se1, m_se2); \ return p; \ } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index 530afbce..d69709ac 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -438,6 +438,41 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[0].weight, 2.0); CHECK_EQ(result[1].weight, 2.0); CHECK_EQ(result[2].weight, 2.0); + + /** 异构并集, 必须使用 addSystem 加入实际的系统实例 */ + sys->reset(); + auto sys1 = sys->clone(); + sys1->setStock(sm["sh600000"]); + auto sys2 = sys->clone(); + sys2->setStock(sm["sz000001"]); + auto sys3 = sys->clone(); + sys3->setStock(sm["sz000002"]); + + se1 = SE_Fixed(0.2); + se1->addSystem(sys1); + se1->addSystem(sys2); + CHECK_EQ(se1->getProtoSystemList().size(), 2); + + se2 = SE_Fixed(0.3); + se2->addSystem(sys2); + se2->addSystem(sys3); + CHECK_EQ(se2->getProtoSystemList().size(), 2); + + se = se1 + se2; + proto_sys_list = se->getProtoSystemList(); + CHECK_EQ(proto_sys_list.size(), 3); + for (const auto& sys : proto_sys_list) { + se->bindRealToProto(sys, sys); + } + se->calculate(proto_sys_list, KQuery(-20)); + result = se->getSelected(Datetime(200001010000L)); + CHECK_EQ(result.size(), 3); + CHECK_EQ(sm["sz000001"], result[0].sys->getStock()); + CHECK_EQ(sm["sz000002"], result[1].sys->getStock()); + CHECK_EQ(sm["sh600000"], result[2].sys->getStock()); + CHECK_EQ(result[0].weight, 0.5); + CHECK_EQ(result[1].weight, 0.3); + CHECK_EQ(result[2].weight, 0.2); } /** @par 检测点 */ From 203b69a0c6cfd95e41999ab581bd6aca74487e50 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 14 Jun 2024 13:22:30 +0800 Subject: [PATCH 377/601] update --- .../selector/imp/OperatorAddSelector.cpp | 49 +--------- .../selector/imp/OperatorDivSelector.cpp | 53 +---------- .../selector/imp/OperatorMulSelector.cpp | 53 +---------- .../selector/imp/OperatorSelector.cpp | 89 +++++++++++++++++++ .../trade_sys/selector/imp/OperatorSelector.h | 7 ++ .../selector/imp/OperatorSubSelector.cpp | 61 +------------ 6 files changed, 100 insertions(+), 212 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp index fdf9ba95..97bc3e96 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorAddSelector.cpp @@ -14,54 +14,7 @@ BOOST_CLASS_EXPORT(hku::OperatorAddSelector) namespace hku { SystemWeightList OperatorAddSelector::getSelected(Datetime date) { - SystemWeightList ret; - SystemWeightList sws1, sws2; - if (m_se1) { - sws1 = m_se1->getSelected(date); - } - if (m_se2) { - sws2 = m_se2->getSelected(date); - } - - if (sws1.empty()) { - ret = std::move(sws2); - return ret; - } - - if (sws2.empty()) { - ret = std::move(sws1); - return ret; - } - - unordered_map sw_dict1; - for (auto& sw : sws1) { - sw_dict1[sw.sys.get()] = &sw; - } - - SystemWeight tmp; - unordered_map sw_dict2; - unordered_map::iterator iter; - for (auto& sw : sws2) { - iter = sw_dict1.find(sw.sys.get()); - tmp.sys = sw.sys; - if (iter != sw_dict1.end()) { - tmp.weight = std::isnan(sw.weight) ? sw.weight : sw.weight + iter->second->weight; - } else { - tmp.weight = sw.weight; - } - auto& back = ret.emplace_back(std::move(tmp)); - sw_dict2[back.sys.get()] = &back; - } - - for (auto& sw : sws1) { - iter = sw_dict2.find(sw.sys.get()); - if (iter == sw_dict2.end()) { - ret.emplace_back(std::move(sw)); - } - } - - sortSystemWeightList(ret); - return ret; + return getUnionSelected(date, [](double w1, double w2) { return w1 + w2; }); } HKU_API SelectorPtr operator+(const SelectorPtr& se1, const SelectorPtr& se2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp index 1dafd190..6fad5b24 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorDivSelector.cpp @@ -14,58 +14,7 @@ BOOST_CLASS_EXPORT(hku::OperatorDivSelector) namespace hku { SystemWeightList OperatorDivSelector::getSelected(Datetime date) { - SystemWeightList ret; - HKU_IF_RETURN(!m_se1 || !m_se2, ret); - - SystemWeightList sws1, sws2; - if (m_se1) { - sws1 = m_se1->getSelected(date); - } - if (m_se2) { - sws2 = m_se2->getSelected(date); - } - - HKU_IF_RETURN(sws1.empty() || sws2.empty(), ret); - - unordered_map sw_dict1; - for (auto& sw : sws1) { - sw_dict1[sw.sys.get()] = &sw; - } - - SystemWeight tmp; - unordered_map sw_dict2; - unordered_map::iterator iter; - for (auto& sw : sws2) { - iter = sw_dict1.find(sw.sys.get()); - tmp.sys = sw.sys; - if (iter != sw_dict1.end()) { - tmp.weight = std::isnan(sw.weight) ? sw.weight : iter->second->weight / sw.weight; - } else { - tmp.weight = 1.0 / sw.weight; - } - auto& back = ret.emplace_back(std::move(tmp)); - sw_dict2[back.sys.get()] = &back; - } - - for (auto& sw : sws1) { - iter = sw_dict2.find(sw.sys.get()); - if (iter == sw_dict2.end()) { - ret.emplace_back(std::move(sw)); - } - } - - std::sort(ret.begin(), ret.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; - }); - - return ret; + return getIntersectionSelected(date, [](double w1, double w2) { return w1 / w2; }); } HKU_API SelectorPtr operator/(const SelectorPtr& se1, const SelectorPtr& se2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp index d426bb8a..b9f47157 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorMulSelector.cpp @@ -14,58 +14,7 @@ BOOST_CLASS_EXPORT(hku::OperatorMulSelector) namespace hku { SystemWeightList OperatorMulSelector::getSelected(Datetime date) { - SystemWeightList ret; - HKU_IF_RETURN(!m_se1 || !m_se2, ret); - - SystemWeightList sws1, sws2; - if (m_se1) { - sws1 = m_se1->getSelected(date); - } - if (m_se2) { - sws2 = m_se2->getSelected(date); - } - - HKU_IF_RETURN(sws1.empty() || sws2.empty(), ret); - - unordered_map sw_dict1; - for (auto& sw : sws1) { - sw_dict1[sw.sys.get()] = &sw; - } - - SystemWeight tmp; - unordered_map sw_dict2; - unordered_map::iterator iter; - for (auto& sw : sws2) { - iter = sw_dict1.find(sw.sys.get()); - tmp.sys = sw.sys; - if (iter != sw_dict1.end()) { - tmp.weight = sw.weight * iter->second->weight; - } else { - tmp.weight = sw.weight; - } - auto& back = ret.emplace_back(std::move(tmp)); - sw_dict2[back.sys.get()] = &back; - } - - for (auto& sw : sws1) { - iter = sw_dict2.find(sw.sys.get()); - if (iter == sw_dict2.end()) { - ret.emplace_back(std::move(sw)); - } - } - - std::sort(ret.begin(), ret.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; - }); - - return ret; + return getIntersectionSelected(date, [](double w1, double w2) { return w1 * w2; }); } HKU_API SelectorPtr operator*(const SelectorPtr& se1, const SelectorPtr& se2) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index dcb50ff1..23bb8441 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -180,4 +180,93 @@ void OperatorSelector::_calculate() { } } +SystemWeightList OperatorSelector::getUnionSelected( + Datetime date, const std::function&& func) { + SystemWeightList ret; + SystemWeightList sws1, sws2; + if (m_se1) { + sws1 = m_se1->getSelected(date); + } + if (m_se2) { + sws2 = m_se2->getSelected(date); + } + + if (sws1.empty()) { + for (const auto& sw : sws2) { + ret.emplace_back(SystemWeight(sw.sys, func(0, sw.weight))); + } + return ret; + } + + if (sws2.empty()) { + ret = std::move(sws1); + return ret; + } + + unordered_map sw_dict1; + for (auto& sw : sws1) { + sw_dict1[sw.sys.get()] = &sw; + } + + SystemWeight tmp; + unordered_map sw_dict2; + unordered_map::iterator iter; + for (auto& sw : sws2) { + iter = sw_dict1.find(sw.sys.get()); + tmp.sys = sw.sys; + if (iter != sw_dict1.end()) { + tmp.weight = std::isnan(sw.weight) ? sw.weight : func(iter->second->weight, sw.weight); + } else { + tmp.weight = func(0.0, sw.weight); + } + auto& back = ret.emplace_back(std::move(tmp)); + sw_dict2[back.sys.get()] = &back; + } + + for (auto& sw : sws1) { + iter = sw_dict2.find(sw.sys.get()); + if (iter == sw_dict2.end()) { + ret.emplace_back(std::move(sw)); + } + } + + sortSystemWeightList(ret); + return ret; +} + +SystemWeightList OperatorSelector::getIntersectionSelected( + Datetime date, const std::function&& func) { + SystemWeightList ret; + HKU_IF_RETURN(!m_se1 || !m_se2, ret); + + SystemWeightList sws1, sws2; + if (m_se1) { + sws1 = m_se1->getSelected(date); + } + if (m_se2) { + sws2 = m_se2->getSelected(date); + } + + HKU_IF_RETURN(sws1.empty() || sws2.empty(), ret); + + unordered_map sw_dict1; + for (auto& sw : sws1) { + sw_dict1[sw.sys.get()] = &sw; + } + + SystemWeight tmp; + unordered_map::iterator iter; + for (auto& sw : sws2) { + iter = sw_dict1.find(sw.sys.get()); + if (iter != sw_dict1.end()) { + tmp.sys = sw.sys; + tmp.weight = func(iter->second->weight, sw.weight); + ret.emplace_back(std::move(tmp)); + } + } + + sortSystemWeightList(ret); + return ret; +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index 0d1a2ab9..7296252f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -34,6 +34,13 @@ protected: m_real_to_proto[real] = proto; } + SystemWeightList getUnionSelected(Datetime date, + const std::function&& func); + + SystemWeightList getIntersectionSelected(Datetime date, + const std::function&& func); + +protected: static void sortSystemWeightList(SystemWeightList& swlist); protected: diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp index b1b89628..8f3739ec 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSubSelector.cpp @@ -14,66 +14,7 @@ BOOST_CLASS_EXPORT(hku::OperatorSubSelector) namespace hku { SystemWeightList OperatorSubSelector::getSelected(Datetime date) { - SystemWeightList ret; - SystemWeightList sws1, sws2; - if (m_se1) { - sws1 = m_se1->getSelected(date); - } - if (m_se2) { - sws2 = m_se2->getSelected(date); - } - - if (sws1.empty()) { - for (const auto& sw : sws2) { - ret.emplace_back(SystemWeight(sw.sys, -sw.weight)); - } - return ret; - } - - if (sws2.empty()) { - ret = std::move(sws1); - return ret; - } - - unordered_map sw_dict1; - for (auto& sw : sws1) { - sw_dict1[sw.sys.get()] = &sw; - } - - SystemWeight tmp; - unordered_map sw_dict2; - unordered_map::iterator iter; - for (auto& sw : sws2) { - iter = sw_dict1.find(sw.sys.get()); - tmp.sys = sw.sys; - if (iter != sw_dict1.end()) { - tmp.weight = std::isnan(sw.weight) ? -sw.weight : iter->second->weight - sw.weight; - } else { - tmp.weight = -sw.weight; - } - auto& back = ret.emplace_back(std::move(tmp)); - sw_dict2[back.sys.get()] = &back; - } - - for (auto& sw : sws1) { - iter = sw_dict2.find(sw.sys.get()); - if (iter == sw_dict2.end()) { - ret.emplace_back(std::move(sw)); - } - } - - std::sort(ret.begin(), ret.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; - }); - - return ret; + return getUnionSelected(date, [](double w1, double w2) { return w1 - w2; }); } HKU_API SelectorPtr operator-(const SelectorPtr& se1, const SelectorPtr& se2) { From 0d18803bcb53391c387e5e3106a2bcf80d85e5fe Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 14 Jun 2024 16:23:53 +0800 Subject: [PATCH 378/601] update --- .../selector/imp/OperatorSelector.cpp | 19 +++---------------- .../trade_sys/selector/imp/OperatorSelector.h | 3 +-- .../selector/imp/OperatorValueSelector.cpp | 5 ++--- .../trade_sys/selector/test_SE_Operator.cpp | 2 ++ 4 files changed, 8 insertions(+), 21 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index 23bb8441..e6e8eab5 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -120,6 +120,7 @@ void OperatorSelector::_reset() { m_se2_set.insert(sys); } } + m_real_to_proto.clear(); } bool OperatorSelector::isMatchAF(const AFPtr& af) { @@ -140,25 +141,11 @@ void OperatorSelector::_addSystem(const SYSPtr& sys) { void OperatorSelector::_removeAll() { m_se1_set.clear(); m_se2_set.clear(); + m_real_to_proto.clear(); } SelectorPtr OperatorSelector::_clone() { - auto p = make_shared(); - if (m_se1) { - p->m_se1 = m_se1->clone(); - auto sys_list = p->m_se1->getProtoSystemList(); - for (auto& sys : sys_list) { - p->m_se1_set.insert(sys); - } - } - if (m_se2) { - p->m_se2 = m_se2->clone(); - auto sys_list = p->m_se2->getProtoSystemList(); - for (auto& sys : sys_list) { - p->m_se2_set.insert(sys); - } - } - return p; + return make_shared(name(), m_se1, m_se2); } void OperatorSelector::_calculate() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index 7296252f..ddbbd702 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -79,8 +79,7 @@ public: \ virtual SystemWeightList getSelected(Datetime date) override; \ \ virtual SelectorPtr _clone() override { \ - auto p = std::make_shared(m_se1, m_se2); \ - return p; \ + return std::make_shared(m_se1, m_se2); \ } #if HKU_SUPPORT_SERIALIZATION diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp index ba281065..d60e5f0e 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.cpp @@ -19,9 +19,8 @@ OperatorValueSelector::OperatorValueSelector(const string& name) : SelectorBase( OperatorValueSelector::OperatorValueSelector(const string& name, const SelectorPtr& se, double value) -: SelectorBase(name), m_value(value) { - if (se) { - m_se = se->clone(); +: SelectorBase(name), m_se(se), m_value(value) { + if (m_se) { m_pro_sys_list = m_se->getProtoSystemList(); } } diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index d69709ac..987ec54a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -415,6 +415,7 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[1].weight, 1.0); CHECK_EQ(result[2].weight, 1.0); +#if 0 /** @arg 正常两个选择器相加 */ se = se1 + se2; CHECK_EQ(se->name(), "SE_Add"); @@ -473,6 +474,7 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[0].weight, 0.5); CHECK_EQ(result[1].weight, 0.3); CHECK_EQ(result[2].weight, 0.2); +#endif } /** @par 检测点 */ From ce4b5bc7e8efbb7b9fdbeaca1d281de733cdb472 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 14 Jun 2024 16:25:19 +0800 Subject: [PATCH 379/601] update --- .../unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index 987ec54a..d69709ac 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -415,7 +415,6 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[1].weight, 1.0); CHECK_EQ(result[2].weight, 1.0); -#if 0 /** @arg 正常两个选择器相加 */ se = se1 + se2; CHECK_EQ(se->name(), "SE_Add"); @@ -474,7 +473,6 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[0].weight, 0.5); CHECK_EQ(result[1].weight, 0.3); CHECK_EQ(result[2].weight, 0.2); -#endif } /** @par 检测点 */ From 3e3cf0abba1327f7bef9fd6781be4d562a3e27b1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 14 Jun 2024 23:33:24 +0800 Subject: [PATCH 380/601] =?UTF-8?q?fixed=20=E5=AF=B9=20etf=20=E7=BC=A9?= =?UTF-8?q?=E8=82=A1=E7=9A=84=E5=A4=8D=E6=9D=83=E5=A4=84=E7=90=86=E9=94=99?= =?UTF-8?q?=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KDataImp.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/KDataImp.cpp b/hikyuu_cpp/hikyuu/KDataImp.cpp index 1ded326e..df4e5f9b 100644 --- a/hikyuu_cpp/hikyuu/KDataImp.cpp +++ b/hikyuu_cpp/hikyuu/KDataImp.cpp @@ -203,7 +203,9 @@ void KDataImp::_recoverForward() { price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + weightIter->increasement()); - price_t denominator = 1.0 + change; // 分母 = (1+流通股份变动比例) + // change 小于 0 时为缩股 + price_t denominator = + change < 0.0 ? std::abs(change * 0.1) : 1.0 + change; // 分母 = (1+流通股份变动比例) price_t temp = weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); if (denominator == 1.0 && temp == 0.0) @@ -255,7 +257,9 @@ void KDataImp::_recoverBackward() { // 流通股份变动比例 price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + weightIter->increasement()); - price_t denominator = 1.0 + change; //(1+流通股份变动比例) + // change 小于 0 时为缩股 + price_t denominator = + change < 0 ? std::abs(change * 0.1) : 1.0 + change; //(1+流通股份变动比例) price_t temp = 0.1 * weightIter->bonus() - weightIter->priceForSell() * change; if (denominator == 1.0 && temp == 0.0) @@ -322,7 +326,9 @@ void KDataImp::_recoverEqualForward() { // 流通股份变动比例 price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + weightIter->increasement()); - price_t denominator = 1.0 + change; //(1+流通股份变动比例) + // change 小于 0 时为缩股 + price_t denominator = + change < 0.0 ? std::abs(change * 0.1) : 1.0 + change; //(1+流通股份变动比例) price_t temp = weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); if (denominator == 0.0 || (denominator == 1.0 && temp == 0.0)) @@ -373,7 +379,9 @@ void KDataImp::_recoverEqualBackward() { // 流通股份变动比例 price_t change = 0.1 * (weightIter->countAsGift() + weightIter->countForSell() + weightIter->increasement()); - price_t denominator = 1.0 + change; //(1+流通股份变动比例) + // change 小于 0 时为缩股 + price_t denominator = + change < 0.0 ? std::abs(change * 0.1) : 1.0 + change; //(1+流通股份变动比例) price_t temp = closePrice + weightIter->priceForSell() * change - 0.1 * weightIter->bonus(); if (temp == 0.0 || denominator == 0.0) { continue; From 6aa40a1c85bdec1e84514d2c7c73399a1292f98d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 15 Jun 2024 23:31:00 +0800 Subject: [PATCH 381/601] fixed INSUM --- hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp index de0d8dd0..d1150fa4 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp @@ -61,7 +61,11 @@ static IndicatorList getAllIndicators(const Block& block, const KQuery& query, static void insum_cum(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { for (const auto& value : inds) { - HKU_ASSERT(value.size() == len); + if (value.empty()) { + continue; + } + HKU_CHECK(value.size() == len, "value len: {}, dst len: {}, stk: {}", value.size(), len, + value.getContext().getStock().name()); const auto* data = value.data(); for (size_t i = 0; i < len; i++) { if (!std::isnan(data[i])) { @@ -78,7 +82,11 @@ static void insum_cum(const IndicatorList& inds, Indicator::value_t* dst, size_t static void insum_mean(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { vector count(len, 0); for (const auto& value : inds) { - HKU_ASSERT(value.size() == len); + if (value.empty()) { + continue; + } + HKU_CHECK(value.size() == len, "value len: {}, dst len: {}, stk: {}", value.size(), len, + value.getContext().getStock().name()); const auto* data = value.data(); for (size_t i = 0; i < len; i++) { if (!std::isnan(data[i])) { @@ -101,6 +109,11 @@ static void insum_mean(const IndicatorList& inds, Indicator::value_t* dst, size_ static void insum_max(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { for (const auto& value : inds) { + if (value.empty()) { + continue; + } + HKU_CHECK(value.size() == len, "value len: {}, dst len: {}, stk: {}", value.size(), len, + value.getContext().getStock().name()); HKU_ASSERT(value.size() == len); const auto* data = value.data(); for (size_t i = 0; i < len; i++) { From 671cc8cf7e502dbabd6a970f8ddf9b82ca868e24 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 15 Jun 2024 23:44:24 +0800 Subject: [PATCH 382/601] =?UTF-8?q?update=20INSUM,=20HKU=5FCHECK=20?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=E5=91=8A=E8=AD=A6=E6=89=93=E5=8D=B0=E5=BF=BD?= =?UTF-8?q?=E7=95=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp index d1150fa4..9c76ddbf 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp @@ -64,8 +64,11 @@ static void insum_cum(const IndicatorList& inds, Indicator::value_t* dst, size_t if (value.empty()) { continue; } - HKU_CHECK(value.size() == len, "value len: {}, dst len: {}, stk: {}", value.size(), len, - value.getContext().getStock().name()); + if (value.size() != len) { + HKU_WARN("Ignore stock: {}, value len: {}, dst len: {}, stk: {}", + value.getContext().getStock().name(), value.size(), len); + continue; + } const auto* data = value.data(); for (size_t i = 0; i < len; i++) { if (!std::isnan(data[i])) { @@ -85,8 +88,11 @@ static void insum_mean(const IndicatorList& inds, Indicator::value_t* dst, size_ if (value.empty()) { continue; } - HKU_CHECK(value.size() == len, "value len: {}, dst len: {}, stk: {}", value.size(), len, - value.getContext().getStock().name()); + if (value.size() != len) { + HKU_WARN("Ignore stock: {}, value len: {}, dst len: {}, stk: {}", + value.getContext().getStock().name(), value.size(), len); + continue; + } const auto* data = value.data(); for (size_t i = 0; i < len; i++) { if (!std::isnan(data[i])) { @@ -112,9 +118,11 @@ static void insum_max(const IndicatorList& inds, Indicator::value_t* dst, size_t if (value.empty()) { continue; } - HKU_CHECK(value.size() == len, "value len: {}, dst len: {}, stk: {}", value.size(), len, - value.getContext().getStock().name()); - HKU_ASSERT(value.size() == len); + if (value.size() != len) { + HKU_WARN("Ignore stock: {}, value len: {}, dst len: {}, stk: {}", + value.getContext().getStock().name(), value.size(), len); + continue; + } const auto* data = value.data(); for (size_t i = 0; i < len; i++) { if (!std::isnan(data[i])) { From c00f897cebae43e03a1aab6f71868217fb79dcd1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 16 Jun 2024 01:31:31 +0800 Subject: [PATCH 383/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E9=80=80=E5=87=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 1ce19261..2aef8bf0 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -84,18 +84,19 @@ void GlobalInitializer::clean() { getLatestVersion(), getLatestVersion()); } -#if !HKU_ENABLE_LEAK_DETECT && !defined(MSVC_LEAKER_DETECT) - // 未启用内存泄漏检测时,直接退出,让系统自行释放全部资源 - fmt::print("Quit Hikyuu system!\n\n"); - return; -#endif - releaseGlobalTaskGroup(); releaseScheduler(); releaseGlobalSpotAgent(); IndicatorImp::releaseDynEngine(); + +#if HKU_ENABLE_LEAK_DETECT || defined(MSVC_LEAKER_DETECT) + // 非内存泄漏检测时,内存让系统自动释放,避免某些场景下 windows 下退出速度过慢 StockManager::quit(); +#else + fmt::print("Quit Hikyuu system!\n\n"); +#endif + DataDriverFactory::release(); #if HKU_ENABLE_HDF5_KDATA From 32b33cbfbc517a3d5855b7c4b5fe5c3a77cc5595 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 16 Jun 2024 02:51:45 +0800 Subject: [PATCH 384/601] update --- docs/source/indicator/indicator.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/source/indicator/indicator.rst b/docs/source/indicator/indicator.rst index 9619919f..7f86b5ac 100644 --- a/docs/source/indicator/indicator.rst +++ b/docs/source/indicator/indicator.rst @@ -174,9 +174,9 @@ BLOCKSET(block, query) - BLOCKSET(category, name, query) + BLOCKSET(stks, query) - :param Block block: 待统计的板块 + :param Block block | sequence stks: 待统计的板块 或 stock list :param Query query: 统计范围 @@ -538,9 +538,9 @@ INSUM(block, query, ind, mode) - INSUM(category, name, query, ind, mode) + INSUM(stks, query, ind, mode) - :param Block block: 指定板块 + :param Block block | sequence stks: 指定板块 或 证券列表 :param Query query: 指定范围 :param Indicator ind: 指定指标 :param int mode: 计算类型:0-累加,1-平均数,2-最大值,3-最小值. From bba5f4c5a7ed6b088cec64372c9fbd1072a97ce2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 16 Jun 2024 02:53:00 +0800 Subject: [PATCH 385/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20INSUM,=20BLOCKSETN?= =?UTF-8?q?UM=20=E5=8F=AF=E7=9B=B4=E6=8E=A5=E8=BE=93=E5=85=A5=20stock=20li?= =?UTF-8?q?st,=20=E5=8F=AF=E4=BB=A5=E5=BF=BD=E7=95=A5=20query=20=E5=8F=82?= =?UTF-8?q?=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h | 10 +--- hikyuu_cpp/hikyuu/indicator/crt/INSUM.h | 7 +-- .../hikyuu/indicator/imp/IBlockSetNum.cpp | 16 +++---- hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp | 25 ++++++---- hikyuu_pywrap/indicator/_build_in.cpp | 46 +++++++++++++++---- 5 files changed, 61 insertions(+), 43 deletions(-) diff --git a/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h b/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h index 2bb851a1..f97d8603 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/BLOCKSETNUM.h @@ -18,14 +18,6 @@ namespace hku { * @return Indicator */ Indicator HKU_API BLOCKSETNUM(const Block& blk, const KQuery& query); - -/** - * 横向统计(返回板块股个数) - * @param category 板块分类 - * @param name 板块名称 - * @param query 统计范围 - * @return Indicator - */ -Indicator HKU_API BLOCKSETNUM(const string& category, const string& name, const KQuery& query); +Indicator HKU_API BLOCKSETNUM(const Block& blk); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h b/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h index 6b7e4512..ea985684 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/INSUM.h @@ -23,14 +23,11 @@ Indicator HKU_API INSUM(const Block& block, const KQuery& query, const Indicator /** * 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. - * @param category 板块类别 - * @param category 板块名称 - * @param query 指定范围 + * @param block 指定板块 * @param ind 指定指标 * @param mode 计算类型:0-累加,1-平均数,2-最大值,3-最小值. * @return Indicator */ -Indicator HKU_API INSUM(const string& category, const string& name, const KQuery& query, - const Indicator& ind, int mode); +Indicator HKU_API INSUM(const Block& block, const Indicator& ind, int mode); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp index fab90646..21168e35 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IBlockSetNum.cpp @@ -43,7 +43,9 @@ void IBlockSetNum::_calculate(const Indicator& ind) { dates = k.getDatetimeList(); } else { KQuery q = getParam("query"); - dates = StockManager::instance().getTradingCalendar(q, getParam("market")); + if (q != KQuery(0, 0)) { + dates = StockManager::instance().getTradingCalendar(q, getParam("market")); + } } size_t total = dates.size(); @@ -72,17 +74,13 @@ Indicator HKU_API BLOCKSETNUM(const Block& block, const KQuery& query) { IndicatorImpPtr p = make_shared(); p->setParam("query", query); p->setParam("block", block); - if (query == Null()) { - p->setParam("ignore_context", true); - } else { - p->calculate(); - } + p->setParam("ignore_context", false); + p->calculate(); return Indicator(p); } -Indicator HKU_API BLOCKSETNUM(const string& category, const string& name, const KQuery& query) { - Block block = StockManager::instance().getBlock(category, name); - return BLOCKSETNUM(block, query); +Indicator HKU_API BLOCKSETNUM(const Block& block) { + return BLOCKSETNUM(block, KQuery(0, 0)); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp index 9c76ddbf..4fca6bce 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp @@ -20,7 +20,7 @@ BOOST_CLASS_EXPORT(hku::IInSum) namespace hku { IInSum::IInSum() : IndicatorImp("INSUM", 1) { - setParam("query", KQueryByIndex(-100)); + setParam("query", KQuery(0, 0)); setParam("block", Block()); setParam("mode", 0); setParam("market", "SH"); @@ -138,7 +138,14 @@ static void insum_max(const IndicatorList& inds, Indicator::value_t* dst, size_t static void insum_min(const IndicatorList& inds, Indicator::value_t* dst, size_t len) { for (const auto& value : inds) { - HKU_ASSERT(value.size() == len); + if (value.empty()) { + continue; + } + if (value.size() != len) { + HKU_WARN("Ignore stock: {}, value len: {}, dst len: {}, stk: {}", + value.getContext().getStock().name(), value.size(), len); + continue; + } const auto* data = value.data(); for (size_t i = 0; i < len; i++) { if (!std::isnan(data[i])) { @@ -163,7 +170,9 @@ void IInSum::_calculate(const Indicator& ind) { dates = k.getDatetimeList(); } else { q = getParam("query"); - dates = StockManager::instance().getTradingCalendar(q, getParam("market")); + if (q != KQuery(0, 0)) { + dates = StockManager::instance().getTradingCalendar(q, getParam("market")); + } } size_t total = dates.size(); @@ -193,16 +202,12 @@ Indicator HKU_API INSUM(const Block& block, const KQuery& query, const Indicator p->setParam("query", query); p->setParam("block", block); p->setParam("mode", mode); - if (query == Null()) { - p->setParam("ignore_context", true); - } + p->setParam("ignore_context", false); return Indicator(p)(ind); } -Indicator HKU_API INSUM(const string& category, const string& name, const KQuery& query, - const Indicator& ind, int mode) { - Block block = StockManager::instance().getBlock(category, name); - return INSUM(block, query, ind, mode); +Indicator HKU_API INSUM(const Block& block, const Indicator& ind, int mode) { + return INSUM(block, KQuery(0, 0), ind, mode); } } /* namespace hku */ diff --git a/hikyuu_pywrap/indicator/_build_in.cpp b/hikyuu_pywrap/indicator/_build_in.cpp index 0d9ac686..f51eaa51 100644 --- a/hikyuu_pywrap/indicator/_build_in.cpp +++ b/hikyuu_pywrap/indicator/_build_in.cpp @@ -1849,6 +1849,7 @@ void export_Indicator_build_in(py::module& m) { :param int ix: 历史财务信息字段索引 :param int name: 历史财务信息字段名称)"); + m.def("BLOCKSETNUM", py::overload_cast(BLOCKSETNUM), py::arg("block")); m.def("BLOCKSETNUM", py::overload_cast(BLOCKSETNUM), py::arg("block"), py::arg("query"), R"(BLOCKSETNUM(block, query) @@ -1858,15 +1859,29 @@ void export_Indicator_build_in(py::module& m) { :param Query query: 统计范围)"); m.def( - "BLOCKSETNUM", py::overload_cast(BLOCKSETNUM), - py::arg("category"), py::arg("name"), py::arg("query"), R"(BLOCKSETNUM(category, name, query) + "BLOCKSETNUM", + [](const py::sequence& stks) { + Block blk; + blk.add(python_list_to_vector(stks)); + return BLOCKSETNUM(blk); + }, + py::arg("stks")); + m.def( + "BLOCKSETNUM", + [](const py::sequence& stks, const KQuery& query) { + Block blk; + blk.add(python_list_to_vector(stks)); + return BLOCKSETNUM(blk, query); + }, + py::arg("stks"), py::arg("query"), R"(BLOCKSETNUM(block, query) 横向统计(返回板块股个数) - :param str category: 板块类别 - :param str name: 板块名称 - :param query 统计范围)"); + :param Sequence stks: stock list + :param Query query: 统计范围)"); + m.def("INSUM", py::overload_cast(INSUM), py::arg("block"), + py::arg("ind"), py::arg("mode")); m.def("INSUM", py::overload_cast(INSUM), py::arg("block"), py::arg("query"), py::arg("ind"), py::arg("mode"), R"(INSUM(block, query, ind, mode) @@ -1881,14 +1896,25 @@ void export_Indicator_build_in(py::module& m) { m.def( "INSUM", - py::overload_cast(INSUM), - py::arg("category"), py::arg("name"), py::arg("query"), py::arg("ind"), py::arg("mode"), - R"(INSUM(category, name, ind, mode) + [](const py::sequence stks, const Indicator& ind, int mode) { + Block blk; + blk.add(python_list_to_vector(stks)); + return INSUM(blk, ind, mode); + }, + py::arg("stks"), py::arg("ind"), py::arg("mode")); + m.def( + "INSUM", + [](const py::sequence stks, const KQuery& query, const Indicator& ind, int mode) { + Block blk; + blk.add(python_list_to_vector(stks)); + return INSUM(blk, query, ind, mode); + }, + py::arg("stks"), py::arg("query"), py::arg("ind"), py::arg("mode"), + R"(INSUM(stks, query, ind, mode) 返回板块各成分该指标相应输出按计算类型得到的计算值.计算类型:0-累加,1-平均数,2-最大值,3-最小值. - :param str category: 板块类别 - :param str name: 板块名称 + :param Sequence stks: stock list :param Query query: 指定范围 :param Indicator ind: 指定指标 :param int mode: 计算类型:0-累加,1-平均数,2-最大值,3-最小值. From a7a48c62a10189a4766f7b96152f6314f70bb054 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 16 Jun 2024 11:55:45 +0800 Subject: [PATCH 386/601] =?UTF-8?q?=E9=99=90=E5=88=B6=20OperatorSelector?= =?UTF-8?q?=20clone=20=E6=93=8D=E4=BD=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trade_sys/selector/SelectorBase.cpp | 14 +-- .../selector/imp/OperatorSelector.cpp | 86 ++++++++++++++++--- .../trade_sys/selector/imp/OperatorSelector.h | 4 +- .../trade_sys/selector/test_SE_Operator.cpp | 26 ++++++ 4 files changed, 104 insertions(+), 26 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp index 6bccbb59..d970283a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/SelectorBase.cpp @@ -70,19 +70,7 @@ void SelectorBase::reset() { } SelectorPtr SelectorBase::clone() { - SelectorPtr p; - try { - p = _clone(); - } catch (...) { - HKU_ERROR("Subclass _clone failed!"); - p = SelectorPtr(); - } - - if (!p || p.get() == this) { - HKU_ERROR("Failed clone! Will use self-ptr!"); - return shared_from_this(); - } - + SelectorPtr p = _clone(); p->m_params = m_params; p->m_name = m_name; p->m_query = m_query; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index e6e8eab5..ed9c2660 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -49,20 +49,21 @@ OperatorSelector::OperatorSelector(const string& name) : SelectorBase(name) {} OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, const SelectorPtr& se2) -: SelectorBase(name) { +: SelectorBase(name), m_se1(se1), m_se2(se2) { auto inter = findIntersection(se1, se2); if (se1 && se2) { - m_se1 = se1->clone(); - m_se1->removeAll(); - m_se2 = se2->clone(); - m_se2->removeAll(); + // m_se1 = se1->clone(); + // m_se1->removeAll(); + // m_se2 = se2->clone(); + // m_se2->removeAll(); std::map tmpdict; const auto& raw_sys_list1 = se1->getProtoSystemList(); for (const auto& sys : raw_sys_list1) { - auto tmpsys = sys->clone(); + // auto tmpsys = sys->clone(); + const auto& tmpsys = sys; m_pro_sys_list.emplace_back(tmpsys); - m_se1->addSystem(tmpsys); + // m_se1->addSystem(tmpsys); m_se1_set.insert(tmpsys); if (inter.find(sys.get()) != inter.end()) { tmpdict[sys.get()] = tmpsys; @@ -72,7 +73,8 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, const auto& raw_sys_list2 = se2->getProtoSystemList(); for (size_t i = 0, total = raw_sys_list2.size(); i < total; i++) { const auto& sys = raw_sys_list2[i]; - auto tmpsys = sys->clone(); + // auto tmpsys = sys->clone(); + const auto& tmpsys = sys; auto iter = inter.find(sys.get()); if (iter == inter.end()) { m_pro_sys_list.emplace_back(tmpsys); @@ -80,11 +82,11 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, } else { m_se2_set.insert(tmpdict[*iter]); } - m_se2->addSystem(tmpsys); + // m_se2->addSystem(tmpsys); } } else if (se1) { - m_se1 = se1->clone(); + // m_se1 = se1->clone(); auto sys_list = m_se1->getProtoSystemList(); for (auto& sys : sys_list) { m_se1_set.insert(sys); @@ -92,7 +94,7 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, m_pro_sys_list = std::move(sys_list); } else if (se2) { - m_se2 = se2->clone(); + // m_se2 = se2->clone(); auto sys_list = m_se2->getProtoSystemList(); for (auto& sys : sys_list) { m_se2_set.insert(sys); @@ -145,7 +147,67 @@ void OperatorSelector::_removeAll() { } SelectorPtr OperatorSelector::_clone() { - return make_shared(name(), m_se1, m_se2); + HKU_THROW("OperatorSelector Could't support clone!"); +#if 0 + // OperatorSelector 不支持 clone 操作 + // 如果要实现 clone, 需要实现类似 indicator 一整套机制 + // 需要寻找最底层的系统策略实例,并在生成 clone 对象后,对原上传该系统实例也需要用该clone对象 + auto p = make_shared(); + p->cloneRebuild(m_se1, m_se2); + return p; +#endif +} + +void OperatorSelector::cloneRebuild(const SelectorPtr& se1, const SelectorPtr& se2) { + auto inter = findIntersection(se1, se2); + if (se1 && se2) { + m_se1 = se1->clone(); + m_se1->removeAll(); + m_se2 = se2->clone(); + m_se2->removeAll(); + + std::map tmpdict; + const auto& raw_sys_list1 = se1->getProtoSystemList(); + for (const auto& sys : raw_sys_list1) { + auto tmpsys = sys->clone(); + m_pro_sys_list.emplace_back(tmpsys); + m_se1->addSystem(tmpsys); + m_se1_set.insert(tmpsys); + if (inter.find(sys.get()) != inter.end()) { + tmpdict[sys.get()] = tmpsys; + } + } + + const auto& raw_sys_list2 = se2->getProtoSystemList(); + for (size_t i = 0, total = raw_sys_list2.size(); i < total; i++) { + const auto& sys = raw_sys_list2[i]; + auto tmpsys = sys->clone(); + auto iter = inter.find(sys.get()); + if (iter == inter.end()) { + m_pro_sys_list.emplace_back(tmpsys); + m_se2_set.insert(tmpsys); + } else { + m_se2_set.insert(tmpdict[*iter]); + } + m_se2->addSystem(tmpsys); + } + + } else if (se1) { + m_se1 = se1->clone(); + auto sys_list = m_se1->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se1_set.insert(sys); + } + m_pro_sys_list = std::move(sys_list); + + } else if (se2) { + m_se2 = se2->clone(); + auto sys_list = m_se2->getProtoSystemList(); + for (auto& sys : sys_list) { + m_se2_set.insert(sys); + m_pro_sys_list.emplace_back(std::move(sys)); + } + } } void OperatorSelector::_calculate() { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index ddbbd702..2a52aab0 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -43,6 +43,8 @@ protected: protected: static void sortSystemWeightList(SystemWeightList& swlist); + void cloneRebuild(const SelectorPtr& se1, const SelectorPtr& se2); + protected: SelectorPtr m_se1; SelectorPtr m_se2; @@ -79,7 +81,7 @@ public: \ virtual SystemWeightList getSelected(Datetime date) override; \ \ virtual SelectorPtr _clone() override { \ - return std::make_shared(m_se1, m_se2); \ + HKU_THROW("OperatorSelector Could't support clone!"); \ } #if HKU_SUPPORT_SERIALIZATION diff --git a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp index d69709ac..6bf6293e 100644 --- a/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/trade_sys/selector/test_SE_Operator.cpp @@ -397,6 +397,7 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[2].weight, 1.0); /** @arg 空指针 + 选择器 */ + se1->removeAll(); se = SEPtr() + se1; CHECK_EQ(se->name(), "SE_Add"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); @@ -416,6 +417,8 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[2].weight, 1.0); /** @arg 正常两个选择器相加 */ + se1->removeAll(); + se2->removeAll(); se = se1 + se2; CHECK_EQ(se->name(), "SE_Add"); sys->setSG(sg); @@ -473,6 +476,9 @@ TEST_CASE("test_SE_Add") { CHECK_EQ(result[0].weight, 0.5); CHECK_EQ(result[1].weight, 0.3); CHECK_EQ(result[2].weight, 0.2); + + /** @arg 尝试 clone 操作 */ + CHECK_THROWS(se->clone()); } /** @par 检测点 */ @@ -529,6 +535,7 @@ TEST_CASE("test_SE_Sub") { CHECK_EQ(result[2].weight, 1.0); /** @arg 空指针 - 选择器 */ + se1->removeAll(); se = SEPtr() - se1; CHECK_EQ(se->name(), "SE_Sub"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); @@ -548,6 +555,8 @@ TEST_CASE("test_SE_Sub") { CHECK_EQ(result[2].weight, -1.0); /** @arg 正常两个选择器相- */ + se1->removeAll(); + se2->removeAll(); se = se1 - se2; CHECK_EQ(se->name(), "SE_Sub"); sys->setSG(sg); @@ -570,6 +579,9 @@ TEST_CASE("test_SE_Sub") { CHECK_EQ(result[0].weight, 0.0); CHECK_EQ(result[1].weight, 0.0); CHECK_EQ(result[2].weight, 0.0); + + /** @arg 尝试 clone 操作 */ + CHECK_THROWS(se->clone()); } /** @par 检测点 */ @@ -607,6 +619,7 @@ TEST_CASE("test_SE_Multi") { CHECK_UNARY(result.empty()); /** @arg 选择器 * 空指针 */ + se1->removeAll(); se = se1 * SEPtr(); CHECK_EQ(se->name(), "SE_Multi"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); @@ -620,6 +633,7 @@ TEST_CASE("test_SE_Multi") { CHECK_UNARY(result.empty()); /** @arg 空指针 * 选择器 */ + se1->removeAll(); se = SEPtr() * se1; CHECK_EQ(se->name(), "SE_Multi"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); @@ -633,6 +647,8 @@ TEST_CASE("test_SE_Multi") { CHECK_UNARY(result.empty()); /** @arg 正常两个选择器相* */ + se1->removeAll(); + se2->removeAll(); se = se1 * se2; CHECK_EQ(se->name(), "SE_Multi"); sys->setSG(sg); @@ -655,6 +671,9 @@ TEST_CASE("test_SE_Multi") { CHECK_EQ(result[0].weight, 0.5); CHECK_EQ(result[1].weight, 0.5); CHECK_EQ(result[2].weight, 0.5); + + /** @arg 尝试 clone 操作 */ + CHECK_THROWS(se->clone()); } /** @par 检测点 */ @@ -692,6 +711,7 @@ TEST_CASE("test_SE_Div") { CHECK_UNARY(result.empty()); /** @arg 选择器 / 空指针 */ + se1->removeAll(); se = se1 / SEPtr(); CHECK_EQ(se->name(), "SE_Div"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); @@ -705,6 +725,7 @@ TEST_CASE("test_SE_Div") { CHECK_UNARY(result.empty()); /** @arg 空指针 / 选择器 */ + se1->removeAll(); se = SEPtr() / se1; CHECK_EQ(se->name(), "SE_Div"); se->addStockList({sm["sh600000"], sm["sz000001"], sm["sz000002"]}, sys); @@ -718,6 +739,8 @@ TEST_CASE("test_SE_Div") { CHECK_UNARY(result.empty()); /** @arg 正常两个选择器相除 */ + se1->removeAll(); + se2->removeAll(); se = se1 / se2; CHECK_EQ(se->name(), "SE_Div"); sys->setSG(sg); @@ -740,6 +763,9 @@ TEST_CASE("test_SE_Div") { CHECK_EQ(result[0].weight, 2.0); CHECK_EQ(result[1].weight, 2.0); CHECK_EQ(result[2].weight, 2.0); + + /** @arg 尝试 clone 操作 */ + CHECK_THROWS(se->clone()); } /** @} */ \ No newline at end of file From 1608e5a05ca2d1a6f2b8b8e1c80af40231284551 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 17 Jun 2024 12:19:10 +0800 Subject: [PATCH 387/601] update release --- docs/source/release.rst | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/docs/source/release.rst b/docs/source/release.rst index b8d5dafe..b8eb20d3 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,27 @@ 版本发布说明 ======================= +2.1.0 - 2024年6月17日 +------------------------- + +fixed 北交所92号段历史财务信息导入 +fixed 对 etf 缩股的复权处理错误 +fixed INSUM,及 优化 +fixed getSystemPartName/getSystemPartEnum 缺失 PF +fixed PF 处理立即买入/延迟卖出的系统 +fixed analysis 在 k 线无数据时报错 +fixed get_current_hub +fixed 通达信本地数据导入时导入历史财务数据的进度通知消息 +优化 INSUM, BLOCKSETNUM 可直接输入 stock list, 可以忽略 query 参数 +优化 HikyuuTDX,避免目录不存在时导入 +优化 SE_MultiFactor 以更好的适应 PF +优化 performance 绘图,参考标的累积收益率使用等比后复权计算 +优化退出 +非泄漏检测模式下直接退出,让系统释放资源 +优化泄漏检测工程;优化clang编译告警;优化shared_ptr创建 +清理 cppcheck 告警,删除多余语句 + + 2.0.9 - 2024年5月27日 ------------------------- From 7187d1ab74a7058e2018c5072246f138880aba31 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 18 Jun 2024 00:34:43 +0800 Subject: [PATCH 388/601] release 2.1.0 --- docs/source/release.rst | 38 +++++++++++++++++++++----------------- xmake.lua | 2 +- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index b8eb20d3..1fc3e141 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,25 +1,29 @@ 版本发布说明 ======================= -2.1.0 - 2024年6月17日 +2.1.0 - 2024年6月18日 ------------------------- -fixed 北交所92号段历史财务信息导入 -fixed 对 etf 缩股的复权处理错误 -fixed INSUM,及 优化 -fixed getSystemPartName/getSystemPartEnum 缺失 PF -fixed PF 处理立即买入/延迟卖出的系统 -fixed analysis 在 k 线无数据时报错 -fixed get_current_hub -fixed 通达信本地数据导入时导入历史财务数据的进度通知消息 -优化 INSUM, BLOCKSETNUM 可直接输入 stock list, 可以忽略 query 参数 -优化 HikyuuTDX,避免目录不存在时导入 -优化 SE_MultiFactor 以更好的适应 PF -优化 performance 绘图,参考标的累积收益率使用等比后复权计算 -优化退出 -非泄漏检测模式下直接退出,让系统释放资源 -优化泄漏检测工程;优化clang编译告警;优化shared_ptr创建 -清理 cppcheck 告警,删除多余语句 +1. 新增特性 + Selector 支持 +-*/、AND、OR 操作,方便验证共振 + +2. 缺陷修复 + - fixed 北交所92号段历史财务信息导入 + - fixed 对 etf 缩股的复权处理错误 + - fixed INSUM 在某些股票无数据时的报错 + - fixed getSystemPartName/getSystemPartEnum 缺失 PF + - fixed PF 处理立即买入/延迟卖出的系统 + - fixed analysis 在 k 线无数据时报错 + - fixed get_current_hub 获取当前 hub 名称时错误 + - fixed 通达信本地数据导入时导入历史财务数据的进度通知消息 + +3. 功能优化 + - 优化 INSUM, BLOCKSETNUM 可直接输入 stock list, 可以忽略 query 参数 + - 优化 HikyuuTDX,避免目录不存在时导入 + - 优化 SE_MultiFactor 以更好的适应 PF + - 优化 performance 绘图,参考标的累积收益率使用等比后复权计算 + - 优化程序退出:非内存泄漏检测模式下由OS系统快速释放内存资源 + - 优化泄漏检测工程;清理优化clang、cppcheck编译告警;优化shared_ptr创建 2.0.9 - 2024年5月27日 diff --git a/xmake.lua b/xmake.lua index 7ece021c..14be5417 100644 --- a/xmake.lua +++ b/xmake.lua @@ -102,7 +102,7 @@ set_project("hikyuu") add_rules("mode.debug", "mode.release") -- version -set_version("2.0.9", {build = "%Y%m%d%H%M"}) +set_version("2.1.0", {build = "%Y%m%d%H%M"}) if get_config("leak_check") then -- 需要 export LD_PRELOAD=libasan.so From c84b47d40d7c7188513b98a8bc81da99cc6b758e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 18 Jun 2024 00:47:16 +0800 Subject: [PATCH 389/601] =?UTF-8?q?=E6=B6=88=E9=99=A4=20gcc=20=E5=91=8A?= =?UTF-8?q?=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../selector/imp/OperatorSelector.cpp | 23 +++++-------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index ed9c2660..ed87da2c 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -20,7 +20,7 @@ std::unordered_set OperatorSelector::findIntersection(const SelectorPtr const auto& sys_list1 = se1->getProtoSystemList(); const auto& sys_list2 = se2->getProtoSystemList(); for (const auto& sys1 : sys_list1) { - for (const auto sys2 : sys_list2) { + for (const auto& sys2 : sys_list2) { if (sys1 == sys2) { ret.insert(sys1.get()); } @@ -52,37 +52,26 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, : SelectorBase(name), m_se1(se1), m_se2(se2) { auto inter = findIntersection(se1, se2); if (se1 && se2) { - // m_se1 = se1->clone(); - // m_se1->removeAll(); - // m_se2 = se2->clone(); - // m_se2->removeAll(); - std::map tmpdict; const auto& raw_sys_list1 = se1->getProtoSystemList(); for (const auto& sys : raw_sys_list1) { - // auto tmpsys = sys->clone(); - const auto& tmpsys = sys; - m_pro_sys_list.emplace_back(tmpsys); - // m_se1->addSystem(tmpsys); - m_se1_set.insert(tmpsys); + m_pro_sys_list.emplace_back(sys); + m_se1_set.insert(sys); if (inter.find(sys.get()) != inter.end()) { - tmpdict[sys.get()] = tmpsys; + tmpdict[sys.get()] = sys; } } const auto& raw_sys_list2 = se2->getProtoSystemList(); for (size_t i = 0, total = raw_sys_list2.size(); i < total; i++) { const auto& sys = raw_sys_list2[i]; - // auto tmpsys = sys->clone(); - const auto& tmpsys = sys; auto iter = inter.find(sys.get()); if (iter == inter.end()) { - m_pro_sys_list.emplace_back(tmpsys); - m_se2_set.insert(tmpsys); + m_pro_sys_list.emplace_back(sys); + m_se2_set.insert(sys); } else { m_se2_set.insert(tmpdict[*iter]); } - // m_se2->addSystem(tmpsys); } } else if (se1) { From 0253dd6a5b07479c1a375a7144064bf64948d544 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 18 Jun 2024 10:18:05 +0800 Subject: [PATCH 390/601] update --- docs/source/release.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 1fc3e141..77af448d 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -5,7 +5,7 @@ ------------------------- 1. 新增特性 - Selector 支持 +-*/、AND、OR 操作,方便验证共振 + - Selector 支持 +-×÷、AND、OR 操作,方便验证共振 2. 缺陷修复 - fixed 北交所92号段历史财务信息导入 From d5ebf69b02345e5253e76e67536b11bd37de2273 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 6 Jul 2024 18:23:36 +0800 Subject: [PATCH 391/601] upgrage hdf5 --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index 14be5417..8970fc1f 100644 --- a/xmake.lua +++ b/xmake.lua @@ -173,7 +173,7 @@ if is_plat("windows") then end local boost_version = "1.85.0" -local hdf5_version = "1.12.2" +local hdf5_version = "1.13.3" local fmt_version = "10.2.1" local flatbuffers_version = "24.3.25" local nng_version = "1.8.0" From 0f26cea4e7aa8bc5833bdfa03520054000a2208f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 10 Jul 2024 11:51:18 +0800 Subject: [PATCH 392/601] =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 4 +- hikyuu_cpp/hikyuu/hikyuu.cpp | 6 +- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 4 +- hikyuu_cpp/hikyuu/utilities/Null.h | 1 + hikyuu_cpp/hikyuu/utilities/TimerManager.h | 2 +- hikyuu_cpp/hikyuu/utilities/arithmetic.h | 52 +++++++++-- .../hikyuu/utilities/datetime/Datetime.cpp | 4 + .../hikyuu/utilities/datetime/Datetime.h | 5 ++ .../hikyuu/utilities/datetime/TimeDelta.cpp | 18 ++++ .../hikyuu/utilities/datetime/TimeDelta.h | 11 +++ .../hikyuu/utilities/db_connect/DBCondition.h | 88 +++++++++---------- .../utilities/db_connect/DBConnectBase.h | 24 +---- .../hikyuu/utilities/db_connect/DBUpgrade.cpp | 7 +- .../utilities/db_connect/SQLStatementBase.h | 10 +-- .../db_connect/mysql/MySQLConnect.cpp | 6 +- .../utilities/db_connect/mysql/MySQLConnect.h | 16 ++-- .../db_connect/mysql/MySQLStatement.cpp | 15 ++-- .../db_connect/mysql/MySQLStatement.h | 30 +++---- .../db_connect/sqlite/SQLiteConnect.cpp | 4 +- .../db_connect/sqlite/SQLiteConnect.h | 4 +- .../db_connect/sqlite/SQLiteStatement.cpp | 8 +- .../db_connect/sqlite/SQLiteStatement.h | 2 +- .../db_connect/sqlite/SQLiteUtil.cpp | 1 + .../utilities/{ => ini_parser}/IniParser.cpp | 2 +- .../utilities/{ => ini_parser}/IniParser.h | 2 +- hikyuu_cpp/hikyuu/utilities/string_view.h | 70 +++++++++++++++ .../hikyuu/utilities/test_arithmetic.cpp | 11 ++- .../hikyuu/utilities/test_iniparser.cpp | 2 +- 28 files changed, 272 insertions(+), 137 deletions(-) rename hikyuu_cpp/hikyuu/utilities/{ => ini_parser}/IniParser.cpp (99%) rename hikyuu_cpp/hikyuu/utilities/{ => ini_parser}/IniParser.h (96%) create mode 100644 hikyuu_cpp/hikyuu/utilities/string_view.h diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index fcf32951..dfca1922 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -15,8 +15,8 @@ #include #include -#include "utilities/IniParser.h" -#include "utilities/thread/ThreadPool.h" +#include "hikyuu/utilities/ini_parser/IniParser.h" +#include "hikyuu/utilities/thread/ThreadPool.h" #include "StockManager.h" #include "global/GlobalTaskGroup.h" #include "global/schedule/inner_tasks.h" diff --git a/hikyuu_cpp/hikyuu/hikyuu.cpp b/hikyuu_cpp/hikyuu/hikyuu.cpp index 6f36639e..7fb44db6 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.cpp +++ b/hikyuu_cpp/hikyuu/hikyuu.cpp @@ -11,7 +11,7 @@ #include #include -#include "utilities/IniParser.h" +#include "utilities/ini_parser/IniParser.h" #include "hikyuu.h" #include "version.h" @@ -40,8 +40,8 @@ void hikyuu_init(const string& config_file_name, bool ignore_preload, hkuParam.set("tmpdir", config.get("hikyuu", "tmpdir", ".")); hkuParam.set("datadir", config.get("hikyuu", "datadir", ".")); - hkuParam.set("quotation_server", config.get("hikyuu", "quotation_server", - "ipc:///tmp/hikyuu_real.ipc")); + hkuParam.set("quotation_server", + config.get("hikyuu", "quotation_server", "ipc:///tmp/hikyuu_real.ipc")); if (!config.hasSection("baseinfo")) { HKU_FATAL("Missing configure of baseinfo!"); diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index 0175d8c2..b097de08 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -7,8 +7,8 @@ #include #include -#include "../utilities/os.h" -#include "../utilities/IniParser.h" +#include "hikyuu/utilities/os.h" +#include "hikyuu/utilities/ini_parser/IniParser.h" #include "../global/schedule/scheduler.h" #include "StrategyBase.h" diff --git a/hikyuu_cpp/hikyuu/utilities/Null.h b/hikyuu_cpp/hikyuu/utilities/Null.h index aed19e66..1d28d5ef 100644 --- a/hikyuu_cpp/hikyuu/utilities/Null.h +++ b/hikyuu_cpp/hikyuu/utilities/Null.h @@ -10,6 +10,7 @@ #define NULL_H_ #include +#include #include #include #include "osdef.h" diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index 81eac01c..28b5bb55 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -399,7 +399,7 @@ private: // 分配 timer_id int getNewTimerId() { int max_int = std::numeric_limits::max(); - HKU_WARN_IF_RETURN(m_timers.size() >= max_int, -1, "Timer queue is full!"); + HKU_WARN_IF_RETURN(m_timers.size() >= size_t(max_int), -1, "Timer queue is full!"); if (m_current_timer_id >= max_int) { m_current_timer_id = 0; diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.h b/hikyuu_cpp/hikyuu/utilities/arithmetic.h index fdf929a8..06d242a6 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.h +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.h @@ -15,9 +15,9 @@ #include #include #include -#include #include +#include "string_view.h" #ifndef HKU_API #define HKU_API #endif @@ -210,6 +210,7 @@ inline void trim(std::string& s) { s.erase(s.find_last_not_of("\n") + 1); } +#if CPP_STANDARD >= CPP_STANDARD_17 /** * 分割字符串 * @param str 待封的字符串 @@ -226,9 +227,7 @@ inline std::vector split(const std::string& str, char c) { pos = view.find_first_of(c, prepos); } - if (prepos < str.size() - 1) { - result.emplace_back(str.substr(prepos)); - } + result.emplace_back(view.substr(prepos)); return result; } @@ -249,9 +248,7 @@ inline std::vector split(const std::string_view& view, char c) pos = view.find_first_of(c, prepos); } - if (prepos < view.size() - 1) { - result.emplace_back(view.substr(prepos)); - } + result.emplace_back(view.substr(prepos)); return result; } @@ -272,11 +269,52 @@ inline std::vector split(const std::string_view& str, pos = str.find(split_str, prepos); } + result.emplace_back(str.substr(prepos)); + return result; +} + +#else +/** + * 分割字符串 + * @param str 待封的字符串 + * @param c 分割符 + */ +inline std::vector split(const std::string &str, char c) { + std::vector result; + size_t prepos = 0; + size_t pos = str.find_first_of(c); + while (pos != std::string::npos) { + result.emplace_back(str.substr(prepos, pos - prepos)); + prepos = pos + 1; + pos = str.find_first_of(c, prepos); + } + + result.emplace_back(str.substr(prepos)); + return result; +} + +inline std::vector split(const std::string &str, const std::string &split_str) { + std::vector result; + size_t split_str_len = split_str.size(); + if (split_str_len == 0) { + result.emplace_back(str); + return result; + } + + size_t prepos = 0; + size_t pos = str.find(split_str); + while (pos != std::string::npos) { + result.emplace_back(str.substr(prepos, pos - prepos)); + prepos = pos + split_str_len; + pos = str.find(split_str, prepos); + } + if (prepos < str.size() - 1) { result.emplace_back(str.substr(prepos)); } return result; } +#endif /* #if CPP_STANDARD >= CPP_STANDARD_17 */ /** * byte 转 16 进制字符串, 如 "abcd" 转换为 "61626364" diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp index 57dcca66..9145115d 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp @@ -23,6 +23,10 @@ HKU_API std::ostream& operator<<(std::ostream& out, const Datetime& d) { } Datetime Datetime::fromHex(uint64_t time) { + if (Null() == time) { + return Datetime(); + } + uint64_t second = 0xFFULL & time; uint64_t minute = (0xFF00ULL & time) >> 8; uint64_t hour = (0xFF0000ULL & time) >> 16; diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h index 349931a0..d0ad4809 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h @@ -408,6 +408,11 @@ public: return d.ticks(); // or use boost::hash_combine } }; + +inline string to_string(const hku::Datetime &date) { + return date.str(); +} + } // namespace std #if FMT_VERSION >= 90000 diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp index 9313b429..19c24ca6 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp @@ -39,6 +39,24 @@ TimeDelta::TimeDelta(bt::time_duration td) { m_duration = td; } +/** 从字符串构造,格式:-1 days, hh:mm:ss.000000) */ +TimeDelta::TimeDelta(const std::string& delta) { + std::string val(delta); + std::string errmsg(fmt::format("Invalid format: {}", delta)); + to_lower(val); + auto vals = split(val, ' '); + HKU_CHECK(vals.size() == 3, errmsg); + int64_t days = std::stoll(std::string(vals[0])); + vals = split(vals[2], ':'); + HKU_CHECK(vals.size() == 3, errmsg); + int64_t hours = std::stoll(std::string(vals[0])); + int64_t minutes = std::stoll(std::string(vals[1])); + int64_t microseconds = static_cast(std::stod(std::string(vals[2])) * 1000000.0); + int64_t total = (((days * 24) + hours) * 60 + minutes) * 60000000LL + microseconds; + HKU_CHECK(total >= m_min_micro_seconds && total <= m_max_micro_seconds, "Out of total range!"); + m_duration = bt::time_duration(0, 0, 0, total); +} + TimeDelta TimeDelta::fromTicks(int64_t ticks) { HKU_CHECK(ticks >= m_min_micro_seconds && ticks <= m_max_micro_seconds, "Out of total range!"); return TimeDelta(bt::time_duration(0, 0, 0, ticks)); diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h index 802e182c..bc10d996 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h +++ b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h @@ -58,6 +58,9 @@ public: /** 通过 boost::posix_time::time_duration 构造 */ explicit TimeDelta(bt::time_duration td); + /** 从字符串构造,格式:-1 days, hh:mm:ss.000000) */ + explicit TimeDelta(const std::string& delta); + /** 赋值构造函数 */ TimeDelta(const TimeDelta&) = default; @@ -315,6 +318,14 @@ inline TimeDelta Microseconds(int64_t microsecs) { } /* namespace hku */ +namespace std { + +inline string to_string(const hku::TimeDelta &delta) { + return delta.str(); +} + +} /* namespace hku */ + #if FMT_VERSION >= 90000 template <> struct fmt::formatter { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h index a9a0b166..d659eeef 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h @@ -22,14 +22,14 @@ namespace hku { struct ASC { - explicit ASC(const char* name) : name(name) {} - explicit ASC(const std::string& name) : name(name) {} + explicit ASC(const char *name) : name(name) {} + explicit ASC(const std::string &name) : name(name) {} std::string name; }; struct DESC { - explicit DESC(const char* name) : name(name) {} - explicit DESC(const std::string& name) : name(name) {} + explicit DESC(const char *name) : name(name) {} + explicit DESC(const std::string &name) : name(name) {} std::string name; }; @@ -41,47 +41,47 @@ struct LIMIT { class HKU_API DBCondition { public: DBCondition() = default; - DBCondition(const DBCondition&) = default; - DBCondition(DBCondition&& rv) : m_condition(std::move(rv.m_condition)) {} + DBCondition(const DBCondition &) = default; + DBCondition(DBCondition &&rv) : m_condition(std::move(rv.m_condition)) {} - explicit DBCondition(const char* cond) : m_condition(cond) {} - explicit DBCondition(const std::string& cond) : m_condition(cond) {} + explicit DBCondition(const char *cond) : m_condition(cond) {} + explicit DBCondition(const std::string &cond) : m_condition(cond) {} - DBCondition& operator=(const DBCondition&) = default; - DBCondition& operator=(DBCondition&& rv) { + DBCondition &operator=(const DBCondition &) = default; + DBCondition &operator=(DBCondition &&rv) { if (this != &rv) { m_condition = std::move(rv.m_condition); } return *this; } - DBCondition& operator&(const DBCondition& other); - DBCondition& operator|(const DBCondition& other); + DBCondition &operator&(const DBCondition &other); + DBCondition &operator|(const DBCondition &other); enum ORDERBY { ORDER_ASC, ORDER_DESC }; - void orderBy(const std::string& field, ORDERBY order) { + void orderBy(const std::string &field, ORDERBY order) { m_condition = order == ORDERBY::ORDER_ASC ? fmt::format("{} order by {} ASC", m_condition, field) : fmt::format("{} order by {} DESC", m_condition, field); } - DBCondition& operator+(const ASC& asc) { + DBCondition &operator+(const ASC &asc) { orderBy(asc.name, ORDER_ASC); return *this; } - DBCondition& operator+(const DESC& desc) { + DBCondition &operator+(const DESC &desc) { orderBy(desc.name, ORDER_DESC); return *this; } - DBCondition& operator+(const LIMIT& limit) { + DBCondition &operator+(const LIMIT &limit) { m_condition = fmt::format("{} limit {}", m_condition, limit.limit); return *this; } - const std::string& str() const { + const std::string &str() const { return m_condition; } @@ -90,27 +90,27 @@ private: }; struct Field { - explicit Field(const char* name) : name(name) {} - explicit Field(const std::string& name) : name(name) {} + explicit Field(const char *name) : name(name) {} + explicit Field(const std::string &name) : name(name) {} // in 和 not_in 不支持 字符串,一般不会用到 in ("stra", "strb") 的 SQL 操作 template - DBCondition in(const std::vector& vals) { + DBCondition in(const std::vector &vals) { HKU_CHECK(!vals.empty(), "input vals can't be empty!"); return DBCondition(fmt::format("({} in ({}))", name, fmt::join(vals, ","))); } template - DBCondition not_in(const std::vector& vals) { + DBCondition not_in(const std::vector &vals) { HKU_CHECK(!vals.empty(), "input vals can't be empty!"); return DBCondition(fmt::format("({} not in ({}))", name, fmt::join(vals, ","))); } - DBCondition like(const std::string& pattern) { + DBCondition like(const std::string &pattern) { return DBCondition(fmt::format(R"(({} like "{}"))", name, pattern)); } - DBCondition like(const char* pattern) { + DBCondition like(const char *pattern) { return DBCondition(fmt::format(R"(({} like "{}"))", name, pattern)); } @@ -120,7 +120,7 @@ struct Field { // linux下类成员函数模板特化必须放在类外实现 // 否则编译时会报:explicit specialization in non-namespace scope template <> -inline DBCondition Field::in(const std::vector& vals) { +inline DBCondition Field::in(const std::vector &vals) { HKU_CHECK(!vals.empty(), "input vals can't be empty!"); std::ostringstream out; out << "(" << name << " in ("; @@ -133,7 +133,7 @@ inline DBCondition Field::in(const std::vector& vals) } template <> -inline DBCondition Field::not_in(const std::vector& vals) { +inline DBCondition Field::not_in(const std::vector &vals) { HKU_CHECK(!vals.empty(), "input vals can't be empty!"); std::ostringstream out; out << "(" << name << " not in ("; @@ -145,103 +145,103 @@ inline DBCondition Field::not_in(const std::vector& va return DBCondition(out.str()); } -inline std::ostream& operator<<(std::ostream& out, const DBCondition& d) { +inline std::ostream &operator<<(std::ostream &out, const DBCondition &d) { out << d.str(); return out; } template -inline DBCondition operator==(const Field& field, T val) { +inline DBCondition operator==(const Field &field, T val) { std::ostringstream out; out << "(" << field.name << "=" << val << ")"; return DBCondition(out.str()); } template -inline DBCondition operator!=(const Field& field, T val) { +inline DBCondition operator!=(const Field &field, T val) { std::ostringstream out; out << "(" << field.name << "<>" << val << ")"; return DBCondition(out.str()); } template -inline DBCondition operator>(const Field& field, T val) { +inline DBCondition operator>(const Field &field, T val) { std::ostringstream out; out << "(" << field.name << ">" << val << ")"; return DBCondition(out.str()); } template -inline DBCondition operator>=(const Field& field, T val) { +inline DBCondition operator>=(const Field &field, T val) { std::ostringstream out; out << "(" << field.name << ">=" << val << ")"; return DBCondition(out.str()); } template -inline DBCondition operator<(const Field& field, T val) { +inline DBCondition operator<(const Field &field, T val) { std::ostringstream out; out << "(" << field.name << "<" << val << ")"; return DBCondition(out.str()); } template -inline DBCondition operator<=(const Field& field, T val) { +inline DBCondition operator<=(const Field &field, T val) { std::ostringstream out; out << "(" << field.name << "<=" << val << ")"; return DBCondition(out.str()); } template <> -inline DBCondition operator!=(const Field& field, const char* val) { +inline DBCondition operator!=(const Field &field, const char *val) { return DBCondition(fmt::format(R"(({}<>"{}"))", field.name, val)); } template <> -inline DBCondition operator>(const Field& field, const char* val) { +inline DBCondition operator>(const Field &field, const char *val) { return DBCondition(fmt::format(R"(({}>"{}"))", field.name, val)); } template <> -inline DBCondition operator<(const Field& field, const char* val) { +inline DBCondition operator<(const Field &field, const char *val) { return DBCondition(fmt::format(R"(({}<"{}"))", field.name, val)); } template <> -inline DBCondition operator>=(const Field& field, const char* val) { +inline DBCondition operator>=(const Field &field, const char *val) { return DBCondition(fmt::format(R"(({}>="{}"))", field.name, val)); } template <> -inline DBCondition operator<=(const Field& field, const char* val) { +inline DBCondition operator<=(const Field &field, const char *val) { return DBCondition(fmt::format(R"(({}<="{}"))", field.name, val)); } -inline DBCondition operator==(const Field& field, const std::string& val) { +inline DBCondition operator==(const Field &field, const std::string &val) { return DBCondition(fmt::format(R"(({}="{}"))", field.name, val)); } -inline DBCondition operator!=(const Field& field, const std::string& val) { +inline DBCondition operator!=(const Field &field, const std::string &val) { return DBCondition(fmt::format(R"(({}<>"{}"))", field.name, val)); } -inline DBCondition operator>(const Field& field, const std::string& val) { +inline DBCondition operator>(const Field &field, const std::string &val) { return DBCondition(fmt::format(R"(({}>"{}"))", field.name, val)); } -inline DBCondition operator<(const Field& field, const std::string& val) { +inline DBCondition operator<(const Field &field, const std::string &val) { return DBCondition(fmt::format(R"(({}<"{}"))", field.name, val)); } -inline DBCondition operator>=(const Field& field, const std::string& val) { +inline DBCondition operator>=(const Field &field, const std::string &val) { return DBCondition(fmt::format(R"(({}>="{}"))", field.name, val)); } -inline DBCondition operator<=(const Field& field, const std::string& val) { +inline DBCondition operator<=(const Field &field, const std::string &val) { return DBCondition(fmt::format(R"(({}<="{}"))", field.name, val)); } -inline DBCondition operator==(const Field& field, const char* val) { +inline DBCondition operator==(const Field &field, const char *val) { return DBCondition(fmt::format(R"(({}="{}"))", field.name, val)); } diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h index a15f04eb..b91ec716 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h @@ -12,6 +12,7 @@ #include "../../DataType.h" #include "../../utilities/Parameter.h" +#include "../Null.h" #include "DBCondition.h" #include "SQLStatementBase.h" #include "SQLException.h" @@ -297,7 +298,7 @@ private: }; /** @ingroup DBConnect */ -typedef shared_ptr DBConnectPtr; +typedef std::shared_ptr DBConnectPtr; //------------------------------------------------------------------------- // inline方法实现 @@ -366,19 +367,16 @@ void DBConnectBase::save(T &item, bool autotrans) { if (autotrans) { commit(); } - } catch (hku::SQLException &e) { if (autotrans) { rollback(); } SQL_THROW(e.errcode(), "failed save! sql: {}! {}", st->getSqlString(), e.what()); - } catch (std::exception &e) { if (autotrans) { rollback(); } HKU_THROW("failed save! sql: {}! {}", st->getSqlString(), e.what()); - } catch (...) { if (autotrans) { rollback(); @@ -409,19 +407,16 @@ void DBConnectBase::batchSave(InputIterator first, InputIterator last, bool auto if (autotrans) { commit(); } - } catch (hku::SQLException &e) { if (autotrans) { rollback(); } SQL_THROW(e.errcode(), "failed batch save! sql: {}! {}", st->getSqlString(), e.what()); - } catch (std::exception &e) { if (autotrans) { rollback(); } HKU_THROW("failed batch save! sql: {}! {}", st->getSqlString(), e.what()); - } catch (...) { if (autotrans) { rollback(); @@ -431,7 +426,7 @@ void DBConnectBase::batchSave(InputIterator first, InputIterator last, bool auto } template -void DBConnectBase::load(T &item, const string &where) { +void DBConnectBase::load(T &item, const std::string &where) { std::ostringstream sql; if (where != "") { sql << T::getSelectSQL() << " where " << where << " limit 1"; @@ -451,7 +446,7 @@ void DBConnectBase::load(T &item, const DBCondition &cond) { } template -void DBConnectBase::batchLoad(Container &container, const string &where) { +void DBConnectBase::batchLoad(Container &container, const std::string &where) { std::ostringstream sql; if (where != "") { sql << Container::value_type::getSelectSQL() << " where " << where; @@ -513,19 +508,16 @@ void DBConnectBase::batchUpdate(InputIterator first, InputIterator last, bool au if (autotrans) { commit(); } - } catch (hku::SQLException &e) { if (autotrans) { rollback(); } SQL_THROW(e.errcode(), "failed batch save! sql: {}! {}", st->getSqlString(), e.what()); - } catch (std::exception &e) { if (autotrans) { rollback(); } HKU_THROW("failed batch update! sql: {}! {}", st->getSqlString(), e.what()); - } catch (...) { if (autotrans) { rollback(); @@ -570,19 +562,16 @@ void DBConnectBase::remove(T &item, bool autotrans) { commit(); } item.rowid(0); - } catch (hku::SQLException &e) { if (autotrans) { rollback(); } SQL_THROW(e.errcode(), "failed delete! sql: {}! {}", st->getSqlString(), e.what()); - } catch (std::exception &e) { if (autotrans) { rollback(); } HKU_THROW("failed delete! sql: {}! {}", st->getSqlString(), e.what()); - } catch (...) { if (autotrans) { rollback(); @@ -610,19 +599,16 @@ void DBConnectBase::batchRemove(InputIterator first, InputIterator last, bool au if (autotrans) { commit(); } - } catch (hku::SQLException &e) { if (autotrans) { rollback(); } SQL_THROW(e.errcode(), "failed batch delete! {}", e.what()); - } catch (std::exception &e) { if (autotrans) { rollback(); } HKU_THROW("failed batch delete! {}", e.what()); - } catch (...) { if (autotrans) { rollback(); @@ -645,13 +631,11 @@ inline void DBConnectBase::remove(const std::string &tablename, const std::strin if (autotrans) { commit(); } - } catch (hku::SQLException &e) { if (autotrans) { rollback(); } SQL_THROW(e.errcode(), "Failed exec sql: {}! {}", sql, e.what()); - } catch (std::exception &e) { if (autotrans) { rollback(); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp index 1302751a..f523e057 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp @@ -6,7 +6,6 @@ */ #include "DBUpgrade.h" -#include "../../debug.h" namespace hku { @@ -14,8 +13,8 @@ namespace hku { * 升级和创建数据库 */ void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name, - const std::vector &upgrade_scripts, int start_version, - const char *create_script) { + const std::vector &upgrade_scripts, int start_version, + const char *create_script) { HKU_TRACE("check {} database version ...", module_name); // 如果模块版本表不存在,则创建该表 @@ -75,7 +74,7 @@ void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name, // 当前版本已经大于等于待升至的版本,无需升级,直接返回 if (version >= to_version) { HKU_TRACE("current version({}) greater the upgrade version({}), ignored!", version, - to_version); + to_version); return; } diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h index ecc1ace3..1fc9d057 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h @@ -21,7 +21,7 @@ namespace hku { class DBConnectBase; /** @ingroup DBConnect */ -typedef shared_ptr DBConnectPtr; +typedef std::shared_ptr DBConnectPtr; /** @ingroup DBConnect */ class null_blob_exception : public exception { @@ -41,12 +41,12 @@ public: * @param driver 数据库连接 * @param sql_statement SQL语句 */ - SQLStatementBase(DBConnectBase *driver, const string &sql_statement); + SQLStatementBase(DBConnectBase *driver, const std::string &sql_statement); virtual ~SQLStatementBase() = default; /** 获取构建时传入的表达式SQL语句 */ - const string &getSqlString() const; + const std::string &getSqlString() const; /** 获取数据驱动 */ DBConnectBase *getConnect() const; @@ -163,7 +163,7 @@ protected: }; /** @ingroup DBConnect */ -typedef shared_ptr SQLStatementPtr; +typedef std::shared_ptr SQLStatementPtr; inline SQLStatementBase ::SQLStatementBase(DBConnectBase *driver, const std::string &sql_statement) : m_driver(driver), m_sql_string(sql_statement) { @@ -277,7 +277,7 @@ inline void SQLStatementBase::getColumn(int idx, std::vector &item) { template typename std::enable_if::is_integer>::type SQLStatementBase::getColumn( int idx, T &item) { - string tmp; + std::string tmp; try { sub_getColumnAsBlob(idx, tmp); } catch (null_blob_exception &) { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp index aa09155c..068c260d 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp @@ -158,11 +158,11 @@ int64_t MySQLConnect::exec(const std::string& sql_string) { return affect_rows; } -SQLStatementPtr MySQLConnect::getStatement(const string& sql_statement) { - return make_shared(this, sql_statement); +SQLStatementPtr MySQLConnect::getStatement(const std::string& sql_statement) { + return std::make_shared(this, sql_statement); } -bool MySQLConnect::tableExist(const string& tablename) { +bool MySQLConnect::tableExist(const std::string& tablename) { bool result = false; try { SQLStatementPtr st = getStatement(fmt::format("SELECT 1 FROM {} LIMIT 1;", tablename)); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h index 477eabac..948ebd10 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h @@ -24,18 +24,18 @@ namespace hku { class HKU_API MySQLConnect : public DBConnectBase { public: - explicit MySQLConnect(const Parameter& param); + explicit MySQLConnect(const Parameter ¶m); virtual ~MySQLConnect(); - MySQLConnect(const MySQLConnect&) = delete; - MySQLConnect& operator=(const MySQLConnect&) = delete; + MySQLConnect(const MySQLConnect &) = delete; + MySQLConnect &operator=(const MySQLConnect &) = delete; virtual bool ping() override; - virtual int64_t exec(const std::string& sql_string) override; - virtual SQLStatementPtr getStatement(const std::string& sql_statement) override; - virtual bool tableExist(const std::string& tablename) override; - virtual void resetAutoIncrement(const std::string& tablename) override; + virtual int64_t exec(const std::string &sql_string) override; + virtual SQLStatementPtr getStatement(const std::string &sql_statement) override; + virtual bool tableExist(const std::string &tablename) override; + virtual void resetAutoIncrement(const std::string &tablename) override; virtual void transaction() noexcept override; virtual void commit() noexcept override; @@ -48,7 +48,7 @@ private: private: friend class MySQLStatement; - MYSQL* m_mysql; + MYSQL *m_mysql; }; } // namespace hku diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp index 4e6a3a59..400b6a50 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp @@ -7,6 +7,7 @@ * Author: fasiondog */ +#include #include "MySQLStatement.h" #include "MySQLConnect.h" @@ -230,7 +231,7 @@ void MySQLStatement::sub_bindDatetime(int idx, const Datetime& item) { m_param_bind[idx].is_null = 0; } -void MySQLStatement::sub_bindText(int idx, const string& item) { +void MySQLStatement::sub_bindText(int idx, const std::string& item) { HKU_CHECK(idx < m_param_bind.size(), "idx out of range! idx: {}, total: {}", idx, m_param_bind.size()); m_param_buffer.push_back(item); @@ -254,7 +255,7 @@ void MySQLStatement::sub_bindText(int idx, const char* item, size_t len) { m_param_bind[idx].is_null = 0; } -void MySQLStatement::sub_bindBlob(int idx, const string& item) { +void MySQLStatement::sub_bindBlob(int idx, const std::string& item) { HKU_CHECK(idx < m_param_bind.size(), "idx out of range! idx: {}, total: {}", idx, m_param_bind.size()); m_param_buffer.push_back(item); @@ -357,7 +358,7 @@ void MySQLStatement::sub_getColumnAsDatetime(int idx, Datetime& item) { } } -void MySQLStatement::sub_getColumnAsText(int idx, string& item) { +void MySQLStatement::sub_getColumnAsText(int idx, std::string& item) { HKU_CHECK(idx < m_result_buffer.size(), "idx out of range! idx: {}, total: {}", m_result_buffer.size()); @@ -369,7 +370,7 @@ void MySQLStatement::sub_getColumnAsText(int idx, string& item) { } try { - vector* p = boost::any_cast>(&(m_result_buffer[idx])); + std::vector* p = boost::any_cast>(&(m_result_buffer[idx])); std::ostringstream buf; for (unsigned long i = 0; i < m_result_length[idx]; i++) { buf << (*p)[i]; @@ -380,7 +381,7 @@ void MySQLStatement::sub_getColumnAsText(int idx, string& item) { } } -void MySQLStatement::sub_getColumnAsBlob(int idx, string& item) { +void MySQLStatement::sub_getColumnAsBlob(int idx, std::string& item) { HKU_CHECK(idx < m_result_buffer.size(), "idx out of range! idx: {}, total: {}", m_result_buffer.size()); @@ -392,7 +393,7 @@ void MySQLStatement::sub_getColumnAsBlob(int idx, string& item) { } try { - vector* p = boost::any_cast>(&m_result_buffer[idx]); + std::vector* p = boost::any_cast>(&m_result_buffer[idx]); std::ostringstream buf; for (unsigned long i = 0; i < m_result_length[idx]; i++) { buf << (*p)[i]; @@ -415,7 +416,7 @@ void MySQLStatement::sub_getColumnAsBlob(int idx, std::vector& item) { } try { - item = boost::any_cast>(m_result_buffer[idx]); + item = boost::any_cast>(m_result_buffer[idx]); } catch (...) { HKU_THROW("Field type mismatch! idx: {}", idx); } diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h index 66b6a111..e49bb10f 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h @@ -35,7 +35,7 @@ namespace hku { class HKU_API MySQLStatement : public SQLStatementBase { public: MySQLStatement() = delete; - MySQLStatement(DBConnectBase* driver, const std::string& sql_statement); + MySQLStatement(DBConnectBase *driver, const std::string &sql_statement); virtual ~MySQLStatement(); virtual void sub_exec() override; @@ -45,28 +45,28 @@ public: virtual void sub_bindNull(int idx) override; virtual void sub_bindInt(int idx, int64_t value) override; virtual void sub_bindDouble(int idx, double item) override; - virtual void sub_bindDatetime(int idx, const Datetime& item) override; - virtual void sub_bindText(int idx, const std::string& item) override; - virtual void sub_bindText(int idx, const char* item, size_t len) override; - virtual void sub_bindBlob(int idx, const std::string& item) override; - virtual void sub_bindBlob(int idx, const std::vector& item) override; + virtual void sub_bindDatetime(int idx, const Datetime &item) override; + virtual void sub_bindText(int idx, const std::string &item) override; + virtual void sub_bindText(int idx, const char *item, size_t len) override; + virtual void sub_bindBlob(int idx, const std::string &item) override; + virtual void sub_bindBlob(int idx, const std::vector &item) override; virtual int sub_getNumColumns() const override; - virtual void sub_getColumnAsInt64(int idx, int64_t& item) override; - virtual void sub_getColumnAsDouble(int idx, double& item) override; - virtual void sub_getColumnAsDatetime(int idx, Datetime& item) override; - virtual void sub_getColumnAsText(int idx, std::string& item) override; - virtual void sub_getColumnAsBlob(int idx, std::string& item) override; - virtual void sub_getColumnAsBlob(int idx, std::vector& item) override; + virtual void sub_getColumnAsInt64(int idx, int64_t &item) override; + virtual void sub_getColumnAsDouble(int idx, double &item) override; + virtual void sub_getColumnAsDatetime(int idx, Datetime &item) override; + virtual void sub_getColumnAsText(int idx, std::string &item) override; + virtual void sub_getColumnAsBlob(int idx, std::string &item) override; + virtual void sub_getColumnAsBlob(int idx, std::vector &item) override; private: void _reset(); void _bindResult(); private: - MYSQL* m_db; - MYSQL_STMT* m_stmt; - MYSQL_RES* m_meta_result; + MYSQL *m_db; + MYSQL_STMT *m_stmt; + MYSQL_RES *m_meta_result; bool m_needs_reset; bool m_has_bind_result; std::vector m_param_bind; diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp index 2804a25f..0c1a93ae 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp @@ -23,7 +23,7 @@ static int sqlite_busy_call_back(void *ptr, int count) { SQLiteConnect::SQLiteConnect(const Parameter ¶m) : DBConnectBase(param), m_db(nullptr) { try { - m_dbname = getParam("db"); + m_dbname = getParam("db"); int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_NOMUTEX; // 多线程模式下,不同数据库连接不能使用 SQLITE_OPEN_SHAREDCACHE // 将导致 table is locked! @@ -134,7 +134,7 @@ SQLStatementPtr SQLiteConnect::getStatement(const std::string &sql_statement) { return std::make_shared(this, sql_statement); } -bool SQLiteConnect::tableExist(const string &tablename) { +bool SQLiteConnect::tableExist(const std::string &tablename) { SQLStatementPtr st = getStatement(fmt::format("select count(1) from sqlite_master where name='{}'", tablename)); st->exec(); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h index 694abce9..34effb7b 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h @@ -78,11 +78,11 @@ private: private: friend class SQLiteStatement; - string m_dbname; + std::string m_dbname; sqlite3 *m_db; }; -typedef shared_ptr SQLiteConnectPtr; +typedef std::shared_ptr SQLiteConnectPtr; } // namespace hku diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp index b9fd634c..31d395d6 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.cpp @@ -12,7 +12,7 @@ namespace hku { -SQLiteStatement::SQLiteStatement(DBConnectBase *driver, const string &sql_statement) +SQLiteStatement::SQLiteStatement(DBConnectBase *driver, const std::string &sql_statement) : SQLStatementBase(driver, sql_statement), m_needs_reset(false), m_step_status(SQLITE_DONE), @@ -20,7 +20,7 @@ SQLiteStatement::SQLiteStatement(DBConnectBase *driver, const string &sql_statem m_db((dynamic_cast(driver))->m_db), m_stmt(NULL) { int status = - sqlite3_prepare_v2(m_db, m_sql_string.c_str(), m_sql_string.size() + 1, &m_stmt, NULL); + sqlite3_prepare_v2(m_db, m_sql_string.c_str(), int(m_sql_string.size() + 1), &m_stmt, NULL); if (status != SQLITE_OK) { sqlite3_finalize(m_stmt); SQL_THROW(status, "Failed prepare sql statement: {}! error msg: {}", m_sql_string, @@ -103,7 +103,7 @@ void SQLiteStatement::sub_bindDatetime(int idx, const Datetime &item) { } } -void SQLiteStatement::sub_bindText(int idx, const string &item) { +void SQLiteStatement::sub_bindText(int idx, const std::string &item) { _reset(); int status = sqlite3_bind_text(m_stmt, idx + 1, item.c_str(), (int)item.size(), SQLITE_TRANSIENT); @@ -122,7 +122,7 @@ void SQLiteStatement::sub_bindDouble(int idx, double item) { SQL_CHECK(status == SQLITE_OK, status, sqlite3_errmsg(m_db)); } -void SQLiteStatement::sub_bindBlob(int idx, const string &item) { +void SQLiteStatement::sub_bindBlob(int idx, const std::string &item) { _reset(); int status = sqlite3_bind_blob(m_stmt, idx + 1, item.data(), (int)item.size(), SQLITE_TRANSIENT); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h index 72df5a86..593e5cbc 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h @@ -28,7 +28,7 @@ public: * @param driver 数据库连接 * @param sql_statement SQL语句 */ - SQLiteStatement(DBConnectBase *driver, const string &sql_statement); + SQLiteStatement(DBConnectBase *driver, const std::string &sql_statement); /** 析构函数 */ virtual ~SQLiteStatement(); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.cpp index 6a21c5bc..803d0508 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#include "hikyuu/utilities/SpendTimer.h" #include "hikyuu/utilities/os.h" #include "SQLiteUtil.h" diff --git a/hikyuu_cpp/hikyuu/utilities/IniParser.cpp b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp similarity index 99% rename from hikyuu_cpp/hikyuu/utilities/IniParser.cpp rename to hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp index 1e6d69bc..ad32dd50 100644 --- a/hikyuu_cpp/hikyuu/utilities/IniParser.cpp +++ b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp @@ -9,7 +9,7 @@ #include #include -#include "arithmetic.h" +#include "../arithmetic.h" #include "IniParser.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/utilities/IniParser.h b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h similarity index 96% rename from hikyuu_cpp/hikyuu/utilities/IniParser.h rename to hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h index 7be0afaf..87f2e183 100644 --- a/hikyuu_cpp/hikyuu/utilities/IniParser.h +++ b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h @@ -75,7 +75,7 @@ public: std::string get(const std::string& section, const std::string& option, const std::string& default_str = std::string()) const; - //以下默认值类型使用string的原因是因为int/float/double/bool类型没有空对象 + // 以下默认值类型使用string的原因是因为int/float/double/bool类型没有空对象 int getInt(const std::string& section, const std::string& option, const std::string& default_str = std::string()) const; diff --git a/hikyuu_cpp/hikyuu/utilities/string_view.h b/hikyuu_cpp/hikyuu/utilities/string_view.h new file mode 100644 index 00000000..c18a752d --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/string_view.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2019~2021, hikyuu + * + * Created on: 2021/12/16 + * Author: fasiondog + */ + +#pragma once + +#include "cppdef.h" + +#if CPP_STANDARD >= CPP_STANDARD_17 +#include +#include +namespace hku { + +using std::string_view; + +} + +#else + +#include +#include +#include "Log.h" + +namespace hku { + +class string_view { +public: + constexpr string_view() noexcept = default; + + string_view(const char *data, std::size_t size) : _data(data), _size(size) { + HKU_CHECK_THROW(data, std::invalid_argument, "Input null ptr!"); + }; + + string_view(const char *data) : _data(data) { // NOSONAR + HKU_CHECK_THROW(data, std::invalid_argument, "Input null ptr!"); + _size = std::strlen(data); + } + + string_view(const std::string &str) : _data(str.data()), _size(str.size()) {} // NOSONAR + + constexpr string_view(const string_view &) noexcept = default; + + string_view &operator=(const string_view &) noexcept = default; + + constexpr const char &operator[](std::size_t pos) const { + return _data[pos]; + } + + constexpr const char *data() const noexcept { + return _data; + } + + constexpr std::size_t size() const noexcept { + return _size; + } + +private: + constexpr string_view(std::nullptr_t) = delete; + +private: + const char *_data = nullptr; + std::size_t _size = 0; +}; + +} // namespace hku + +#endif diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp index b621fa8f..82fb2900 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp @@ -102,15 +102,16 @@ TEST_CASE("test_split_by_char") { x = "100.1."; splits = split(x, '.'); - CHECK_EQ(splits.size(), 2); + CHECK_EQ(splits.size(), 3); CHECK_EQ(splits[0], "100"); CHECK_EQ(splits[1], "1"); x = ".."; splits = split(x, '.'); - CHECK_EQ(splits.size(), 2); + CHECK_EQ(splits.size(), 3); CHECK_EQ(splits[0], ""); CHECK_EQ(splits[1], ""); + CHECK_EQ(splits[2], ""); } TEST_CASE("test_split_by_string") { @@ -129,9 +130,10 @@ TEST_CASE("test_split_by_string") { // 分割字符串长度为1 x = "100.1."; splits = split(x, "."); - CHECK_EQ(splits.size(), 2); + CHECK_EQ(splits.size(), 3); CHECK_EQ(splits[0], "100"); CHECK_EQ(splits[1], "1"); + CHECK_EQ(splits[2], ""); // 分割字符串长度为2 x = "100.1.234.1.56"; @@ -143,9 +145,10 @@ TEST_CASE("test_split_by_string") { x = ".."; splits = split(x, "."); - CHECK_EQ(splits.size(), 2); + CHECK_EQ(splits.size(), 3); CHECK_EQ(splits[0], ""); CHECK_EQ(splits[1], ""); + CHECK_EQ(splits[2], ""); } /** @} */ \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_iniparser.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_iniparser.cpp index 023309be..7d7ba447 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_iniparser.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_iniparser.cpp @@ -11,7 +11,7 @@ #include #include #include -#include +#include using namespace hku; using namespace std; From ee16cfa938fb5a4c53ee1e88b337541a7c886bee Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Jul 2024 02:08:25 +0800 Subject: [PATCH 393/601] update --- xmake.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index 14be5417..1069ffe8 100644 --- a/xmake.lua +++ b/xmake.lua @@ -232,7 +232,15 @@ add_requires("boost " .. boost_version, { add_requires("spdlog", {system = false, configs = {header_only = true, fmt_external = true}}) add_requireconfs("spdlog.fmt", {override = true, version = fmt_version, configs = {header_only = true}}) add_requires("sqlite3 " .. sqlite_version, {system = false, configs = {shared = true, cxflags = "-fPIC"}}) -add_requires("flatbuffers v" .. flatbuffers_version, {system = false}) +if is_plat("windows") then + if is_mode("release") then + add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs={runtimes="MD"}}) + else + add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs={runtimes="MDd"}}) + end +else + add_requires("flatbuffers v" .. flatbuffers_version, {system = false}) +end add_requires("nng " .. nng_version, {system = false, configs = {cxflags = "-fPIC"}}) add_requires("nlohmann_json", {system = false}) add_requires("cpp-httplib " .. cpp_httplib_version, {system = false, configs = {zlib = true, ssl = true}}) From deb894ebb0a2e3fe725746a3b0cfdd653487a147 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Jul 2024 12:41:23 +0800 Subject: [PATCH 394/601] update --- copy_dependents.lua | 84 +++++++++++++++++++++++++++++++++++++ hikyuu_cpp/hikyuu/xmake.lua | 31 ++++---------- xmake.lua | 1 + 3 files changed, 94 insertions(+), 22 deletions(-) create mode 100644 copy_dependents.lua diff --git a/copy_dependents.lua b/copy_dependents.lua new file mode 100644 index 00000000..48b6fc5d --- /dev/null +++ b/copy_dependents.lua @@ -0,0 +1,84 @@ + +-- 拷贝依赖的第三方库头文件及lib到指定目录 +task("copy_dependents") + set_category("plugin") + + -- 设置运行脚本 + -- destpath 目标目录 + -- onlylib 只拷贝lib库 + on_run(function(target, destpath, onlylib) + local libdir = destpath .. '/lib' + + -- 将依赖的库拷贝至build的输出目录 + for libname, pkg in pairs(target:pkgs()) do + if pkg:installdir() == nil then + print(libname .. ": Not found installdir, maybe it is system lib!"); + goto continue + end + + print("dependent package: " .. pkg:installdir()) + --local linkdirs = pkg:get("linkdirs") + -- 部分库没有 linkdirs ,如:MNN, Paddle-lite,所以使用 includedirs + local pkg_path = pkg:get("includedirs") + if pkg_path == nil then + pkg_path = pkg:get("sysincludedirs") -- xmake 2.3.9 改为了 sysincludedirs + end + + if pkg_path == nil then + goto continue + end + + -- 安装模式下拷贝所有依赖库的头文件 + if not onlylib then + if type(pkg_path) == 'string' then + local pos = string.find(pkg_path, "opencv") + if pos == nil then + os.trycp(pkg_path, destpath) + else + os.trycp(pkg_path .. "/opencv2", destpath .. "/include") + end + elseif type(pkg_path) == 'table' then + for i=1, #pkg_path do + local pos = string.find(pkg_path[i], "yh_utils") + if pos == nil then + pos = string.find(pkg_path[i], "opencv") + if pos == nil then + os.trycp(pkg_path[i], destpath) + else + os.trycp(pkg_path[i] .. "/opencv2", destpath .. "/include") + end + else + for _, filedir in ipairs(os.dirs(pkg_path[i] .. "/*")) do + local pos = string.find(filedir, "yihua") + if pos == nil then + os.trycp(filedir, destpath .. "/include") + else + os.trycp(filedir .. "/utils", destpath .. "/include/yihua") + end + end + end + end + end + end + + -- 拷贝依赖的库文件 + os.trycp(pkg:installdir() .. "/lib/*", libdir) + if is_plat("windows") then + os.trycp(pkg:installdir() .. "/bin/*.dll", libdir) + end + + :: continue :: + end + end) + + set_menu { + -- usage + usage = "xmake copy_dependents [options]" + + -- description + , description = "拷贝依赖的第三方库头文件及lib到指定目录!" + + -- options + , options = {} + } +task_end() \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index dd5b20da..6daf0736 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -105,32 +105,19 @@ target("hikyuu") end after_build(function(target) - -- 不同平台的库后缀名 - local lib_suffix = ".so" - if is_plat("windows") then - lib_suffix = ".dll" - elseif is_plat("macosx") then - lib_suffix = ".dylib" - end - - local libdir = get_config("buildir") .. "/" .. get_config("mode") .. "/" .. get_config("plat") .. "/" .. - get_config("arch") .. "/lib" - -- 将依赖的库拷贝至build的输出目录 - for libname, pkg in pairs(target:pkgs()) do - local pkg_path = pkg:installdir() - if pkg_path ~= nil then - print("copy dependents: " .. pkg_path) - os.trycp(pkg_path .. "/bin/*" .. lib_suffix, libdir) - os.trycp(pkg_path .. "/lib/*" .. lib_suffix, libdir) - os.trycp(pkg_path .. "/lib/*.so.*", libdir) - end - end + local destpath = get_config("buildir") .. "/" .. get_config("mode") .. "/" .. get_config("plat") .. "/" .. get_config("arch") + print(destpath) + import("core.project.task") + task.run("copy_dependents", {}, target, destpath, true) end) - + after_install(function(target) local dst_path = target:installdir() .. "/include/hikyuu/python/" os.cp("$(projectdir)/hikyuu_pywrap/pybind_utils.h", dst_path) os.cp("$(projectdir)/hikyuu_pywrap/pickle_support.h", dst_path) - end) + local destpath = target:installdir() + import("core.project.task") + task.run("copy_dependents", {}, target, destpath, true) + end) target_end() diff --git a/xmake.lua b/xmake.lua index 1069ffe8..93165299 100644 --- a/xmake.lua +++ b/xmake.lua @@ -290,6 +290,7 @@ end -- -- add_defines("HKU_ENABLE_SSE2", "HKU_ENABLE_SSE3", "HKU_ENABLE_SSE41", "HKU_ENABLE_AVX", "HKU_ENABLE_AVX2") -- end +includes("./copy_dependents.lua") includes("./hikyuu_cpp/hikyuu") includes("./hikyuu_pywrap") includes("./hikyuu_cpp/unit_test") From 583431f3775833f648a756ea8fec7e749ab71fe0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 11 Jul 2024 17:08:04 +0800 Subject: [PATCH 395/601] update --- README.rst | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.rst b/README.rst index 5ac522e9..648684d9 100644 --- a/README.rst +++ b/README.rst @@ -1,4 +1,4 @@ -.. image:: https://img2.imgtp.com/2024/05/29/mSBrbO7R.png +.. image:: https://fasiondog.cn/wp-content/uploads/2024/05/00000_title-1.png :target: https://gitee.com/fasiondog/hikyuu :align: left :alt: Hikyuu @@ -52,7 +52,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm) sys.run(sm['sz000001'], Query(-150)) -.. figure:: https://img2.imgtp.com/2024/05/29/xTEvXesP.png +.. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/10000-overview.png :width: 600px 完整示例参见:``_ @@ -63,7 +63,7 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 - **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下: - .. figure:: https://img2.imgtp.com/2024/05/29/9SrY9vI1.png + .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/10002-function-arc.png :width: 600px - **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 @@ -92,22 +92,22 @@ Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架 **加入知识星球** 更多示例与策略部件的及时分享(您的加入将视为对项目的捐赠) - .. figure:: https://img2.imgtp.com/2024/05/29/3sEP6Re0.png + .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png **项目交流和问题答复将转移至知识星球-【Hikyuu量化】。** - 关注公众号: - .. figure:: https://img2.imgtp.com/2024/05/29/1NQztICj.jpg + .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/weixin_gongzhonghao.jpg - 加入微信群(请注明“加入hikyuu”): - .. figure:: https://img2.imgtp.com/2024/05/29/HD0dAgbn.jpg + .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/weixin_group.jpg - QQ交流群(逐渐废弃):114910869, 或扫码加入: - .. figure:: https://img2.imgtp.com/2024/05/29/xAH2PesY.png + .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png From fa186b30039f4791b2d10f4cd621ba7569260499 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 12 Jul 2024 12:21:08 +0800 Subject: [PATCH 396/601] update --- xmake.lua | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/xmake.lua b/xmake.lua index 93165299..c9dc9180 100644 --- a/xmake.lua +++ b/xmake.lua @@ -220,6 +220,7 @@ add_requires("boost " .. boost_version, { debug = is_mode("debug"), configs = { shared = is_plat("windows"), + runtimes = get_config("runtimes"), multi = true, date_time = true, filesystem = false, @@ -229,18 +230,11 @@ add_requires("boost " .. boost_version, { }, }) +add_requires("fmt " .. fmt_version, {system = false}) add_requires("spdlog", {system = false, configs = {header_only = true, fmt_external = true}}) -add_requireconfs("spdlog.fmt", {override = true, version = fmt_version, configs = {header_only = true}}) +add_requireconfs("spdlog.fmt", {override = true, version = fmt_version, system = false}) add_requires("sqlite3 " .. sqlite_version, {system = false, configs = {shared = true, cxflags = "-fPIC"}}) -if is_plat("windows") then - if is_mode("release") then - add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs={runtimes="MD"}}) - else - add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs={runtimes="MDd"}}) - end -else - add_requires("flatbuffers v" .. flatbuffers_version, {system = false}) -end +add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs= {runtimes = get_config("runtimes")}}) add_requires("nng " .. nng_version, {system = false, configs = {cxflags = "-fPIC"}}) add_requires("nlohmann_json", {system = false}) add_requires("cpp-httplib " .. cpp_httplib_version, {system = false, configs = {zlib = true, ssl = true}}) From ba266a35beea852ed016d57f487233f0203216c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=80=9Czh?= <1790669061@qq.com> Date: Tue, 16 Jul 2024 00:06:06 +0800 Subject: [PATCH 397/601] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=94=B6=E7=9B=98?= =?UTF-8?q?=E4=BB=B7=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KRecord.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index 8de33a1e..03623065 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -23,7 +23,7 @@ public: price_t openPrice; ///< 开盘价 price_t highPrice; ///< 最高价 price_t lowPrice; ///< 最低价 - price_t closePrice; ///< 最低价 + price_t closePrice; ///< 收盘价 price_t transAmount; ///< 成交金额(千元) price_t transCount; ///< 成交量(手),日线以下为股数 From 302a33698684ace6f33d1aaef7c79323ac82202c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Jul 2024 01:43:21 +0800 Subject: [PATCH 398/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20log.h=20=E4=BD=8D?= =?UTF-8?q?=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/DataType.h | 2 +- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 2 +- hikyuu_cpp/hikyuu/analysis/combinate.h | 2 +- .../base_info/mysql/MySQLBaseInfoDriver.cpp | 11 ++++------- .../data_driver/kdata/cvs/KDataTempCsvDriver.cpp | 3 +-- .../data_driver/kdata/mysql/MySQLKDataDriver.cpp | 2 +- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 2 +- hikyuu_cpp/hikyuu/global/node/NodeError.h | 2 +- hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp | 4 ++-- hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp | 2 +- .../hikyuu/trade_manage/imp/FixedATradeCost.cpp | 2 +- hikyuu_cpp/hikyuu/{ => utilities}/Log.cpp | 6 +++--- hikyuu_cpp/hikyuu/{ => utilities}/Log.h | 2 +- hikyuu_cpp/hikyuu/utilities/Parameter.cpp | 2 +- hikyuu_cpp/hikyuu/utilities/TimerManager.h | 2 +- hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp | 2 +- hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp | 2 +- hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h | 2 +- hikyuu_cpp/hikyuu/utilities/db_connect/SQLException.h | 2 +- hikyuu_cpp/hikyuu/utilities/db_connect/SQLResultSet.h | 2 +- .../utilities/db_connect/sqlite/SQLiteConnect.cpp | 2 +- hikyuu_cpp/hikyuu/{ => utilities}/exception.h | 0 hikyuu_cpp/hikyuu/utilities/os.cpp | 2 +- hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Block.cpp | 2 +- .../unit_test/hikyuu/hikyuu/test_StockManager.cpp | 2 +- .../hikyuu/utilities/datetime/test_Datetime.cpp | 2 +- .../hikyuu/utilities/db_connect/test_DBCondition.cpp | 2 +- .../unit_test/hikyuu/utilities/test_Parameter.cpp | 2 +- .../unit_test/hikyuu/utilities/test_TimerManager.cpp | 2 +- .../unit_test/hikyuu/utilities/test_arithmetic.cpp | 2 +- hikyuu_cpp/unit_test/hikyuu/utilities/test_os.cpp | 2 +- .../hikyuu/utilities/thread/test_ThreadPool.cpp | 2 +- hikyuu_pywrap/_Log.cpp | 2 +- 33 files changed, 38 insertions(+), 42 deletions(-) rename hikyuu_cpp/hikyuu/{ => utilities}/Log.cpp (98%) rename hikyuu_cpp/hikyuu/{ => utilities}/Log.h (99%) rename hikyuu_cpp/hikyuu/{ => utilities}/exception.h (100%) diff --git a/hikyuu_cpp/hikyuu/DataType.h b/hikyuu_cpp/hikyuu/DataType.h index f2c2987e..99f5f1ad 100644 --- a/hikyuu_cpp/hikyuu/DataType.h +++ b/hikyuu_cpp/hikyuu/DataType.h @@ -29,7 +29,7 @@ #include #include -#include "Log.h" +#include "utilities/Log.h" #include "utilities/osdef.h" #include "utilities/cppdef.h" #include "utilities/datetime/Datetime.h" diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 2aef8bf0..39e22143 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -20,7 +20,7 @@ #include #endif -#include "Log.h" +#include "utilities/Log.h" #include "hikyuu.h" #include "GlobalInitializer.h" #include "StockManager.h" diff --git a/hikyuu_cpp/hikyuu/analysis/combinate.h b/hikyuu_cpp/hikyuu/analysis/combinate.h index c8ffb975..02906d76 100644 --- a/hikyuu_cpp/hikyuu/analysis/combinate.h +++ b/hikyuu_cpp/hikyuu/analysis/combinate.h @@ -10,7 +10,7 @@ #include "hikyuu/indicator/Indicator.h" #include "hikyuu/trade_sys/system/System.h" #include "hikyuu/trade_manage/Performance.h" -#include "../Log.h" +#include "hikyuu/utilities/Log.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp index 975513fb..2be59990 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/mysql/MySQLBaseInfoDriver.cpp @@ -9,8 +9,8 @@ #include #include "MySQLBaseInfoDriver.h" +#include "hikyuu/utilities/Log.h" #include "../../../StockManager.h" -#include "../../../Log.h" #include "../table/MarketInfoTable.h" #include "../table/StockTypeInfoTable.h" #include "../table/StockWeightTable.h" @@ -281,12 +281,9 @@ Parameter MySQLBaseInfoDriver::getFinanceInfo(const string &market, const string << "f.zhuyinglirun, f.yingshouzhangkuan, f.yingyelirun, f.touzishouyu," << "f.jingyingxianjinliu, f.zongxianjinliu, f.cunhuo, f.lirunzonghe," << "f.shuihoulirun, f.jinglirun, f.weifenpeilirun, f.meigujingzichan," - << "f.baoliu2 from stkfinance f, stock s, market m " - << "where m.market='" << market << "'" - << " and s.code = '" << code << "'" - << " and s.marketid = m.marketid" - << " and f.stockid = s.stockid" - << " order by updated_date DESC limit 1"; + << "f.baoliu2 from stkfinance f, stock s, market m " << "where m.market='" << market << "'" + << " and s.code = '" << code << "'" << " and s.marketid = m.marketid" + << " and f.stockid = s.stockid" << " order by updated_date DESC limit 1"; auto con = m_pool->getConnect(); diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp index 6ab9fcd9..8f6c1ec1 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/cvs/KDataTempCsvDriver.cpp @@ -8,10 +8,9 @@ #include #include #include +#include "hikyuu/utilities/Log.h" #include "KDataTempCsvDriver.h" -#include "../../../Log.h" - namespace hku { KDataTempCsvDriver::~KDataTempCsvDriver() {} diff --git a/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp index d7bd927e..2e29c7cb 100644 --- a/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/kdata/mysql/MySQLKDataDriver.cpp @@ -6,7 +6,7 @@ */ #include -#include "../../../Log.h" +#include "hikyuu/utilities/Log.h" #include "MySQLKDataDriver.h" #include "KRecordTable.h" diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp index d7ab1d1d..c3018f9c 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp @@ -8,8 +8,8 @@ */ #include "hikyuu/GlobalInitializer.h" +#include "hikyuu/utilities/Log.h" #include "GlobalTaskGroup.h" -#include "../Log.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/global/node/NodeError.h b/hikyuu_cpp/hikyuu/global/node/NodeError.h index 1a7268ef..49f56212 100644 --- a/hikyuu_cpp/hikyuu/global/node/NodeError.h +++ b/hikyuu_cpp/hikyuu/global/node/NodeError.h @@ -7,7 +7,7 @@ #pragma once -#include "hikyuu/exception.h" +#include "hikyuu/utilities/exception.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp index bf0037c1..bc5ccc55 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp @@ -5,10 +5,10 @@ * Author: fasiondog */ -#include +#include "hikyuu/GlobalInitializer.h" +#include "hikyuu/utilities/Log.h" #include #include "scheduler.h" -#include "../../Log.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp index 61398ae5..462c4c05 100644 --- a/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp +++ b/hikyuu_cpp/hikyuu/indicator/IndicatorImp.cpp @@ -7,10 +7,10 @@ #include #include #include +#include "hikyuu/utilities/Log.h" #include "Indicator.h" #include "IndParam.h" #include "../Stock.h" -#include "../Log.h" #include "../GlobalInitializer.h" #include "imp/ICval.h" diff --git a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp index 0d874ade..f4c76754 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/imp/FixedATradeCost.cpp @@ -6,8 +6,8 @@ */ #include "FixedATradeCost.h" +#include "hikyuu/utilities/Log.h" #include "../../StockTypeInfo.h" -#include "../../Log.h" #if HKU_SUPPORT_SERIALIZATION BOOST_CLASS_EXPORT(hku::FixedATradeCost) diff --git a/hikyuu_cpp/hikyuu/Log.cpp b/hikyuu_cpp/hikyuu/utilities/Log.cpp similarity index 98% rename from hikyuu_cpp/hikyuu/Log.cpp rename to hikyuu_cpp/hikyuu/utilities/Log.cpp index 78cb743d..9b211a6b 100644 --- a/hikyuu_cpp/hikyuu/Log.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Log.cpp @@ -6,9 +6,9 @@ */ #include -#include "config.h" -#include "utilities/os.h" -#include "GlobalInitializer.h" +#include "hikyuu/config.h" +#include "hikyuu/GlobalInitializer.h" +#include "os.h" #include "Log.h" #if USE_SPDLOG_LOGGER diff --git a/hikyuu_cpp/hikyuu/Log.h b/hikyuu_cpp/hikyuu/utilities/Log.h similarity index 99% rename from hikyuu_cpp/hikyuu/Log.h rename to hikyuu_cpp/hikyuu/utilities/Log.h index e3989287..7e819521 100644 --- a/hikyuu_cpp/hikyuu/Log.h +++ b/hikyuu_cpp/hikyuu/utilities/Log.h @@ -13,7 +13,7 @@ #define NOMINMAX #endif -#include "config.h" +#include "../config.h" #include "exception.h" // clang-format off diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp index 2aff1f3d..abb683ba 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.cpp @@ -6,8 +6,8 @@ */ #include +#include "hikyuu/utilities/Log.h" #include "hikyuu/Block.h" -#include "../Log.h" #include "Parameter.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index 28b5bb55..9c6aa4a9 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -11,7 +11,7 @@ #include #include #include "hikyuu/utilities/datetime/Datetime.h" -#include "hikyuu/Log.h" +#include "hikyuu/utilities/Log.h" #include "thread/ThreadPool.h" #include "cppdef.h" diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp index 9145115d..75fdad74 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp @@ -11,7 +11,7 @@ #include #include "hikyuu/utilities/Null.h" -#include "hikyuu/Log.h" +#include "hikyuu/utilities/Log.h" #include "hikyuu/utilities/arithmetic.h" #include "Datetime.h" diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp index 19c24ca6..542a982b 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp @@ -10,7 +10,7 @@ #include #include "TimeDelta.h" #include "hikyuu/utilities/arithmetic.h" -#include "hikyuu/Log.h" +#include "hikyuu/utilities/Log.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h index d659eeef..376df11c 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h @@ -13,7 +13,7 @@ #include #include #include -#include "../../Log.h" +#include "hikyuu/utilities/Log.h" #ifndef HKU_API #define HKU_API diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLException.h b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLException.h index ef43de49..445917f4 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLException.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLException.h @@ -8,7 +8,7 @@ #pragma once #include -#include "hikyuu/exception.h" +#include "hikyuu/utilities/exception.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLResultSet.h b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLResultSet.h index 812320e3..6ff58ec7 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLResultSet.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLResultSet.h @@ -9,7 +9,7 @@ #include #include "hikyuu/utilities/arithmetic.h" -#include "hikyuu/Log.h" +#include "hikyuu/utilities/Log.h" #include "hikyuu/utilities/osdef.h" #include "DBConnectBase.h" diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp index 0c1a93ae..2728c7bb 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp @@ -9,7 +9,7 @@ #include #include "../../../config.h" -#include "../../../Log.h" +#include "hikyuu/utilities/Log.h" #include "SQLiteConnect.h" #include "SQLiteStatement.h" diff --git a/hikyuu_cpp/hikyuu/exception.h b/hikyuu_cpp/hikyuu/utilities/exception.h similarity index 100% rename from hikyuu_cpp/hikyuu/exception.h rename to hikyuu_cpp/hikyuu/utilities/exception.h diff --git a/hikyuu_cpp/hikyuu/utilities/os.cpp b/hikyuu_cpp/hikyuu/utilities/os.cpp index c17e528f..042af5ec 100644 --- a/hikyuu_cpp/hikyuu/utilities/os.cpp +++ b/hikyuu_cpp/hikyuu/utilities/os.cpp @@ -33,7 +33,7 @@ #include #include #include "arithmetic.h" -#include "hikyuu/Log.h" +#include "Log.h" #include "os.h" #ifdef _MSC_VER diff --git a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Block.cpp b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Block.cpp index 9efb6946..9f201a8a 100644 --- a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Block.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_Block.cpp @@ -7,7 +7,7 @@ #include "doctest/doctest.h" #include -#include +#include "hikyuu/utilities/Log.h" #include #include diff --git a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp index 11c9c1a0..81e19fe8 100644 --- a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StockManager.cpp @@ -8,7 +8,7 @@ #include "doctest/doctest.h" #include #include -#include +#include using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp index f38e59d6..4f252ff5 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp @@ -9,7 +9,7 @@ #include #include -#include +#include "hikyuu/utilities/Log.h" using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/db_connect/test_DBCondition.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/db_connect/test_DBCondition.cpp index a986a32f..a4e551ea 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/db_connect/test_DBCondition.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/db_connect/test_DBCondition.cpp @@ -7,7 +7,7 @@ #include "doctest/doctest.h" #include -#include +#include "hikyuu/utilities/Log.h" #include using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp index f28316f2..e00984e0 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_Parameter.cpp @@ -6,7 +6,7 @@ */ #include "doctest/doctest.h" -#include +#include "hikyuu/utilities/Log.h" #include #include diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_TimerManager.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_TimerManager.cpp index 81c8b114..d4d88e80 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_TimerManager.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_TimerManager.cpp @@ -7,7 +7,7 @@ #include "doctest/doctest.h" #include -#include +#include "hikyuu/utilities/Log.h" using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp index 82fb2900..b60fac2b 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_arithmetic.cpp @@ -7,7 +7,7 @@ #include "doctest/doctest.h" #include -#include +#include "hikyuu/utilities/Log.h" using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/test_os.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/test_os.cpp index e7fe4f0a..e8522256 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/test_os.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/test_os.cpp @@ -7,7 +7,7 @@ */ #include -#include +#include "hikyuu/utilities/Log.h" #include using namespace hku; diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_ThreadPool.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_ThreadPool.cpp index 829c502e..d319a62f 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_ThreadPool.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/thread/test_ThreadPool.cpp @@ -13,7 +13,7 @@ #include #include #include -#include +#include "hikyuu/utilities/Log.h" using namespace hku; diff --git a/hikyuu_pywrap/_Log.cpp b/hikyuu_pywrap/_Log.cpp index 4d4e3ba3..25fdcc2d 100644 --- a/hikyuu_pywrap/_Log.cpp +++ b/hikyuu_pywrap/_Log.cpp @@ -5,7 +5,7 @@ * Author: fasiondog */ -#include +#include "hikyuu/utilities/Log.h" #include "pybind_utils.h" using namespace hku; From 40b423fd674f7e9c542991c6ce09231f8601e27e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Jul 2024 01:57:19 +0800 Subject: [PATCH 399/601] update --- .gitignore | 3 ++- config_utils.h.in | 9 +++++++++ hikyuu_cpp/hikyuu/xmake.lua | 1 + 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 config_utils.h.in diff --git a/.gitignore b/.gitignore index 21281b29..5c82358b 100644 --- a/.gitignore +++ b/.gitignore @@ -77,4 +77,5 @@ hikyuu/cpp/libhku_hdf5_hl.so.200.1.0 hikyuu/cpp/libhku_hdf5.so.200 hikyuu/cpp/libhku_hdf5.so.200.2.0 hikyuu/include -.virtual_documents \ No newline at end of file +.virtual_documents +hikyuu_cpp/hikyuu/utilities/config.h diff --git a/config_utils.h.in b/config_utils.h.in new file mode 100644 index 00000000..fdd60f7f --- /dev/null +++ b/config_utils.h.in @@ -0,0 +1,9 @@ +#pragma once +#ifndef HKU_UTILS_CONFIG_H_ +#define HKU_UTILS_CONFIG_H_ + +// clang-format off + +// clang-format on + +#endif /* HKU_UTILS_CONFIG_H_ */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 6daf0736..7a0d3e65 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -20,6 +20,7 @@ target("hikyuu") set_configdir("./") add_configfiles("$(projectdir)/config.h.in") add_configfiles("$(projectdir)/version.h.in") + add_configfiles("$(projectdir)/config_utils.h.in", {prefixdir="utilities", filename="config.h"}) add_defines("CPPHTTPLIB_OPENSSL_SUPPORT", "CPPHTTPLIB_ZLIB_SUPPORT") From 099e99d0a858d5123362a4e54d5b3aee99ee741f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Jul 2024 11:08:04 +0800 Subject: [PATCH 400/601] update --- config_utils.h.in | 8 +++ hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp | 6 +- hikyuu_cpp/hikyuu/utilities/SpendTimer.h | 19 +++--- hikyuu_cpp/hikyuu/utilities/arithmetic.cpp | 12 ++-- hikyuu_cpp/hikyuu/utilities/arithmetic.h | 48 +++++++------- .../hikyuu/utilities/datetime/Datetime.cpp | 24 +++---- .../hikyuu/utilities/datetime/Datetime.h | 65 ++++++++++--------- .../hikyuu/utilities/datetime/TimeDelta.cpp | 8 +-- .../hikyuu/utilities/datetime/TimeDelta.h | 28 ++++---- .../hikyuu/utilities/ini_parser/IniParser.h | 11 +++- hikyuu_cpp/hikyuu/utilities/os.cpp | 42 ++++++------ hikyuu_cpp/hikyuu/utilities/os.h | 31 ++++----- .../utilities/thread/MQStealThreadPool.h | 6 +- .../hikyuu/utilities/thread/MQThreadPool.h | 6 +- .../hikyuu/utilities/thread/StealThreadPool.h | 6 +- .../hikyuu/utilities/thread/ThreadPool.h | 6 +- hikyuu_cpp/hikyuu/xmake.lua | 3 +- hikyuu_cpp/unit_test/xmake.lua | 11 ++-- hikyuu_pywrap/xmake.lua | 4 +- xmake.lua | 3 +- 20 files changed, 186 insertions(+), 161 deletions(-) diff --git a/config_utils.h.in b/config_utils.h.in index fdd60f7f..2ed22e83 100644 --- a/config_utils.h.in +++ b/config_utils.h.in @@ -4,6 +4,14 @@ // clang-format off +#define HKU_SUPPORT_DATETIME 1 + +#define HKU_ENABLE_INI_PARSER 1 + +${define HKU_ENABLE_STACK_TRACE} + +${define HKU_CLOSE_SPEND_TIME} + // clang-format on #endif /* HKU_UTILS_CONFIG_H_ */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp b/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp index 1596a5cd..edcf72d3 100644 --- a/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp +++ b/hikyuu_cpp/hikyuu/utilities/SpendTimer.cpp @@ -126,15 +126,15 @@ double SpendTimer::value() const { return sec.count(); } -void HKU_API close_spend_time() { // NOSONAR +void HKU_UTILS_API close_spend_time() { // NOSONAR SpendTimer::ms_closed = true; } -void HKU_API open_spend_time() { // NOSONAR +void HKU_UTILS_API open_spend_time() { // NOSONAR SpendTimer::ms_closed = false; } -bool HKU_API get_spend_time_status() { +bool HKU_UTILS_API get_spend_time_status() { return !SpendTimer::ms_closed; } diff --git a/hikyuu_cpp/hikyuu/utilities/SpendTimer.h b/hikyuu_cpp/hikyuu/utilities/SpendTimer.h index 43bb31d4..694f2b31 100644 --- a/hikyuu_cpp/hikyuu/utilities/SpendTimer.h +++ b/hikyuu_cpp/hikyuu/utilities/SpendTimer.h @@ -21,9 +21,10 @@ #include #include #include +#include "hikyuu/utilities/config.h" -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif #ifdef __ANDROID__ #include @@ -138,7 +139,7 @@ namespace hku { * @note 不建议直接使用,应使用相关工具宏 * @see SPEND_TIME, SPEND_TIME_MSG, SPEND_TIMG_CONTROL */ -class HKU_API SpendTimer { +class HKU_UTILS_API SpendTimer { public: /** 构造函数,记录当前系统时间 */ explicit SpendTimer() @@ -237,19 +238,19 @@ private: std::vector m_keep_desc; static bool ms_closed; - friend void HKU_API close_spend_time(); - friend void HKU_API open_spend_time(); - friend bool HKU_API get_spend_time_status(); + friend void HKU_UTILS_API close_spend_time(); + friend void HKU_UTILS_API open_spend_time(); + friend bool HKU_UTILS_API get_spend_time_status(); }; /** 全局关闭耗时打印输出 */ -void HKU_API close_spend_time(); +void HKU_UTILS_API close_spend_time(); /** 全局开启耗时打印输出 */ -void HKU_API open_spend_time(); +void HKU_UTILS_API open_spend_time(); /** 获取全局耗时打印输出状态:true - 开启,false - 关闭 */ -bool HKU_API get_spend_time_status(); +bool HKU_UTILS_API get_spend_time_status(); /** * 耗时计时器开启关闭状态看守,记录之前的耗时开关状态,并置为指定状态,释放时恢复原状态 diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp b/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp index 8b00b660..ce64a8ea 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp @@ -24,7 +24,7 @@ namespace hku { * @return 以GB2312编码的字符串 * @note 仅在Windows平台下生效 */ -std::string HKU_API utf8_to_gb(const char *szinput) { +std::string HKU_UTILS_API utf8_to_gb(const char *szinput) { wchar_t *strSrc; char *szRes; std::string nullStr; @@ -60,7 +60,7 @@ std::string HKU_API utf8_to_gb(const char *szinput) { return result; } -std::string HKU_API utf8_to_gb(const std::string &szinput) { +std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput) { return utf8_to_gb(szinput.c_str()); } @@ -70,7 +70,7 @@ std::string HKU_API utf8_to_gb(const std::string &szinput) { * @return 以UTF8编码的字符串 * @note 仅在Windows平台下生效 */ -std::string HKU_API gb_to_utf8(const char *szinput) { +std::string HKU_UTILS_API gb_to_utf8(const char *szinput) { wchar_t *strSrc; char *szRes; std::string nullstr; @@ -107,12 +107,12 @@ std::string HKU_API gb_to_utf8(const char *szinput) { return result; } -std::string HKU_API gb_to_utf8(const std::string &szinput) { +std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput) { return gb_to_utf8(szinput.c_str()); } #else /* else for defined(_MSC_VER) */ -std::string HKU_API utf8_to_gb(const std::string &szinput) { +std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput) { char *inbuf = const_cast(szinput.c_str()); size_t inlen = strlen(inbuf); size_t outlen = inlen; @@ -128,7 +128,7 @@ std::string HKU_API utf8_to_gb(const std::string &szinput) { return result; } -std::string HKU_API gb_to_utf8(const std::string &szinput) { +std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput) { char *inbuf = const_cast(szinput.c_str()); size_t inlen = strlen(inbuf); size_t outlen = inlen * 2; diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.h b/hikyuu_cpp/hikyuu/utilities/arithmetic.h index 06d242a6..9d3af2b2 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.h +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.h @@ -18,8 +18,9 @@ #include #include "string_view.h" -#ifndef HKU_API -#define HKU_API + +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -30,13 +31,13 @@ namespace hku { */ #if defined(_MSC_VER) -std::string HKU_API utf8_to_gb(const char* szinput); -std::string HKU_API utf8_to_gb(const std::string& szinput); -std::string HKU_API gb_to_utf8(const char* szinput); -std::string HKU_API gb_to_utf8(const std::string& szinput); +std::string HKU_UTILS_API utf8_to_gb(const char *szinput); +std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput); +std::string HKU_UTILS_API gb_to_utf8(const char *szinput); +std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput); #else -std::string HKU_API utf8_to_gb(const std::string& szinput); -std::string HKU_API gb_to_utf8(const std::string& szinput); +std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput); +std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput); #endif #define UTF8ToGB hku::utf8_to_gb @@ -81,7 +82,6 @@ std::string HKU_API gb_to_utf8(const std::string& szinput); * @param ndigits 保留小数位数 * @return 处理过的数据 */ -// double HKU_API roundEx(double number, int ndigits = 0); template ValueT roundEx(ValueT number, int ndigits = 0) { // 切换至:ROUND_HALF_EVEN 银行家舍入法 @@ -189,17 +189,17 @@ ValueT roundDown(ValueT number, int ndigits = 0) { #endif /** 转小写字符串 */ -inline void to_lower(std::string& s) { +inline void to_lower(std::string &s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); }); } /** 转大写字符串 */ -inline void to_upper(std::string& s) { +inline void to_upper(std::string &s) { std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); }); } /** 删除字符串两端空格 */ -inline void trim(std::string& s) { +inline void trim(std::string &s) { if (s.empty()) { return; } @@ -216,7 +216,7 @@ inline void trim(std::string& s) { * @param str 待封的字符串 * @param c 分割符 */ -inline std::vector split(const std::string& str, char c) { +inline std::vector split(const std::string &str, char c) { std::vector result; std::string_view view(str); size_t prepos = 0; @@ -238,7 +238,7 @@ inline std::vector split(const std::string& str, char c) { * @return string_view 组成的 vector * @note 注意返回结果的生命周期应小于输入的字符串相同! */ -inline std::vector split(const std::string_view& view, char c) { +inline std::vector split(const std::string_view &view, char c) { std::vector result; size_t prepos = 0; size_t pos = view.find_first_of(c); @@ -252,8 +252,8 @@ inline std::vector split(const std::string_view& view, char c) return result; } -inline std::vector split(const std::string_view& str, - const std::string& split_str) { +inline std::vector split(const std::string_view &str, + const std::string &split_str) { std::vector result; size_t split_str_len = split_str.size(); if (split_str_len == 0) { @@ -321,14 +321,14 @@ inline std::vector split(const std::string &str, const std::string * @param in_byte 输入的 byte 数组 * @param in_len byte 数组长度 */ -inline std::string byteToHexStr(const char* bytes, size_t in_len) { +inline std::string byteToHexStr(const char *bytes, size_t in_len) { std::string hexstr; - const unsigned char* in_byte = (const unsigned char*)bytes; + const unsigned char *in_byte = (const unsigned char *)bytes; if (in_byte == nullptr) { return hexstr; } - char* buf = new char[2 * in_len + 1]; + char *buf = new char[2 * in_len + 1]; size_t buf_ix = 0; for (size_t i = 0; i < in_len; ++i) { @@ -349,7 +349,7 @@ inline std::string byteToHexStr(const char* bytes, size_t in_len) { * byte 转 16 进制字符串, 如 "abcd" 转换为 "61626364" * @param in_byte std::string 格式的输入 */ -inline std::string byteToHexStr(const std::string& bytes) { +inline std::string byteToHexStr(const std::string &bytes) { return byteToHexStr(bytes.c_str(), bytes.size()); } @@ -358,14 +358,14 @@ inline std::string byteToHexStr(const std::string& bytes) { * @param in_byte 输入的 byte 数组 * @param in_len byte 数组长度 */ -inline std::string byteToHexStrForPrint(const char* bytes, size_t in_len) { +inline std::string byteToHexStrForPrint(const char *bytes, size_t in_len) { std::string hexstr; - const unsigned char* in_byte = (const unsigned char*)bytes; + const unsigned char *in_byte = (const unsigned char *)bytes; if (in_byte == nullptr) { return hexstr; } - char* buf = new char[5 * in_len + 1]; + char *buf = new char[5 * in_len + 1]; size_t buf_ix = 0; for (size_t i = 0; i < in_len; ++i) { @@ -393,7 +393,7 @@ inline std::string byteToHexStrForPrint(const char* bytes, size_t in_len) { * byte 转 16 进制字符串, 如 "abcd" 转换为 "61626364" * @param in_byte 输入的 byte 数组 */ -inline std::string byteToHexStrForPrint(const std::string& bytes) { +inline std::string byteToHexStrForPrint(const std::string &bytes) { return byteToHexStrForPrint(bytes.c_str(), bytes.size()); } diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp index 75fdad74..b072ece4 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp @@ -17,7 +17,7 @@ namespace hku { -HKU_API std::ostream& operator<<(std::ostream& out, const Datetime& d) { +HKU_UTILS_API std::ostream &operator<<(std::ostream &out, const Datetime &d) { out << d.str(); return out; } @@ -92,7 +92,7 @@ Datetime::Datetime(unsigned long long datetime) { } } -Datetime::Datetime(const std::string& ts) { +Datetime::Datetime(const std::string &ts) { std::string timeStr(ts); trim(timeStr); if ("+infinity" == timeStr) { @@ -117,7 +117,7 @@ bool Datetime::isNull() const { return (m_data == null_date) ? true : false; } -Datetime& Datetime::operator=(const Datetime& d) { +Datetime &Datetime::operator=(const Datetime &d) { if (this == &d) return *this; m_data = d.m_data; @@ -159,7 +159,7 @@ uint64_t Datetime::number() const noexcept { return (unsigned long long)year() * 100000000ULL + (unsigned long long)month() * 1000000ULL + (unsigned long long)day() * 10000ULL + (unsigned long long)hour() * 100ULL + (unsigned long long)minute(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -173,7 +173,7 @@ uint64_t Datetime::ym() const noexcept { try { HKU_IF_RETURN(isNull(), Null()); return (unsigned long long)year() * 100ULL + (unsigned long long)month(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -188,7 +188,7 @@ uint64_t Datetime::ymd() const noexcept { HKU_IF_RETURN(isNull(), Null()); return (unsigned long long)year() * 10000ULL + (unsigned long long)month() * 100ULL + (unsigned long long)day(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -203,7 +203,7 @@ uint64_t Datetime::ymdh() const noexcept { HKU_IF_RETURN(isNull(), Null()); return (unsigned long long)year() * 1000000ULL + (unsigned long long)month() * 10000ULL + (unsigned long long)day() * 100ULL + (unsigned long long)hour(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -219,7 +219,7 @@ uint64_t Datetime::ymdhm() const noexcept { return (unsigned long long)year() * 100000000LL + (unsigned long long)month() * 1000000LL + (unsigned long long)day() * 10000LL + (unsigned long long)hour() * 100LL + (unsigned long long)minute(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -236,7 +236,7 @@ uint64_t Datetime::ymdhms() const noexcept { (unsigned long long)month() * 100000000ULL + (unsigned long long)day() * 1000000ULL + (unsigned long long)hour() * 10000ULL + (unsigned long long)minute() * 100ULL + (unsigned long long)second(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -259,7 +259,7 @@ uint64_t Datetime::hex() const noexcept { ret |= (low_y << 40); ret |= (high_y << 48); return ret; - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -273,7 +273,7 @@ uint64_t Datetime::ticks() const noexcept { HKU_IF_RETURN(isNull(), Null()); TimeDelta d = (*this) - Datetime::min(); return d.ticks(); - } catch (const std::exception& e) { + } catch (const std::exception &e) { HKU_ERROR(e.what()); return Null(); } catch (...) { @@ -341,7 +341,7 @@ Datetime Datetime::today() { return Datetime(x.year(), x.month(), x.day()); } -DatetimeList HKU_API getDateRange(const Datetime& start, const Datetime& end) { +DatetimeList HKU_UTILS_API getDateRange(const Datetime &start, const Datetime &end) { DatetimeList result; bd::date start_day = start.date(); bd::date end_day = end.date(); diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h index d0ad4809..7da8ccdc 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h @@ -17,14 +17,19 @@ #include #include #include + +#include "hikyuu/utilities/config.h" +#if !HKU_SUPPORT_DATETIME +#error "Don't support datetime, you can config with --datetime=y" +#endif #include "TimeDelta.h" #if defined(_MSC_VER) #pragma warning(disable : 4251) #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -37,7 +42,7 @@ namespace bd = boost::gregorian; * @details 构造失败将抛出异常 std::out_of_range * @ingroup DataType */ -class HKU_API Datetime { +class HKU_UTILS_API Datetime { public: /** 返回所能表示的最小日期:1400-Jan-01 00:00:00 */ static Datetime min(); @@ -61,7 +66,7 @@ public: /** 默认构造函数,Null */ Datetime(); - Datetime(const Datetime&); + Datetime(const Datetime &); /** * 构造函数 @@ -78,10 +83,10 @@ public: long millisec = 0, long microsec = 0); /** 从boost::gregorian::date构造日期类型 */ - explicit Datetime(const bd::date&); + explicit Datetime(const bd::date &); /** 从boost::posix_time::ptime构造 */ - explicit Datetime(const bt::ptime&); + explicit Datetime(const bt::ptime &); /** * 通过数字方式构造日期类型 @@ -103,9 +108,9 @@ public: * 4、"20010101T181159" * */ - explicit Datetime(const std::string&); + explicit Datetime(const std::string &); - Datetime& operator=(const Datetime&); + Datetime &operator=(const Datetime &); /** 年份,如果是 Null 将抛出异常 */ long year() const; @@ -283,7 +288,7 @@ private: bt::ptime m_data; }; -HKU_API std::ostream& operator<<(std::ostream&, const Datetime&); +HKU_UTILS_API std::ostream &operator<<(std::ostream &, const Datetime &); /** * 日期列表 @@ -297,41 +302,41 @@ typedef std::vector DatetimeList; * @param end 结束日期 * @return [start, end)范围内的日历日期 */ -DatetimeList HKU_API getDateRange(const Datetime& start, const Datetime& end); +DatetimeList HKU_UTILS_API getDateRange(const Datetime &start, const Datetime &end); /////////////////////////////////////////////////////////////////////////////// // // 关系比较函数, 不直接在类中定义是为了支持 Null<>() == d,Null可以放在左边 // /////////////////////////////////////////////////////////////////////////////// -bool operator==(const Datetime&, const Datetime&); -bool operator!=(const Datetime&, const Datetime&); -bool operator>(const Datetime&, const Datetime&); -bool operator<(const Datetime&, const Datetime&); -bool operator>=(const Datetime&, const Datetime&); -bool operator<=(const Datetime&, const Datetime&); +bool operator==(const Datetime &, const Datetime &); +bool operator!=(const Datetime &, const Datetime &); +bool operator>(const Datetime &, const Datetime &); +bool operator<(const Datetime &, const Datetime &); +bool operator>=(const Datetime &, const Datetime &); +bool operator<=(const Datetime &, const Datetime &); -inline bool operator==(const Datetime& d1, const Datetime& d2) { +inline bool operator==(const Datetime &d1, const Datetime &d2) { return d1.ptime() == d2.ptime(); } -inline bool operator!=(const Datetime& d1, const Datetime& d2) { +inline bool operator!=(const Datetime &d1, const Datetime &d2) { return d1.ptime() != d2.ptime(); } -inline bool operator>(const Datetime& d1, const Datetime& d2) { +inline bool operator>(const Datetime &d1, const Datetime &d2) { return d1.ptime() > d2.ptime(); } -inline bool operator<(const Datetime& d1, const Datetime& d2) { +inline bool operator<(const Datetime &d1, const Datetime &d2) { return d1.ptime() < d2.ptime(); } -inline bool operator>=(const Datetime& d1, const Datetime& d2) { +inline bool operator>=(const Datetime &d1, const Datetime &d2) { return d1.ptime() >= d2.ptime(); } -inline bool operator<=(const Datetime& d1, const Datetime& d2) { +inline bool operator<=(const Datetime &d1, const Datetime &d2) { return d1.ptime() <= d2.ptime(); } @@ -340,11 +345,11 @@ inline bool operator<=(const Datetime& d1, const Datetime& d2) { // 加、减法运算补充 // /////////////////////////////////////////////////////////////////////////////// -inline Datetime operator+(const TimeDelta& delta, const Datetime& date) { +inline Datetime operator+(const TimeDelta &delta, const Datetime &date) { return date + delta; } -inline TimeDelta operator-(const Datetime& d1, const Datetime& d2) { +inline TimeDelta operator-(const Datetime &d1, const Datetime &d2) { return TimeDelta(d1.ptime() - d2.ptime()); } @@ -359,11 +364,11 @@ inline Datetime::Datetime() { m_data = bt::ptime(d, bt::time_duration(0, 0, 0)); } -inline Datetime::Datetime(const Datetime& d) : m_data(d.m_data) {} +inline Datetime::Datetime(const Datetime &d) : m_data(d.m_data) {} -inline Datetime::Datetime(const bd::date& d) : m_data(bt::ptime(d, bt::time_duration(0, 0, 0))) {} +inline Datetime::Datetime(const bd::date &d) : m_data(bt::ptime(d, bt::time_duration(0, 0, 0))) {} -inline Datetime::Datetime(const bt::ptime& d) : m_data(d) {} +inline Datetime::Datetime(const bt::ptime &d) : m_data(d) {} inline bt::ptime Datetime::ptime() const { return m_data; @@ -404,7 +409,7 @@ namespace std { template <> class hash { public: - size_t operator()(hku::Datetime const& d) const noexcept { + size_t operator()(hku::Datetime const &d) const noexcept { return d.ticks(); // or use boost::hash_combine } }; @@ -418,12 +423,12 @@ inline string to_string(const hku::Datetime &date) { #if FMT_VERSION >= 90000 template <> struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); } template - auto format(const hku::Datetime& d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const hku::Datetime &d, FormatContext &ctx) const -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", d.str()); } }; diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp index 542a982b..77304232 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp @@ -138,28 +138,28 @@ int64_t TimeDelta::microseconds() const { return std::abs(ticks() % 1000); } -TimeDelta HKU_API Hours(int64_t hours) { +TimeDelta HKU_UTILS_API Hours(int64_t hours) { HKU_CHECK(hours >= TimeDelta::minTicks() / 3600000000LL && hours <= TimeDelta::maxTicks() / 3600000000LL, "Out of total range!"); return TimeDelta::fromTicks(hours * 3600000000LL); } -TimeDelta HKU_API Minutes(int64_t mins) { +TimeDelta HKU_UTILS_API Minutes(int64_t mins) { HKU_CHECK( mins >= TimeDelta::minTicks() / 60000000LL && mins <= TimeDelta::maxTicks() / 60000000LL, "Out of total range!"); return TimeDelta::fromTicks(mins * 60000000LL); } -TimeDelta HKU_API Seconds(int64_t secs) { +TimeDelta HKU_UTILS_API Seconds(int64_t secs) { HKU_CHECK( secs >= TimeDelta::minTicks() / 1000000LL && secs <= TimeDelta::maxTicks() / 1000000LL, "Out of total range!"); return TimeDelta::fromTicks(secs * 1000000LL); } -TimeDelta HKU_API Milliseconds(int64_t milliseconds) { +TimeDelta HKU_UTILS_API Milliseconds(int64_t milliseconds) { HKU_CHECK(milliseconds >= TimeDelta::minTicks() / 1000LL && milliseconds <= TimeDelta::maxTicks() / 1000LL, "Out of total range!"); diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h index bc10d996..510c0dd6 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h +++ b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.h @@ -20,8 +20,8 @@ #include #include -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -33,7 +33,7 @@ namespace bd = boost::gregorian; * 时长,用于时间计算 * @ingroup DataType */ -class HKU_API TimeDelta { +class HKU_UTILS_API TimeDelta { public: /** * 构造函数 @@ -62,10 +62,10 @@ public: explicit TimeDelta(const std::string& delta); /** 赋值构造函数 */ - TimeDelta(const TimeDelta&) = default; + TimeDelta(const TimeDelta &) = default; /** 赋值拷贝函数 */ - TimeDelta& operator=(const TimeDelta& other) { + TimeDelta &operator=(const TimeDelta &other) { if (this != &other) { m_duration = other.m_duration; } @@ -262,8 +262,8 @@ private: static constexpr const int64_t m_one_day_ticks = 24 * 60 * 60 * 1000000LL; }; -std::ostream& operator<<(std::ostream& out, TimeDelta td); -inline std::ostream& operator<<(std::ostream& out, TimeDelta td) { +std::ostream &operator<<(std::ostream &out, TimeDelta td); +inline std::ostream &operator<<(std::ostream &out, TimeDelta td) { out << td.str(); return out; } @@ -283,28 +283,28 @@ inline TimeDelta Days(int64_t days) { * @param hours 小时数 * @ingroup DataType */ -TimeDelta HKU_API Hours(int64_t hours); +TimeDelta HKU_UTILS_API Hours(int64_t hours); /** * TimeDelta 快捷创建函数 * @param mins 分钟数 * @ingroup DataType */ -TimeDelta HKU_API Minutes(int64_t mins); +TimeDelta HKU_UTILS_API Minutes(int64_t mins); /** * TimeDelta 快捷创建函数 * @param secs 秒数 * @ingroup DataType */ -TimeDelta HKU_API Seconds(int64_t secs); +TimeDelta HKU_UTILS_API Seconds(int64_t secs); /** * TimeDelta 快捷创建函数 * @param milliseconds 毫秒数 * @ingroup DataType */ -TimeDelta HKU_API Milliseconds(int64_t milliseconds); +TimeDelta HKU_UTILS_API Milliseconds(int64_t milliseconds); /** * TimeDelta 快捷创建函数 @@ -324,17 +324,17 @@ inline string to_string(const hku::TimeDelta &delta) { return delta.str(); } -} /* namespace hku */ +} // namespace std #if FMT_VERSION >= 90000 template <> struct fmt::formatter { - constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) { + constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) { return ctx.end(); } template - auto format(const hku::TimeDelta& d, FormatContext& ctx) const -> decltype(ctx.out()) { + auto format(const hku::TimeDelta &d, FormatContext &ctx) const -> decltype(ctx.out()) { return fmt::format_to(ctx.out(), "{}", d.str()); } }; diff --git a/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h index 87f2e183..04644bff 100644 --- a/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h +++ b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h @@ -9,6 +9,11 @@ #ifndef INIPARSER_H_ #define INIPARSER_H_ +#include "hikyuu/utilities/config.h" +#if !HKU_ENABLE_INI_PARSER +#error "Don't enable ini_parser, please config with --ini_parser=y" +#endif + #include #include #include @@ -21,8 +26,8 @@ #pragma warning(disable : 4290) #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -52,7 +57,7 @@ namespace hku { * @ingroup Utilities */ -class HKU_API IniParser { +class HKU_UTILS_API IniParser { public: typedef std::list StringList; typedef std::shared_ptr > StringListPtr; diff --git a/hikyuu_cpp/hikyuu/utilities/os.cpp b/hikyuu_cpp/hikyuu/utilities/os.cpp index 042af5ec..b53e4d8e 100644 --- a/hikyuu_cpp/hikyuu/utilities/os.cpp +++ b/hikyuu_cpp/hikyuu/utilities/os.cpp @@ -43,7 +43,7 @@ namespace hku { -bool HKU_API existFile(const std::string& filename) noexcept { +bool HKU_UTILS_API existFile(const std::string &filename) noexcept { #ifdef _WIN32 auto attribs = GetFileAttributesA(HKU_PATH(filename).c_str()); return attribs != INVALID_FILE_ATTRIBUTES; @@ -53,7 +53,7 @@ bool HKU_API existFile(const std::string& filename) noexcept { #endif } -bool HKU_API createDir(const std::string& pathname) noexcept { +bool HKU_UTILS_API createDir(const std::string &pathname) noexcept { std::string npath = HKU_PATH(pathname); // 目录已存在,直接返回成功 @@ -76,36 +76,36 @@ bool HKU_API createDir(const std::string& pathname) noexcept { return access(npath.c_str(), 0) == 0; } -bool HKU_API isColorTerminal() noexcept { +bool HKU_UTILS_API isColorTerminal() noexcept { #if defined(_WIN32) || defined(__linux__) || defined(__ANDROID__) return true; #elif TARGET_OS_IPHONE return false; #else - static constexpr std::array Terms = { + static constexpr std::array Terms = { {"ansi", "color", "console", "cygwin", "gnome", "konsole", "kterm", "linux", "msys", "putty", "rxvt", "screen", "vt100", "xterm"}}; - const char* env_p = std::getenv("TERM"); + const char *env_p = std::getenv("TERM"); if (env_p == nullptr) { return false; } static const bool result = std::any_of(std::begin(Terms), std::end(Terms), - [&](const char* term) { return std::strstr(env_p, term) != nullptr; }); + [&](const char *term) { return std::strstr(env_p, term) != nullptr; }); return result; #endif } // 删除文件 -bool HKU_API removeFile(const std::string& filename) noexcept { +bool HKU_UTILS_API removeFile(const std::string &filename) noexcept { return std::remove(HKU_PATH(filename).c_str()) == 0; } #ifdef _WIN32 // 删除目录及其包含的文件和子目录 -bool HKU_API removeDir(const std::string& path) noexcept { +bool HKU_UTILS_API removeDir(const std::string &path) noexcept { std::string strPath = HKU_PATH(path); struct _finddata_t fb; // 查找相同属性文件的存储结构体 // 制作用于正则化路径 @@ -148,14 +148,14 @@ bool HKU_API removeDir(const std::string& path) noexcept { #else // #ifdef _WIN32 // 删除目录及其包含的文件和子目录 -bool HKU_API removeDir(const std::string& path) noexcept { +bool HKU_UTILS_API removeDir(const std::string &path) noexcept { std::string strPath(path); if (strPath.at(strPath.length() - 1) != '\\' && strPath.at(strPath.length() - 1) != '/') { strPath.append("/"); } - DIR* d = opendir(strPath.c_str()); // 打开这个目录 + DIR *d = opendir(strPath.c_str()); // 打开这个目录 if (d != NULL) { - struct dirent* dt = NULL; + struct dirent *dt = NULL; // 逐个读取目录中的文件到dt while (NULL != (dt = readdir(d))) { @@ -178,7 +178,7 @@ bool HKU_API removeDir(const std::string& path) noexcept { } #endif // #ifdef _WIN32 -bool HKU_API copyFile(const std::string& src, const std::string& dst, bool flush) noexcept { +bool HKU_UTILS_API copyFile(const std::string &src, const std::string &dst, bool flush) noexcept { bool success = false; try { std::ifstream srcio(HKU_PATH(src), std::ios::binary); @@ -194,8 +194,8 @@ bool HKU_API copyFile(const std::string& src, const std::string& dst, bool flush return success; } -bool HKU_API renameFile(const std::string& oldname, const std::string& newname, - bool overlay) noexcept { +bool HKU_UTILS_API renameFile(const std::string &oldname, const std::string &newname, + bool overlay) noexcept { // 先判定文件是否存在,保证 std::rename 的行为和系统无关 if (overlay) { HKU_ERROR_IF_RETURN(existFile(newname) && !removeFile(newname), false, @@ -214,7 +214,7 @@ bool HKU_API renameFile(const std::string& oldname, const std::string& newname, * 获取用户路径 */ static std::string _getUserDir() { - char* home = getenv("HOME"); + char *home = getenv("HOME"); if (home) { return std::string(home); } @@ -233,7 +233,7 @@ static std::string _getUserDir() { return std::string(); } -std::string HKU_API getUserDir() { +std::string HKU_UTILS_API getUserDir() { #ifdef _WIN32 return GBToUTF8(_getUserDir()); #else @@ -241,9 +241,9 @@ std::string HKU_API getUserDir() { #endif } -std::string HKU_API getCurrentDir() { +std::string HKU_UTILS_API getCurrentDir() { std::string ret; - char* buffer = NULL; + char *buffer = NULL; #if HKU_OS_WINSOWS buffer = _getcwd(buffer, 0); #else @@ -261,7 +261,7 @@ std::string HKU_API getCurrentDir() { return ret; } -uint64_t HKU_API getDiskFreeSpace(const char* path) { +uint64_t HKU_UTILS_API getDiskFreeSpace(const char *path) { #if HKU_OS_WINDOWS uint64_t freespace = Null(); std::string npath = HKU_PATH(path); @@ -283,7 +283,7 @@ uint64_t HKU_API getDiskFreeSpace(const char* path) { #endif } -std::string HKU_API getPlatform() { +std::string HKU_UTILS_API getPlatform() { std::string ret; #if HKU_OS_WINDOWS ret = "windows"; @@ -303,7 +303,7 @@ std::string HKU_API getPlatform() { return ret; } -std::string HKU_API getCpuArch() { +std::string HKU_UTILS_API getCpuArch() { std::string ret; #if HKU_ARCH_ARM ret = "arm"; diff --git a/hikyuu_cpp/hikyuu/utilities/os.h b/hikyuu_cpp/hikyuu/utilities/os.h index 9f4e30bf..021ac392 100644 --- a/hikyuu_cpp/hikyuu/utilities/os.h +++ b/hikyuu_cpp/hikyuu/utilities/os.h @@ -17,8 +17,8 @@ #include "arithmetic.h" #include -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -27,27 +27,27 @@ namespace hku { * 判断文件或目录是否存在 * @param filename 文件名或目录名 */ -bool HKU_API existFile(const std::string& filename) noexcept; +bool HKU_UTILS_API existFile(const std::string &filename) noexcept; /** * 创建目录 * @param pathname 路径名 * @return 如果目录已存在或者创建成功返回 true,否则返回 false */ -bool HKU_API createDir(const std::string& pathname) noexcept; +bool HKU_UTILS_API createDir(const std::string &pathname) noexcept; /** * 删除文件 * @param filename 文件名 * @return 删除失败或文件不存在时返回false */ -bool HKU_API removeFile(const std::string& filename) noexcept; +bool HKU_UTILS_API removeFile(const std::string &filename) noexcept; /** * 删除目录及其包含的文件和子目录 * @param path 待删除目录 */ -bool HKU_API removeDir(const std::string& path) noexcept; +bool HKU_UTILS_API removeDir(const std::string &path) noexcept; /** * 拷贝文件 @@ -55,7 +55,8 @@ bool HKU_API removeDir(const std::string& path) noexcept; * @param dst 目标文件 * @param flush 是否立即落盘 */ -bool HKU_API copyFile(const std::string& src, const std::string& dst, bool flush = false) noexcept; +bool HKU_UTILS_API copyFile(const std::string &src, const std::string &dst, + bool flush = false) noexcept; /** * 文件、目录改名或移动 @@ -65,23 +66,23 @@ bool HKU_API copyFile(const std::string& src, const std::string& dst, bool flush * @return true 成功 * @return false 失败 旧名文件不存在,或文件被占用等其他原因导致的失败 */ -bool HKU_API renameFile(const std::string& oldname, const std::string& newname, - bool overlay = false) noexcept; +bool HKU_UTILS_API renameFile(const std::string &oldname, const std::string &newname, + bool overlay = false) noexcept; /** * 获取用户路径 */ -std::string HKU_API getUserDir(); +std::string HKU_UTILS_API getUserDir(); /** * 获取程序当前所在路径 */ -std::string HKU_API getCurrentDir(); +std::string HKU_UTILS_API getCurrentDir(); /** * 输出终端是否支持彩色控制字符 */ -bool HKU_API isColorTerminal() noexcept; +bool HKU_UTILS_API isColorTerminal() noexcept; /** * @brief 获取硬盘剩余存储空间大小 @@ -90,12 +91,12 @@ bool HKU_API isColorTerminal() noexcept; * @param path 指定路径名 * @return uint64_t 获取失败时,返回 Null() */ -uint64_t HKU_API getDiskFreeSpace(const char* path); +uint64_t HKU_UTILS_API getDiskFreeSpace(const char *path); /** 获取当前系统名称 */ -std::string HKU_API getPlatform(); +std::string HKU_UTILS_API getPlatform(); /** 获取当前CPU架构名称 */ -std::string HKU_API getCpuArch(); +std::string HKU_UTILS_API getCpuArch(); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h index c943f440..3c50803b 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h @@ -22,8 +22,8 @@ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wsign-compare" #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -35,7 +35,7 @@ namespace hku { #ifdef _MSC_VER class MQStealThreadPool { #else -class HKU_API MQStealThreadPool { +class HKU_UTILS_API MQStealThreadPool { #endif public: /** diff --git a/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h index fb016d14..61245eb7 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/MQThreadPool.h @@ -23,8 +23,8 @@ #pragma GCC diagnostic ignored "-Wsign-compare" #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -38,7 +38,7 @@ namespace hku { #ifdef _MSC_VER class MQThreadPool { #else -class HKU_API MQThreadPool { +class HKU_UTILS_API MQThreadPool { #endif public: /** diff --git a/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h index b1682002..a286939a 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/StealThreadPool.h @@ -24,8 +24,8 @@ #pragma GCC diagnostic ignored "-Wsign-compare" #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -39,7 +39,7 @@ namespace hku { #ifdef _MSC_VER class StealThreadPool { #else -class HKU_API StealThreadPool { +class HKU_UTILS_API StealThreadPool { #endif public: /** diff --git a/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h index 401f4524..7241644e 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/ThreadPool.h @@ -25,8 +25,8 @@ #pragma GCC diagnostic ignored "-Wsign-compare" #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -40,7 +40,7 @@ namespace hku { #ifdef _MSC_VER class ThreadPool { #else -class HKU_API ThreadPool { +class HKU_UTILS_API ThreadPool { #endif public: /** diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 7a0d3e65..3cbd9318 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -7,7 +7,7 @@ target("hikyuu") -- set_kind("shared") -- end - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level") + add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") add_packages("boost", "fmt", "spdlog", "flatbuffers", "nng", "nlohmann_json", "cpp-httplib") if is_plat("windows", "linux", "cross") then @@ -39,6 +39,7 @@ target("hikyuu") if is_plat("windows") then if is_kind("shared") then add_defines("HKU_API=__declspec(dllexport)") + add_defines("HKU_UTILS_API=__declspec(dllexport)") end if get_config("hdf5") then if is_mode("release") then diff --git a/hikyuu_cpp/unit_test/xmake.lua b/hikyuu_cpp/unit_test/xmake.lua index 25f95c12..ffd55333 100644 --- a/hikyuu_cpp/unit_test/xmake.lua +++ b/hikyuu_cpp/unit_test/xmake.lua @@ -59,7 +59,7 @@ target("unit-test") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level") + add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then @@ -81,6 +81,7 @@ target("unit-test") if is_plat("windows") and get_config("kind") == "shared" then add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") end add_deps("hikyuu") @@ -106,7 +107,7 @@ target("small-test") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level") + add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then @@ -130,8 +131,9 @@ target("small-test") add_cxflags("-Wno-sign-compare") end - if is_plat("windows") and is_mode("release") then + if is_plat("windows") and get_config("kind") == "shared" then add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") end add_deps("hikyuu") @@ -152,7 +154,7 @@ target("real-test") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level") + add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then @@ -174,6 +176,7 @@ target("real-test") if is_plat("windows") and get_config("kind") == "shared" then add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") end add_defines("HKU_USE_REAL_DATA_TEST") diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 9e023b97..1a67540f 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -9,8 +9,7 @@ target("core") -- --set_enable(false) --set_enable(false)会彻底禁用这个target,连target的meta也不会被加载,vcproj不会保留它 -- end - -- add_options("stacktrace") - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time", "log_level") + add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") add_deps("hikyuu") add_packages("boost", "fmt", "spdlog", "flatbuffers", "pybind11", "cpp-httplib") @@ -23,6 +22,7 @@ target("core") if is_plat("windows") and get_config("kind") == "shared" then add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") add_cxflags("-wd4566") end diff --git a/xmake.lua b/xmake.lua index c9dc9180..62e5725a 100644 --- a/xmake.lua +++ b/xmake.lua @@ -64,7 +64,6 @@ option("spend_time") set_showmenu(true) set_category("hikyuu") set_description("Enable spend time.") - add_defines("HKU_CLOSE_SPEND_TIME=0") option_end() option("feedback") @@ -159,6 +158,8 @@ set_configvar("HKU_ENABLE_TDX_KDATA", get_config("tdx") and 1 or 0) set_configvar("HKU_USE_LOW_PRECISION", get_config("low_precision") and 1 or 0) +set_configvar("HKU_CLOSE_SPEND_TIME", get_config("spend_time") and 0 or 1) + set_warnings("all") -- set language: C99, c++ standard From 4ed0d93024ea5d01d20a457ccf6ccd35375c5e47 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Jul 2024 14:19:25 +0800 Subject: [PATCH 401/601] =?UTF-8?q?=E5=90=8C=E6=AD=A5=20hku=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config.h.in | 12 -- config_utils.h.in | 21 ++- hikyuu_cpp/hikyuu/DataType.h | 1 + hikyuu_cpp/hikyuu/utilities/Log.cpp | 69 ++++++---- hikyuu_cpp/hikyuu/utilities/Log.h | 123 +++++++++++------- hikyuu_cpp/hikyuu/utilities/Parameter.h | 3 +- .../hikyuu/utilities/db_connect/DBCondition.h | 6 +- .../hikyuu/utilities/db_connect/DBConnect.h | 10 ++ .../utilities/db_connect/DBConnectBase.h | 3 +- .../hikyuu/utilities/db_connect/DBUpgrade.cpp | 2 +- .../hikyuu/utilities/db_connect/DBUpgrade.h | 6 +- .../utilities/db_connect/SQLStatementBase.h | 4 +- .../hikyuu/utilities/db_connect/TableMacro.h | 3 +- .../db_connect/mysql/MySQLConnect.cpp | 3 +- .../utilities/db_connect/mysql/MySQLConnect.h | 2 +- .../db_connect/mysql/MySQLStatement.h | 6 +- .../db_connect/sqlite/SQLiteConnect.cpp | 6 +- .../db_connect/sqlite/SQLiteConnect.h | 2 +- .../db_connect/sqlite/SQLiteStatement.h | 2 +- .../utilities/db_connect/sqlite/SQLiteUtil.h | 12 +- hikyuu_cpp/hikyuu/xmake.lua | 2 - hikyuu_cpp/unit_test/xmake.lua | 6 - hikyuu_pywrap/xmake.lua | 2 - xmake.lua | 41 ++++-- 24 files changed, 211 insertions(+), 136 deletions(-) diff --git a/config.h.in b/config.h.in index 798234db..0344c552 100644 --- a/config.h.in +++ b/config.h.in @@ -19,18 +19,6 @@ // 检查下标越界 #define CHECK_ACCESS_BOUND ${CHECK_ACCESS_BOUND} -// 默认激活的日志级别 -#define LOG_ACTIVE_LEVEL ${LOG_ACTIVE_LEVEL} - -// 是否使用 spdlog -#define USE_SPDLOG_LOGGER ${USE_SPDLOG_LOGGER} - -// 使用异步 logger -#define HKU_USE_SPDLOG_ASYNC_LOGGER ${USE_SPDLOG_ASYNC_LOGGER} - -// spdlog默认日志级别 -#define SPDLOG_ACTIVE_LEVEL ${LOG_ACTIVE_LEVEL} - // 启用MSVC内存泄漏检查 #define ENABLE_MSVC_LEAK_DETECT ${ENABLE_MSVC_LEAK_DETECT} diff --git a/config_utils.h.in b/config_utils.h.in index 2ed22e83..f7437193 100644 --- a/config_utils.h.in +++ b/config_utils.h.in @@ -2,16 +2,33 @@ #ifndef HKU_UTILS_CONFIG_H_ #define HKU_UTILS_CONFIG_H_ +#include "osdef.h" + // clang-format off -#define HKU_SUPPORT_DATETIME 1 +${define HKU_ENABLE_MYSQL} +#if HKU_ENABLE_MYSQL && HKU_OS_WINDOWS +#ifndef NOMINMAX +#define NOMINMAX +#endif +#endif -#define HKU_ENABLE_INI_PARSER 1 +${define HKU_ENABLE_SQLITE} +${define HKU_ENABLE_SQLCIPHER} +${define HKU_SQL_TRACE} + +${define HKU_SUPPORT_DATETIME} + +${define HKU_ENABLE_INI_PARSER} ${define HKU_ENABLE_STACK_TRACE} ${define HKU_CLOSE_SPEND_TIME} +${define HKU_DEFAULT_LOG_NAME} +${define HKU_USE_SPDLOG_ASYNC_LOGGER} +${define HKU_LOG_ACTIVE_LEVEL} + // clang-format on #endif /* HKU_UTILS_CONFIG_H_ */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/DataType.h b/hikyuu_cpp/hikyuu/DataType.h index 99f5f1ad..42011faf 100644 --- a/hikyuu_cpp/hikyuu/DataType.h +++ b/hikyuu_cpp/hikyuu/DataType.h @@ -29,6 +29,7 @@ #include #include +#include "config.h" #include "utilities/Log.h" #include "utilities/osdef.h" #include "utilities/cppdef.h" diff --git a/hikyuu_cpp/hikyuu/utilities/Log.cpp b/hikyuu_cpp/hikyuu/utilities/Log.cpp index 9b211a6b..06f388da 100644 --- a/hikyuu_cpp/hikyuu/utilities/Log.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Log.cpp @@ -6,7 +6,6 @@ */ #include -#include "hikyuu/config.h" #include "hikyuu/GlobalInitializer.h" #include "os.h" #include "Log.h" @@ -23,14 +22,12 @@ #endif /* HKU_USE_SPDLOG_ASYNC_LOGGER */ #endif /* #if USE_SPDLOG_LOGGER */ +#ifndef HKU_DEFAULT_LOG_NAME +#define HKU_DEFAULT_LOG_NAME "hikyuu" +#endif + namespace hku { -static std::thread::id g_main_thread_id = std::this_thread::get_id(); - -bool isLogInMainThread() { - return std::this_thread::get_id() == g_main_thread_id; -} - static LOG_LEVEL g_log_level = LOG_LEVEL::LOG_TRACE; std::string g_unknown_error_msg{"Unknown error!"}; @@ -44,12 +41,12 @@ LOG_LEVEL get_log_level() { *********************************************/ #if USE_SPDLOG_LOGGER std::shared_ptr getHikyuuLogger() { - return spdlog::get("hikyuu"); + return spdlog::get(HKU_DEFAULT_LOG_NAME); } #if HKU_USE_SPDLOG_ASYNC_LOGGER -void initLogger(bool inJupyter) { - std::string logname = "hikyuu"; +void HKU_UTILS_API initLogger(bool inJupyter) { + std::string logname(HKU_DEFAULT_LOG_NAME); spdlog::drop(logname); std::shared_ptr logger = spdlog::get(logname); if (logger) { @@ -63,8 +60,21 @@ void initLogger(bool inJupyter) { } stdout_sink->set_level(spdlog::level::trace); - spdlog::init_thread_pool(8192, 1); + std::shared_ptr rotating_sink; + if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) { + std::string log_filename = + fmt::format("{}/.hikyuu/{}.log", getUserDir(), HKU_DEFAULT_LOG_NAME); + rotating_sink = + std::make_shared(log_filename, 1024 * 1024 * 10, 3); + rotating_sink->set_level(spdlog::level::warn); + } + std::vector sinks{stdout_sink}; + if (rotating_sink) { + sinks.emplace_back(rotating_sink); + } + + spdlog::init_thread_pool(8192, 1); logger = std::make_shared(logname, sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); @@ -72,13 +82,15 @@ void initLogger(bool inJupyter) { logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::trace); logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v [%!]"); - spdlog::register_logger(logger); + // logger->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [HKU-%L] - %v (%s:%#)%$"); + // spdlog::register_logger(logger); + spdlog::set_default_logger(logger); } #else /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */ -void initLogger(bool inJupyter) { - std::string logname = "hikyuu"; +void HKU_UTILS_API initLogger(bool inJupyter) { + std::string logname(HKU_DEFAULT_LOG_NAME); spdlog::drop(logname); std::shared_ptr logger = spdlog::get(logname); if (logger) { @@ -91,18 +103,27 @@ void initLogger(bool inJupyter) { stdout_sink = std::make_shared(); } stdout_sink->set_level(spdlog::level::trace); - std::string log_filename = fmt::format("{}/.hikyuu/hikyuu.log", getUserDir()); - auto rotating_sink = - std::make_shared(log_filename, 1024 * 1024 * 10, 3); - rotating_sink->set_level(spdlog::level::warn); - std::vector sinks{stdout_sink, rotating_sink}; + + std::shared_ptr rotating_sink; + if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) { + std::string log_filename = + fmt::format("{}/.hikyuu/{}.log", getUserDir(), HKU_DEFAULT_LOG_NAME); + rotating_sink = + std::make_shared(log_filename, 1024 * 1024 * 10, 3); + rotating_sink->set_level(spdlog::level::warn); + } + + std::vector sinks{stdout_sink}; + if (rotating_sink) { + sinks.emplace_back(rotating_sink); + } logger = std::make_shared(logname, sinks.begin(), sinks.end()); logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::trace); - // logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v [%!] (%@)"); - // logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v (%@)"); logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v (%s:%#)"); - spdlog::register_logger(logger); + // logger->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [HKU-%L] - %v (%s:%#)%$"); + // spdlog::register_logger(logger); + spdlog::set_default_logger(logger); } #endif /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */ @@ -116,13 +137,13 @@ void set_log_level(LOG_LEVEL level) { /********************************************** * Use SPDLOG for logging *********************************************/ -void initLogger(bool inJupyter) {} +void HKU_UTILS_API initLogger(bool inJupyter) {} void set_log_level(LOG_LEVEL level) { g_log_level = level; } -std::string HKU_API getLocalTime() { +std::string HKU_UTILS_API getLocalTime() { auto now = std::chrono::system_clock::now(); uint64_t dis_millseconds = std::chrono::duration_cast(now.time_since_epoch()).count() - diff --git a/hikyuu_cpp/hikyuu/utilities/Log.h b/hikyuu_cpp/hikyuu/utilities/Log.h index 7e819521..0097e35a 100644 --- a/hikyuu_cpp/hikyuu/utilities/Log.h +++ b/hikyuu_cpp/hikyuu/utilities/Log.h @@ -13,11 +13,20 @@ #define NOMINMAX #endif -#include "../config.h" +#include "config.h" #include "exception.h" +#ifndef HKU_LOG_ACTIVE_LEVEL +#define HKU_LOG_ACTIVE_LEVEL 0 +#endif + +#if !defined(USE_SPDLOG_LOGGER) +#define USE_SPDLOG_LOGGER 1 +#endif + // clang-format off #if USE_SPDLOG_LOGGER + #define SPDLOG_ACTIVE_LEVEL HKU_LOG_ACTIVE_LEVEL #include #include #if HKU_USE_SPDLOG_ASYNC_LOGGER @@ -30,26 +39,20 @@ #include #include -#ifdef HKU_ENABLE_STACK_TRACE +#ifndef HKU_ENABLE_STACK_TRACE +#define HKU_ENABLE_STACK_TRACE 0 +#endif + +#if HKU_ENABLE_STACK_TRACE #include #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { -/** - * @ingroup Utilities - * @addtogroup logging Logging tools 日志工具 - * @details 打印等级: - * TRACE < DEBUG < INFO < WARN < ERROR < FATAL - * @{ - */ - -bool HKU_API isLogInMainThread(); - /********************************************** * Use SPDLOG for logging *********************************************/ @@ -69,15 +72,15 @@ enum LOG_LEVEL { * 获取当前日志级别 * @return */ -LOG_LEVEL HKU_API get_log_level(); +LOG_LEVEL HKU_UTILS_API get_log_level(); /** * 设置日志级别 * @param level 指定的日志级别 */ -void HKU_API set_log_level(LOG_LEVEL level); +void HKU_UTILS_API set_log_level(LOG_LEVEL level); -std::shared_ptr HKU_API getHikyuuLogger(); +std::shared_ptr HKU_UTILS_API getHikyuuLogger(); #define HKU_TRACE(...) SPDLOG_LOGGER_TRACE(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_DEBUG(...) SPDLOG_LOGGER_DEBUG(hku::getHikyuuLogger(), __VA_ARGS__) @@ -86,7 +89,7 @@ std::shared_ptr HKU_API getHikyuuLogger(); #define HKU_ERROR(...) SPDLOG_LOGGER_ERROR(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_FATAL(...) SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__) -void initLogger(bool inJupyter = false); +void HKU_UTILS_API initLogger(bool inJupyter = false); #else enum LOG_LEVEL { @@ -99,14 +102,14 @@ enum LOG_LEVEL { LOG_OFF = 6, }; -LOG_LEVEL HKU_API get_log_level(); -void HKU_API set_log_level(LOG_LEVEL level); -void initLogger(bool inJupyter = false); +LOG_LEVEL HKU_UTILS_API get_log_level(); +void HKU_UTILS_API set_log_level(LOG_LEVEL level); +void HKU_UTILS_API initLogger(bool inJupyter = false); /** 获取系统当前时间,精确到毫秒,如:2001-01-02 13:01:02.001 */ -std::string HKU_API getLocalTime(); +std::string HKU_UTILS_API getLocalTime(); -#if LOG_ACTIVE_LEVEL <= 0 +#if HKU_LOG_ACTIVE_LEVEL <= 0 #define HKU_TRACE(...) \ fmt::print("[{}] [HKU-T] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ __LINE__); @@ -114,7 +117,7 @@ std::string HKU_API getLocalTime(); #define HKU_TRACE(...) #endif -#if LOG_ACTIVE_LEVEL <= 1 +#if HKU_LOG_ACTIVE_LEVEL <= 1 #define HKU_DEBUG(...) \ fmt::print("[{}] [HKU-D] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ __LINE__); @@ -122,7 +125,7 @@ std::string HKU_API getLocalTime(); #define HKU_DEBUG(...) #endif -#if LOG_ACTIVE_LEVEL <= 2 +#if HKU_LOG_ACTIVE_LEVEL <= 2 #define HKU_INFO(...) \ fmt::print("[{}] [HKU-I] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ __LINE__); @@ -130,7 +133,7 @@ std::string HKU_API getLocalTime(); #define HKU_INFO(...) #endif -#if LOG_ACTIVE_LEVEL <= 3 +#if HKU_LOG_ACTIVE_LEVEL <= 3 #define HKU_WARN(...) \ fmt::print("[{}] [HKU-W] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ __LINE__); @@ -138,7 +141,7 @@ std::string HKU_API getLocalTime(); #define HKU_WARN(...) #endif -#if LOG_ACTIVE_LEVEL <= 4 +#if HKU_LOG_ACTIVE_LEVEL <= 4 #define HKU_ERROR(...) \ fmt::print("[{}] [HKU-E] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ __LINE__); @@ -146,7 +149,7 @@ std::string HKU_API getLocalTime(); #define HKU_ERROR(...) #endif -#if LOG_ACTIVE_LEVEL <= 5 +#if HKU_LOG_ACTIVE_LEVEL <= 5 #define HKU_FATAL(...) \ fmt::print("[{}] [HKU-F] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ __LINE__); @@ -166,7 +169,7 @@ std::string HKU_API getLocalTime(); #define HKU_FUNCTION __FUNCTION__ #endif -#ifndef HKU_ENABLE_STACK_TRACE +#if !HKU_ENABLE_STACK_TRACE /** * 若表达式为 false,将抛出 hku::exception 异常, 并附带传入信息 * @note 用于外部入参及结果检查 @@ -212,23 +215,9 @@ std::string HKU_API getLocalTime(); __FILE__, __LINE__)); \ } \ } while (0) -#endif // #ifndef HKU_ENABLE_STACK_TRACE +#endif // #if !HKU_ENABLE_STACK_TRACE -#if HKU_DEBUG_MODE -#define HKU_ASSERT_DEBUG(expr) -#else -/** 仅在 debug 模式下生效 */ -#define HKU_ASSERT_DEBUG(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) - -#endif /* #if HKU_DEBUG_MODE */ -#ifdef HKU_ENABLE_STACK_TRACE +#if HKU_ENABLE_STACK_TRACE /** * 若表达式为 false,将抛出 hku::exception 异常 * @note 仅用于内部入参检查,编译时可通过 HKU_DISABLE_ASSERT 宏关闭 @@ -253,9 +242,9 @@ std::string HKU_API getLocalTime(); } \ } while (0) -#endif // #ifndef HKU_ENABLE_STACK_TRACE +#endif // #if HKU_ENABLE_STACK_TRACE -#ifndef HKU_ENABLE_STACK_TRACE +#if !HKU_ENABLE_STACK_TRACE /** 抛出 hku::exception 及传入信息 */ #define HKU_THROW(...) \ do { \ @@ -286,7 +275,7 @@ std::string HKU_API getLocalTime(); throw except( \ fmt::format("EXCEPTION: {} [{}] ({}:{})", errmsg, HKU_FUNCTION, __FILE__, __LINE__)); \ } while (0) -#endif // #ifndef HKU_ENABLE_STACK_TRACE +#endif // #if !HKU_ENABLE_STACK_TRACE /** * 满足指定条件时,打印 TRACE 信息 @@ -426,6 +415,44 @@ extern std::string g_unknown_error_msg; #define HKU_ERROR_UNKNOWN HKU_ERROR(g_unknown_error_msg) #define HKU_FATAL_UNKNOWN HKU_FATAL(g_unknown_error_msg) +#define CLASS_LOGGER_IMP(cls) \ +public: \ + inline static const char* ms_logger = #cls; + +#define CLS_TRACE(...) HKU_TRACE(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_DEBUG(...) HKU_DEBUG(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_INFO(...) HKU_INFO(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_WARN(...) HKU_WARN(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_ERROR(...) HKU_ERROR(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_FATAL(...) HKU_FATAL(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) + +#define CLS_TRACE_IF(expr, ...) \ + HKU_TRACE_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_DEBUG_IF(expr, ...) \ + HKU_DEBUG_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_INFO_IF(expr, ...) \ + HKU_INFO_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_WARN_IF(expr, ...) \ + HKU_WARN_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_ERROR_IF(expr, ...) \ + HKU_ERROR_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_FATAL_IF(expr, ...) \ + HKU_FATAL_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) + +#define CLS_IF_RETURN(expr, ret) HKU_IF_RETURN(expr, ret) +#define CLS_TRACE_IF_RETURN(expr, ret, ...) \ + HKU_TRACE_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_DEBUG_IF_RETURN(expr, ret, ...) \ + HKU_DEBUG_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_INFO_IF_RETURN(expr, ret, ...) \ + HKU_INFO_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_WARN_IF_RETURN(expr, ret, ...) \ + HKU_WARN_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_ERROR_IF_RETURN(expr, ret, ...) \ + HKU_ERROR_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_FATAL_IF_RETURN(expr, ret, ...) \ + HKU_FATAL_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) + /** @} */ } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index 8b8fb569..6b9042e2 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -17,7 +17,8 @@ #include #include -#include "../config.h" +#include "hikyuu/config.h" +#include "hikyuu/utilities/config.h" #if HKU_SUPPORT_SERIALIZATION #include diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h index 376df11c..59898048 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBCondition.h @@ -15,8 +15,8 @@ #include #include "hikyuu/utilities/Log.h" -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { @@ -38,7 +38,7 @@ struct LIMIT { int limit = 1; }; -class HKU_API DBCondition { +class HKU_UTILS_API DBCondition { public: DBCondition() = default; DBCondition(const DBCondition &) = default; diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnect.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnect.h index 30d8a9ee..e0a33ae7 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnect.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnect.h @@ -17,4 +17,14 @@ #include "TableMacro.h" #include "DBUpgrade.h" +#include "hikyuu/utilities/config.h" +#if HKU_ENABLE_MYSQL +#include "mysql/MySQLConnect.h" +#endif + +#if HKU_ENABLE_SQLITE +#include "sqlite/SQLiteConnect.h" +#include "sqlite/SQLiteUtil.h" +#endif + #endif /* HIKYUU_DB_CONNECT_H */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h index b91ec716..fb6ea81b 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBConnectBase.h @@ -10,7 +10,6 @@ #ifndef HIKYUU_DB_CONNECT_DBCONNECTBASE_H #define HIKYUU_DB_CONNECT_DBCONNECTBASE_H -#include "../../DataType.h" #include "../../utilities/Parameter.h" #include "../Null.h" #include "DBCondition.h" @@ -26,7 +25,7 @@ class SQLResultSet; * 数据库连接基类 * @ingroup DBConnect */ -class HKU_API DBConnectBase : public std::enable_shared_from_this { +class HKU_UTILS_API DBConnectBase : public std::enable_shared_from_this { PARAMETER_SUPPORT // NOSONAR public : diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp index f523e057..ac3c2797 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp @@ -12,7 +12,7 @@ namespace hku { /* * 升级和创建数据库 */ -void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name, +void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name, const std::vector &upgrade_scripts, int start_version, const char *create_script) { HKU_TRACE("check {} database version ...", module_name); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.h b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.h index 883fa1d7..dd70ea46 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.h @@ -21,8 +21,8 @@ namespace hku { * @param create_script 数据库创建脚本,若对应的数据库不存在则使用该脚本创建数据库 * @ingroup DataDriver */ -void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name, - const std::vector &upgrade_scripts, int start_version = 2, - const char *create_script = nullptr); +void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name, + const std::vector &upgrade_scripts, int start_version = 2, + const char *create_script = nullptr); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h index 1fc9d057..a76580c1 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/SQLStatementBase.h @@ -34,7 +34,7 @@ public: * SQL Statement 基类 * @ingroup DBConnect */ -class HKU_API SQLStatementBase { +class HKU_UTILS_API SQLStatementBase { public: /** * 构造函数 @@ -183,7 +183,7 @@ inline void SQLStatementBase::bind(int idx, float item) { } inline void SQLStatementBase::exec() { -#ifdef HKU_SQL_TRACE +#if HKU_SQL_TRACE HKU_DEBUG(m_sql_string); #endif sub_exec(); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/TableMacro.h b/hikyuu_cpp/hikyuu/utilities/db_connect/TableMacro.h index a6d9b956..b8a4e3eb 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/TableMacro.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/TableMacro.h @@ -2168,8 +2168,7 @@ public: } #define TABLE_NO_AUTOID_BIND12(ROWID, table, f1, f2, f3, f4, f5, f6, f7, f8, f9, f10, f11, f12) \ - pprivate: \ - uint64_t m_rowid = 0; \ + pprivate : uint64_t m_rowid = 0; \ \ public: \ bool valid() const { \ diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp index 068c260d..fbf6a2e9 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.cpp @@ -7,6 +7,7 @@ * Author: fasiondog */ +#include "hikyuu/utilities/config.h" #include "MySQLConnect.h" #ifdef __GNUC__ @@ -110,7 +111,7 @@ bool MySQLConnect::ping() { } int64_t MySQLConnect::exec(const std::string& sql_string) { -#ifdef HKU_SQL_TRACE +#if HKU_SQL_TRACE HKU_DEBUG(sql_string); #endif if (!m_mysql) { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h index 948ebd10..bf66202e 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLConnect.h @@ -22,7 +22,7 @@ namespace hku { -class HKU_API MySQLConnect : public DBConnectBase { +class HKU_UTILS_API MySQLConnect : public DBConnectBase { public: explicit MySQLConnect(const Parameter ¶m); virtual ~MySQLConnect(); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h index e49bb10f..3df3a012 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.h @@ -26,13 +26,13 @@ typedef bool my_bool; #endif -#ifndef HKU_API -#define HKU_API +#ifndef HKU_UTILS_API +#define HKU_UTILS_API #endif namespace hku { -class HKU_API MySQLStatement : public SQLStatementBase { +class HKU_UTILS_API MySQLStatement : public SQLStatementBase { public: MySQLStatement() = delete; MySQLStatement(DBConnectBase *driver, const std::string &sql_statement); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp index 2728c7bb..b8ae369b 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.cpp @@ -8,7 +8,7 @@ */ #include -#include "../../../config.h" +#include "hikyuu/utilities/config.h" #include "hikyuu/utilities/Log.h" #include "SQLiteConnect.h" #include "SQLiteStatement.h" @@ -35,7 +35,7 @@ SQLiteConnect::SQLiteConnect(const Parameter ¶m) : DBConnectBase(param), m_d int rc = sqlite3_open_v2(m_dbname.c_str(), &m_db, flags, NULL); SQL_CHECK(rc == SQLITE_OK, rc, sqlite3_errmsg(m_db)); -#ifdef HKU_ENABLE_SQLCIPHER +#if HKU_ENABLE_SQLCIPHER if (haveParam("key")) { std::string key = getParam("key"); if (!key.empty()) { @@ -91,7 +91,7 @@ void SQLiteConnect::close() { } int64_t SQLiteConnect::exec(const std::string &sql_string) { -#ifdef HKU_SQL_TRACE +#if HKU_SQL_TRACE HKU_DEBUG(sql_string); #endif int rc = sqlite3_exec(m_db, sql_string.c_str(), NULL, NULL, NULL); diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h index 34effb7b..a1cd42c3 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h @@ -25,7 +25,7 @@ namespace hku { * SQLite连接 * @ingroup SQLite */ -class HKU_API SQLiteConnect : public DBConnectBase { +class HKU_UTILS_API SQLiteConnect : public DBConnectBase { public: /** * 构造函数 diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h index 593e5cbc..516c4c48 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteStatement.h @@ -19,7 +19,7 @@ namespace hku { * SQLite Statemen * @ingroup DBConnect */ -class HKU_API SQLiteStatement : public SQLStatementBase { +class HKU_UTILS_API SQLiteStatement : public SQLStatementBase { public: SQLiteStatement() = delete; diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.h b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.h index 53c46b39..f5ac2d96 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.h +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/sqlite/SQLiteUtil.h @@ -15,7 +15,7 @@ namespace hku { * @brief SQLite 其他相关操作方法集合 * @ingroup DBConnect */ -class HKU_API SQLiteUtil { +class HKU_UTILS_API SQLiteUtil { public: SQLiteUtil() = default; ~SQLiteUtil() = default; @@ -43,8 +43,8 @@ public: * @param n_page 分批备份时每次循环备份的 page 数,小于等于0时,一次性备份,不进行分批备份 * @param step_sleep 分批备份时每次循环后,休眠间隔时长(毫秒),以便让出cpu */ - static BackupResult onlineBackup(const std::shared_ptr& conn, - const std::string& dst, int n_page = -1, + static BackupResult onlineBackup(const std::shared_ptr &conn, + const std::string &dst, int n_page = -1, int step_sleep = 250) noexcept; /** * @brief 在线备份数据库,不影响其他数据库连接进行操作 @@ -53,7 +53,7 @@ public: * @param n_page 分批备份时每次循环备份的 page 数,小于等于0时,一次性备份,不进行分批备份 * @param step_sleep 分批备份时每次循环后,休眠间隔时长(毫秒),以便让出cpu */ - static BackupResult onlineBackup(const std::string& src, const std::string& dst, + static BackupResult onlineBackup(const std::string &src, const std::string &dst, int n_page = -1, int step_sleep = 250) noexcept; /** @@ -65,7 +65,7 @@ public: * @param save_bad 是否保存损坏的数据,将 dst 及 dst-journal 加上后缀 .bad 另存 * @return RecoverResult */ - static RecoverResult recoverFromBackup(const std::string& backup, const std::string& dst, + static RecoverResult recoverFromBackup(const std::string &backup, const std::string &dst, bool save_bad = false) noexcept; /** @@ -74,7 +74,7 @@ public: * @return true 指定的数据库文件及其日志文件都被成功删除或都不存在时,返回成功 * @return false 指定的数据文件及其日志文件,其中一个删除失败都会返回删除失败 */ - static bool removeDBFile(const std::string& dbfilename); + static bool removeDBFile(const std::string &dbfilename); }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 3cbd9318..90fab2ef 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -7,8 +7,6 @@ target("hikyuu") -- set_kind("shared") -- end - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") - add_packages("boost", "fmt", "spdlog", "flatbuffers", "nng", "nlohmann_json", "cpp-httplib") if is_plat("windows", "linux", "cross") then add_packages("sqlite3") diff --git a/hikyuu_cpp/unit_test/xmake.lua b/hikyuu_cpp/unit_test/xmake.lua index ffd55333..40108e33 100644 --- a/hikyuu_cpp/unit_test/xmake.lua +++ b/hikyuu_cpp/unit_test/xmake.lua @@ -59,8 +59,6 @@ target("unit-test") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then if is_plat("macosx") then @@ -107,8 +105,6 @@ target("small-test") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then if is_plat("macosx") then @@ -154,8 +150,6 @@ target("real-test") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then if is_plat("macosx") then diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 1a67540f..7647f5cc 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -9,8 +9,6 @@ target("core") -- --set_enable(false) --set_enable(false)会彻底禁用这个target,连target的meta也不会被加载,vcproj不会保留它 -- end - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "log_level") - add_deps("hikyuu") add_packages("boost", "fmt", "spdlog", "flatbuffers", "pybind11", "cpp-httplib") if is_plat("windows") then diff --git a/xmake.lua b/xmake.lua index 62e5725a..04e1b6f0 100644 --- a/xmake.lua +++ b/xmake.lua @@ -50,13 +50,19 @@ option("tdx") set_description("Enable tdx kdata engine.") option_end() +option("sql_trace") + set_default(false) + set_showmenu(true) + set_category("hikyuu") + set_description("打印执行的 SQL 语句") +option_end() + -- 注意:stacktrace 在 windows 下会严重影响性能 option("stacktrace") set_default(false) set_showmenu(true) set_category("hikyuu") set_description("Enable check/assert with stack trace info.") - add_defines("HKU_ENABLE_STACK_TRACE") option_end() option("spend_time") @@ -77,7 +83,7 @@ option("low_precision") set_default(false) set_showmenu(true) set_category("hikyuu") - set_description("Enable send feedback.") + set_description("Enable low precision.") option_end() option("log_level") @@ -88,6 +94,13 @@ option("log_level") set_description("set log level") option_end() +option("async_log") + set_default(false) + set_showmenu(true) + set_category("hikyuu") + set_description("Use async log.") +option_end() + option("leak_check") set_default(false) set_showmenu(true) @@ -116,19 +129,19 @@ if is_mode("debug") then level = "trace" end if level == "trace" then - set_configvar("LOG_ACTIVE_LEVEL", 0) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 0) elseif level == "debug" then - set_configvar("LOG_ACTIVE_LEVEL", 1) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 1) elseif level == "info" then - set_configvar("LOG_ACTIVE_LEVEL", 2) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 2) elseif level == "warn" then - set_configvar("LOG_ACTIVE_LEVEL", 3) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 3) elseif level == "error" then - set_configvar("LOG_ACTIVE_LEVEL", 4) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 4) elseif level == "fatal" then - set_configvar("LOG_ACTIVE_LEVEL", 5) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 5) else - set_configvar("LOG_ACTIVE_LEVEL", 6) + set_configvar("HKU_LOG_ACTIVE_LEVEL", 6) end if is_mode("debug") then @@ -136,7 +149,6 @@ if is_mode("debug") then else set_configvar("HKU_DEBUG_MODE", 0) end -set_configvar("USE_SPDLOG_LOGGER", 1) -- 是否使用spdlog作为日志输出 set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) if is_plat("macosx") or get_config("leak_check") then @@ -152,12 +164,21 @@ set_configvar("HKU_ENABLE_LEAK_DETECT", get_config("leak_check") and 1 or 0) set_configvar("HKU_ENABLE_SEND_FEEDBACK", get_config("feedback") and 1 or 0) set_configvar("HKU_ENABLE_HDF5_KDATA", get_config("hdf5") and 1 or 0) +set_configvar("HKU_ENABLE_MYSQL", get_config("mysql") and 1 or 0) set_configvar("HKU_ENABLE_MYSQL_KDATA", get_config("mysql") and 1 or 0) +set_configvar("HKU_ENABLE_SQLITE", (get_config("sqlite") or get_config("hdf5")) and 1 or 0) set_configvar("HKU_ENABLE_SQLITE_KDATA", get_config("sqlite") and 1 or 0) set_configvar("HKU_ENABLE_TDX_KDATA", get_config("tdx") and 1 or 0) set_configvar("HKU_USE_LOW_PRECISION", get_config("low_precision") and 1 or 0) +set_configvar("HKU_DEFAULT_LOG_NAME", "hikyuu") +set_configvar("HKU_SUPPORT_DATETIME", 1) +set_configvar("HKU_ENABLE_SQLCIPHER", 0) +set_configvar("HKU_SQL_TRACE", get_config("sql_trace")) +set_configvar("HKU_ENABLE_INI_PARSER", 1) +set_configvar("HKU_USE_SPDLOG_ASYNC_LOGGER", get_config("async_log") and 1 or 0) +set_configvar("HKU_ENABLE_STACK_TRACE", get_config("stacktrace") and 1 or 0) set_configvar("HKU_CLOSE_SPEND_TIME", get_config("spend_time") and 0 or 1) set_warnings("all") From 38954f1b079a4b8f059b740b392cec26f0410e5f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Jul 2024 14:58:16 +0800 Subject: [PATCH 402/601] =?UTF-8?q?=E5=8E=BB=E9=99=A4=E4=B8=8D=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E7=9A=84=E4=BE=9D=E8=B5=96=E5=8C=85=EF=BC=8C=E5=B1=8F?= =?UTF-8?q?=E8=94=BD=E6=9A=82=E6=97=B6=E4=B8=8D=E7=94=A8=E7=9A=84=E4=BB=A3?= =?UTF-8?q?=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/xmake.lua | 3 +-- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 2 +- hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp | 4 +++- hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h | 4 +++- hikyuu_cpp/hikyuu/xmake.lua | 2 +- hikyuu_pywrap/strategy/_AccountTradeManager.cpp | 4 +++- hikyuu_pywrap/strategy/_strategy_main.cpp | 4 ++-- hikyuu_pywrap/xmake.lua | 2 +- xmake.lua | 3 --- 9 files changed, 15 insertions(+), 13 deletions(-) diff --git a/hikyuu_cpp/demo/xmake.lua b/hikyuu_cpp/demo/xmake.lua index 3b000680..4dbb2feb 100644 --- a/hikyuu_cpp/demo/xmake.lua +++ b/hikyuu_cpp/demo/xmake.lua @@ -2,8 +2,6 @@ target("demo") set_kind("binary") set_default(false) - add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time") - add_packages("boost", "spdlog", "fmt", "flatbuffers") add_includedirs("..") @@ -14,6 +12,7 @@ target("demo") if is_plat("windows") and get_config("kind") == "shared" then add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") add_defines("SQLITE_API=__declspec(dllimport)") end diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index e1e5ff83..a47306e0 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +// #include #include #include "hikyuu/version.h" #include "hikyuu/DataType.h" diff --git a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp index 689c3237..8b4e67af 100644 --- a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#if 0 #include #include "../utilities/arithmetic.h" #include "AccountTradeManager.h" @@ -81,4 +82,5 @@ AccountTradeManager::AccountTradeManager(const string& name, const string& pwd) } } -} // namespace hku \ No newline at end of file +} // namespace hku +#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h index 6540b593..51435190 100644 --- a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h @@ -7,6 +7,7 @@ #pragma once +#if 0 #include #include "../trade_manage/TradeManagerBase.h" @@ -426,4 +427,5 @@ inline TMPtr crtAccountTM(const string& name, const string& pwd) { return std::make_shared(name, pwd); } -} // namespace hku \ No newline at end of file +} // namespace hku +#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 90fab2ef..28744057 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -7,7 +7,7 @@ target("hikyuu") -- set_kind("shared") -- end - add_packages("boost", "fmt", "spdlog", "flatbuffers", "nng", "nlohmann_json", "cpp-httplib") + add_packages("boost", "fmt", "spdlog", "flatbuffers", "nng", "nlohmann_json") if is_plat("windows", "linux", "cross") then add_packages("sqlite3") end diff --git a/hikyuu_pywrap/strategy/_AccountTradeManager.cpp b/hikyuu_pywrap/strategy/_AccountTradeManager.cpp index dc350029..ff9ee90d 100644 --- a/hikyuu_pywrap/strategy/_AccountTradeManager.cpp +++ b/hikyuu_pywrap/strategy/_AccountTradeManager.cpp @@ -5,6 +5,7 @@ * Author: fasiondog */ +#if 0 #include "../pybind_utils.h" #include @@ -13,4 +14,5 @@ namespace py = pybind11; void export_AccountTradeManger(py::module& m) { m.def("crtAccountTM", crtAccountTM); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_strategy_main.cpp b/hikyuu_pywrap/strategy/_strategy_main.cpp index 9f9422f3..8ac8c992 100644 --- a/hikyuu_pywrap/strategy/_strategy_main.cpp +++ b/hikyuu_pywrap/strategy/_strategy_main.cpp @@ -10,9 +10,9 @@ namespace py = pybind11; void export_Strategy(py::module& m); -void export_AccountTradeManger(py::module& m); +// void export_AccountTradeManger(py::module& m); void export_strategy_main(py::module& m) { export_Strategy(m); - export_AccountTradeManger(m); + // export_AccountTradeManger(m); } \ No newline at end of file diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 7647f5cc..9a942a58 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -10,7 +10,7 @@ target("core") -- end add_deps("hikyuu") - add_packages("boost", "fmt", "spdlog", "flatbuffers", "pybind11", "cpp-httplib") + add_packages("boost", "fmt", "spdlog", "flatbuffers", "pybind11") if is_plat("windows") then set_filename("core.pyd") add_cxflags("-wd4251") diff --git a/xmake.lua b/xmake.lua index 04e1b6f0..30749a9a 100644 --- a/xmake.lua +++ b/xmake.lua @@ -199,7 +199,6 @@ local hdf5_version = "1.12.2" local fmt_version = "10.2.1" local flatbuffers_version = "24.3.25" local nng_version = "1.8.0" -local cpp_httplib_version = "0.14.3" local sqlite_version = "3.46.0+0" local mysql_version = "8.0.31" if is_plat("windows") or (is_plat("linux", "cross") and is_arch("aarch64", "arm64.*")) then @@ -259,8 +258,6 @@ add_requires("sqlite3 " .. sqlite_version, {system = false, configs = {shared = add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs= {runtimes = get_config("runtimes")}}) add_requires("nng " .. nng_version, {system = false, configs = {cxflags = "-fPIC"}}) add_requires("nlohmann_json", {system = false}) -add_requires("cpp-httplib " .. cpp_httplib_version, {system = false, configs = {zlib = true, ssl = true}}) -add_requires("zlib", {system = false}) add_defines("SPDLOG_DISABLE_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger From e03b8aa94362963b33ebaa6325aff243231e413b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 17 Jul 2024 15:30:06 +0800 Subject: [PATCH 403/601] update fmt version --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index 30749a9a..6484a30e 100644 --- a/xmake.lua +++ b/xmake.lua @@ -196,7 +196,7 @@ end local boost_version = "1.85.0" local hdf5_version = "1.12.2" -local fmt_version = "10.2.1" +local fmt_version = "11.0.1" local flatbuffers_version = "24.3.25" local nng_version = "1.8.0" local sqlite_version = "3.46.0+0" From 6cfb99249bba53d820d39ea6e1f25601d0665c4c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 18 Jul 2024 09:39:35 +0800 Subject: [PATCH 404/601] =?UTF-8?q?=E5=90=8C=E6=AD=A5=20utils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/Log.h | 8 +++++++- hikyuu_cpp/hikyuu/utilities/arithmetic.h | 4 +--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/Log.h b/hikyuu_cpp/hikyuu/utilities/Log.h index 0097e35a..6fe26dd4 100644 --- a/hikyuu_cpp/hikyuu/utilities/Log.h +++ b/hikyuu_cpp/hikyuu/utilities/Log.h @@ -415,9 +415,15 @@ extern std::string g_unknown_error_msg; #define HKU_ERROR_UNKNOWN HKU_ERROR(g_unknown_error_msg) #define HKU_FATAL_UNKNOWN HKU_FATAL(g_unknown_error_msg) +#if CPP_STANDARD >= CPP_STANDARD_17 #define CLASS_LOGGER_IMP(cls) \ -public: \ +protected: \ inline static const char* ms_logger = #cls; +#else +#define CLASS_LOGGER_IMP(cls) \ +protected: \ + const char* ms_logger = #cls; +#endif #define CLS_TRACE(...) HKU_TRACE(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) #define CLS_DEBUG(...) HKU_DEBUG(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.h b/hikyuu_cpp/hikyuu/utilities/arithmetic.h index 9d3af2b2..2e8b228d 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.h +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.h @@ -309,9 +309,7 @@ inline std::vector split(const std::string &str, const std::string pos = str.find(split_str, prepos); } - if (prepos < str.size() - 1) { - result.emplace_back(str.substr(prepos)); - } + result.emplace_back(str.substr(prepos)); return result; } #endif /* #if CPP_STANDARD >= CPP_STANDARD_17 */ From bf55a8e1510625fc3045dde4d90ccfae46419d1d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 2 Aug 2024 18:15:43 +0800 Subject: [PATCH 405/601] =?UTF-8?q?=E5=90=8C=E6=AD=A5=20hku=5Futils?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config_utils.h.in | 9 +- copy_dependents.lua | 6 +- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 14 +- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 7 +- hikyuu_cpp/hikyuu/utilities/FilterNode.h | 267 ++++++ hikyuu_cpp/hikyuu/utilities/Log.cpp | 118 +-- hikyuu_cpp/hikyuu/utilities/Log.h | 107 +-- hikyuu_cpp/hikyuu/utilities/ResourcePool.h | 636 +++++++++++++ hikyuu_cpp/hikyuu/utilities/base64.cpp | 108 +++ hikyuu_cpp/hikyuu/utilities/base64.h | 59 ++ hikyuu_cpp/hikyuu/utilities/exception.h | 1 + .../utilities/http_client/HttpClient.cpp | 208 +++++ .../hikyuu/utilities/http_client/HttpClient.h | 191 ++++ .../hikyuu/utilities/http_client/nng_wrap.h | 478 ++++++++++ .../hikyuu/utilities/http_client/url.cpp | 56 ++ hikyuu_cpp/hikyuu/utilities/http_client/url.h | 25 + hikyuu_cpp/hikyuu/utilities/mo/mo.cpp | 23 + hikyuu_cpp/hikyuu/utilities/mo/mo.h | 48 + hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h | 836 ++++++++++++++++++ hikyuu_cpp/hikyuu/utilities/snowflake.h | 110 +++ hikyuu_cpp/hikyuu/xmake.lua | 2 +- setup.py | 5 +- xmake.lua | 148 +--- 23 files changed, 3155 insertions(+), 307 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/utilities/FilterNode.h create mode 100644 hikyuu_cpp/hikyuu/utilities/ResourcePool.h create mode 100644 hikyuu_cpp/hikyuu/utilities/base64.cpp create mode 100644 hikyuu_cpp/hikyuu/utilities/base64.h create mode 100644 hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp create mode 100644 hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h create mode 100644 hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h create mode 100644 hikyuu_cpp/hikyuu/utilities/http_client/url.cpp create mode 100644 hikyuu_cpp/hikyuu/utilities/http_client/url.h create mode 100644 hikyuu_cpp/hikyuu/utilities/mo/mo.cpp create mode 100644 hikyuu_cpp/hikyuu/utilities/mo/mo.h create mode 100644 hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h create mode 100644 hikyuu_cpp/hikyuu/utilities/snowflake.h diff --git a/config_utils.h.in b/config_utils.h.in index f7437193..3479a6c5 100644 --- a/config_utils.h.in +++ b/config_utils.h.in @@ -25,10 +25,17 @@ ${define HKU_ENABLE_STACK_TRACE} ${define HKU_CLOSE_SPEND_TIME} -${define HKU_DEFAULT_LOG_NAME} ${define HKU_USE_SPDLOG_ASYNC_LOGGER} ${define HKU_LOG_ACTIVE_LEVEL} +${define HKU_ENABLE_MO} + +${define HKU_ENABLE_HTTP_CLIENT} +${define HKU_ENABLE_HTTP_CLIENT_SSL} +${define HKU_ENABLE_HTTP_CLIENT_ZIP} + +${define HKU_ENABLE_NODE} + // clang-format on #endif /* HKU_UTILS_CONFIG_H_ */ \ No newline at end of file diff --git a/copy_dependents.lua b/copy_dependents.lua index 48b6fc5d..f9e7e3e4 100644 --- a/copy_dependents.lua +++ b/copy_dependents.lua @@ -39,7 +39,7 @@ task("copy_dependents") end elseif type(pkg_path) == 'table' then for i=1, #pkg_path do - local pos = string.find(pkg_path[i], "yh_utils") + local pos = string.find(pkg_path[i], "hku_utils") if pos == nil then pos = string.find(pkg_path[i], "opencv") if pos == nil then @@ -49,11 +49,11 @@ task("copy_dependents") end else for _, filedir in ipairs(os.dirs(pkg_path[i] .. "/*")) do - local pos = string.find(filedir, "yihua") + local pos = string.find(filedir, "hikyuu") if pos == nil then os.trycp(filedir, destpath .. "/include") else - os.trycp(filedir .. "/utils", destpath .. "/include/yihua") + os.trycp(filedir .. "/utilities", destpath .. "/include/hikyuu") end end end diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 39e22143..e3038991 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -21,6 +21,7 @@ #endif #include "utilities/Log.h" +#include "utilities/os.h" #include "hikyuu.h" #include "GlobalInitializer.h" #include "StockManager.h" @@ -56,12 +57,11 @@ void GlobalInitializer::init() { fmt::print("Initialize hikyuu_{} ...\n", getVersionWithBuild()); #endif - initLogger(); -#if defined(_DEBUG) || defined(DEBUG) - set_log_level(LOG_LEVEL::LOG_TRACE); -#else - set_log_level(LOG_LEVEL::LOG_INFO); -#endif + if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) { + initLogger(false, fmt::format("{}/.hikyuu/hikyuu.log", getUserDir())); + } else { + initLogger(); + } #if HKU_ENABLE_SEND_FEEDBACK sendFeedback(); @@ -103,9 +103,7 @@ void GlobalInitializer::clean() { H5close(); #endif -#if USE_SPDLOG_LOGGER spdlog::drop_all(); -#endif #ifdef MSVC_LEAKER_DETECT // MSVC 内存泄露检测,输出至 VS 的输出窗口 diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index a47306e0..5c58a09c 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -10,7 +10,6 @@ #include #include #include -// #include #include #include "hikyuu/version.h" #include "hikyuu/DataType.h" @@ -51,7 +50,11 @@ bool HKU_API pythonInJupyter() { void HKU_API setPythonInJupyter(bool injupyter) { g_pythonInJupyter = injupyter; - initLogger(injupyter); + if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) { + initLogger(injupyter, fmt::format("{}/.hikyuu/hikyuu.log", getUserDir())); + } else { + initLogger(injupyter); + } } bool HKU_API CanUpgrade() { diff --git a/hikyuu_cpp/hikyuu/utilities/FilterNode.h b/hikyuu_cpp/hikyuu/utilities/FilterNode.h new file mode 100644 index 00000000..8df20f36 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/FilterNode.h @@ -0,0 +1,267 @@ +/* + * Copyright (c) 2023 hikyuu.org + * + * Created on: 2023-01-13 + * Author: fasiondog + */ + +#pragma once + +#include +#include +#include +#include +#include "thread/ThreadPool.h" +#include "any_to_string.h" +#include "Log.h" + +namespace hku { + +/** + * @brief 过滤节点 + */ +class FilterNode { +public: + FilterNode() = default; + FilterNode(const FilterNode&) = default; + virtual ~FilterNode() = default; + + /** + * @brief 构造函数 + * @param exclusive 是否排他。为 true 时,只执行第一个遇到的满足过滤条件的子节点 + */ + explicit FilterNode(bool exclusive) : m_exclusive(exclusive) {} + + FilterNode(FilterNode&& rv) + : m_value(std::move(rv.m_value)), + m_children(std::move(rv.m_children)), + m_exclusive(rv.m_exclusive) {} + + FilterNode& operator=(const FilterNode& rv) { + if (this == &rv) + return *this; + m_value = rv.m_value; + m_children = rv.m_children; + m_exclusive = rv.m_exclusive; + return *this; + } + + FilterNode& operator=(FilterNode&& rv) { + if (this == &rv) + return *this; + m_value = std::move(rv.m_value); + m_children = std::move(rv.m_children); + m_exclusive = rv.m_exclusive; + return *this; + } + + using ptr_t = std::shared_ptr; + + ptr_t addChild(const ptr_t& child) { + HKU_CHECK(child, "Invalid input child! child is null!"); + m_children.push_front(child); + return child; + } + + bool exclusive() const { + return m_exclusive; + } + + void exclusive(bool exclusive) { + m_exclusive = exclusive; + } + + using const_iterator = std::forward_list::const_iterator; + using iterator = std::forward_list::iterator; + const_iterator cbegin() const { + return m_children.cbegin(); + } + + const_iterator cend() const { + return m_children.cend(); + } + + iterator begin() { + return m_children.begin(); + } + + iterator end() { + return m_children.end(); + } + + bool run(const any_t& data) noexcept { + if (_filter(data)) { + _process(data); + for (auto& node : m_children) { + if (node->run(data) && m_exclusive) { + return true; + } + } + return true; + } + return false; + } + + virtual bool filter(const any_t& data) { + return true; + } + + virtual void process(const any_t& data) {} + + template + ValueT value() const { + return any_cast(m_value); + } + + template + void value(const ValueT& value) { + m_value = value; + } + + bool has_value() const { +#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17 + return m_value.has_value(); +#else + return !m_value.empty(); +#endif + } + +private: + bool _filter(const any_t& data) noexcept { + try { + return filter(data); + } catch (const std::exception& e) { + HKU_WARN("Node filter exist error! {}", e.what()); + } catch (...) { + HKU_WARN("Node filter exist unknown error!"); + } + return false; + } + + void _process(const any_t& data) noexcept { + try { + process(data); + } catch (const std::exception& e) { + HKU_WARN("Node process exist error! {}", e.what()); + } catch (...) { + HKU_WARN("Node process exist unknown error!"); + } + } + +protected: + any_t m_value; + +private: + std::forward_list m_children; + bool m_exclusive = false; +}; + +template <> +inline const any_t& FilterNode::value() const { + return m_value; +} + +typedef std::shared_ptr FilterNodePtr; + +/** + * @brief 绑定过滤节点,通过 std::function 绑定自定义的 filter 和 process 处理函数 + */ +class BindFilterNode : public FilterNode { +public: + BindFilterNode() = default; + virtual ~BindFilterNode() = default; + + using filter_func = std::function; + using process_func = std::function; + + explicit BindFilterNode(const process_func& process) : FilterNode(false), m_process(process) {} + explicit BindFilterNode(process_func&& process) + : FilterNode(false), m_process(std::move(process)) {} + + BindFilterNode(const filter_func& filter, const process_func& process, bool exclusive = false) + : FilterNode(exclusive), m_filter(filter), m_process(process) {} + + BindFilterNode(filter_func&& filter, process_func&& process, bool exclusive = false) + : FilterNode(exclusive), m_filter(std::move(filter)), m_process(std::move(process)) {} + + virtual bool filter(const any_t& data) { + return m_filter ? m_filter(this, data) : true; + } + + virtual void process(const any_t& data) { + if (m_process) { + m_process(this, data); + } + } + +private: + filter_func m_filter; + process_func m_process; +}; + +/** + * @brief 异步串行事件处理器 + * @tparam EventT + */ +template +class AsyncSerialEventProcessor { +public: + /** + * @brief 构造函数 + * @param quit_wait 退出时等待所有任务完成 + */ + explicit AsyncSerialEventProcessor(bool quit_wait = true) : m_quit_wait(quit_wait) { + m_tg = std::unique_ptr(new ThreadPool(1)); + } + + /** 析构函数 */ + virtual ~AsyncSerialEventProcessor() { + if (m_quit_wait) { + m_tg->join(); + } else { + m_tg->stop(); + } + } + + /** + * @brief 添加事件处理节点 + * + * @param event 事件 + * @param action 对应的处理节点 + * @return 返回加入的节点 + */ + FilterNodePtr addAction(const EventT& event, const FilterNodePtr& action) { + HKU_CHECK(action, "Input action is null!"); + std::lock_guard lock(m_mutex); + auto iter = m_trees.find(event); + if (iter != m_trees.end()) { + iter->second->addChild(action); + } else { + m_trees[event] = action; + } + return action; + } + + /** + * @brief 分派事件消息 + * + * @param event 事件 + * @param data 事件附加信息 + */ + void dispatch(const EventT& event, const any_t& data) { + m_tg->submit([=] { + auto iter = m_trees.find(event); + HKU_WARN_IF_RETURN(iter == m_trees.end(), void(), + "There is no matching handling method for the event({})!", event); + iter->second->run(data); + }); + } + +private: + mutable std::mutex m_mutex; + std::unordered_map m_trees; + std::unique_ptr m_tg; + bool m_quit_wait = true; +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/Log.cpp b/hikyuu_cpp/hikyuu/utilities/Log.cpp index 06f388da..4cce287a 100644 --- a/hikyuu_cpp/hikyuu/utilities/Log.cpp +++ b/hikyuu_cpp/hikyuu/utilities/Log.cpp @@ -10,7 +10,6 @@ #include "os.h" #include "Log.h" -#if USE_SPDLOG_LOGGER // 使用 stdout_color 将无法将日志输出重定向至 python #include #include @@ -20,11 +19,6 @@ #if HKU_USE_SPDLOG_ASYNC_LOGGER #include #endif /* HKU_USE_SPDLOG_ASYNC_LOGGER */ -#endif /* #if USE_SPDLOG_LOGGER */ - -#ifndef HKU_DEFAULT_LOG_NAME -#define HKU_DEFAULT_LOG_NAME "hikyuu" -#endif namespace hku { @@ -36,88 +30,51 @@ LOG_LEVEL get_log_level() { return g_log_level; } -/********************************************** - * Use SPDLOG for logging - *********************************************/ -#if USE_SPDLOG_LOGGER -std::shared_ptr getHikyuuLogger() { - return spdlog::get(HKU_DEFAULT_LOG_NAME); +void set_log_level(LOG_LEVEL level) { + g_log_level = level; + getHikyuuLogger()->set_level((spdlog::level::level_enum)level); } -#if HKU_USE_SPDLOG_ASYNC_LOGGER -void HKU_UTILS_API initLogger(bool inJupyter) { - std::string logname(HKU_DEFAULT_LOG_NAME); +std::shared_ptr getHikyuuLogger() { + auto logger = spdlog::get("hikyuu"); + return logger ? logger : spdlog::default_logger(); +} + +void HKU_UTILS_API initLogger(bool not_use_color, const std::string& filename) { + std::string logname("hikyuu"); spdlog::drop(logname); std::shared_ptr logger = spdlog::get(logname); if (logger) { spdlog::drop(logname); } + spdlog::sink_ptr stdout_sink; - if (inJupyter) { + if (not_use_color) { stdout_sink = std::make_shared(std::cout, true); } else { stdout_sink = std::make_shared(); } stdout_sink->set_level(spdlog::level::trace); - std::shared_ptr rotating_sink; - if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) { - std::string log_filename = - fmt::format("{}/.hikyuu/{}.log", getUserDir(), HKU_DEFAULT_LOG_NAME); - rotating_sink = - std::make_shared(log_filename, 1024 * 1024 * 10, 3); - rotating_sink->set_level(spdlog::level::warn); - } + std::string logfile = filename.empty() ? "./hikyuu.log" : filename; + auto rotating_sink = + std::make_shared(logfile, 1024 * 1024 * 10, 3); + rotating_sink->set_level(spdlog::level::warn); std::vector sinks{stdout_sink}; if (rotating_sink) { sinks.emplace_back(rotating_sink); } +#if HKU_USE_SPDLOG_ASYNC_LOGGER spdlog::init_thread_pool(8192, 1); logger = std::make_shared(logname, sinks.begin(), sinks.end(), spdlog::thread_pool(), spdlog::async_overflow_policy::block); - - logger->set_level(spdlog::level::trace); - logger->flush_on(spdlog::level::trace); - logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v [%!]"); - // logger->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [HKU-%L] - %v (%s:%#)%$"); - // spdlog::register_logger(logger); - spdlog::set_default_logger(logger); -} - -#else /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */ - -void HKU_UTILS_API initLogger(bool inJupyter) { - std::string logname(HKU_DEFAULT_LOG_NAME); - spdlog::drop(logname); - std::shared_ptr logger = spdlog::get(logname); - if (logger) { - spdlog::drop(logname); - } - spdlog::sink_ptr stdout_sink; - if (inJupyter) { - stdout_sink = std::make_shared(std::cout, true); - } else { - stdout_sink = std::make_shared(); - } - stdout_sink->set_level(spdlog::level::trace); - - std::shared_ptr rotating_sink; - if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) { - std::string log_filename = - fmt::format("{}/.hikyuu/{}.log", getUserDir(), HKU_DEFAULT_LOG_NAME); - rotating_sink = - std::make_shared(log_filename, 1024 * 1024 * 10, 3); - rotating_sink->set_level(spdlog::level::warn); - } - - std::vector sinks{stdout_sink}; - if (rotating_sink) { - sinks.emplace_back(rotating_sink); - } +#else logger = std::make_shared(logname, sinks.begin(), sinks.end()); +#endif + logger->set_level(spdlog::level::trace); logger->flush_on(spdlog::level::trace); logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v (%s:%#)"); @@ -126,39 +83,4 @@ void HKU_UTILS_API initLogger(bool inJupyter) { spdlog::set_default_logger(logger); } -#endif /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */ - -void set_log_level(LOG_LEVEL level) { - g_log_level = level; - getHikyuuLogger()->set_level((spdlog::level::level_enum)level); -} - -#else /* #if USE_SPDLOG_LOGGER */ -/********************************************** - * Use SPDLOG for logging - *********************************************/ -void HKU_UTILS_API initLogger(bool inJupyter) {} - -void set_log_level(LOG_LEVEL level) { - g_log_level = level; -} - -std::string HKU_UTILS_API getLocalTime() { - auto now = std::chrono::system_clock::now(); - uint64_t dis_millseconds = - std::chrono::duration_cast(now.time_since_epoch()).count() - - std::chrono::duration_cast(now.time_since_epoch()).count() * 1000; - time_t tt = std::chrono::system_clock::to_time_t(now); -#ifdef _WIN32 - struct tm now_time; - localtime_s(&now_time, &tt); -#else - struct tm now_time; - localtime_r(&tt, &now_time); -#endif - return fmt::format("{:%Y-%m-%d %H:%M:%S}.{:<3d}", now_time, dis_millseconds); -} - -#endif /* #if USE_SPDLOG_LOGGER */ - } // namespace hku diff --git a/hikyuu_cpp/hikyuu/utilities/Log.h b/hikyuu_cpp/hikyuu/utilities/Log.h index 6fe26dd4..598d0353 100644 --- a/hikyuu_cpp/hikyuu/utilities/Log.h +++ b/hikyuu_cpp/hikyuu/utilities/Log.h @@ -20,18 +20,15 @@ #define HKU_LOG_ACTIVE_LEVEL 0 #endif -#if !defined(USE_SPDLOG_LOGGER) -#define USE_SPDLOG_LOGGER 1 +// clang-format off +#ifndef SPDLOG_ACTIVE_LEVEL +#define SPDLOG_ACTIVE_LEVEL HKU_LOG_ACTIVE_LEVEL #endif -// clang-format off -#if USE_SPDLOG_LOGGER - #define SPDLOG_ACTIVE_LEVEL HKU_LOG_ACTIVE_LEVEL - #include - #include - #if HKU_USE_SPDLOG_ASYNC_LOGGER - #include "spdlog/async.h" - #endif +#include +#include +#if HKU_USE_SPDLOG_ASYNC_LOGGER + #include "spdlog/async.h" #endif // clang-format on @@ -56,7 +53,7 @@ namespace hku { /********************************************** * Use SPDLOG for logging *********************************************/ -#if USE_SPDLOG_LOGGER + /** 日志级别 */ enum LOG_LEVEL { LOG_TRACE = SPDLOG_LEVEL_TRACE, ///< 跟踪 @@ -68,6 +65,14 @@ enum LOG_LEVEL { LOG_OFF = SPDLOG_LEVEL_OFF, ///< 关闭日志打印 }; +/** + * 初始化 logger + * @param not_use_color 不使用彩色输出 + * @param filename 日志文件名,为空时默认为当前目录下 "./hikyuu.log",需自行保存存放目录存在且可写入 + */ +void HKU_UTILS_API initLogger(bool not_use_color = false, + const std::string& filename = std::string()); + /** * 获取当前日志级别 * @return @@ -89,76 +94,6 @@ std::shared_ptr HKU_UTILS_API getHikyuuLogger(); #define HKU_ERROR(...) SPDLOG_LOGGER_ERROR(hku::getHikyuuLogger(), __VA_ARGS__) #define HKU_FATAL(...) SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__) -void HKU_UTILS_API initLogger(bool inJupyter = false); - -#else -enum LOG_LEVEL { - LOG_TRACE = 0, - LOG_DEBUG = 1, - LOG_INFO = 2, - LOG_WARN = 3, - LOG_ERROR = 4, - LOG_FATAL = 5, - LOG_OFF = 6, -}; - -LOG_LEVEL HKU_UTILS_API get_log_level(); -void HKU_UTILS_API set_log_level(LOG_LEVEL level); -void HKU_UTILS_API initLogger(bool inJupyter = false); - -/** 获取系统当前时间,精确到毫秒,如:2001-01-02 13:01:02.001 */ -std::string HKU_UTILS_API getLocalTime(); - -#if HKU_LOG_ACTIVE_LEVEL <= 0 -#define HKU_TRACE(...) \ - fmt::print("[{}] [HKU-T] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ - __LINE__); -#else -#define HKU_TRACE(...) -#endif - -#if HKU_LOG_ACTIVE_LEVEL <= 1 -#define HKU_DEBUG(...) \ - fmt::print("[{}] [HKU-D] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ - __LINE__); -#else -#define HKU_DEBUG(...) -#endif - -#if HKU_LOG_ACTIVE_LEVEL <= 2 -#define HKU_INFO(...) \ - fmt::print("[{}] [HKU-I] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ - __LINE__); -#else -#define HKU_INFO(...) -#endif - -#if HKU_LOG_ACTIVE_LEVEL <= 3 -#define HKU_WARN(...) \ - fmt::print("[{}] [HKU-W] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ - __LINE__); -#else -#define HKU_WARN(...) -#endif - -#if HKU_LOG_ACTIVE_LEVEL <= 4 -#define HKU_ERROR(...) \ - fmt::print("[{}] [HKU-E] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ - __LINE__); -#else -#define HKU_ERROR(...) -#endif - -#if HKU_LOG_ACTIVE_LEVEL <= 5 -#define HKU_FATAL(...) \ - fmt::print("[{}] [HKU-F] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \ - __LINE__); -#else -#define HKU_FATAL(...) -#endif - -#endif /* USE_SPDLOG_LOGGER */ - /////////////////////////////////////////////////////////////////////////////// // // clang/gcc 下使用 __PRETTY_FUNCTION__ 会包含函数参数,可以在编译时指定 @@ -409,6 +344,7 @@ std::string HKU_UTILS_API getLocalTime(); /** 用于 catch (...) 中打印,减少编译后代码大小 */ extern std::string g_unknown_error_msg; +#define HKU_THROW_UNKNOWN HKU_THROW(g_unknown_error_msg); #define HKU_TRACE_UNKNOWN HKU_TRACE(g_unknown_error_msg) #define HKU_DEBUG_UNKNOWN HKU_DEBUG(g_unknown_error_msg) #define HKU_INFO_UNKNOWN HKU_INFO(g_unknown_error_msg) @@ -459,6 +395,15 @@ protected: \ #define CLS_FATAL_IF_RETURN(expr, ret, ...) \ HKU_FATAL_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_ASSERT HKU_ASSERT +#define CLS_CHECK(expr, ...) \ + HKU_CHECK(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_CHECK_THROW(expr, except, ...) \ + HKU_CHECK_THROW(expr, except, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_THROW(...) HKU_THROW(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) +#define CLS_THROW_EXCEPTION(except, ...) \ + HKU_THROW_EXCEPTION(except, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__))) + /** @} */ } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/utilities/ResourcePool.h b/hikyuu_cpp/hikyuu/utilities/ResourcePool.h new file mode 100644 index 00000000..f246ceaf --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/ResourcePool.h @@ -0,0 +1,636 @@ +/* + * ResourcePool.h + * + * Copyright (c) 2019, hikyuu.org + * + * Created on: 2019-8-5 + * Author: fasiondog + */ +#pragma once +#ifndef HKU_UTILS_RESOURCE_POOL_H +#define HKU_UTILS_RESOURCE_POOL_H + +#include +#include +#include +#include +#include +#include +#include "Parameter.h" +#include "Log.h" + +namespace hku { + +/** + * 资源获取超时异常 + */ +class GetResourceTimeoutException : public hku::exception { +public: + GetResourceTimeoutException(const char *msg) + : hku::exception(fmt::format("GetResourceTimeoutException {}", msg)) {} + + GetResourceTimeoutException(const std::string &msg) + : hku::exception(fmt::format("GetResourceTimeoutException {}", msg)) {} + + virtual ~GetResourceTimeoutException() {} +}; + +/** + * 新资源创建失败异常 + */ +class CreateResourceException : public hku::exception { +public: + CreateResourceException(const char *msg) + : hku::exception(fmt::format("CreateResourceException {}", msg)) {} + + CreateResourceException(const std::string &msg) + : hku::exception(fmt::format("CreateResourceException {}", msg)) {} + + virtual ~CreateResourceException() {} +}; + +/** + * 通用共享资源池 + * @ingroup Utilities + */ +template +class ResourcePool { +public: + ResourcePool() = delete; + ResourcePool(const ResourcePool &) = delete; + ResourcePool &operator=(const ResourcePool &) = delete; + + /** + * 构造函数 + * @param param 连接参数 + * @param maxPoolSize 允许的最大共享资源数,为 0 表示不限制 + * @param maxIdleNum 运行的最大空闲资源数,为 0 表示用完即刻释放,无缓存 + */ + explicit ResourcePool(const Parameter ¶m, size_t maxPoolSize = 0, size_t maxIdleNum = 100) + : m_maxPoolSize(maxPoolSize), m_maxIdelSize(maxIdleNum), m_count(0), m_param(param) {} + + /** + * 析构函数,释放所有缓存的资源 + */ + virtual ~ResourcePool() { + std::unique_lock lock(m_mutex); + + // 将所有已分配资源的 closer 和 pool 解绑 + for (auto iter = m_closer_set.begin(); iter != m_closer_set.end(); ++iter) { + (*iter)->unbind(); + } + + // 删除所有空闲资源 + while (!m_resourceList.empty()) { + ResourceType *p = m_resourceList.front(); + m_resourceList.pop(); + if (p) { + delete p; + } + } + } + + /** 获取当前允许的最大资源数 */ + size_t maxPoolSize() const { + return m_maxIdelSize; + } + + /** 获取当前允许的最大空闲资源数 */ + size_t maxIdleSize() const { + return m_maxIdelSize; + } + + /** 设置最大资源数 */ + void maxPoolSize(size_t num) { + std::lock_guard lock(m_mutex); + m_maxPoolSize = num; + } + + /** 设置允许的最大空闲资源数 */ + void maxIdleSize(size_t num) { + std::lock_guard lock(m_mutex); + m_maxIdelSize = num; + } + + /** 资源实例指针类型 */ + typedef std::shared_ptr ResourcePtr; + + /** + * 获取可用资源,如超出允许的最大资源数将返回空指针 + * @exception CreateResourceException 新资源创建可能抛出异常 + */ + ResourcePtr get() { + std::lock_guard lock(m_mutex); + ResourcePtr result; + ResourceType *p = nullptr; + if (m_resourceList.empty()) { + if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) { + return result; + } + try { + p = new ResourceType(m_param); + } catch (const std::exception &e) { + HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}", + e.what()); + } catch (...) { + HKU_THROW_EXCEPTION(CreateResourceException, + "Failed create a new Resource! Unknown error!"); + } + m_count++; + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + p = m_resourceList.front(); + m_resourceList.pop(); + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + + /** + * 在指定的超时时间内获取可用资源 + * @param ms_timeout 超时时间,单位毫秒 + * @exception GetResourceTimeoutException, CreateResourceException + */ + ResourcePtr getWaitFor(uint64_t ms_timeout) { // NOSONAR + std::unique_lock lock(m_mutex); + ResourcePtr result; + ResourceType *p = nullptr; + if (m_resourceList.empty()) { + if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) { + // HKU_TRACE("超出最大资源数,等待空闲资源"); + if (ms_timeout > 0) { + if (m_cond.wait_for(lock, + std::chrono::duration(ms_timeout), + [&] { return !m_resourceList.empty(); })) { + HKU_CHECK_THROW(!m_resourceList.empty(), GetResourceTimeoutException, + "Failed get resource!"); + } else { + HKU_THROW_EXCEPTION(GetResourceTimeoutException, "Failed get resource!"); + } + } else { + m_cond.wait(lock, [this] { return !m_resourceList.empty(); }); + } + } else { + try { + p = new ResourceType(m_param); + } catch (const std::exception &e) { + HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}", + e.what()); + } catch (...) { + HKU_THROW_EXCEPTION(CreateResourceException, + "Failed create a new Resource! Unknown error!"); + } + m_count++; + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + } + p = m_resourceList.front(); + m_resourceList.pop(); + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + + /** + * 获取可用资源,如超出允许的最大资源数,将阻塞等待直到获得空闲资源 + * @exception CreateResourceException 新资源创建可能抛出异常 + */ + ResourcePtr getAndWait() { + return getWaitFor(0); + } + + /** 当前活动的资源数, 即全部资源数(含空闲及被使用的资源) */ + size_t count() const { + return m_count; + } + + /** 当前空闲的资源数 */ + size_t idleCount() const { + return m_resourceList.size(); + } + + /** 释放当前所有的空闲资源 */ + void releaseIdleResource() { + std::lock_guard lock(m_mutex); + _releaseIdleResourceNoLock(); + } + +private: + void _releaseIdleResourceNoLock() { + while (!m_resourceList.empty()) { + ResourceType *p = m_resourceList.front(); + m_resourceList.pop(); + m_count--; + if (p) { + delete p; + } + } + } + +private: + size_t m_maxPoolSize; // 允许的最大共享资源数 + size_t m_maxIdelSize; // 允许的最大空闲资源数 + size_t m_count; // 当前活动的资源数 + Parameter m_param; + std::mutex m_mutex; + std::condition_variable m_cond; + std::queue m_resourceList; + + class ResourceCloser { + public: + explicit ResourceCloser(ResourcePool *pool) : m_pool(pool) { // NOSONAR + } + + void operator()(ResourceType *conn) { // NOSONAR + if (conn) { + // 如果绑定了 pool,则归还资源;否则删除 + if (m_pool) { + // HKU_DEBUG("retuan to pool"); + m_pool->returnResource(conn, this); + } else { + // HKU_DEBUG("delete resource not in pool"); + delete conn; + } + } + } + + // 解绑资源池 + void unbind() { + m_pool = nullptr; + } + + private: + ResourcePool *m_pool; + }; + + /** 归还至资源池 */ + void returnResource(ResourceType *p, ResourceCloser *closer) { + std::unique_lock lock(m_mutex); + if (p) { + if (m_resourceList.size() < m_maxIdelSize) { + m_resourceList.push(p); + m_cond.notify_all(); + } else { + delete p; + m_count--; + } + } else { + m_count--; + // HKU_WARN("Trying to return an empty pointer!"); + } + if (closer) { + m_closer_set.erase(closer); // 移除该 closer + } + } + + std::unordered_set m_closer_set; // 占用资源的 closer +}; + +/** + * @brief 带版本的资源接口,可由需要版本管理的资源继承 + * @details 自带的 getVersion 和 setVerion 方法由 ResourceVersionPool 调用,不建议带有其他用途 + */ +class ResourceWithVersion { +public: + /** 默认构造函数 */ + ResourceWithVersion() : m_version(0) {} + + /** 析构函数 */ + virtual ~ResourceWithVersion() {} + + /** 获取资源版本 */ + int getVersion() const { + return m_version; + } + + /** 设置资源版本 **/ + void setVersion(int version) { + m_version = version; + } + +protected: + int m_version; +}; + +/** + * 通用版本的共享资源池,当资源池参数变更时,保证新资源使用新参数,老版本的资源再使用完毕后被自动回收 + * @details 要求资源类具备 int getVersion() 和 void setVersion(int) 另个接口函数 + * @ingroup Utilities + */ +template +class ResourceVersionPool { +public: + ResourceVersionPool() = delete; + ResourceVersionPool(const ResourceVersionPool &) = delete; + ResourceVersionPool &operator=(const ResourceVersionPool &) = delete; + + /** + * 构造函数 + * @param param 连接参数 + * @param maxPoolSize 允许的最大共享资源数,为 0 表示不限制 + * @param maxIdleNum 运行的最大空闲资源数,为 0 表示用完即刻释放,无缓存 + */ + explicit ResourceVersionPool(const Parameter ¶m, size_t maxPoolSize = 0, + size_t maxIdleNum = 100) + : m_maxPoolSize(maxPoolSize), + m_maxIdelSize(maxIdleNum), + m_count(0), + m_param(param), + m_version(0) {} + + /** + * 析构函数,释放所有缓存的资源 + */ + virtual ~ResourceVersionPool() { + std::unique_lock lock(m_mutex); + + // 将所有已分配资源的 closer 和 pool 解绑 + for (auto iter = m_closer_set.begin(); iter != m_closer_set.end(); ++iter) { + (*iter)->unbind(); + } + + // 删除所有空闲资源 + while (!m_resourceList.empty()) { + ResourceType *p = m_resourceList.front(); + m_resourceList.pop(); + if (p) { + delete p; + } + } + } + + /** 获取当前允许的最大资源数 */ + size_t maxPoolSize() const { + return m_maxIdelSize; + } + + /** 获取当前允许的最大空闲资源数 */ + size_t maxIdleSize() const { + return m_maxIdelSize; + } + + /** 设置最大资源数 */ + void maxPoolSize(size_t num) { + std::lock_guard lock(m_mutex); + m_maxPoolSize = num; + } + + /** 设置允许的最大空闲资源数 */ + void maxIdleSize(size_t num) { + std::lock_guard lock(m_mutex); + m_maxIdelSize = num; + } + + /** 指定参数是否存在 */ + bool haveParam(const std::string &name) { + std::lock_guard lock(m_mutex); + return m_param.have(name); + } + + /** 获取指定参数的值,如参数不存在或类型不匹配抛出异常 */ + template + ValueType getParam(const std::string &name) { + std::lock_guard lock(m_mutex); + return m_param.get(name); + } + + /** + * @brief 设定指定参数的值,参数仅在生成新的资源时生效 + * @details 在原本存在该参数的情况下,新设定的值类型须和原有参数类型相同,否则将抛出异常 + * @param name 参数名 + * @param value 参数值 + * @exception std::logic_error + */ + template + void setParam(const std::string &name, const ValueType &value) { + std::lock_guard lock(m_mutex); + // 如果参数未实际发送变化,则直接返回 + HKU_IF_RETURN(m_param.have(name) && value == m_param.get(name), void()); + m_param.set(name, value); + m_version++; + _releaseIdleResourceNoLock(); // 释放当前空闲资源,以便新参数值生效 + } + + /** + * @brief 设置资源参数,参数仅在生成新的资源时生效 + * @param param 参数对象 + */ + void setParameter(const Parameter ¶m) { + std::lock_guard lock(m_mutex); + m_param = param; + m_version++; + _releaseIdleResourceNoLock(); // 释放当前空闲资源,以便新参数值生效 + } + + /** + * @brief 设置资源参数,参数仅在生成新的资源时生效 + * @param param 参数对象 + */ + void setParameter(Parameter &¶m) { + std::lock_guard lock(m_mutex); + m_param = std::move(param); + m_version++; + _releaseIdleResourceNoLock(); // 释放当前空闲资源,以便新参数值生效 + } + + /** 获取当前资源池版本 */ + int getVersion() { + std::lock_guard lock(m_mutex); + return m_version; + } + + /** 递增当前资源池版本,相当于通知资源池资源版本发生变化 */ + void incVersion(int version) { + std::lock_guard lock(m_mutex); + m_version++; + } + + /** 资源实例指针类型 */ + typedef std::shared_ptr ResourcePtr; + + /** + * 获取可用资源,如超出允许的最大资源数将返回空指针 + * @exception CreateResourceException 新资源创建可能抛出异常 + */ + ResourcePtr get() { + std::lock_guard lock(m_mutex); + ResourcePtr result; + ResourceType *p = nullptr; + if (m_resourceList.empty()) { + if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) { + return result; + } + try { + p = new ResourceType(m_param); + p->setVersion(m_version); + } catch (const std::exception &e) { + HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}", + e.what()); + } catch (...) { + HKU_THROW_EXCEPTION(CreateResourceException, + "Failed create a new Resource! Unknown error!"); + } + m_count++; + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + p = m_resourceList.front(); + m_resourceList.pop(); + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + + /** + * 在指定的超时时间内获取可用资源 + * @param ms_timeout 超时时间,单位毫秒 + * @exception GetResourceTimeoutException, CreateResourceException + */ + ResourcePtr getWaitFor(uint64_t ms_timeout) { // NOSONAR + std::unique_lock lock(m_mutex); + ResourcePtr result; + ResourceType *p = nullptr; + if (m_resourceList.empty()) { + if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) { + // HKU_TRACE("超出最大资源数,等待空闲资源"); + if (ms_timeout > 0) { + if (m_cond.wait_for(lock, + std::chrono::duration(ms_timeout), + [&] { return !m_resourceList.empty(); })) { + HKU_CHECK_THROW(!m_resourceList.empty(), GetResourceTimeoutException, + "Failed get resource!"); + } else { + HKU_THROW_EXCEPTION(GetResourceTimeoutException, "Failed get resource!"); + } + } else { + m_cond.wait(lock, [this] { return !m_resourceList.empty(); }); + } + } else { + try { + p = new ResourceType(m_param); + p->setVersion(m_version); + } catch (const std::exception &e) { + HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}", + e.what()); + } catch (...) { + HKU_THROW_EXCEPTION(CreateResourceException, + "Failed create a new Resource! Unknown error!"); + } + m_count++; + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + } + p = m_resourceList.front(); + m_resourceList.pop(); + result = ResourcePtr(p, ResourceCloser(this)); + m_closer_set.insert(std::get_deleter(result)); + return result; + } + + /** + * 获取可用资源,如超出允许的最大资源数,将阻塞等待直到获得空闲资源 + * @exception CreateResourceException 新资源创建可能抛出异常 + */ + ResourcePtr getAndWait() { + return getWaitFor(0); + } + + /** 当前活动的资源数, 即全部资源数(含空闲及被使用的资源) */ + size_t count() const { + return m_count; + } + + /** 当前空闲的资源数 */ + size_t idleCount() const { + return m_resourceList.size(); + } + + /** 释放当前所有的空闲资源 */ + void releaseIdleResource() { + std::lock_guard lock(m_mutex); + _releaseIdleResourceNoLock(); + } + +private: + void _releaseIdleResourceNoLock() { + while (!m_resourceList.empty()) { + ResourceType *p = m_resourceList.front(); + m_resourceList.pop(); + m_count--; + if (p) { + delete p; + } + } + } + +private: + size_t m_maxPoolSize; // 允许的最大共享资源数 + size_t m_maxIdelSize; // 允许的最大空闲资源数 + size_t m_count; // 当前活动的资源数 + Parameter m_param; + std::mutex m_mutex; + std::condition_variable m_cond; + std::queue m_resourceList; + int m_version; + + class ResourceCloser { + public: + explicit ResourceCloser(ResourceVersionPool *pool) : m_pool(pool) { // NOSONAR + } + + void operator()(ResourceType *conn) { // NOSONAR + if (conn) { + // 如果绑定了 pool,则归还资源;否则删除 + if (m_pool) { + // HKU_DEBUG("retuan to pool"); + m_pool->returnResource(conn, this); + } else { + // HKU_DEBUG("delete resource not in pool"); + delete conn; + } + } + } + + // 解绑资源池 + void unbind() { + m_pool = nullptr; + } + + private: + ResourceVersionPool *m_pool; + }; + + /** 归还至资源池 */ + void returnResource(ResourceType *p, ResourceCloser *closer) { + std::unique_lock lock(m_mutex); + if (p) { + // 当前归还资源的版本和资源池版本相等,且空闲资源列表小于最大空闲资源数时,接受归还的资源 + if (p->getVersion() == m_version && m_resourceList.size() < m_maxIdelSize) { + m_resourceList.push(p); + m_cond.notify_all(); + } else { + delete p; + m_count--; + } + } else { + m_count--; + // HKU_WARN("Trying to return an empty pointer!"); + } + if (closer) { + m_closer_set.erase(closer); // 移除该 closer + } + } + + std::unordered_set m_closer_set; // 占用资源的 closer +}; + +} // namespace hku + +#endif /* HKU_UTILS_RESOURCE_POOL_H */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/base64.cpp b/hikyuu_cpp/hikyuu/utilities/base64.cpp new file mode 100644 index 00000000..09dcb1f6 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/base64.cpp @@ -0,0 +1,108 @@ +/* + * Copyright (c) hikyuu.org + * + * Created on: 2020-6-2 + * Author: fasiondog + */ + +#include "base64.h" +#include "Log.h" + +namespace hku { + +static const std::string base64_chars = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/"; + +std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len) { // NOSONAR + HKU_CHECK(bytes_to_encode, "Input null ptr!"); + std::string ret; + HKU_IF_RETURN(in_len == 0, ret); + + int i = 0; + unsigned char char_array_3[3]; + unsigned char char_array_4[4]; + + while (in_len--) { + char_array_3[i++] = *(bytes_to_encode++); + if (i == 3) { + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (i = 0; (i < 4); i++) + ret += base64_chars[char_array_4[i]]; + i = 0; + } + } + + if (i) { + for (int j = i; j < 3; j++) + char_array_3[j] = '\0'; + + char_array_4[0] = (char_array_3[0] & 0xfc) >> 2; + char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4); + char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6); + char_array_4[3] = char_array_3[2] & 0x3f; + + for (int j = 0; (j < i + 1); j++) + ret += base64_chars[char_array_4[j]]; + + while ((i++ < 3)) + ret += '='; + } + + return ret; +} + +static inline bool is_base64(unsigned char c) { + return (isalnum(c) || (c == '+') || (c == '/')); +} + +std::string base64_decode(unsigned char const* encoded_string, size_t in_len) { + HKU_CHECK(encoded_string, "Input null ptr!"); + std::string ret; + HKU_IF_RETURN(in_len == 0, ret); + + int i = 0; + int in_ = 0; + unsigned char char_array_4[4], char_array_3[3]; + + while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) { + char_array_4[i++] = encoded_string[in_]; + in_++; + if (i == 4) { + for (i = 0; i < 4; i++) + char_array_4[i] = (unsigned char)base64_chars.find(char_array_4[i]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (i = 0; (i < 3); i++) + ret += char_array_3[i]; + i = 0; + } + } + + if (i) { + for (int j = i; j < 4; j++) + char_array_4[j] = 0; + + for (int j = 0; j < 4; j++) + char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]); + + char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4); + char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2); + char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3]; + + for (int j = 0; (j < i - 1); j++) + ret += char_array_3[j]; + } + + return ret; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/base64.h b/hikyuu_cpp/hikyuu/utilities/base64.h new file mode 100644 index 00000000..54ea3d99 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/base64.h @@ -0,0 +1,59 @@ +/* + * Copyright (c) hikyuu.org + * + * Created on: 2020-6-2 + * Author: fasiondog + */ + +#pragma once +#ifndef HKU_UTILS_BASE64_H +#define HKU_UTILS_BASE64_H + +#include +#include "string_view.h" + +#ifndef HKU_UTILS_API +#define HKU_UTILS_API +#endif + +namespace hku { + +/** + * 将二进制 bytes 数组编码成 base64 字符串 + * @param bytes_to_encode 内存起始地址 + * @param in_len 待计算的字节长度 + */ +std::string HKU_UTILS_API base64_encode(unsigned char const* bytes_to_encode, size_t in_len); + +/** + * 字符串编码为 base64 + * @param bytes_to_encode 内存起始地址 + * @param in_len 待计算的字节长度 + * @note 通过 func(unsigned char *, unsigned int) 函数实现,而不是直接只提供 string_view + * 版本的原因是:c++17 string_view 处理 nullptr 时,程序会直接挂掉,无异常 + */ +inline std::string base64_encode(string_view src) { + return base64_encode((unsigned char const*)src.data(), src.size()); +} + +/** + * 将 base64 字符串解码 + * @param encoded_string base64 编码的字符串 + * @param in_len 字符串长度 + * @return string 实际解码后的二进制内容保存在返回的字符串对象中 + * @note 如果传入的base64编码字符串中含有非法字符,不会告警,仅处理到能处理的字符 + */ +std::string HKU_UTILS_API base64_decode(unsigned char const* encoded_string, size_t in_len); + +/** + * 将 base64 字符串解码 + * @param encoded_string base64 编码的字符串 + * @return string 实际解码后的二进制内容保存在返回的字符串对象中 + */ +inline std::string base64_decode(string_view encoded_string) { + return base64_decode((unsigned char const*)encoded_string.data(), encoded_string.size()); +} + +} // namespace hku + +#endif // HKU_UTILS_BASE64_H \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/exception.h b/hikyuu_cpp/hikyuu/utilities/exception.h index 4a6faae9..da0cafdf 100644 --- a/hikyuu_cpp/hikyuu/utilities/exception.h +++ b/hikyuu_cpp/hikyuu/utilities/exception.h @@ -29,6 +29,7 @@ public: exception(const std::string& msg) // cppcheck-suppress noExplicitConstructor : std::exception(msg.c_str()) {} exception(const char* msg) : std::exception(msg) {} // cppcheck-suppress noExplicitConstructor + virtual ~exception() noexcept {}; }; #else diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp new file mode 100644 index 00000000..1f5fb194 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-07-26 + * Author: fasiondog + */ + +#include "HttpClient.h" + +#if HKU_ENABLE_HTTP_CLIENT_ZIP +#include "gzip/compress.hpp" +#include "gzip/decompress.hpp" +#endif + +#include +#include "hikyuu/utilities/Log.h" +#include "hikyuu/utilities/os.h" +#include "url.h" + +namespace hku { + +HttpResponse::HttpResponse() { + NNG_CHECK(nng_http_res_alloc(&m_res)); +} + +HttpResponse::~HttpResponse() { + if (m_res) { + nng_http_res_free(m_res); + } +} + +void HttpResponse::reset() { + if (m_res) { + nng_http_res_free(m_res); + NNG_CHECK(nng_http_res_alloc(&m_res)); + } + m_body.clear(); +} + +HttpResponse::HttpResponse(HttpResponse&& rhs) : m_res(rhs.m_res), m_body(std::move(rhs.m_body)) { + rhs.m_res = nullptr; +} + +HttpResponse& HttpResponse::operator=(HttpResponse&& rhs) { + if (this != &rhs) { + if (m_res != nullptr) { + nng_http_res_free(m_res); + } + m_res = rhs.m_res; + rhs.m_res = nullptr; + m_body = std::move(rhs.m_body); + } + return *this; +} + +HttpClient::~HttpClient() { + reset(); +#if HKU_ENABLE_HTTP_CLIENT_SSL + m_tls_cfg.release(); +#endif +} + +void HttpClient::reset() { + m_client.release(); + m_conn.close(); + m_aio.release(); +} + +void HttpClient::setCaFile(const std::string& filename) { +#if HKU_ENABLE_HTTP_CLIENT_SSL + HKU_CHECK(!filename.empty(), "Input filename is empty!"); + HKU_IF_RETURN(filename == m_ca_file, void()); + HKU_CHECK(existFile(filename), "Not exist file: {}", filename); + m_tls_cfg.set_ca_file(filename); + m_ca_file = filename; + reset(); +#else + HKU_THROW("Not support https! Please complie with --http_client_ssl!"); +#endif +} + +void HttpClient::_connect() { + HKU_CHECK(m_url.valid(), "Invalid url: {}", m_url.raw_url()); + + m_client.set_url(m_url); + + if (m_url.is_https()) { +#if HKU_ENABLE_HTTP_CLIENT_SSL + auto* old_cfg = m_client.get_tls_cfg(); + if (!old_cfg || old_cfg != m_tls_cfg.get()) { + m_client.set_tls_cfg(m_tls_cfg.get()); + } +#endif + } + + m_aio.alloc(m_timeout_ms); + m_client.connect(m_aio); + + if (!m_conn.valid()) { + NNG_CHECK(m_aio.wait().result()); + m_conn = std::move(nng::http_conn((nng_http_conn*)m_aio.get_output(0))); + } +} + +HttpResponse HttpClient::request(const std::string& method, const std::string& path, + const HttpParams& params, const HttpHeaders& headers, + const char* body, size_t body_len, + const std::string& content_type) { + HKU_CHECK(m_url.valid(), "Invalid url: {}", m_url.raw_url()); + + HttpResponse res; + try { + std::ostringstream buf; + bool first = true; + for (auto iter = params.cbegin(); iter != params.cend(); ++iter) { + if (first) { + buf << "?"; + } else { + buf << "&"; + } + buf << iter->first << "=" << iter->second; + } + + std::string uri = buf.str(); + uri = uri.empty() ? path : fmt::format("{}{}", path, uri); + res = _readResChunk(method, uri, headers, body, body_len, content_type); + + if (res.getHeader("Connection") == "close") { + HKU_WARN("Connect closed"); + reset(); + } + + } catch (const std::exception&) { + reset(); + throw; + } catch (...) { + reset(); + HKU_THROW_UNKNOWN; + } + return res; +} + +HttpResponse HttpClient::_readResChunk(const std::string& method, const std::string& uri, + const HttpHeaders& headers, const char* body, + size_t body_len, const std::string& content_type) { + HttpResponse res; + nng::http_req req(m_url); + req.set_method(method).set_uri(uri).add_headers(m_default_headers).add_headers(headers); + if (body != nullptr) { + HKU_CHECK(body_len > 0, "Body is not null, but len is zero!"); + req.add_header("Content-Type", content_type); + +#if HKU_ENABLE_HTTP_CLIENT_ZIP + if (req.get_header("Content-Encoding") == "gzip") { + gzip::Compressor comp(Z_DEFAULT_COMPRESSION); + std::string output; + comp.compress(output, body, body_len); + req.copy_data(output.data(), output.size()); + } else { + req.set_data(body, body_len); + } +#else + req.del_header("Content-Encoding").set_data(body, body_len); +#endif + } + + int count = 0; + while (count < 2) { + count++; + _connect(); + + m_conn.transact(req.get(), res.get(), m_aio); + int rv = m_aio.wait().result(); + if (0 == rv) { + break; + } else if (NNG_ETIMEDOUT == rv) { + throw HttpTimeoutException(); + } else if (NNG_ECLOSED == rv || NNG_ECONNSHUT == rv || NNG_ECONNREFUSED == rv) { + // HKU_DEBUG("rv: {}", nng_strerror(rv)); + reset(); + res.reset(); + } else { + HKU_THROW("[NNG_ERROR] {} ", nng_strerror(rv)); + } + } + + HKU_IF_RETURN(res.status() != NNG_HTTP_STATUS_OK, res); + + void* data; + size_t len; + nng_http_res_get_data(res.get(), &data, &len); + +#if HKU_ENABLE_HTTP_CLIENT_ZIP + if (res.getHeader("Content-Encoding") == "gzip") { + res.m_body = gzip::decompress((const char*)data, len); + } else { + res._resizeBody(len); + memcpy(res.m_body.data(), data, len); + } +#else + res._resizeBody(len); + memcpy(res.m_body.data(), data, len); +#endif + + return res; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h new file mode 100644 index 00000000..3ecba6fd --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-07-26 + * Author: fasiondog + */ + +#pragma once +#ifndef HKU_UTILS_HTTP_CLIENT_H +#define HKU_UTILS_HTTP_CLIENT_H + +#include "hikyuu/utilities/config.h" +#if !HKU_ENABLE_HTTP_CLIENT +#error "Don't enable http client, please config with --http_client=y" +#endif + +#include +#include +#include "nng_wrap.h" + +#ifndef HKU_UTILS_API +#define HKU_UTILS_API +#endif + +namespace hku { + +using json = nlohmann::json; + +class HKU_UTILS_API HttpClient; + +class HKU_UTILS_API HttpResponse final { + friend class HKU_UTILS_API HttpClient; + +public: + HttpResponse(); + ~HttpResponse(); + + HttpResponse(const HttpResponse&) = delete; + HttpResponse& operator=(const HttpResponse&) = delete; + + HttpResponse(HttpResponse&& rhs); + HttpResponse& operator=(HttpResponse&& rhs); + + const std::string& body() const noexcept { + return m_body; + } + + hku::json json() const { + return json::parse(m_body); + } + + int status() const noexcept { + return nng_http_res_get_status(m_res); + } + + std::string reason() noexcept { + return nng_http_res_get_reason(m_res); + } + + std::string getHeader(const std::string& key) noexcept { + const char* hdr = nng_http_res_get_header(m_res, key.c_str()); + return hdr ? std::string(hdr) : std::string(); + } + + size_t getContentLength() noexcept { + std::string slen = getHeader("Content-Length"); + return slen.empty() ? 0 : std::stoi(slen); + } + +private: + void _resizeBody(size_t len) { + m_body.resize(len); + } + + nng_http_res* get() const noexcept { + return m_res; + } + + void reset(); + +private: + nng_http_res* m_res{nullptr}; + std::string m_body; +}; + +class HKU_UTILS_API HttpClient { +public: + HttpClient() = default; + explicit HttpClient(const std::string& url) : m_url(nng::url(url)) {}; + virtual ~HttpClient(); + + bool valid() const noexcept { + return m_url.valid(); + } + + const std::string& url() const noexcept { + return m_url.raw_url(); + } + + void setUrl(const std::string& url) noexcept { + m_url = std::move(nng::url(url)); + reset(); + } + + // #define NNG_DURATION_INFINITE (-1) + // #define NNG_DURATION_DEFAULT (-2) + // #define NNG_DURATION_ZERO (0) + void setTimeout(int32_t ms) { + if (m_timeout_ms != ms) { + m_timeout_ms = ms; + reset(); + } + } + + void setDefaultHeaders(const HttpHeaders& headers) { + m_default_headers = headers; + } + + void setDefaultHeaders(HttpHeaders&& headers) { + m_default_headers = std::move(headers); + } + + void setCaFile(const std::string& filename); + + void reset(); + + HttpResponse request(const std::string& method, const std::string& path, + const HttpParams& params, const HttpHeaders& headers, const char* body, + size_t body_len, const std::string& content_type); + + HttpResponse get(const std::string& path, const HttpHeaders& headers = HttpHeaders()) { + return request("GET", path, HttpParams(), headers, nullptr, 0, ""); + } + + HttpResponse get(const std::string& path, const HttpParams& params, + const HttpHeaders& headers) { + return request("GET", path, params, headers, nullptr, 0, ""); + } + + HttpResponse post(const std::string& path, const HttpParams& params, const HttpHeaders& headers, + const char* body, size_t len, const std::string& content_type) { + return request("POST", path, params, headers, body, len, content_type); + } + + HttpResponse post(const std::string& path, const HttpHeaders& headers, const char* body, + size_t len, const std::string& content_type) { + return request("POST", path, HttpParams(), headers, body, len, content_type); + } + + HttpResponse post(const std::string& path, const HttpParams& params, const HttpHeaders& headers, + const std::string& content, const std::string& content_type = "text/plaint") { + return post(path, params, headers, content.data(), content.size(), content_type); + } + + HttpResponse post(const std::string& path, const HttpHeaders& headers, + const std::string& content, const std::string& content_type = "text/plaint") { + return post(path, HttpParams(), headers, content, content_type); + } + + HttpResponse post(const std::string& path, const HttpParams& params, const HttpHeaders& headers, + const json& body) { + return post(path, params, headers, body.dump(), "application/json"); + } + + HttpResponse post(const std::string& path, const HttpHeaders& headers, const json& body) { + return post(path, HttpParams(), headers, body); + } + +private: + void _connect(); + HttpResponse _readResChunk(const std::string& method, const std::string& uri, + const HttpHeaders& headers, const char* body, size_t body_len, + const std::string& content_type); + +private: + HttpHeaders m_default_headers; + nng::url m_url; + nng::http_client m_client; + nng::aio m_aio; + nng::http_conn m_conn; +#if HKU_ENABLE_HTTP_CLIENT_SSL + nng::tls_config m_tls_cfg; + std::string m_ca_file; +#endif + + int32_t m_timeout_ms{NNG_DURATION_DEFAULT}; +}; + +} // namespace hku + +#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h b/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h new file mode 100644 index 00000000..722e8d14 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h @@ -0,0 +1,478 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-07-26 + * Author: fasiondog + */ + +#pragma once +#ifndef HKU_UTILS_NNG_WRAP_H +#define HKU_UTILS_NNG_WRAP_H + +#include +#include +#include +#include "hikyuu/utilities/Log.h" + +#include "hikyuu/utilities/config.h" +#if HKU_ENABLE_HTTP_CLIENT_SSL +#include +#endif + +namespace hku { + +struct HttpTimeoutException : hku::exception { + HttpTimeoutException() : hku::exception("Http timeout!") {} + virtual ~HttpTimeoutException() noexcept = default; +}; + +using HttpHeaders = std::map; +using HttpParams = std::map; + +} // namespace hku + +namespace hku { +namespace nng { + +#ifndef NNG_CHECK +#define NNG_CHECK(rv) \ + { \ + if (rv != 0) { \ + HKU_THROW("[NNG_ERROR] {} ", nng_strerror(rv)); \ + } \ + } +#endif + +#ifndef NNG_CHECK_M +#define NNG_CHECK_M(rv, ...) \ + { \ + if (rv != 0) { \ + HKU_THROW("{} | [NNG_ERROR] {}", fmt::format(__VA_ARGS__), nng_strerror(rv)); \ + } \ + } +#endif + +class url final { +public: + url() = default; + explicit url(const std::string& url_) noexcept : m_rawurl(url_) { + nng_url_parse(&m_url, m_rawurl.c_str()); + } + + url(const url&) = delete; + url(url&& rhs) noexcept : m_rawurl(std::move(rhs.m_rawurl)), m_url(rhs.m_url) { + rhs.m_url = nullptr; + } + + url& operator=(const url&) = delete; + url& operator=(url&& rhs) noexcept { + if (this != &rhs) { + if (m_url != nullptr) { + nng_url_free(m_url); + } + m_url = rhs.m_url; + m_rawurl = std::move(rhs.m_rawurl); + rhs.m_url = nullptr; + } + return *this; + } + + ~url() { + if (m_url) { + nng_url_free(m_url); + } + } + + const std::string& raw_url() const noexcept { + return m_rawurl; + } + + nng_url* get() const noexcept { + return m_url; + } + + nng_url* operator->() const noexcept { + return m_url; + } + + bool valid() const noexcept { + return m_url != nullptr; + } + + bool is_https() const noexcept { + return m_url == nullptr ? false : strcmp("https", m_url->u_scheme) == 0; + } + +private: + std::string m_rawurl; + nng_url* m_url{nullptr}; +}; + +class aio final { +public: + aio() = default; + aio(const aio&) = delete; + ~aio() { + if (m_aio) { + nng_aio_free(m_aio); + } + } + + bool valid() const noexcept { + return m_aio != nullptr; + } + + nng_aio* get() const noexcept { + return m_aio; + } + + nng_aio* operator->() const noexcept { + return m_aio; + } + + void alloc(int32_t timeout_ms) { + if (m_aio == nullptr) { + NNG_CHECK(nng_aio_alloc(&m_aio, NULL, NULL)); + } + set_timeout(timeout_ms); + } + + void release() { + if (m_aio) { + nng_aio_free(m_aio); + m_aio = nullptr; + } + } + + aio& wait() { + nng_aio_wait(m_aio); + return *this; + } + + int result() { + // 直接返回结果,在调用处判断异常,才知道是在具体哪里 + return nng_aio_result(m_aio); + } + + void* get_output(unsigned index) { + return nng_aio_get_output(m_aio, index); + } + + /* + * 0 - 恢复默认值 + * <0 - 不限制 + */ + void set_timeout(int32_t ms) { + if (ms != m_timeout) { + m_timeout = ms; + nng_aio_set_timeout(m_aio, ms); + } + } + + int32_t get_timeout() const noexcept { + return m_timeout; + } + + void set_iov(unsigned n, const nng_iov* iov) { + NNG_CHECK(nng_aio_set_iov(m_aio, n, iov)); + } + +private: + nng_aio* m_aio{nullptr}; + int32_t m_timeout{NNG_DURATION_DEFAULT}; +}; + +#if HKU_ENABLE_HTTP_CLIENT_SSL +class tls_config final { +public: + tls_config() = default; + + tls_config(const tls_config& th) : m_cfg(th.m_cfg) { + if (m_cfg) { + nng_tls_config_hold(th.m_cfg); + } + } + + tls_config(tls_config&& rhs) : m_cfg(rhs.m_cfg) { + rhs.m_cfg = nullptr; + } + + ~tls_config() { + if (m_cfg) { + nng_tls_config_free(m_cfg); + } + } + + tls_config& operator=(const tls_config& th) { + if (this != &th) { + m_cfg = th.m_cfg; + if (m_cfg) { + nng_tls_config_hold(m_cfg); + } + } + return *this; + } + + tls_config& operator=(tls_config&& rhs) { + if (this != &rhs) { + m_cfg = rhs.m_cfg; + rhs.m_cfg = nullptr; + } + return *this; + } + + void release() { + if (m_cfg) { + nng_tls_config_free(m_cfg); + m_cfg = nullptr; + } + } + + nng_tls_config* get() const noexcept { + return m_cfg; + } + + tls_config& set_ca_file(const std::string& filename) { + NNG_CHECK(alloc()); + NNG_CHECK(nng_tls_config_ca_file(m_cfg, filename.c_str())); + return *this; + } + +private: + int alloc() { + return m_cfg ? 0 : nng_tls_config_alloc(&m_cfg, NNG_TLS_MODE_CLIENT); + } + +private: + nng_tls_config* m_cfg{nullptr}; +}; +#endif // #if HKU_ENABLE_HTTP_CLIENT_SSL + +class http_client final { +public: + http_client() = default; + ~http_client() { + if (m_client) { + nng_http_client_free(m_client); + } + } + + void set_url(const nng::url& url) { +#if !HKU_ENABLE_HTTP_CLIENT_SSL + if (url.is_https()) { + HKU_THROW("Not support https: ({})! Please compile with --http_client_ssl", + url.raw_url()); + } +#endif + if (!m_client) { + NNG_CHECK(nng_http_client_alloc(&m_client, url.get())); + m_tls_cfg = nullptr; + m_aio = nullptr; + } + } + + void connect(const aio& aio) { + if (m_aio != aio.get()) { + nng_http_client_connect(m_client, aio.get()); + m_aio = aio.get(); + } + } + + void set_tls_cfg(nng_tls_config* cfg) { + if (cfg != m_tls_cfg) { + NNG_CHECK(nng_http_client_set_tls(m_client, cfg)); + m_tls_cfg = cfg; + } + } + + nng_tls_config* get_tls_cfg() const noexcept { + return m_tls_cfg; + } + + nng_http_client* get() const noexcept { + return m_client; + } + + nng_http_client* operator->() const noexcept { + return m_client; + } + + explicit operator bool() const noexcept { + return m_client != nullptr; + } + + void release() { + if (m_client) { + nng_http_client_free(m_client); + m_client = nullptr; + m_aio = nullptr; + m_tls_cfg = nullptr; + } + } + +private: + nng_http_client* m_client{nullptr}; + nng_aio* m_aio{nullptr}; + nng_tls_config* m_tls_cfg{nullptr}; +}; + +class http_req final { +public: + http_req() = default; + explicit http_req(const url& url) { + NNG_CHECK(nng_http_req_alloc(&m_req, url.get())); + } + + http_req(const http_req&) = delete; + http_req(http_req&& rhs) : m_req(rhs.m_req) { + rhs.m_req = nullptr; + } + + ~http_req() { + if (m_req) { + nng_http_req_free(m_req); + } + } + + http_req& operator=(const http_req&) = delete; + http_req& operator=(http_req&& rhs) { + if (this != &rhs) { + if (m_req) { + nng_http_req_free(m_req); + } + m_req = rhs.m_req; + rhs.m_req = nullptr; + } + return *this; + } + + nng_http_req* get() const noexcept { + return m_req; + } + + http_req& set_method(const std::string& method) { + NNG_CHECK(nng_http_req_set_method(m_req, method.c_str())); + return *this; + } + + http_req& set_uri(const std::string& uri) { + NNG_CHECK(nng_http_req_set_uri(m_req, uri.c_str())); + return *this; + } + + http_req& add_header(const std::string& key, const std::string& val) { + NNG_CHECK_M(nng_http_req_add_header(m_req, key.c_str(), val.c_str()), + "Failed add head {}: {}", key, val); + return *this; + } + + http_req& add_headers(const HttpHeaders& headers) { + for (auto iter = headers.cbegin(); iter != headers.cend(); ++iter) { + NNG_CHECK_M(nng_http_req_add_header(m_req, iter->first.c_str(), iter->second.c_str()), + "Failed add header {}: {}", iter->first, iter->second); + } + return *this; + } + + std::string get_header(const std::string& key) { + const char* head = nng_http_req_get_header(m_req, key.c_str()); + return head ? std::string(head) : std::string(); + } + + http_req& del_header(const std::string& key) { + nng_http_req_del_header(m_req, key.c_str()); + return *this; + } + + /* 注: data 需要自行管理且在 req 释放之前应该一直存在,主要避免拷贝 */ + http_req& set_data(const char* data, size_t len) { + if (data != nullptr && len != 0) { + NNG_CHECK(nng_http_req_set_data(m_req, data, len)); + } + return *this; + } + + http_req& copy_data(const char* data, size_t len) { + if (data != nullptr && len != 0) { + NNG_CHECK(nng_http_req_copy_data(m_req, data, len)); + } + return *this; + } + +private: + nng_http_req* m_req{nullptr}; +}; + +class http_conn final { +public: + http_conn() = default; + explicit http_conn(nng_http_conn* conn_) noexcept : m_conn(conn_) {} + + http_conn(const http_conn&) = delete; + + http_conn(http_conn&& rhs) noexcept : m_conn(rhs.m_conn) { + rhs.m_conn = nullptr; + } + + http_conn& operator=(const http_conn& rhs) = delete; + + http_conn& operator=(http_conn&& rhs) noexcept { + if (this != &rhs) { + if (m_conn != nullptr) { + nng_http_conn_close(m_conn); + } + m_conn = rhs.m_conn; + rhs.m_conn = nullptr; + } + return *this; + } + + ~http_conn() { + if (m_conn) { + nng_http_conn_close(m_conn); + } + } + + void close() { + if (m_conn) { + nng_http_conn_close(m_conn); + m_conn = nullptr; + } + } + + nng_http_conn* get() const noexcept { + return m_conn; + } + + nng_http_conn* operator->() const noexcept { + return m_conn; + } + + bool valid() const noexcept { + return m_conn != nullptr; + } + + void write_req(const http_req& req, const aio& aio) { + nng_http_conn_write_req(m_conn, req.get(), aio.get()); + } + + void read_res(nng_http_res* res, const aio& aio) { + nng_http_conn_read_res(m_conn, res, aio.get()); + } + + void read_all(const aio& aio) { + nng_http_conn_read_all(m_conn, aio.get()); + } + + void transact(nng_http_req* req, nng_http_res* res, const aio& aio) { + nng_http_conn_transact(m_conn, req, res, aio.get()); + } + +private: + nng_http_conn* m_conn{nullptr}; +}; + +} // namespace nng +} // namespace hku + +#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp new file mode 100644 index 00000000..4052ff9d --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp @@ -0,0 +1,56 @@ +/* + * Copyright(C) 2021 hikyuu.org + * + * Create on: 2021-03-07 + * Author: fasiondog + */ + +#include "url.h" + +namespace hku { + +#define IS_ALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z')) +#define IS_NUM(c) ((c) >= '0' && (c) <= '9') +#define IS_ALPHANUM(c) (IS_ALPHA(c) || IS_NUM(c)) +#define IS_HEX(c) (IS_NUM(c) || ((c) >= 'a' && (c) <= 'f') || ((c) >= 'A' && (c) <= 'F')) + +static inline bool is_unambiguous(char c) { + return IS_ALPHANUM(c) || c == '-' || c == '_' || c == '.' || c == '~'; +} + +static inline unsigned char hex2i(char hex) { + return hex <= '9' ? hex - '0' : hex <= 'F' ? hex - 'A' + 10 : hex - 'a' + 10; +} + +std::string url_escape(const char* istr) { + std::string ostr; + const char* p = istr; + char szHex[4] = {0}; + while (*p != '\0') { + if (is_unambiguous(*p)) { + ostr += *p; + } else { + sprintf(szHex, "%%%02X", *p); + ostr += szHex; + } + ++p; + } + return ostr; +} + +std::string url_unescape(const char* istr) { + std::string ostr; + const char* p = istr; + while (*p != '\0') { + if (*p == '%' && IS_HEX(p[1]) && IS_HEX(p[2])) { + ostr += ((hex2i(p[1]) << 4) | hex2i(p[2])); + p += 3; + } else { + ostr += *p; + ++p; + } + } + return ostr; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/url.h b/hikyuu_cpp/hikyuu/utilities/http_client/url.h new file mode 100644 index 00000000..aa432066 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/http_client/url.h @@ -0,0 +1,25 @@ +/* + * Copyright(C) 2021 hikyuu.org + * + * Create on: 2021-03-07 + * Author: fasiondog + */ + +#pragma once +#ifndef HKU_UTILS_URL_H +#define HKU_UTILS_URL_H + +#include + +#ifndef HKU_UTILS_API +#define HKU_UTILS_API +#endif + +namespace hku { + +std::string HKU_UTILS_API url_escape(const char* istr); +std::string HKU_UTILS_API url_unescape(const char* istr); + +} // namespace hku + +#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/mo/mo.cpp b/hikyuu_cpp/hikyuu/utilities/mo/mo.cpp new file mode 100644 index 00000000..4b314fb1 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/mo/mo.cpp @@ -0,0 +1,23 @@ +/* + * Copyright(C) 2021 hikyuu.org + * + * Create on: 2021-05-02 + * Author: fasiondog + */ + +#include "hikyuu/utilities/os.h" +#include "hikyuu/utilities/Log.h" +#include "mo.h" + +namespace hku { + +std::unordered_map MOHelper::ms_dict; + +void MOHelper::init() { + HKU_WARN_IF_RETURN(!existFile("i8n/zh_CN.mo"), void(), + "There is no internationalized language file: i8n/zh_CN.mo"); + ms_dict["zh_cn"] = moFileLib::moFileReader(); + ms_dict["zh_cn"].ReadFile("i8n/zh_CN.mo"); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/mo/mo.h b/hikyuu_cpp/hikyuu/utilities/mo/mo.h new file mode 100644 index 00000000..7fe168fc --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/mo/mo.h @@ -0,0 +1,48 @@ +/* + * Copyright(C) 2021 hikyuu.org + * + * Create on: 2021-05-01 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/utilities/config.h" +#if !HKU_ENABLE_MO +#error "Don't enable mo, please config with --mo=y" +#endif + +#include +#include "hikyuu/utilities/string_view.h" +#include "moFileReader.h" + +#if defined(_MSC_VER) +// moFileReader.hpp 最后打开了4251告警,这里关闭 +#pragma warning(disable : 4251) +#endif /* _MSC_VER */ + +#ifndef HKU_UTILS_API +#define HKU_UTILS_API +#endif + +namespace hku { + +class HKU_UTILS_API MOHelper { +public: + static void init(); + + static std::string translate(const std::string &lang, const char *id) { + auto iter = ms_dict.find(lang); + return iter != ms_dict.end() ? ms_dict[lang].Lookup(id) : std::string(id); + } + + static std::string translate(const std::string &lang, const char *ctx, const char *id) { + auto iter = ms_dict.find(lang); + return iter != ms_dict.end() ? ms_dict[lang].LookupWithContext(ctx, id) : std::string(id); + } + +private: + static std::unordered_map ms_dict; +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h b/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h new file mode 100644 index 00000000..862d8d4f --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h @@ -0,0 +1,836 @@ +/* + * moFileReader - A simple .mo-File-Reader + * Copyright (C) 2009 Domenico Gentner (scorcher24@gmail.com) + * Copyright (C) 2018-2021 Edgar (Edgar@AnotherFoxGuy.com) + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. The names of its contributors may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __MOFILEREADER_SINGLE_INCLUDE_H_INCLUDED__ +#define __MOFILEREADER_SINGLE_INCLUDE_H_INCLUDED__ + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wsign-compare" +#endif + +#if defined(_MSC_VER) +#pragma warning(disable : 4267) +#endif /* _MSC_VER */ + +#include // this is for memset when compiling with gcc. +#include +#include +#include +#include +#include + +//------------------------------------------------------------- +// Path-Seperators are different on other OS. +//------------------------------------------------------------- +#ifndef MO_PATHSEP +#ifdef WIN32 +#define MO_PATHSEP std::string("\\") +#else +#define MO_PATHSEP std::string("/") +#endif +#endif + +//------------------------------------------------------------- +// Defines the beginning of the namespace moFileLib. +//------------------------------------------------------------- +#ifndef MO_BEGIN_NAMESPACE +#define MO_BEGIN_NAMESPACE namespace moFileLib { +#endif +//------------------------------------------------------------- +// Ends the current namespace. +//------------------------------------------------------------- +#ifndef MO_END_NAMESPACE +#define MO_END_NAMESPACE } +#endif + +/** \mainpage moFileReaderSDK + * + * + *

Include in project

+ * + * Usage of this library is quite easy, simply add moFileReader.hpp to your project. Thats all you + * have to do. You can safely exclude mo.cpp, since this file keeps the entry-points of the .exe + * only. + * + *

Usage

+ * + * This is moFileReader, a simple gettext-replacement. The usage of this library is, hopefully, + * fairly simple: \code + * + * // Instanciate the class + * moFileLib::moFileReader reader; + * + * // Load a .mo-File. + * if ( reader.ReadFile("myTranslationFile.mo") != moFileLib::moFileReader::EC_SUCCESS ) + * { + * // Error Handling + * } + * + * // Now, you can lookup the strings you stored in the .mo-File: + * std::cout << reader.Lookup("MyTranslationString") << std::endl; + * + * \endcode + * Thats all! This small code has no dependencies, except the C/C++-runtime of your compiler, + * so it should work on all machines where a C++-runtime is provided. + * + * \note We do not yet support .mo-Files with reversed magic-numbers, since I don't have + * a file to test it and I hate to release stuff I wasn't able to test. + * + *

Changelog

+ * + * - Version 1.2.0 + * - Proper implementation of contexts strings + * Now it uses a separate 2D map for storing strings with context + * - Fixed MagicNumber check not working on Linux + * - Removed duplicate code + * - Added option to disable convenience Class + * + * - Version 1.1.0 + * - Converted library to a header-only library + * + * - Version 1.0.0 + * - Added new function: LookupWithContext + * - Added unit-tests + * - Added support for packaging with Conan + * - Moved project to https://github.com/AnotherFoxGuy/MofileReader + * + * - Version 0.1.2 + * - Generic improvements to the documentation. + * - Generic improvements to the code + * - Fixed a bug in mo.cpp which caused the application not to print the help + * message if only --export or --lookup where missing. + * - Added -h, --help and -? to moReader[.exe]. It will print the help-screen. + * - Added --version and -v to moReader[.exe]. It will print some informations about the + * program. + * - Added --license to moReader[.exe]. This will print its license. + * - --export gives now a feedback about success or failure. + * - The HTML-Dump-Method outputs now the whole table from the empty msgid in a nice html-table, + * not only a few hardcoded. + * - I had an issue-report that the Error-Constants can collide with foreign code under certain + * conditions, so I added a patch which renamed the error-constants to more compatible names. + * + * - Version 0.1.1 + * - Added the ability to export mo's as HTML. + * - Fixed a bug causing a crash when passing an invalid value to moFileReader::Lookup(). + * - Added a new file, moFileConfig.h, holding the macros for the project. + * - Added the ability to be configured by cmake. + * - Added some more inline-functions, which really enhance the singleton. + * + * - Version 0.1.0 + * - Initial Version and release to http://googlecode.com + * + * + *

Credits

+ * + * Gettext is part of the GNU-Tools and (C) by the Free Software + * Foundation.\n Visual C++ Express is a registered Trademark of Microsoft, One Microsoft Way, + * Redmond, USA.\n All other Trademarks are property of their respective owners. \n \n Thanks for + * using this piece of OpenSource-Software.\n Submit patches and/or bugs on + * https://github.com/AnotherFoxGuy/MofileReader. Send your flames, dumb comments etc to /dev/null, + * thank you. + */ + +/** \namespace moFileLib + * \brief This is the only namespace of this small sourcecode. + */ +MO_BEGIN_NAMESPACE + +const std::string g_css = R"( +body { + background-color: black; + color: silver; +} +table { + width: 80%; +} +th { + background-color: orange; + color: black; +} +hr { + color: red; + width: 80%; + size: 5px; +} +a:link{ + color: gold; +} +a:visited{ + color: grey; +} +a:hover{ + color:blue; +} +.copyleft{ + font-size: 12px; + text-align: center; +})"; + +/** + * \brief Keeps the Description of translated and original strings. + * + * + * To load a String from the file, we need its offset and its length. + * This struct helps us grouping this information. + */ +struct moTranslationPairInformation { + /// \brief Constructor + moTranslationPairInformation() : m_orLength(0), m_orOffset(0), m_trLength(0), m_trOffset(0) {} + + /// \brief Length of the Original String + int m_orLength; + + /// \brief Offset of the Original String (absolute) + int m_orOffset; + + /// \brief Length of the Translated String + int m_trLength; + + /// \brief Offset of the Translated String (absolute) + int m_trOffset; +}; + +/** + * \brief Describes the "Header" of a .mo-File. + * + * + * The File info keeps the header of a .mo-file and + * a list of the string-descriptions. + * The typedef is for the type of the string-list. + * The constructor ensures, that all members get a nice + * initial value. + */ +struct moFileInfo { + /// \brief Type for the list of all Translation-Pair-Descriptions. + typedef std::deque moTranslationPairList; + + /// \brief Constructor + moFileInfo() + : m_magicNumber(0), + m_fileVersion(0), + m_numStrings(0), + m_offsetOriginal(0), + m_offsetTranslation(0), + m_sizeHashtable(0), + m_offsetHashtable(0), + m_reversed(false) {} + + /// \brief The Magic Number, compare it to g_MagicNumber. + int m_magicNumber; + + /// \brief The File Version, 0 atm according to the manpage. + int m_fileVersion; + + /// \brief Number of Strings in the .mo-file. + int m_numStrings; + + /// \brief Offset of the Table of the Original Strings + int m_offsetOriginal; + + /// \brief Offset of the Table of the Translated Strings + int m_offsetTranslation; + + /// \brief Size of 1 Entry in the Hashtable. + int m_sizeHashtable; + + /// \brief The Offset of the Hashtable. + int m_offsetHashtable; + + /** \brief Tells you if the bytes are reversed + * \note When this is true, the bytes are reversed and the Magic number is like g_MagicReversed + */ + bool m_reversed; + + /// \brief A list containing offset and length of the strings in the file. + moTranslationPairList m_translationPairInformation; +}; + +/** + * \brief This class is a gettext-replacement. + * + * + * The usage is quite simple:\n + * Tell the class which .mo-file it shall load via + * moFileReader::ReadFile(). The method will attempt to load + * the file, all translations will be stored in memory. + * Afterwards you can lookup the strings with moFileReader::Lookup() just + * like you would do with gettext. + * Additionally, you can call moFileReader::ReadFile() for as much files as you + * like. But please be aware, that if there are duplicated keys (original strings), + * that they will replace each other in the lookup-table. There is no check done, if a + * key already exists. + * + * \note If you add "Lookup" to the keywords of the gettext-parser (like poEdit), + * it will recognize the Strings loaded with an instance of this class. + * \note I strongly recommend poEdit from Vaclav Slavik for editing .po-Files, + * get it at http://poedit.net for various systems :). + */ +class moFileReader { +protected: + /// \brief Type for the map which holds the translation-pairs later. + typedef std::map moLookupList; + + /// \brief Type for the 2D map which holds the translation-pairs later. + typedef std::map moContextLookupList; + +public: + /// \brief The Magic Number describes the endianess of bytes on the system. + static const unsigned int MagicNumber = 0x950412DE; + + /// \brief If the Magic Number is Reversed, we need to swap the bytes. + static const unsigned int MagicReversed = 0xDE120495; + + /// \brief The character that is used to separate context strings + static const char ContextSeparator = '\x04'; + + /// \brief The possible errorcodes for methods of this class + enum eErrorCode { + /// \brief Indicated success + EC_SUCCESS = 0, + + /// \brief Indicates an error + EC_ERROR, + + /// \brief The given File was not found. + EC_FILENOTFOUND, + + /// \brief The file is invalid. + EC_FILEINVALID, + + /// \brief Empty Lookup-Table (returned by ExportAsHTML()) + EC_TABLEEMPTY, + + /// \brief The magic number did not match + EC_MAGICNUMBER_NOMATCH, + + /** + * \brief The magic number is reversed. + * \note This is an error until the class supports it. + */ + EC_MAGICNUMBER_REVERSED, + }; + + /** \brief Reads a .mo-file + * \param[in] _filename The path to the file to load. + * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. + * + * This is the core-feature. This method loads the .mo-file and stores + * all translation-pairs in a map. You can access this map via the method + * moFileReader::Lookup(). + */ + moFileReader::eErrorCode ParseData(const std::string &data) { + // Opening the file. + std::stringstream stream(data); + + return ReadStream(stream); + } + + /** \brief Reads a .mo-file + * \param[in] _filename The path to the file to load. + * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. + * + * This is the core-feature. This method loads the .mo-file and stores + * all translation-pairs in a map. You can access this map via the method + * moFileReader::Lookup(). + */ + eErrorCode ReadFile(const char *filename) { + // Opening the file. + std::ifstream stream(filename, std::ios_base::binary | std::ios_base::in); + if (!stream.is_open()) { + m_error = std::string("Cannot open File ") + std::string(filename); + return moFileReader::EC_FILENOTFOUND; + } + + eErrorCode res = ReadStream(stream); + stream.close(); + + return res; + } + + /** \brief Reads data from a stream + * \param[in] stream + * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. + * + */ + template + eErrorCode ReadStream(T &stream) { + // Creating a file-description. + moFileInfo moInfo; + + // Reference to the List inside moInfo. + moFileInfo::moTranslationPairList &TransPairInfo = moInfo.m_translationPairInformation; + + // Read in all the 4 bytes of fire-magic, offsets and stuff... + stream.read((char *)&moInfo.m_magicNumber, 4); + stream.read((char *)&moInfo.m_fileVersion, 4); + stream.read((char *)&moInfo.m_numStrings, 4); + stream.read((char *)&moInfo.m_offsetOriginal, 4); + stream.read((char *)&moInfo.m_offsetTranslation, 4); + stream.read((char *)&moInfo.m_sizeHashtable, 4); + stream.read((char *)&moInfo.m_offsetHashtable, 4); + + if (stream.bad()) { + m_error = + "Stream bad during reading. The .mo-file seems to be invalid or has bad " + "descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + // Checking the Magic Number + if (MagicNumber != moInfo.m_magicNumber) { + if (MagicReversed != moInfo.m_magicNumber) { + m_error = "The Magic Number does not match in all cases!"; + return moFileReader::EC_MAGICNUMBER_NOMATCH; + } else { + moInfo.m_reversed = true; + m_error = "Magic Number is reversed. We do not support this yet!"; + return moFileReader::EC_MAGICNUMBER_REVERSED; + } + } + + // Now we search all Length & Offsets of the original strings + for (int i = 0; i < moInfo.m_numStrings; i++) { + moTranslationPairInformation _str; + stream.read((char *)&_str.m_orLength, 4); + stream.read((char *)&_str.m_orOffset, 4); + if (stream.bad()) { + m_error = + "Stream bad during reading. The .mo-file seems to be invalid or has bad " + "descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + TransPairInfo.push_back(_str); + } + + // Get all Lengths & Offsets of the translated strings + // Be aware: The Descriptors already exist in our list, so we just mod. refs from the deque. + for (int i = 0; i < moInfo.m_numStrings; i++) { + moTranslationPairInformation &_str = TransPairInfo[i]; + stream.read((char *)&_str.m_trLength, 4); + stream.read((char *)&_str.m_trOffset, 4); + if (stream.bad()) { + m_error = + "Stream bad during reading. The .mo-file seems to be invalid or has bad " + "descriptions!"; + return moFileReader::EC_FILEINVALID; + } + } + + // Normally you would read the hash-table here, but we don't use it. :) + + // Now to the interesting part, we read the strings-pairs now + for (int i = 0; i < moInfo.m_numStrings; i++) { + // We need a length of +1 to catch the trailing \0. + int orLength = TransPairInfo[i].m_orLength + 1; + int trLength = TransPairInfo[i].m_trLength + 1; + + int orOffset = TransPairInfo[i].m_orOffset; + int trOffset = TransPairInfo[i].m_trOffset; + + // Original + char *original = new char[orLength]; + memset(original, 0, sizeof(char) * orLength); + + stream.seekg(orOffset); + stream.read(original, orLength); + + if (stream.bad()) { + m_error = + "Stream bad during reading. The .mo-file seems to be invalid or has bad " + "descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + // Translation + char *translation = new char[trLength]; + memset(translation, 0, sizeof(char) * trLength); + + stream.seekg(trOffset); + stream.read(translation, trLength); + + if (stream.bad()) { + m_error = + "Stream bad during reading. The .mo-file seems to be invalid or has bad " + "descriptions!"; + return moFileReader::EC_FILEINVALID; + } + + std::string original_str = original; + std::string translation_str = translation; + auto ctxSeparator = original_str.find(ContextSeparator); + + // Store it in the map. + if (ctxSeparator == std::string::npos) { + m_lookup[original_str] = translation_str; + numStrings++; + } else { + // try-catch for handling out_of_range exceptions + try { + m_lookup_context[original_str.substr(0, ctxSeparator)][original_str.substr( + ctxSeparator + 1, original_str.length())] = translation_str; + numStrings++; + } catch (...) { + m_error = + "Stream bad during reading. The .mo-file seems to be invalid or has bad " + "descriptions!"; + return moFileReader::EC_ERROR; + } + } + + // Cleanup... + delete[] original; + delete[] translation; + } + + // Done :) + return moFileReader::EC_SUCCESS; + } + + /** \brief Returns the searched translation or returns the input. + * \param[in] id The id of the translation to search for. + * \return The value you passed in via _id or the translated string. + */ + std::string Lookup(const char *id) const { + if (m_lookup.empty()) + return id; + auto iterator = m_lookup.find(id); + + return iterator == m_lookup.end() ? id : iterator->second; + } + + /** \brief Returns the searched translation or returns the input, restricted to the context + * given by context. See https://www.gnu.org/software/gettext/manual/html_node/Contexts.html for + * more info. \param[in] context Restrict to the context given. \param[in] id The id of the + * translation to search for. \return The value you passed in via _id or the translated string. + */ + std::string LookupWithContext(const char *context, const char *id) const { + if (m_lookup_context.empty()) + return id; + auto iterator = m_lookup_context.find(context); + + if (iterator == m_lookup_context.end()) + return id; + auto iterator2 = iterator->second.find(id); + + return iterator2 == iterator->second.end() ? id : iterator2->second; + } + + /// \brief Returns the Error Description. + const std::string &GetErrorDescription() const { + return m_error; + } + + /// \brief Empties the Lookup-Table. + void ClearTable() { + m_lookup.clear(); + m_lookup_context.clear(); + numStrings = 0; + } + + /** \brief Returns the Number of Entries in our Lookup-Table. + * \note The mo-File-table always contains an empty msgid, which contains informations + * about the tranlsation-project. So the real number of strings is always minus 1. + */ + unsigned int GetNumStrings() const { + return numStrings; + } + + /** \brief Exports the whole content of the .mo-File as .html + * \param[in] infile The .mo-File to export. + * \param[in] filename Where to store the .html-file. If empty, the path and filename of the + * _infile with .html appended. \param[in,out] css The css-script for the visual style of the + * file, in case you don't like mine ;). + * \see g_css for the possible and used css-values. + */ + static eErrorCode ExportAsHTML(const std::string &infile, const std::string &filename = "", + const std::string &css = g_css) { + // Read the file + moFileReader reader; + moFileReader::eErrorCode r = reader.ReadFile(infile.c_str()); + if (r != moFileReader::EC_SUCCESS) { + return r; + } + if (reader.m_lookup.empty()) { + return moFileReader::EC_TABLEEMPTY; + } + + // Beautify Output + std::string fname; + unsigned int pos = infile.find_last_of(MO_PATHSEP); + if (pos != std::string::npos) { + fname = infile.substr(pos + 1, infile.length()); + } else { + fname = infile; + } + + // if there is no filename given, we set it to the .mo + html, e.g. test.mo.html + std::string htmlfile(filename); + if (htmlfile.empty()) { + htmlfile = infile + std::string(".html"); + } + + // Ok, now prepare output. + std::ofstream stream(htmlfile.c_str()); + if (stream.is_open()) { + stream + << R"()" + << std::endl; + stream << "" << std::endl; + stream << R"()" + << std::endl; + stream << "Dump of " << fname << "" << std::endl; + stream << "" << std::endl; + stream << "
" << std::endl; + stream << "

" << fname << "

" << std::endl; + stream << R"()" << std::endl; + + std::stringstream parsee; + parsee << reader.Lookup(""); + + while (!parsee.eof()) { + char buffer[1024]; + parsee.getline(buffer, 1024); + std::string name; + std::string value; + + reader.GetPoEditorString(buffer, name, value); + if (!(name.empty() || value.empty())) { + stream << "" + << std::endl; + } + } + stream << "
Project Info
" << name << "" << value << "
" << std::endl; + stream << "
" << std::endl; + + // Now output the content + stream << R"()" << std::endl; + for (const auto &it : reader.m_lookup) { + if (!it.first.empty()) // Skip the empty msgid, its the table we handled above. + { + stream << "" + << std::endl; + } + } + stream << "
Content
" << it.first << "" << it.second << "

" << std::endl; + + // Separate tables for each context + for (const auto &it : reader.m_lookup_context) { + stream << R"(" + << std::endl; + for (const auto &its : it.second) { + stream << "" + << std::endl; + } + stream << "
)" << it.first << "
" << its.first << "" << its.second << "

" << std::endl; + } + + stream << "
" << std::endl; + stream << "
File generated by moFileReaderSDK
" + << std::endl; + stream << "" << std::endl; + stream.close(); + } else { + return moFileReader::EC_FILENOTFOUND; + } + + return moFileReader::EC_SUCCESS; + } + +protected: + /// \brief Keeps the last error as String. + std::string m_error; + + /** \brief Swap the endianness of a 4 byte WORD. + * \param[in] in The value to swap. + * \return The swapped value. + */ + unsigned long SwapBytes(unsigned long in) { + unsigned long b0 = (in >> 0) & 0xff; + unsigned long b1 = (in >> 8) & 0xff; + unsigned long b2 = (in >> 16) & 0xff; + unsigned long b3 = (in >> 24) & 0xff; + + return (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + } + +private: + // Holds the lookup-table + moLookupList m_lookup; + moContextLookupList m_lookup_context; + + int numStrings = 0; + + // Replaces < with ( to satisfy html-rules. + static void MakeHtmlConform(std::string &_inout) { + std::string temp = _inout; + for (unsigned int i = 0; i < temp.length(); i++) { + if (temp[i] == '>') { + _inout.replace(i, 1, ")"); + } + if (temp[i] == '<') { + _inout.replace(i, 1, "("); + } + } + } + + // Extracts a value-pair from the po-edit-information + bool GetPoEditorString(const char *_buffer, std::string &_name, std::string &_value) { + std::string line(_buffer); + size_t first = line.find_first_of(':'); + + if (first != std::string::npos) { + _name = line.substr(0, first); + _value = line.substr(first + 1, line.length()); + + // Replace <> with () for Html-Conformity. + MakeHtmlConform(_value); + MakeHtmlConform(_name); + + // Remove spaces from front and end. + Trim(_value); + Trim(_name); + + return true; + } + return false; + } + + // Removes spaces from front and end. + static void Trim(std::string &_in) { + if (_in.empty()) { + return; + } + + _in.erase(0, _in.find_first_not_of(" ")); + _in.erase(_in.find_last_not_of(" ") + 1); + + /*while (_in[0] == ' ') + { + _in = _in.substr(1, _in.length()); + } + while (_in[_in.length()] == ' ') + { + _in = _in.substr(0, _in.length() - 1); + }*/ + } +}; + +#ifndef MO_NO_CONVENIENCE_CLASS +/** \brief Convenience Class + * + * + * This class derives from moFileReader and builds a singleton to access its methods + * in a global manner. + * \note This class is a Singleton. Please access it via moFileReaderSingleton::GetInstance() + * or use the provided wrappers:\n + * - moReadMoFile() + * - _() + * - moFileClearTable() + * - moFileGetErrorDescription() + * - moFileGetNumStrings(); + */ +class moFileReaderSingleton : public moFileReader { +private: + // Private Contructor and Copy-Constructor to avoid + // that this class is instanced. + moFileReaderSingleton() {} + + moFileReaderSingleton(const moFileReaderSingleton &); + + moFileReaderSingleton &operator=(const moFileReaderSingleton &) { + return *this; + } + +public: + /** \brief Singleton-Accessor. + * \return A static instance of moFileReaderSingleton. + */ + static moFileReaderSingleton &GetInstance() { + static moFileReaderSingleton theoneandonly; + return theoneandonly; + } +}; + +/** \brief Reads the .mo-File. + * \param[in] _filename The path to the file to use. + * \see moFileReader::ReadFile() for details. + */ +inline moFileReader::eErrorCode moReadMoFile(const char *_filename) { + moFileReader::eErrorCode r = moFileReaderSingleton::GetInstance().ReadFile(_filename); + return r; +} + +/** \brief Looks for the spec. string to translate. + * \param[in] id The string-id to search. + * \return The translation if found, otherwise it returns id. + */ +inline std::string _(const char *id) { + std::string r = moFileReaderSingleton::GetInstance().Lookup(id); + return r; +} + +/// \brief Resets the Lookup-Table. +inline void moFileClearTable() { + moFileReaderSingleton::GetInstance().ClearTable(); +} + +/// \brief Returns the last known error as string or an empty class. +inline std::string moFileGetErrorDescription() { + std::string r = moFileReaderSingleton::GetInstance().GetErrorDescription(); + return r; +} + +/// \brief Returns the number of entries loaded from the .mo-File. +inline int moFileGetNumStrings() { + int r = moFileReaderSingleton::GetInstance().GetNumStrings(); + return r; +} +#endif + +MO_END_NAMESPACE + +#if defined(_MSC_VER) +#pragma warning(default : 4251) +#endif /* _MSC_VER */ + +#ifdef __GNUC__ +#pragma GCC diagnostic pop +#endif + +#endif /* __MOFILEREADER_SINGLE_INCLUDE_H_INCLUDED__ */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/snowflake.h b/hikyuu_cpp/hikyuu/utilities/snowflake.h new file mode 100644 index 00000000..577dbae4 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/snowflake.h @@ -0,0 +1,110 @@ +/* + * Copyright(C) 2021 hikyuu.org + * + * The code comes from: https://github.com/sniper00/snowflake-cpp + * Thanks sniper00 + * + * Create on: 2021-04-13 + * Author: fasiondog + */ + +#pragma once + +#include +#include +#include +#include + +namespace hku { + +class snowflake_nonlock { +public: + void lock() {} + void unlock() {} +}; + +template +class snowflake { + using lock_type = Lock; + static constexpr int64_t TWEPOCH = Twepoch; + static constexpr int64_t WORKER_ID_BITS = 5L; + static constexpr int64_t DATACENTER_ID_BITS = 5L; + static constexpr int64_t MAX_WORKER_ID = (1 << WORKER_ID_BITS) - 1; + static constexpr int64_t MAX_DATACENTER_ID = (1 << DATACENTER_ID_BITS) - 1; + static constexpr int64_t SEQUENCE_BITS = 12L; + static constexpr int64_t WORKER_ID_SHIFT = SEQUENCE_BITS; + static constexpr int64_t DATACENTER_ID_SHIFT = SEQUENCE_BITS + WORKER_ID_BITS; + static constexpr int64_t TIMESTAMP_LEFT_SHIFT = + SEQUENCE_BITS + WORKER_ID_BITS + DATACENTER_ID_BITS; + static constexpr int64_t SEQUENCE_MASK = (1 << SEQUENCE_BITS) - 1; + + using time_point = std::chrono::time_point; + + time_point start_time_point_ = std::chrono::steady_clock::now(); + int64_t start_millsecond_ = std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + int64_t last_timestamp_ = -1; + int64_t workerid_ = 0; + int64_t datacenterid_ = 0; + int64_t sequence_ = 0; + lock_type lock_; + +public: + snowflake() = default; + + snowflake(const snowflake&) = delete; + + snowflake& operator=(const snowflake&) = delete; + + void init(int64_t workerid, int64_t datacenterid) { + std::lock_guard lock(lock_); + if (workerid > MAX_WORKER_ID || workerid < 0) { + throw std::runtime_error("worker Id can't be greater than 31 or less than 0"); + } + + if (datacenterid > MAX_DATACENTER_ID || datacenterid < 0) { + throw std::runtime_error("datacenter Id can't be greater than 31 or less than 0"); + } + + workerid_ = workerid; + datacenterid_ = datacenterid; + } + + int64_t nextid() { + std::lock_guard lock(lock_); + // std::chrono::steady_clock cannot decrease as physical time moves forward + auto timestamp = millsecond(); + if (last_timestamp_ == timestamp) { + sequence_ = (sequence_ + 1) & SEQUENCE_MASK; + if (sequence_ == 0) { + timestamp = wait_next_millis(last_timestamp_); + } + } else { + sequence_ = 0; + } + + last_timestamp_ = timestamp; + + return ((timestamp - TWEPOCH) << TIMESTAMP_LEFT_SHIFT) | + (datacenterid_ << DATACENTER_ID_SHIFT) | (workerid_ << WORKER_ID_SHIFT) | sequence_; + } + +private: + int64_t millsecond() const { + auto diff = std::chrono::duration_cast( + std::chrono::steady_clock::now() - start_time_point_); + return start_millsecond_ + diff.count(); + } + + int64_t wait_next_millis(int64_t last) const { + auto timestamp = millsecond(); + while (timestamp <= last) { + timestamp = millsecond(); + } + return timestamp; + } +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 28744057..89aabdc8 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -77,7 +77,7 @@ target("hikyuu") -- 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("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp|utilities/mo/*.cpp") add_files("./data_driver/*.cpp") if get_config("hdf5") or get_config("sqlite") then add_files("./data_driver/base_info/sqlite/**.cpp") diff --git a/setup.py b/setup.py index a0ace2fe..52b2ef12 100644 --- a/setup.py +++ b/setup.py @@ -123,8 +123,9 @@ def start_build(verbose=False, mode='release', feedback=True, worker_num=2, low_ if py_version != history_compile_info[ 'py_version'] or history_compile_info['mode'] != mode: clear_with_python_changed(mode) - cmd = "xmake f {} -c -y -m {} --feedback={} -k {} --low_precision={}".format( - "-v -D" if verbose else "", mode, feedback, "shared" if mode == 'release' else "static", low_precision) + cmd = "xmake f {} -c -y -m {} --feedback={} -k {} --low_precision={} --log_level={}".format( + "-v -D" if verbose else "", mode, feedback, "shared" if mode == 'release' else "static", low_precision, + 2 if mode == 'release' else 0) print(cmd) os.system(cmd) diff --git a/xmake.lua b/xmake.lua index 6484a30e..34521c6d 100644 --- a/xmake.lua +++ b/xmake.lua @@ -1,11 +1,18 @@ set_xmakever("2.8.2") -option("hdf5") - set_default(true) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable hdf5 kdata engine.") -option_end() +-- project +set_project("hikyuu") + +add_rules("mode.debug", "mode.release") + +-- version +set_version("2.1.0", {build = "%Y%m%d%H%M"}) + +set_warnings("all") + +-- set language: C99, c++ standard +set_languages("cxx17", "c99") + option("mysql") set_default(true) @@ -36,85 +43,19 @@ option("mysql") end option_end() -option("sqlite") - set_default(true) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable sqlite kdata engine.") -option_end() - -option("tdx") - set_default(true) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable tdx kdata engine.") -option_end() - -option("sql_trace") - set_default(false) - set_showmenu(true) - set_category("hikyuu") - set_description("打印执行的 SQL 语句") -option_end() +option("hdf5", {description = "Enable hdf5 kdata engine.", default = true}) +option("sqlite", {description = "Enable sqlite kdata engine.", default = true}) +option("tdx", {description = "Enable tdx kdata engine.", default = true}) +option("sql_trace", {description = "trace print sql", default = false}) -- 注意:stacktrace 在 windows 下会严重影响性能 -option("stacktrace") - set_default(false) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable check/assert with stack trace info.") -option_end() - -option("spend_time") - set_default(true) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable spend time.") -option_end() - -option("feedback") - set_default(true) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable send feedback.") -option_end() - -option("low_precision") - set_default(false) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable low precision.") -option_end() - -option("log_level") - set_default("info") - set_values("trace", "debug", "info", "warn", "error", "fatal", "off") - set_showmenu(true) - set_category("hikyuu") - set_description("set log level") -option_end() - -option("async_log") - set_default(false) - set_showmenu(true) - set_category("hikyuu") - set_description("Use async log.") -option_end() - -option("leak_check") - set_default(false) - set_showmenu(true) - set_category("hikyuu") - set_description("Enable leak check for test") -option_end() - --- project -set_project("hikyuu") - -add_rules("mode.debug", "mode.release") - --- version -set_version("2.1.0", {build = "%Y%m%d%H%M"}) +option("stacktrace", {description = "Enable check/assert with stack trace info.", default = false}) +option("spend_time", {description = "Enable spend time.", default = true}) +option("feedback", {description = "Enable send feedback.", default = true}) +option("low_precision", {description = "Enable low precision.", default = false}) +option("log_level", {description = "set log level.", default = 2, values = {1, 2, 3, 4, 5, 6}}) +option("async_log", {description = "Use async log.", default = false}) +option("leak_check", {description = "Enable leak check for test", default = false}) if get_config("leak_check") then -- 需要 export LD_PRELOAD=libasan.so @@ -124,32 +65,18 @@ if get_config("leak_check") then -- set_policy("build.sanitizer.thread", true) end -local level = get_config("log_level") -if is_mode("debug") then - level = "trace" -end -if level == "trace" then - set_configvar("HKU_LOG_ACTIVE_LEVEL", 0) -elseif level == "debug" then - set_configvar("HKU_LOG_ACTIVE_LEVEL", 1) -elseif level == "info" then - set_configvar("HKU_LOG_ACTIVE_LEVEL", 2) -elseif level == "warn" then - set_configvar("HKU_LOG_ACTIVE_LEVEL", 3) -elseif level == "error" then - set_configvar("HKU_LOG_ACTIVE_LEVEL", 4) -elseif level == "fatal" then - set_configvar("HKU_LOG_ACTIVE_LEVEL", 5) -else - set_configvar("HKU_LOG_ACTIVE_LEVEL", 6) +-- SPDLOG_ACTIVE_LEVEL 需要单独加 +local log_level = get_config("log_level") +if log_level == nil then + log_level = 2 end +add_defines("SPDLOG_ACTIVE_LEVEL=" .. log_level) if is_mode("debug") then set_configvar("HKU_DEBUG_MODE", 1) else set_configvar("HKU_DEBUG_MODE", 0) end -set_configvar("USE_SPDLOG_ASYNC_LOGGER", 0) -- 使用异步的spdlog set_configvar("CHECK_ACCESS_BOUND", 1) if is_plat("macosx") or get_config("leak_check") then set_configvar("SUPPORT_SERIALIZATION", 0) @@ -172,19 +99,18 @@ set_configvar("HKU_ENABLE_TDX_KDATA", get_config("tdx") and 1 or 0) set_configvar("HKU_USE_LOW_PRECISION", get_config("low_precision") and 1 or 0) -set_configvar("HKU_DEFAULT_LOG_NAME", "hikyuu") set_configvar("HKU_SUPPORT_DATETIME", 1) set_configvar("HKU_ENABLE_SQLCIPHER", 0) set_configvar("HKU_SQL_TRACE", get_config("sql_trace")) set_configvar("HKU_ENABLE_INI_PARSER", 1) -set_configvar("HKU_USE_SPDLOG_ASYNC_LOGGER", get_config("async_log") and 1 or 0) set_configvar("HKU_ENABLE_STACK_TRACE", get_config("stacktrace") and 1 or 0) set_configvar("HKU_CLOSE_SPEND_TIME", get_config("spend_time") and 0 or 1) - -set_warnings("all") - --- set language: C99, c++ standard -set_languages("cxx17", "c99") +set_configvar("HKU_USE_SPDLOG_ASYNC_LOGGER", get_config("async_log") and 1 or 0) +set_configvar("HKU_LOG_ACTIVE_LEVEL", get_config("log_level")) +set_configvar("HKU_ENABLE_MO", 0) +set_configvar("HKU_ENABLE_HTTP_CLIENT", 1) +set_configvar("HKU_ENABLE_HTTP_CLIENT_SSL", 0) +set_configvar("HKU_ENABLE_HTTP_CLIENT_ZIP", 0) if is_plat("windows") then if is_mode("release") then @@ -229,10 +155,10 @@ elseif is_plat("linux", "cross") then elseif is_plat("macosx") then if get_config("hdf5") then - add_requires("brew::hdf5") + add_requires("brew::hdf5", {alias = "hdf5"}) end if get_config("mysql") then - add_requires("brew::mysql-client") + add_requires("brew::mysql-client", {alias = "mysql"}) end end From 054b18c058e7f94289fbd8c00edc59729b00817f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 3 Aug 2024 11:32:44 +0800 Subject: [PATCH 406/601] update --- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 43 +-- hikyuu_cpp/hikyuu/utilities/LRUCache11.h | 230 ++++++++++++++ hikyuu_cpp/hikyuu/utilities/any_to_string.h | 142 +++++++++ .../hikyuu/utilities/http_client/HttpClient.h | 4 + .../hikyuu/utilities/ini_parser/IniParser.cpp | 2 +- .../hikyuu/utilities/ini_parser/IniParser.h | 2 +- hikyuu_cpp/hikyuu/utilities/md5.cpp | 291 ++++++++++++++++++ hikyuu_cpp/hikyuu/utilities/md5.h | 41 +++ .../{global => utilities}/node/NodeClient.h | 43 +-- .../{global => utilities}/node/NodeError.h | 2 +- .../{global => utilities}/node/NodeMessage.h | 5 +- hikyuu_cpp/hikyuu/utilities/node/NodeServer.h | 249 +++++++++++++++ 12 files changed, 999 insertions(+), 55 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/utilities/LRUCache11.h create mode 100644 hikyuu_cpp/hikyuu/utilities/any_to_string.h create mode 100644 hikyuu_cpp/hikyuu/utilities/md5.cpp create mode 100644 hikyuu_cpp/hikyuu/utilities/md5.h rename hikyuu_cpp/hikyuu/{global => utilities}/node/NodeClient.h (79%) rename hikyuu_cpp/hikyuu/{global => utilities}/node/NodeError.h (97%) rename hikyuu_cpp/hikyuu/{global => utilities}/node/NodeMessage.h (93%) create mode 100644 hikyuu_cpp/hikyuu/utilities/node/NodeServer.h diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index 5c58a09c..b3a0828c 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -14,12 +14,13 @@ #include "hikyuu/version.h" #include "hikyuu/DataType.h" #include "hikyuu/utilities/os.h" -#include "node/NodeClient.h" +#include "hikyuu/utilities/http_client/HttpClient.h" #include "sysinfo.h" using json = nlohmann::json; -#define FEEDBACK_SERVER_ADDR "tcp://1.tcp.cpolar.cn:20981" +// #define FEEDBACK_SERVER_ADDR "tcp://1.tcp.cpolar.cn:20981" +#define FEEDBACK_SERVER_ADDR "http://127.0.0.1:80" namespace hku { @@ -116,27 +117,15 @@ void sendFeedback() { saveUUID(uid); } - NodeClient client(FEEDBACK_SERVER_ADDR); - client.dial(); - - json req, res; - req["cmd"] = 2; - client.post(req, res); - std::string host = res["host"].get(); - uint64_t port = res["port"].get(); - g_latest_version = res.contains("last_version") ? res["last_version"].get() : 0; - client.close(); - - client.setServerAddr(fmt::format("tcp://{}:{}", host, port)); - client.dial(); - req["cmd"] = 1; + HttpClient client(FEEDBACK_SERVER_ADDR); + json req; req["uid"] = boost::uuids::to_string(uid); req["part"] = "hikyuu"; req["version"] = HKU_VERSION; req["build"] = fmt::format("{}", HKU_VERSION_BUILD); req["platform"] = getPlatform(); req["arch"] = getCpuArch(); - client.post(req, res); + client.post("/hku/visit", req); } catch (...) { // do nothing @@ -149,24 +138,14 @@ void sendFeedback() { void sendPythonVersionFeedBack(int major, int minor, int micro) { std::thread t([=]() { try { - NodeClient client(FEEDBACK_SERVER_ADDR); - client.dial(); - - json req, res; - req["cmd"] = 2; - client.post(req, res); - std::string host = res["host"].get(); - uint64_t port = res["port"].get(); - g_latest_version = res.contains("last_version") ? res["last_version"].get() : 0; - client.close(); - - client.setServerAddr(fmt::format("tcp://{}:{}", host, port)); - client.dial(); - req["cmd"] = 3; + HttpClient client(FEEDBACK_SERVER_ADDR); + json req; req["major"] = major; req["minor"] = minor; req["micro"] = micro; - client.post(req, res); + auto res = client.post("/hku/pyver", req); + json r = res.json(); + g_latest_version = r["data"]["last_version"].get(); } catch (...) { // do nothing } diff --git a/hikyuu_cpp/hikyuu/utilities/LRUCache11.h b/hikyuu_cpp/hikyuu/utilities/LRUCache11.h new file mode 100644 index 00000000..e7832dcb --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/LRUCache11.h @@ -0,0 +1,230 @@ +/* + * LRUCache11 - a templated C++11 based LRU cache class that allows + * specification of + * key, value and optionally the map container type (defaults to + * std::unordered_map) + * By using the std::unordered_map and a linked list of keys it allows O(1) insert, delete + * and + * refresh operations. + * + * This is a header-only library and all you need is the LRUCache11.hpp file + * + * Github: https://github.com/mohaps/lrucache11 + * + * This is a follow-up to the LRUCache project - + * https://github.com/mohaps/lrucache + * + * Copyright (c) 2012-22 SAURAV MOHAPATRA + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#pragma once +#include +#include +#include +#include +#include +#include +#include + +namespace lru11 { +/* + * a noop lockable concept that can be used in place of std::mutex + */ +class NullLock { +public: + void lock() {} + void unlock() {} + bool try_lock() { + return true; + } +}; + +/** + * error raised when a key not in cache is passed to get() + */ +class KeyNotFound : public std::invalid_argument { +public: + KeyNotFound() : std::invalid_argument("key_not_found") {} +}; + +template +struct KeyValuePair { +public: + K key; + V value; + + KeyValuePair(K k, V v) : key(std::move(k)), value(std::move(v)) {} +}; + +/** + * The LRU Cache class templated by + * Key - key type + * Value - value type + * MapType - an associative container like std::unordered_map + * LockType - a lock type derived from the Lock class (default: + *NullLock = no synchronization) + * + * The default NullLock based template is not thread-safe, however passing + *Lock=std::mutex will make it + * thread-safe + */ +template >::iterator>> +class Cache { +public: + typedef KeyValuePair node_type; + typedef std::list> list_type; + typedef Map map_type; + typedef Lock lock_type; + using Guard = std::lock_guard; + /** + * the maxSize is the soft limit of keys and (maxSize + elasticity) is the + * hard limit + * the cache is allowed to grow till (maxSize + elasticity) and is pruned back + * to maxSize keys + * set maxSize = 0 for an unbounded cache (but in that case, you're better off + * using a std::unordered_map + * directly anyway! :) + */ + explicit Cache(size_t maxSize = 64, size_t elasticity = 10) + : maxSize_(maxSize), elasticity_(elasticity) {} + virtual ~Cache() = default; + size_t size() const { + Guard g(lock_); + return cache_.size(); + } + bool empty() const { + Guard g(lock_); + return cache_.empty(); + } + void clear() { + Guard g(lock_); + cache_.clear(); + keys_.clear(); + } + void insert(const Key& k, const Value& v) { + Guard g(lock_); + const auto iter = cache_.find(k); + if (iter != cache_.end()) { + iter->second->value = v; + keys_.splice(keys_.begin(), keys_, iter->second); + return; + } + + keys_.emplace_front(k, v); + cache_[k] = keys_.begin(); + prune(); + } + void insert(const Key& k, Value&& v) { + Guard g(lock_); + const auto iter = cache_.find(k); + if (iter != cache_.end()) { + iter->second->value = std::move(v); + keys_.splice(keys_.begin(), keys_, iter->second); + return; + } + + keys_.emplace_front(k, std::move(v)); + cache_[k] = keys_.begin(); + prune(); + } + bool tryGet(const Key& kIn, Value& vOut) { + Guard g(lock_); + const auto iter = cache_.find(kIn); + if (iter == cache_.end()) { + return false; + } + keys_.splice(keys_.begin(), keys_, iter->second); + vOut = iter->second->value; + return true; + } + /** + * The const reference returned here is only + * guaranteed to be valid till the next insert/delete + * 修改为非常量引用,以便修改。但请注意这是危险操作! + */ + Value& get(const Key& k) { + Guard g(lock_); + const auto iter = cache_.find(k); + if (iter == cache_.end()) { + throw KeyNotFound(); + } + keys_.splice(keys_.begin(), keys_, iter->second); + return iter->second->value; + } + /** + * returns a copy of the stored object (if found) + */ + Value getCopy(const Key& k) { + return get(k); + } + bool remove(const Key& k) { + Guard g(lock_); + auto iter = cache_.find(k); + if (iter == cache_.end()) { + return false; + } + keys_.erase(iter->second); + cache_.erase(iter); + return true; + } + bool contains(const Key& k) const { + Guard g(lock_); + return cache_.find(k) != cache_.end(); + } + + size_t getMaxSize() const { + return maxSize_; + } + size_t getElasticity() const { + return elasticity_; + } + size_t getMaxAllowedSize() const { + return maxSize_ + elasticity_; + } + template + void cwalk(F& f) const { + Guard g(lock_); + std::for_each(keys_.begin(), keys_.end(), f); + } + +protected: + size_t prune() { + size_t maxAllowed = maxSize_ + elasticity_; + if (maxSize_ == 0 || cache_.size() < maxAllowed) { + return 0; + } + size_t count = 0; + while (cache_.size() > maxSize_) { + cache_.erase(keys_.back().key); + keys_.pop_back(); + ++count; + } + return count; + } + +private: + // Disallow copying. + Cache(const Cache&) = delete; + Cache& operator=(const Cache&) = delete; + + mutable Lock lock_; + Map cache_; + list_type keys_; + size_t maxSize_; + size_t elasticity_; +}; + +} // namespace lru11 \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/any_to_string.h b/hikyuu_cpp/hikyuu/utilities/any_to_string.h new file mode 100644 index 00000000..64341c5c --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/any_to_string.h @@ -0,0 +1,142 @@ +/* + * Copyright (c) 2023 hikyuu.org + * + * Created on: 2023-01-15 + * Author: fasiondog + */ + +#pragma once + +#include + +#include "osdef.h" +#include "cppdef.h" +#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17 +#include +#else +#include +#endif + +#if defined(HKU_SUPPORT_DATETIME) +#include "hikyuu/utilities/datetime/Datetime.h" +#endif + +namespace hku { + +#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17 +using any_t = std::any; +using std::any_cast; +#else +using any_t = boost::any; +using boost::any_cast; +#endif + +//------------------------------------------------------------------------------ +// +// 常见基本类型包装的 any_t 和 std::string 的互相转换函数 +// any_to_string 要用户自定义类型需包含从 std::string 进行构造的构造函数 +// string_to_any 需要用户自定义实现 std::to_string 特化方法 +// +//------------------------------------------------------------------------------ + +template +inline std::string any_to_string(const any_t& data) { + return any_cast(data).str(); +} + +template +inline any_t string_to_any(const std::string& data) { + return any_t(ValueT(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline std::string any_to_string(const any_t& data) { + return std::to_string(any_cast(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stoi(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stol(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stoll(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t((unsigned int)(std::stoul(data))); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stoul(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stoull(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stof(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stod(data)); +} + +template <> +inline any_t string_to_any(const std::string& data) { + return any_t(std::stold(data)); +} + +} // namespace hku diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h index 3ecba6fd..4c1d7f47 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h @@ -166,6 +166,10 @@ public: return post(path, HttpParams(), headers, body); } + HttpResponse post(const std::string& path, const json& body) { + return post(path, HttpHeaders(), body); + } + private: void _connect(); HttpResponse _readResChunk(const std::string& method, const std::string& uri, diff --git a/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp index ad32dd50..9f005a7f 100644 --- a/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp +++ b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.cpp @@ -1,4 +1,4 @@ -/* +/* * IniFile.cpp * * Created on: 2010-5-19 diff --git a/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h index 04644bff..9d1b88cb 100644 --- a/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h +++ b/hikyuu_cpp/hikyuu/utilities/ini_parser/IniParser.h @@ -1,4 +1,4 @@ -/* +/* * IniFile.h * * Created on: 2010-5-19 diff --git a/hikyuu_cpp/hikyuu/utilities/md5.cpp b/hikyuu_cpp/hikyuu/utilities/md5.cpp new file mode 100644 index 00000000..86e65a30 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/md5.cpp @@ -0,0 +1,291 @@ +/* + * Copyright (c) hikyuu + * Created on: 2021/12/06 + * Author: fasiondog + * + * 注: boost的md5计算在windows和linux平台上结果不一致,所以改从 dlib 移植 + */ + +#include "arithmetic.h" +#include "md5.h" +#include "Log.h" + +namespace hku { + +inline uint32_t F(uint32_t x, uint32_t y, uint32_t z) { + return ((x & y) | ((~x) & z)); +} + +// ------------------------------------------------------------------------------------ + +inline uint32_t G(uint32_t x, uint32_t y, uint32_t z) { + return ((x & z) | (y & (~z))); +} + +// ------------------------------------------------------------------------------------ + +inline uint32_t H(uint32_t x, uint32_t y, uint32_t z) { + return (x ^ y ^ z); +} + +// ------------------------------------------------------------------------------------ + +inline uint32_t I(uint32_t x, uint32_t y, uint32_t z) { + return (y ^ (x | (~z))); +} + +// ------------------------------------------------------------------------------------ + +inline uint32_t rotate_left(uint32_t x, uint32_t n) { + return ((x << n) | (x >> (32 - n))); +} + +// ------------------------------------------------------------------------------------ + +inline void FF(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, + uint32_t ac) { + a += F(b, c, d) + x + ac; + a = rotate_left(a, s); + a += b; +} + +// ------------------------------------------------------------------------------------ + +inline void GG(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, + uint32_t ac) { + a += G(b, c, d) + x + ac; + a = rotate_left(a, s); + a += b; +} + +// ------------------------------------------------------------------------------------ + +inline void HH(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, + uint32_t ac) { + a += H(b, c, d) + x + ac; + a = rotate_left(a, s); + a += b; +} + +// ------------------------------------------------------------------------------------ + +inline void II(uint32_t& a, uint32_t b, uint32_t c, uint32_t d, uint32_t x, uint32_t s, + uint32_t ac) { + a += I(b, c, d) + x + ac; + a = rotate_left(a, s); + a += b; +} + +void scramble_block(uint32_t& a, uint32_t& b, uint32_t& c, uint32_t& d, uint32_t* x) { + const uint32_t S11 = 7; + const uint32_t S12 = 12; + const uint32_t S13 = 17; + const uint32_t S14 = 22; + const uint32_t S21 = 5; + const uint32_t S22 = 9; + const uint32_t S23 = 14; + const uint32_t S24 = 20; + const uint32_t S31 = 4; + const uint32_t S32 = 11; + const uint32_t S33 = 16; + const uint32_t S34 = 23; + const uint32_t S41 = 6; + const uint32_t S42 = 10; + const uint32_t S43 = 15; + const uint32_t S44 = 21; + + // round 1 + FF(a, b, c, d, x[0], S11, 0xd76aa478); // 1 + FF(d, a, b, c, x[1], S12, 0xe8c7b756); // 2 + FF(c, d, a, b, x[2], S13, 0x242070db); // 3 + FF(b, c, d, a, x[3], S14, 0xc1bdceee); // 4 + FF(a, b, c, d, x[4], S11, 0xf57c0faf); // 5 + FF(d, a, b, c, x[5], S12, 0x4787c62a); // 6 + FF(c, d, a, b, x[6], S13, 0xa8304613); // 7 + FF(b, c, d, a, x[7], S14, 0xfd469501); // 8 + FF(a, b, c, d, x[8], S11, 0x698098d8); // 9 + FF(d, a, b, c, x[9], S12, 0x8b44f7af); // 10 + FF(c, d, a, b, x[10], S13, 0xffff5bb1); // 11 + FF(b, c, d, a, x[11], S14, 0x895cd7be); // 12 + FF(a, b, c, d, x[12], S11, 0x6b901122); // 13 + FF(d, a, b, c, x[13], S12, 0xfd987193); // 14 + FF(c, d, a, b, x[14], S13, 0xa679438e); // 15 + FF(b, c, d, a, x[15], S14, 0x49b40821); // 16 + + // Round 2 + GG(a, b, c, d, x[1], S21, 0xf61e2562); // 17 + GG(d, a, b, c, x[6], S22, 0xc040b340); // 18 + GG(c, d, a, b, x[11], S23, 0x265e5a51); // 19 + GG(b, c, d, a, x[0], S24, 0xe9b6c7aa); // 20 + GG(a, b, c, d, x[5], S21, 0xd62f105d); // 21 + GG(d, a, b, c, x[10], S22, 0x2441453); // 22 + GG(c, d, a, b, x[15], S23, 0xd8a1e681); // 23 + GG(b, c, d, a, x[4], S24, 0xe7d3fbc8); // 24 + GG(a, b, c, d, x[9], S21, 0x21e1cde6); // 25 + GG(d, a, b, c, x[14], S22, 0xc33707d6); // 26 + GG(c, d, a, b, x[3], S23, 0xf4d50d87); // 27 + GG(b, c, d, a, x[8], S24, 0x455a14ed); // 28 + GG(a, b, c, d, x[13], S21, 0xa9e3e905); // 29 + GG(d, a, b, c, x[2], S22, 0xfcefa3f8); // 30 + GG(c, d, a, b, x[7], S23, 0x676f02d9); // 31 + GG(b, c, d, a, x[12], S24, 0x8d2a4c8a); // 32 + + // Round 3 + HH(a, b, c, d, x[5], S31, 0xfffa3942); // 33 + HH(d, a, b, c, x[8], S32, 0x8771f681); // 34 + HH(c, d, a, b, x[11], S33, 0x6d9d6122); // 35 + HH(b, c, d, a, x[14], S34, 0xfde5380c); // 36 + HH(a, b, c, d, x[1], S31, 0xa4beea44); // 37 + HH(d, a, b, c, x[4], S32, 0x4bdecfa9); // 38 + HH(c, d, a, b, x[7], S33, 0xf6bb4b60); // 39 + HH(b, c, d, a, x[10], S34, 0xbebfbc70); // 40 + HH(a, b, c, d, x[13], S31, 0x289b7ec6); // 41 + HH(d, a, b, c, x[0], S32, 0xeaa127fa); // 42 + HH(c, d, a, b, x[3], S33, 0xd4ef3085); // 43 + HH(b, c, d, a, x[6], S34, 0x4881d05); // 44 + HH(a, b, c, d, x[9], S31, 0xd9d4d039); // 45 + HH(d, a, b, c, x[12], S32, 0xe6db99e5); // 46 + HH(c, d, a, b, x[15], S33, 0x1fa27cf8); // 47 + HH(b, c, d, a, x[2], S34, 0xc4ac5665); // 48 + + // Round 4 + II(a, b, c, d, x[0], S41, 0xf4292244); // 49 + II(d, a, b, c, x[7], S42, 0x432aff97); // 50 + II(c, d, a, b, x[14], S43, 0xab9423a7); // 51 + II(b, c, d, a, x[5], S44, 0xfc93a039); // 52 + II(a, b, c, d, x[12], S41, 0x655b59c3); // 53 + II(d, a, b, c, x[3], S42, 0x8f0ccc92); // 54 + II(c, d, a, b, x[10], S43, 0xffeff47d); // 55 + II(b, c, d, a, x[1], S44, 0x85845dd1); // 56 + II(a, b, c, d, x[8], S41, 0x6fa87e4f); // 57 + II(d, a, b, c, x[15], S42, 0xfe2ce6e0); // 58 + II(c, d, a, b, x[6], S43, 0xa3014314); // 59 + II(b, c, d, a, x[13], S44, 0x4e0811a1); // 60 + II(a, b, c, d, x[4], S41, 0xf7537e82); // 61 + II(d, a, b, c, x[11], S42, 0xbd3af235); // 62 + II(c, d, a, b, x[2], S43, 0x2ad7d2bb); // 63 + II(b, c, d, a, x[9], S44, 0xeb86d391); // 64 +} + +std::string HKU_UTILS_API md5(const unsigned char* input, size_t len) { + HKU_CHECK(input, "char *buf is null!"); + // make a temp version of input with enough space for padding and len appended + unsigned long extra_len = 64 - len % 64; + if (extra_len <= 8) + extra_len += 64; + unsigned char* temp = new unsigned char[extra_len + len]; + + // number of 16 word blocks + const unsigned long N = (extra_len + static_cast(len)) / 64; + + const unsigned char* input2 = input; + unsigned char* temp2 = temp; + unsigned char* end = temp + len; + + // copy input into temp + while (temp2 != end) { + *temp2 = *input2; + ++temp2; + ++input2; + } + + // pad temp + end += extra_len - 8; + *temp2 = static_cast(0x80); + ++temp2; + while (temp2 != end) { + *temp2 = 0; + ++temp2; + } + + // make len the number of bits in the original message + // but first multiply len by 8 and since len is only 32 bits the number might + // overflow so we will carry out the multiplication manually and end up with + // the result in the base 65536 number with three digits + // result = low + high*65536 + upper*65536*65536 + unsigned long low = len & 0xFFFF; + unsigned long high = static_cast(len) >> 16; + unsigned long upper; + unsigned long tmp; + tmp = low * 8; + low = tmp & 0xFFFF; + tmp = high * 8 + (tmp >> 16); + high = tmp & 0xFFFF; + upper = tmp >> 16; + + // append the length + *temp2 = static_cast(low & 0xFF); + ++temp2; + *temp2 = static_cast((low >> 8) & 0xFF); + ++temp2; + *temp2 = static_cast((high) & 0xFF); + ++temp2; + *temp2 = static_cast((high >> 8) & 0xFF); + ++temp2; + *temp2 = static_cast((upper) & 0xFF); + ; + ++temp2; + *temp2 = static_cast((upper >> 8) & 0xFF); + ; + ++temp2; + *temp2 = 0; + ++temp2; + *temp2 = 0; + + uint32_t a = 0x67452301; + uint32_t b = 0xefcdab89; + uint32_t c = 0x98badcfe; + uint32_t d = 0x10325476; + + // an array of 16 words + uint32_t x[16]; + + for (unsigned long i = 0; i < N; ++i) { + // copy a block of 16 words from m into x + for (unsigned long j = 0; j < 16; ++j) { + x[j] = ((static_cast(temp[4 * (j + 16 * i) + 3]) << 24) | + (static_cast(temp[4 * (j + 16 * i) + 2]) << 16) | + (static_cast(temp[4 * (j + 16 * i) + 1]) << 8) | + (static_cast(temp[4 * (j + 16 * i)]))); + } + + uint32_t aa = a; + uint32_t bb = b; + uint32_t cc = c; + uint32_t dd = d; + + scramble_block(a, b, c, d, x); + + a = a + aa; + b = b + bb; + c = c + cc; + d = d + dd; + } + + unsigned char output[16]; + // put a, b, c, and d into output + output[0] = static_cast((a) & 0xFF); + output[1] = static_cast((a >> 8) & 0xFF); + output[2] = static_cast((a >> 16) & 0xFF); + output[3] = static_cast((a >> 24) & 0xFF); + + output[4] = static_cast((b) & 0xFF); + output[5] = static_cast((b >> 8) & 0xFF); + output[6] = static_cast((b >> 16) & 0xFF); + output[7] = static_cast((b >> 24) & 0xFF); + + output[8] = static_cast((c) & 0xFF); + output[9] = static_cast((c >> 8) & 0xFF); + output[10] = static_cast((c >> 16) & 0xFF); + output[11] = static_cast((c >> 24) & 0xFF); + + output[12] = static_cast((d) & 0xFF); + output[13] = static_cast((d >> 8) & 0xFF); + output[14] = static_cast((d >> 16) & 0xFF); + output[15] = static_cast((d >> 24) & 0xFF); + + delete[] temp; + return byteToHexStr((const char*)output, 16); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/md5.h b/hikyuu_cpp/hikyuu/utilities/md5.h new file mode 100644 index 00000000..e2552e2e --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/md5.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019~2021, hikyuu + * + * Created on: 2021/12/06 + * Author: fasiondog + */ + +#pragma once +#ifndef HKU_UTILS_MD5_H +#define HKU_UTILS_MD5_H + +#include + +#ifndef HKU_UTILS_API +#define HKU_UTILS_API +#endif + +namespace hku { + +/** + * @brief 计算 md5 值 + * + * @param input 待计算数据起始指针 + * @param len 待计算数据字节长度 + * @return std::string + */ +std::string HKU_UTILS_API md5(const unsigned char* input, size_t len); + +/** + * @brief 计算字符串 md5 + * + * @param src 待计算的字符串 + * @return std::string + */ +inline std::string md5(const std::string& src) { + return md5((const unsigned char*)src.data(), src.size()); +} + +} // namespace hku + +#endif // #define HKU_UTILS_MD5_H \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/global/node/NodeClient.h b/hikyuu_cpp/hikyuu/utilities/node/NodeClient.h similarity index 79% rename from hikyuu_cpp/hikyuu/global/node/NodeClient.h rename to hikyuu_cpp/hikyuu/utilities/node/NodeClient.h index b7512784..f42d5b61 100644 --- a/hikyuu_cpp/hikyuu/global/node/NodeClient.h +++ b/hikyuu_cpp/hikyuu/utilities/node/NodeClient.h @@ -7,6 +7,11 @@ #pragma once +#include "hikyuu/utilities/config.h" +#if !HKU_ENABLE_NODE +#error "Don't enable node client, please config with --node=y" +#endif + #include #include #include @@ -59,13 +64,11 @@ public: return true; + } catch (const std::exception& e) { + HKU_ERROR_IF(m_show_log, "Failed dail server: {}! {}", m_server_addr, e.what()); } catch (...) { + HKU_ERROR_IF(m_show_log, "Failed dail server: {}! Unknown error!", m_server_addr); } - // } catch (const std::exception& e) { - // HKU_ERROR("Failed dail server: {}! {}", m_server_addr, e.what()); - // } catch (...) { - // HKU_ERROR("Failed dail server: {}! Unknown error!", m_server_addr); - // } m_connected = false; nng_close(m_socket); @@ -101,6 +104,10 @@ public: return _send(req) && _recv(res); } + void showLog(bool show) { + m_show_log = show; + } + private: bool _send(const json& req) const noexcept { bool success = false; @@ -118,13 +125,11 @@ private: NODE_NNG_CHECK(rv, "Failed nng_sendmsg!"); success = true; + } catch (const std::exception& e) { + HKU_ERROR_IF(m_show_log, "Failed send result! {}", e.what()); } catch (...) { + HKU_ERROR_IF(m_show_log, "Failed send result! Unknown error!"); } - // } catch (const std::exception& e) { - // HKU_ERROR("Failed send result! {}", e.what()); - // } catch (...) { - // HKU_ERROR("Failed send result! Unknown error!"); - // } if (!success) { nng_msg_free(msg); @@ -137,21 +142,22 @@ private: bool success = false; nng_msg* msg{nullptr}; int rv = nng_recvmsg(m_socket, &msg, 0); - // HKU_ERROR_IF_RETURN(rv != 0, success, "Failed nng_recvmsg! {}", nng_strerror(rv)); - HKU_IF_RETURN(rv != 0, success); + if (rv != 0) { + HKU_ERROR_IF(m_show_log, "Failed nng_recvmsg! {}", nng_strerror(rv)); + return success; + } + m_last_ack_time = Datetime::now(); try { res = decodeMsg(msg); success = true; + } catch (const std::exception& e) { + HKU_ERROR_IF(m_show_log, "Failed recv response! {}", e.what()); } catch (...) { + HKU_ERROR_IF(m_show_log, "Failed recv response! Unknown error!"); } - // } catch (const std::exception& e) { - // HKU_ERROR("Failed recv response! {}", e.what()); - // } catch (...) { - // HKU_ERROR("Failed recv response! Unknown error!"); - // } nng_msg_free(msg); return success; @@ -161,8 +167,9 @@ private: std::mutex m_mutex; std::string m_server_addr; // 服务端地址 nng_socket m_socket; - std::atomic_bool m_connected{false}; Datetime m_last_ack_time{Datetime::now()}; // 最后一次接收服务端响应的时间 + std::atomic_bool m_connected{false}; + std::atomic_bool m_show_log{true}; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/global/node/NodeError.h b/hikyuu_cpp/hikyuu/utilities/node/NodeError.h similarity index 97% rename from hikyuu_cpp/hikyuu/global/node/NodeError.h rename to hikyuu_cpp/hikyuu/utilities/node/NodeError.h index 49f56212..1e32608e 100644 --- a/hikyuu_cpp/hikyuu/global/node/NodeError.h +++ b/hikyuu_cpp/hikyuu/utilities/node/NodeError.h @@ -7,7 +7,7 @@ #pragma once -#include "hikyuu/utilities/exception.h" +#include namespace hku { diff --git a/hikyuu_cpp/hikyuu/global/node/NodeMessage.h b/hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h similarity index 93% rename from hikyuu_cpp/hikyuu/global/node/NodeMessage.h rename to hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h index b3260db8..68e727fc 100644 --- a/hikyuu_cpp/hikyuu/global/node/NodeMessage.h +++ b/hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h @@ -12,6 +12,7 @@ #include #include #include +#include #include "NodeError.h" using json = nlohmann::json; @@ -26,8 +27,8 @@ namespace hku { * req -> * {"cmd": int, ...} * - * req -> - * {"cmd": int, "ret": code, "msg": str} + * <- res + * {"ret": code, "msg": str} // msg 存在错误时返回错误信息 (可选) * * */ diff --git a/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h b/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h new file mode 100644 index 00000000..63d66454 --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h @@ -0,0 +1,249 @@ +/* + * Copyright (c) 2022 hikyuu.org + * + * Created on: 2022-04-15 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/utilities/config.h" +#if !HKU_ENABLE_NODE +#error "Don't enable node server, please config with --node=y" +#endif + +#include +#include +#include +#include + +#include +#if HKU_OS_WINDOWS +#ifndef NOMINMAX +#define NOMINMAX +#endif +#include +#include +#else +#include +#endif + +#include "hikyuu/utilities/Log.h" +#include "NodeMessage.h" + +namespace hku { + +class NodeServer { + CLASS_LOGGER_IMP(NodeServer) + +private: + static constexpr size_t PARALLEL = 128; + +public: + NodeServer() = default; + explicit NodeServer(const std::string& addr) : m_addr(addr) {} + virtual ~NodeServer() { + stop(); + } + + void setAddr(const std::string& addr) { + m_addr = addr; + } + + void regHandle(const std::string& cmd, const std::function& handle) { + m_handles[cmd] = handle; + } + + void regHandle(const std::string& cmd, std::function&& handle) { + m_handles[cmd] = std::move(handle); + } + + void start() { + CLS_CHECK(!m_addr.empty(), "You must set NodeServer's addr first!"); + + // 启动 node server + int rv = nng_rep0_open(&m_socket); + CLS_CHECK(0 == rv, "Failed open server socket! {}", nng_strerror(rv)); + rv = nng_listen(m_socket, m_addr.c_str(), &m_listener, 0); + CLS_CHECK(0 == rv, "Failed listen node server socket ({})! {}", m_addr, nng_strerror(rv)); + CLS_TRACE("channel lisenter server: {}", m_addr); + + m_works.resize(PARALLEL); + for (size_t i = 0, total = m_works.size(); i < total; i++) { + Work* w = &m_works[i]; + rv = nng_aio_alloc(&w->aio, _serverCallback, w); + CLS_CHECK(0 == rv, "Failed create work {}! {}", i, nng_strerror(rv)); + rv = nng_ctx_open(&w->ctx, m_socket); + CLS_CHECK(0 == rv, "Failed open ctx {}! {}", i, nng_strerror(rv)); + w->state = Work::INIT; + w->server = this; + } + + for (size_t i = 0, total = m_works.size(); i < total; i++) { + _serverCallback(&m_works[i]); + } + } + + void loop() { + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + } + + void stop() { + HKU_IF_RETURN(m_works.empty(), void()); + for (size_t i = 0, total = m_works.size(); i < total; i++) { + Work* w = &m_works[i]; + w->server = nullptr; + w->state = Work::FINISH; + if (w->aio) { + nng_aio_stop(w->aio); + nng_aio_free(w->aio); + nng_ctx_close(w->ctx); + w->aio = nullptr; + } + } + + // 关闭 socket 服务节点 + nng_listener_close(m_listener); + nng_close(m_socket); + m_works.clear(); + CLS_INFO("stopped node server."); + } + +private: + struct Work { + enum { INIT, RECV, SEND, FINISH } state = INIT; + nng_aio* aio{nullptr}; + nng_ctx ctx; + NodeServer* server{nullptr}; + }; + + static void _serverCallback(void* arg) { + Work* work = static_cast(arg); + int rv = 0; + switch (work->state) { + case Work::INIT: + work->state = Work::RECV; + nng_ctx_recv(work->ctx, work->aio); + break; + + case Work::RECV: + _processRequest(work); + break; + + case Work::SEND: + if ((rv = nng_aio_result(work->aio)) != 0) { + CLS_FATAL("Failed nng_ctx_send! {}", nng_strerror(rv)); + work->state = Work::FINISH; + return; + } + work->state = Work::RECV; + nng_ctx_recv(work->ctx, work->aio); + break; + + case Work::FINISH: + break; + + default: + CLS_FATAL("nng bad state!"); + break; + } + } + + static void _processRequest(Work* work) { + NodeServer* server = work->server; + CLS_IF_RETURN(!server || !work->aio, void()); + nng_msg* msg = nullptr; + json res; + + try { + int rv = nng_aio_result(work->aio); + HKU_CHECK(rv == 0, "Failed nng_aio_result!"); + + msg = nng_aio_get_msg(work->aio); + json req = decodeMsg(msg); + NODE_CHECK(req.contains("cmd"), NodeErrorCode::MISSING_CMD, "Missing command!"); + + // 兼容老版本数字cmd + std::string cmd = req["cmd"].is_number() ? fmt::format("{}", req["cmd"].get()) + : req["cmd"].get(); + auto iter = server->m_handles.find(cmd); + NODE_CHECK(iter != server->m_handles.end(), NodeErrorCode::INVALID_CMD, + "The server does not know how to process the message: {}", cmd); + + // tcp 连接尝试获取客户端地址和端口加入 req 中 + req["remote_host"] = ""; + req["remote_port"] = 0; + nng_pipe p = nng_msg_get_pipe(msg); + if (nng_pipe_id(p) > 0) { + uint16_t port = 0; + nng_sockaddr ra; + rv = nng_pipe_get_addr(p, NNG_OPT_REMADDR, &ra); + if (rv == 0) { + if (ra.s_family == NNG_AF_INET) { + char ipAddr[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, (void*)&ra.s_in.sa_addr, ipAddr, INET_ADDRSTRLEN); + port = ntohs(ra.s_in.sa_port); + req["remote_host"] = ipAddr; + req["remote_port"] = port; + } else if (ra.s_family == NNG_AF_INET6) { + char ipAddr[INET6_ADDRSTRLEN]; + inet_ntop(AF_INET6, (void*)&ra.s_in.sa_addr, ipAddr, INET6_ADDRSTRLEN); + port = ntohs(ra.s_in6.sa_port); + req["remote_host"] = ipAddr; + req["remote_port"] = port; + } + } + } + + res = iter->second(std::move(req)); + res["ret"] = NodeErrorCode::SUCCESS; + encodeMsg(msg, res); + + nng_aio_set_msg(work->aio, msg); + work->state = Work::SEND; + nng_ctx_send(work->ctx, work->aio); + + } catch (const NodeNngError& e) { + CLS_FATAL(e.what()); + work->state = Work::FINISH; + + } catch (const NodeError& e) { + CLS_ERROR(e.what()); + res["ret"] = e.errcode(); + res["msg"] = e.what(); + encodeMsg(msg, res); + nng_aio_set_msg(work->aio, msg); + work->state = Work::SEND; + nng_ctx_send(work->ctx, work->aio); + + } catch (const std::exception& e) { + CLS_ERROR(e.what()); + res["ret"] = NodeErrorCode::UNKNOWN_ERROR; + res["msg"] = e.what(); + encodeMsg(msg, res); + nng_aio_set_msg(work->aio, msg); + work->state = Work::SEND; + nng_ctx_send(work->ctx, work->aio); + + } catch (...) { + std::string errmsg = "Unknown error!"; + CLS_ERROR(errmsg); + res["ret"] = NodeErrorCode::UNKNOWN_ERROR; + res["msg"] = errmsg; + nng_aio_set_msg(work->aio, msg); + work->state = Work::SEND; + nng_ctx_send(work->ctx, work->aio); + } + } + +private: + std::string m_addr; + nng_socket m_socket; + nng_listener m_listener; + std::vector m_works; + std::unordered_map> m_handles; +}; + +} // namespace hku \ No newline at end of file From ae4fa07b47d3556e0859fc1983bd1b6e6284100e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 5 Aug 2024 18:38:03 +0800 Subject: [PATCH 407/601] update --- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index b3a0828c..48d421de 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -19,8 +19,7 @@ using json = nlohmann::json; -// #define FEEDBACK_SERVER_ADDR "tcp://1.tcp.cpolar.cn:20981" -#define FEEDBACK_SERVER_ADDR "http://127.0.0.1:80" +#define FEEDBACK_SERVER_ADDR "http://hikyuu.cpolar.cn" namespace hku { From d38a3740d0aecb66f86303a7c6987ea5adce3909 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 5 Aug 2024 18:45:11 +0800 Subject: [PATCH 408/601] update --- .../hikyuu/utilities/db_connect/DBUpgrade.cpp | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp index ac3c2797..d980e413 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp @@ -7,6 +7,15 @@ #include "DBUpgrade.h" +#include "hikyuu/utilities/config.h" +#if HKU_ENABLE_MYSQL +#include "mysql/MySQLConnect.h" +#endif + +#if HKU_ENABLE_SQLITE +#include "sqlite/SQLiteConnect.h" +#endif + namespace hku { /* @@ -19,9 +28,29 @@ void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name // 如果模块版本表不存在,则创建该表 if (!driver->tableExist("module_version")) { - driver->exec( - "CREATE TABLE `module_version` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,`module` TEXT, " - "`version` INTEGER NOT NULL);"); + bool need_create = true; +#if HKU_ENABLE_SQLITE + if (need_create && typeid(*driver) == typeid(SQLiteConnect)) { + driver->exec( + "CREATE TABLE `module_version` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,`module` " + "TEXT, " + "`version` INTEGER NOT NULL);"); + need_create = false; + } +#endif + +#if HKU_ENABLE_MYSQL + if (need_create && typeid(*driver) == typeid(MySQLConnect)) { + driver->exec( + R"(CREATE TABLE `module_version` ( + `id` int NOT NULL AUTO_INCREMENT, + `module` varchar(20) DEFAULT NULL, + `version` int NOT NULL, + PRIMARY KEY (`id`) +) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;)"); + need_create = false; + } +#endif } // 如果没有升级脚本,也没有创建脚本,则直接返回 From 57dab7f64a2d1c4daf68f3fd23420cf664065f65 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 6 Aug 2024 15:24:40 +0800 Subject: [PATCH 409/601] update --- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index 48d421de..24bbb4b0 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -124,7 +124,9 @@ void sendFeedback() { req["build"] = fmt::format("{}", HKU_VERSION_BUILD); req["platform"] = getPlatform(); req["arch"] = getCpuArch(); - client.post("/hku/visit", req); + auto res = client.post("/hku/visit", req); + json r = res.json(); + g_latest_version = r["data"]["last_version"].get(); } catch (...) { // do nothing @@ -142,9 +144,7 @@ void sendPythonVersionFeedBack(int major, int minor, int micro) { req["major"] = major; req["minor"] = minor; req["micro"] = micro; - auto res = client.post("/hku/pyver", req); - json r = res.json(); - g_latest_version = r["data"]["last_version"].get(); + client.post("/hku/pyver", req); } catch (...) { // do nothing } From 92505b21a3eac0e2dd25f21c2ed4627f8161a42b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 7 Aug 2024 00:51:47 +0800 Subject: [PATCH 410/601] =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/sysinfo.cpp | 4 +- .../utilities/http_client/HttpClient.cpp | 2 +- .../hikyuu/utilities/http_client/HttpClient.h | 36 ++++++++++++++++- .../hikyuu/utilities/http_client/nng_wrap.h | 39 +++++++++++++++++++ hikyuu_cpp/hikyuu/utilities/node/NodeServer.h | 7 +--- 5 files changed, 79 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/sysinfo.cpp b/hikyuu_cpp/hikyuu/global/sysinfo.cpp index 24bbb4b0..d30d45ef 100644 --- a/hikyuu_cpp/hikyuu/global/sysinfo.cpp +++ b/hikyuu_cpp/hikyuu/global/sysinfo.cpp @@ -116,7 +116,7 @@ void sendFeedback() { saveUUID(uid); } - HttpClient client(FEEDBACK_SERVER_ADDR); + HttpClient client(FEEDBACK_SERVER_ADDR, 2000); json req; req["uid"] = boost::uuids::to_string(uid); req["part"] = "hikyuu"; @@ -139,7 +139,7 @@ void sendFeedback() { void sendPythonVersionFeedBack(int major, int minor, int micro) { std::thread t([=]() { try { - HttpClient client(FEEDBACK_SERVER_ADDR); + HttpClient client(FEEDBACK_SERVER_ADDR, 2000); json req; req["major"] = major; req["minor"] = minor; diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp index 1f5fb194..117e94a9 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp @@ -126,7 +126,7 @@ HttpResponse HttpClient::request(const std::string& method, const std::string& p res = _readResChunk(method, uri, headers, body, body_len, content_type); if (res.getHeader("Connection") == "close") { - HKU_WARN("Connect closed"); + // HKU_TRACE("Connect closed"); reset(); } diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h index 4c1d7f47..237343b7 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h @@ -86,9 +86,43 @@ private: class HKU_UTILS_API HttpClient { public: HttpClient() = default; - explicit HttpClient(const std::string& url) : m_url(nng::url(url)) {}; + explicit HttpClient(const std::string& url, int32_t timeout_ms = NNG_DURATION_DEFAULT) + : m_url(nng::url(url)), m_timeout_ms(timeout_ms) {}; virtual ~HttpClient(); + HttpClient(const HttpClient&) = delete; + HttpClient& operator=(const HttpClient&) = delete; + + HttpClient(HttpClient&& rhs) + : m_default_headers(std::move(rhs.m_default_headers)), + m_url(std::move(rhs.m_url)), + m_client(std::move(rhs.m_client)), + m_aio(std::move(rhs.m_aio)), + m_conn(std::move(rhs.m_conn)), +#if HKU_ENABLE_HTTP_CLIENT_SSL + m_tls_cfg(std::move(rhs.m_tls_cfg)), + m_ca_file(std::move(rhs.m_ca_file)), +#endif + m_timeout_ms(rhs.m_timeout_ms) { + } + + HttpClient& operator=(HttpClient&& rhs) { + if (this != &rhs) { + m_default_headers = std::move(rhs.m_default_headers); + m_url = std::move(rhs.m_url); + m_client = (std::move(rhs.m_client)); + m_aio = std::move(rhs.m_aio); + m_conn = std::move(rhs.m_conn); +#if HKU_ENABLE_HTTP_CLIENT_SSL + m_tls_cfg = std::move(rhs.m_tls_cfg); + m_ca_file = std::move(rhs.m_ca_file); +#endif + m_timeout_ms = rhs.m_timeout_ms; + rhs.m_timeout_ms = NNG_DURATION_DEFAULT; + } + return *this; + } + bool valid() const noexcept { return m_url.valid(); } diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h b/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h index 722e8d14..7c704c6f 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h +++ b/hikyuu_cpp/hikyuu/utilities/http_client/nng_wrap.h @@ -118,6 +118,23 @@ public: } } + aio(aio&& rhs) : m_aio(rhs.m_aio), m_timeout(rhs.m_timeout) { + rhs.m_aio = nullptr; + rhs.m_timeout = NNG_DURATION_DEFAULT; + } + + aio& operator=(const aio&) = delete; + + aio& operator=(aio&& rhs) { + if (this != &rhs) { + m_aio = rhs.m_aio; + m_timeout = rhs.m_timeout; + rhs.m_aio = nullptr; + rhs.m_timeout = NNG_DURATION_DEFAULT; + } + return *this; + } + bool valid() const noexcept { return m_aio != nullptr; } @@ -257,6 +274,28 @@ public: } } + http_client(const http_client&) = delete; + http_client& operator=(const http_client&) = delete; + + http_client(http_client&& rhs) + : m_client(rhs.m_client), m_aio(rhs.m_aio), m_tls_cfg(rhs.m_tls_cfg) { + rhs.m_client = nullptr; + rhs.m_aio = nullptr; + rhs.m_tls_cfg = nullptr; + } + + http_client& operator=(http_client&& rhs) { + if (this != &rhs) { + m_client = rhs.m_client; + m_aio = rhs.m_aio; + m_tls_cfg = rhs.m_tls_cfg; + rhs.m_client = nullptr; + rhs.m_aio = nullptr; + rhs.m_tls_cfg = nullptr; + } + return *this; + } + void set_url(const nng::url& url) { #if !HKU_ENABLE_HTTP_CLIENT_SSL if (url.is_https()) { diff --git a/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h b/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h index 63d66454..9867d135 100644 --- a/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h +++ b/hikyuu_cpp/hikyuu/utilities/node/NodeServer.h @@ -36,9 +36,6 @@ namespace hku { class NodeServer { CLASS_LOGGER_IMP(NodeServer) -private: - static constexpr size_t PARALLEL = 128; - public: NodeServer() = default; explicit NodeServer(const std::string& addr) : m_addr(addr) {} @@ -58,7 +55,7 @@ public: m_handles[cmd] = std::move(handle); } - void start() { + void start(size_t max_parrel = 128) { CLS_CHECK(!m_addr.empty(), "You must set NodeServer's addr first!"); // 启动 node server @@ -68,7 +65,7 @@ public: CLS_CHECK(0 == rv, "Failed listen node server socket ({})! {}", m_addr, nng_strerror(rv)); CLS_TRACE("channel lisenter server: {}", m_addr); - m_works.resize(PARALLEL); + m_works.resize(max_parrel); for (size_t i = 0, total = m_works.size(); i < total; i++) { Work* w = &m_works[i]; rv = nng_aio_alloc(&w->aio, _serverCallback, w); From eb952fe3361f4429b2abc1b25e6aa0338db6c38e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 7 Aug 2024 14:49:58 +0800 Subject: [PATCH 411/601] =?UTF-8?q?=E5=B1=8F=E8=94=BD=20url.cpp=20windows?= =?UTF-8?q?=20=E5=AE=89=E5=85=A8=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/http_client/url.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp index 4052ff9d..f5821584 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp +++ b/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp @@ -5,6 +5,10 @@ * Author: fasiondog */ +#ifndef _CRT_SECURE_NO_WARNINGS +#define _CRT_SECURE_NO_WARNINGS +#endif + #include "url.h" namespace hku { From a0cdac5c1e3b7b98c3f21289b8cdc025dae5c026 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 7 Aug 2024 16:15:20 +0800 Subject: [PATCH 412/601] =?UTF-8?q?=E8=B0=83=E6=95=B4readme=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.rst | 113 ---------------------------------------------- hikyuu/README.rst | 79 -------------------------------- readme.md | 67 +++++++++++++++++++++++++++ sub_setup.py | 5 +- 4 files changed, 70 insertions(+), 194 deletions(-) delete mode 100644 README.rst delete mode 100644 hikyuu/README.rst create mode 100644 readme.md diff --git a/README.rst b/README.rst deleted file mode 100644 index 648684d9..00000000 --- a/README.rst +++ /dev/null @@ -1,113 +0,0 @@ -.. image:: https://fasiondog.cn/wp-content/uploads/2024/05/00000_title-1.png - :target: https://gitee.com/fasiondog/hikyuu - :align: left - :alt: Hikyuu - ------------ - -.. image:: https://static.pepy.tech/badge/hikyuu - :target: https://pepy.tech/project/hikyuu - -.. image:: https://static.pepy.tech/badge/hikyuu/month - :target: https://pepy.tech/project/hikyuu - -.. image:: https://static.pepy.tech/badge/hikyuu/week - :target: https://pepy.tech/project/hikyuu - -.. image:: https://github.com/fasiondog/hikyuu/workflows/win-build/badge.svg - :target: https://github.com/fasiondog/hikyuu/actions - -.. image:: https://github.com/fasiondog/hikyuu/workflows/ubuntu-build/badge.svg - :target: https://github.com/fasiondog/hikyuu/actions - -.. image:: https://github.com/fasiondog/hikyuu/workflows/ubuntu-aarch64-build/badge.svg - :target: https://github.com/fasiondog/hikyuu/actions - -.. image:: https://img.shields.io/github/license/mashape/apistatus.svg - :target: https://github.com/fasiondog/hikyuu/blob/master/LICENSE.txt - :alt: GitHub - -Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架,用于策略分析及回测(目前主要用于国内A股市场)。其核心思想基于当前成熟的系统化交易方法,将整个系统化交易抽象为由市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法七大组件,你可以分别构建这些组件的策略资产库,在实际研究中对它们自由组合来观察系统的有效性、稳定性以及单一种类策略的效果。 - -详细文档: ``_ - - -感谢网友提供的 Hikyuu Ubuntu虚拟机环境, 百度网盘下载(提取码: ht8j): ``_ - - -示例: - -:: - - #创建模拟交易账户进行回测,初始资金30万 - my_tm = crtTM(init_cash = 300000) - - #创建信号指示器(以5日EMA为快线,5日EMA自身的10日EMA作为慢线,快线向上穿越慢线时买入,反之卖出) - my_sg = SG_Flex(EMA(CLOSE(), n=5), slow_n=10) - - #固定每次买入1000股 - my_mm = MM_FixedCount(1000) - - #创建交易系统并运行 - sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm) - sys.run(sm['sz000001'], Query(-150)) - -.. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/10000-overview.png - :width: 600px - -完整示例参见:``_ - - -为什么选择 Hikyuu? --------------------- - -- **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下: - - .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/10002-function-arc.png - :width: 600px - -- **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 - - - AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒,详见: `性能实测 `_ - - - C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 - - - Python库(hikyuu),提供了对C++库的包装,同时集成了talib库(如TA_SMA,对应talib.SMA),可以与numpy、pandas数据结构进行互相转换,为使用其他成熟的python数据分析工具提供了便利。 - - - hikyuu.interactive 交互式探索工具,提供了K线、指标、系统信号等的基本绘图功能,用于对量化策略的探索和回测。 - -- **代码简洁,探索更便捷、自由** 同时支持面向对象和命令行编程范式。其中,命令行在进行策略探索时,代码简洁、探索更便捷、自由。 - -- **安全、自由、隐私,搭建自己的专属云量化平台** 结合 Python + Jupyter 的强大能力与云服务器,可以搭建自己专属的云量化平台。将Jupyter部署在云服务器上,随时随地的访问自己的云平台,即刻实现自己新的想法,如下图所示通过手机访问自己的云平台。结合Python强大成熟的数据分析、人工智能工具(如 numpy、scipy、pandas、TensorFlow)搭建更强大的人工智能平台。 - -- **数据存储方式可扩展** 目前支持本地HDF5格式、MySQL存储。默认使用HDF5,数据文件体积小、速度更快、备份更便利。截止至2017年4月21日,沪市日线数据文件149M、深市日线数据文件184M、5分钟线数据各不到2G。 - -.. image:: https://api.star-history.com/svg?repos=fasiondog/hikyuu&type=Date - :target: https://star-history.com/#fasiondog/hikyuu&Date - :alt: Star History Chart - - -想要更多了解Hikyuu?请使用以下方式联系: --------------------------------------------------- - -**加入知识星球** 更多示例与策略部件的及时分享(您的加入将视为对项目的捐赠) - - .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png - - -**项目交流和问题答复将转移至知识星球-【Hikyuu量化】。** - -- 关注公众号: - - .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/weixin_gongzhonghao.jpg - - -- 加入微信群(请注明“加入hikyuu”): - - .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/weixin_group.jpg - - -- QQ交流群(逐渐废弃):114910869, 或扫码加入: - - .. figure:: https://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png - diff --git a/hikyuu/README.rst b/hikyuu/README.rst deleted file mode 100644 index 883fcfb0..00000000 --- a/hikyuu/README.rst +++ /dev/null @@ -1,79 +0,0 @@ -.. image:: https://hikyuu.org/images/00000_title.png - :target: https://hikyuu.org - :align: center - :alt: Hikyuu - -.. image:: https://travis-ci.org/fasiondog/hikyuu.svg?branch=master - :target: https://travis-ci.org/fasiondog/hikyuu - -.. image:: https://img.shields.io/github/license/mashape/apistatus.svg - :target: https://github.com/fasiondog/hikyuu/blob/master/LICENSE.txt - :alt: GitHub - -.. image:: https://img.shields.io/badge/license-Anti%20996-blue.svg - :target: https://github.com/996icu/996.ICU/blob/master/LICENSE - :alt: GitHub - - ------------ - -Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架,用于策略分析及回测(仅受限于数据,如有数据也可用于期货等)。其核心思想基于当前成熟的系统化交易方法,将整个系统化交易抽象为由市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法七大组件,你可以分别构建这些组件的策略资产库,在实际研究中对它们自由组合来观察系统的有效性、稳定性以及单一种类策略的效果。 - - -祝贺 HIKYUU 入选 GITEE 最有价值开源项目 GVP ------------------------------------------------ - -.. image:: https://hikyuu.org/images/gitee_GVP.jpg - :target: https://gitee.com/gvp - :alt: Gitee - - -**给作者加点油,每天扫扫红包,或者请作者喝杯咖啡** - -.. image:: https://hikyuu.org/images/juanzeng.jpg - - -示例: - -:: - - #创建模拟交易账户进行回测,初始资金30万 - my_tm = crtTM(init_cash = 300000) - - #创建信号指示器(以5日EMA为快线,5日EMA自身的10日EMA作为慢线,快线向上穿越慢线时买入,反之卖出) - my_sg = SG_Flex(EMA(CLOSE(), n=5), slow_n=10) - - #固定每次买入1000股 - my_mm = MM_FixedCount(1000) - - #创建交易系统并运行 - sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm) - sys.run(sm['sz000001'], Query(-150)) - -.. figure:: https://hikyuu.org/images/10000-overview.png - :width: 600px - -完整示例参见:``_ - - -为什么选择 Hikyuu? --------------------- - -- **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下: - - .. figure:: https://hikyuu.org/images/10002-function-arc.png - :width: 600px - -- **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 - - - C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 - - - Python库(hikyuu),提供了对C++库的包装,同时集成了talib库(如TA_SMA,对应talib.SMA),可以与numpy、pandas数据结构进行互相转换,为使用其他成熟的python数据分析工具提供了便利。 - - - hikyuu.interactive 交互式探索工具,提供了K线、指标、系统信号等的基本绘图功能,用于对量化策略的探索和回测。 - -- **代码简洁,探索更便捷、自由** 同时支持面向对象和命令行编程范式。其中,命令行在进行策略探索时,代码简洁、探索更便捷、自由。 - -- **安全、自由、隐私,搭建自己的专属云量化平台** 结合 Python + Jupyter 的强大能力与云服务器,可以搭建自己专属的云量化平台。将Jupyter部署在云服务器上,随时随地的访问自己的云平台,即刻实现自己新的想法,如下图所示通过手机访问自己的云平台。结合Python强大成熟的数据分析、人工智能工具(如 numpy、scipy、pandas、TensorFlow)搭建更强大的人工智能平台。 - -- **数据存储方式可扩展** 目前支持本地HDF5格式、MySQL存储。默认使用HDF5,数据文件体积小、速度更快、备份更便利。截止至2017年4月21日,沪市日线数据文件149M、深市日线数据文件184M、5分钟线数据各不到2G。 \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 00000000..34fa7b86 --- /dev/null +++ b/readme.md @@ -0,0 +1,67 @@ +![title](https://fasiondog.cn/wp-content/uploads/2024/05/00000_title-1.png) + +--- + +![img](https://static.pepy.tech/badge/hikyuu) ![img](https://static.pepy.tech/badge/hikyuu/month) ![img](https://static.pepy.tech/badge/hikyuu/week) ![img](https://github.com/fasiondog/hikyuu/workflows/win-build/badge.svg) ![img](https://github.com/fasiondog/hikyuu/workflows/ubuntu-build/badge.svg) ![img](https://img.shields.io/github/license/mashape/apistatus.svg) + +Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架,用于策略分析及回测(目前主要用于国内A股市场)。其核心思想基于当前成熟的系统化交易方法,将整个系统化交易抽象为由市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法七大组件,你可以分别构建这些组件的策略资产库,在实际研究中对它们自由组合来观察系统的有效性、稳定性以及单一种类策略的效果。 + +详细文档:[https://hikyuu.org/](https://hikyuu.org/) + +感谢网友提供的 Hikyuu Ubuntu虚拟机环境, 百度网盘下载(提取码: ht8j): [https://pan.baidu.com/s/1CAiUWDdgV0c0VhPpe4AgVw?pwd=ht8j](https://pan.baidu.com/s/1CAiUWDdgV0c0VhPpe4AgVw?pwd=ht8j) + +示例: + +```python + #创建模拟交易账户进行回测,初始资金30万 + my_tm = crtTM(init_cash = 300000) + + #创建信号指示器(以5日EMA为快线,5日EMA自身的10日EMA作为慢线,快线向上穿越慢线时买入,反之卖出) + my_sg = SG_Flex(EMA(CLOSE(), n=5), slow_n=10) + + #固定每次买入1000股 + my_mm = MM_FixedCount(1000) + + #创建交易系统并运行 + sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm) + sys.run(sm['sz000001'], Query(-150)) +``` + +![img](https://fasiondog.cn/wp-content/uploads/2024/05/10000-overview.png) + +完整示例参见:[https://nbviewer.jupyter.org/github/fasiondog/hikyuu/blob/master/hikyuu/examples/notebook/000-Index.ipynb?flush_cache=True](https://nbviewer.jupyter.org/github/fasiondog/hikyuu/blob/master/hikyuu/examples/notebook/000-Index.ipynb?flush_cache=True) + +# 为什么选择 Hikyuu? + +* **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象,包含了九大策略组件:市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下:![img](https://fasiondog.cn/wp-content/uploads/2024/05/10002-function-arc.png) +* **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分:基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。 + * AMD 7950x 实测:A股全市场(1913万日K线)仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒,数据加载完毕后计算耗时 166 毫秒,详见: [性能实测](https://mp.weixin.qq.com/s?__biz=MzkwMzY1NzYxMA==&mid=2247483768&idx=1&sn=33e40aa9633857fa7b4c7ded51c95ae7&chksm=c093a09df7e4298b3f543121ba01334c0f8bf76e75c643afd6fc53aea1792ebb92de9a32c2be&mpshare=1&scene=23&srcid=05297ByHT6DEv6XAmyje1oOr&sharer_shareinfo=b38f5f91b4efd8fb60303a4ef4774748&sharer_shareinfo_first=b38f5f91b4efd8fb60303a4ef4774748#rd) + * C++核心库,提供了整体的策略框架,在保证性能的同时,已经考虑了对多线程和多核处理的支持,在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。 + * Python库(hikyuu),提供了对C++库的包装,同时集成了talib库(如TA_SMA,对应talib.SMA),可以与numpy、pandas数据结构进行互相转换,为使用其他成熟的python数据分析工具提供了便利。 + * hikyuu.interactive 交互式探索工具,提供了K线、指标、系统信号等的基本绘图功能,用于对量化策略的探索和回测。 +* **代码简洁,探索更便捷、自由** 同时支持面向对象和命令行编程范式。其中,命令行在进行策略探索时,代码简洁、探索更便捷、自由。 +* **安全、自由、隐私,搭建自己的专属云量化平台** 结合 Python + Jupyter 的强大能力与云服务器,可以搭建自己专属的云量化平台。将Jupyter部署在云服务器上,随时随地的访问自己的云平台,即刻实现自己新的想法,如下图所示通过手机访问自己的云平台。结合Python强大成熟的数据分析、人工智能工具(如 numpy、scipy、pandas、TensorFlow)搭建更强大的人工智能平台。 +* **数据存储方式可扩展** 目前支持本地HDF5格式、MySQL存储。默认使用HDF5,数据文件体积小、速度更快、备份更便利。截止至2017年4月21日,沪市日线数据文件149M、深市日线数据文件184M、5分钟线数据各不到2G。 + +![img](https://api.star-history.com/svg?repos=fasiondog/hikyuu&type=Date "Star History Chart") + +# 想要更多了解Hikyuu?请使用以下方式联系: + +## **加入知识星球** + +更多示例与策略部件的及时分享(您的加入将视为对项目的捐赠)。**项目交流和问题答复将转移至知识星球-【Hikyuu量化】。** + +![img](https://fasiondog.cn/wp-content/uploads/2024/05/zhishixingqiu.png) + + +## 关注公众号: + +![img](https://fasiondog.cn/wp-content/uploads/2024/05/weixin_gongzhonghao.jpg) + +## 加入微信群(请注明“加入hikyuu”): + +![img](https://fasiondog.cn/wp-content/uploads/2024/05/weixin_group.jpg) + +## QQ交流群(逐渐废弃):114910869, 或扫码加入 + +![img](https://fasiondog.cn/wp-content/uploads/2024/05/10003-qq.png) diff --git a/sub_setup.py b/sub_setup.py index 0ebd059b..9b6866f8 100644 --- a/sub_setup.py +++ b/sub_setup.py @@ -48,7 +48,7 @@ hku_platforms = "Independant" hku_url = "http://hikyuu.org/" hku_description = "Hikyuu Quant Framework for System Trading Analysis and backtester" -with open("./hikyuu/README.rst", encoding='utf-8') as f: +with open("./readme.md", encoding='utf-8') as f: hku_long_description = f.read() hku_data_files = [] @@ -99,7 +99,8 @@ setup( name=hku_name, version=hku_version, description=hku_description, - long_description_content_type="text/x-rst", + # long_description_content_type="text/x-rst", + long_description_content_type='text/markdown', long_description=hku_long_description, author=hku_author, author_email=hku_author_email, From 8d10c8be9538f3f056b6bbd02f029046c3cf4f7f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 7 Aug 2024 22:00:06 +0800 Subject: [PATCH 413/601] =?UTF-8?q?fixed=20MySQLStatement=20sub=5FgetColum?= =?UTF-8?q?nAsBlob,=20=E5=AF=BC=E8=87=B4=20FINANCE=20=E5=8D=A0=E7=94=A8?= =?UTF-8?q?=E5=B7=A8=E5=A4=A7=E5=86=85=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp index 400b6a50..8598f661 100755 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/mysql/MySQLStatement.cpp @@ -416,7 +416,11 @@ void MySQLStatement::sub_getColumnAsBlob(int idx, std::vector& item) { } try { - item = boost::any_cast>(m_result_buffer[idx]); + unsigned long len = m_result_length[idx]; + std::vector* p = boost::any_cast>(&m_result_buffer[idx]); + item.resize(len); + memcpy(item.data(), p->data(), len); + } catch (...) { HKU_THROW("Field type mismatch! idx: {}", idx); } From c4fdc61ff5e9a22727ce794113b95b3aaf6b2358 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 10:04:24 +0800 Subject: [PATCH 414/601] =?UTF-8?q?=E9=A2=84=E5=8A=A0=E8=BD=BD=E5=8E=86?= =?UTF-8?q?=E5=8F=B2=E8=B4=A2=E5=8A=A1=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 13 +++++++++++++ hikyuu_cpp/hikyuu/StockManager.h | 3 +++ 2 files changed, 16 insertions(+) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index dfca1922..0023b50f 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -142,6 +142,9 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa // 加载 K 线至缓存 loadAllKData(); + // 加载历史财务信息 + loadHistoryFinance(); + // add special Market, for temp csv file m_marketInfoDict["TMP"] = MarketInfo("TMP", "Temp Csv file", "temp load from csv file", "000001", Null(), @@ -310,6 +313,8 @@ void StockManager::reload() { } } } + + loadHistoryFinance(); } string StockManager::tmpdir() const { @@ -615,4 +620,12 @@ vector> StockManager::getHistoryFinanceAllFields() con return ret; } +void StockManager::loadHistoryFinance() { + auto* tg = getGlobalTaskGroup(); + std::lock_guard lock1(*m_stockDict_mutex); + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { + tg->submit([=]() { iter->second.getHistoryFinance(); }); + } +} + } // namespace hku diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 2cd32999..c6acd0e8 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -255,6 +255,9 @@ private: /** 加载历史财经字段索引 */ void loadHistoryFinanceField(); + /** 加载历史财务数据 */ + void loadHistoryFinance(); + private: StockManager(); From 814c3b368065831dbe43b0f3c907106e1defd60b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 10:36:05 +0800 Subject: [PATCH 415/601] try github windows action --- .github/workflows/windows.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 330cc1ae..1eea9de8 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -26,7 +26,8 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.2 + xmake-version: 2.9.3 + actions-cache-folder: '.xmake-cache' - name: configure shell: cmd From af2ea7095a26152383585558c915629ac7dc02e4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 11:08:16 +0800 Subject: [PATCH 416/601] update github actions --- .github/workflows/ubuntu.yml | 2 +- .github/workflows/ubuntu_aarch64.yml | 3 ++- .github/workflows/ubuntu_python.yml | 2 +- .github/workflows/windows.yml | 17 +++++++++-------- .github/workflows/windows_python.yml | 4 +++- 5 files changed, 16 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 592eda61..c0142c7f 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -34,7 +34,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.2 + xmake-version: 2.9.3 actions-cache-folder: '.xmake-cache' actions-cache-key: 'ubuntu' diff --git a/.github/workflows/ubuntu_aarch64.yml b/.github/workflows/ubuntu_aarch64.yml index 0184d18c..10c8f107 100644 --- a/.github/workflows/ubuntu_aarch64.yml +++ b/.github/workflows/ubuntu_aarch64.yml @@ -41,8 +41,9 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.2 + xmake-version: 2.9.3 actions-cache-folder: '.xmake-cache' + actions-cache-key: 'ubuntu-aarch64' - name: Installation musl run: | diff --git a/.github/workflows/ubuntu_python.yml b/.github/workflows/ubuntu_python.yml index 7e90c977..3a60f600 100644 --- a/.github/workflows/ubuntu_python.yml +++ b/.github/workflows/ubuntu_python.yml @@ -34,7 +34,7 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.2 + xmake-version: 2.9.3 actions-cache-folder: '.xmake-cache' actions-cache-key: 'ubuntu' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 1eea9de8..99c11d06 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -28,18 +28,19 @@ jobs: with: xmake-version: 2.9.3 actions-cache-folder: '.xmake-cache' + actions-cache-key: 'windows' - name: configure shell: cmd run: | xmake f -c --feedback=n -k shared -y -vD - - name: build - shell: cmd - run: | - xmake -b small-test + # - name: build + # shell: cmd + # run: | + # xmake -b small-test - - name: test - shell: cmd - run: | - xmake r small-test + # - name: test + # shell: cmd + # run: | + # xmake r small-test diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index cba1625f..114af9e4 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -26,7 +26,9 @@ jobs: - uses: xmake-io/github-action-setup-xmake@v1 with: - xmake-version: 2.9.2 + xmake-version: 2.9.3 + actions-cache-folder: '.xmake-cache' + actions-cache-key: 'windows' - name: configure shell: cmd From f27db59c6ce4f0db2502ce4e1e346744ea194b8e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 11:33:53 +0800 Subject: [PATCH 417/601] update --- .github/workflows/windows.yml | 8 ++++---- test_data/stock.db | Bin 2549760 -> 2575360 bytes 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 99c11d06..539ca464 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -35,10 +35,10 @@ jobs: run: | xmake f -c --feedback=n -k shared -y -vD - # - name: build - # shell: cmd - # run: | - # xmake -b small-test + - name: build + shell: cmd + run: | + xmake -bvD small-test # - name: test # shell: cmd diff --git a/test_data/stock.db b/test_data/stock.db index 547b57c6080935270035d43c07b0bc3f27229743..97e92f2bbc2f4b7f19cc1a042a55791148deea04 100644 GIT binary patch delta 24689 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 WjuazPYd;d7fqo69mH$Mw@&5sF1K4^1 delta 292 zcmYL>y-EX77)0;4ce61j(X26QOw{;`%7UVn78|s&vB(od7{Ldy6+)D$QmkAg3&B#s zCSbM`Bw7lBK8FurW8<}Qsxt>>TJf#4K6?^X{Gn2rC-tA`I@p!rV>8lT-r8Iy`PSKW zSND}pQo)0Et!$WHa6H(SLP_5!S2{}fojdum@M1-1{Mx1o!;AF!>4xp^Ca5<}d~lX@ z>N!Ws<~1yt{#IjfDAl5U^Q5bEtoNKZ=SY5}oA~>el5|gY#5{)|f5SEiAsj*zE|DTU zB2A1C8Df;k5;-DIj1l8Rf$)h5qDV{ Date: Thu, 8 Aug 2024 11:34:03 +0800 Subject: [PATCH 418/601] update --- .github/workflows/windows.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 539ca464..5ef98f70 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -40,7 +40,7 @@ jobs: run: | xmake -bvD small-test - # - name: test - # shell: cmd - # run: | - # xmake r small-test + - name: test + shell: cmd + run: | + xmake r small-test From 52ad45b5702cf69d2453ddb36fa4bf29ab98c820 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 12:13:47 +0800 Subject: [PATCH 419/601] update --- .github/workflows/windows.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 5ef98f70..539ca464 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -40,7 +40,7 @@ jobs: run: | xmake -bvD small-test - - name: test - shell: cmd - run: | - xmake r small-test + # - name: test + # shell: cmd + # run: | + # xmake r small-test From e9de800d4441e38f1eaae55391ed621a6989a5f2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 16:23:27 +0800 Subject: [PATCH 420/601] =?UTF-8?q?serial=E6=94=AF=E6=8C=81=E4=BD=9C?= =?UTF-8?q?=E4=B8=BA=E7=BC=96=E8=AF=91=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/windows.yml | 10 +++++----- hikyuu_cpp/hikyuu/xmake.lua | 17 ++++++++++++----- xmake.lua | 26 +++++++++++++------------- 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 539ca464..2bd81888 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -33,14 +33,14 @@ jobs: - name: configure shell: cmd run: | - xmake f -c --feedback=n -k shared -y -vD + xmake f -c --feedback=n -y -vD - name: build shell: cmd run: | xmake -bvD small-test - # - name: test - # shell: cmd - # run: | - # xmake r small-test + - name: test + shell: cmd + run: | + xmake r small-test diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 89aabdc8..ca87483d 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -1,11 +1,6 @@ target("hikyuu") set_kind("$(kind)") - -- if is_mode("debug", "coverage", "asan", "msan", "tsan", "lsan") then - -- set_kind("static") - -- else - -- set_kind("shared") - -- end add_packages("boost", "fmt", "spdlog", "flatbuffers", "nng", "nlohmann_json") if is_plat("windows", "linux", "cross") then @@ -104,6 +99,18 @@ target("hikyuu") add_files("./utilities/db_connect/mysql/**.cpp") end + on_config(function(target) + -- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 + -- windows下如果使用 serialize 且希望使用动态库,需要自行设置 runtimes 参数为 "MD" + -- "MT" 方式下,serialize 会挂 + if is_plat("windows") and has_config("serialize") and target:kind() == "shared" then + local runtime = get_config("runtimes") + if runtime == nil or runtime == "MT" then + raise('${red}Not support the option "--serialize=y" and "--runtimes=MT" for DLL on Windows at same time!') + end + end + end) + after_build(function(target) local destpath = get_config("buildir") .. "/" .. get_config("mode") .. "/" .. get_config("plat") .. "/" .. get_config("arch") print(destpath) diff --git a/xmake.lua b/xmake.lua index 34521c6d..a941437c 100644 --- a/xmake.lua +++ b/xmake.lua @@ -57,6 +57,18 @@ option("log_level", {description = "set log level.", default = 2, values = {1, 2 option("async_log", {description = "Use async log.", default = false}) option("leak_check", {description = "Enable leak check for test", default = false}) +-- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 +-- windows下如果使用 serialize 且希望使用动态库,需要设置 runtimes 参数为 "MD" +-- "MT" 方式下,serialize 会挂 +option("serialize", {description = "Enable support serialize object and pickle in python", default = true}) +if is_plat("windows") then + if is_mode("release") then + set_runtimes("MD") + else + set_runtimes("MDd") + end +end + if get_config("leak_check") then -- 需要 export LD_PRELOAD=libasan.so set_policy("build.sanitizer.address", true) @@ -78,11 +90,7 @@ else set_configvar("HKU_DEBUG_MODE", 0) end set_configvar("CHECK_ACCESS_BOUND", 1) -if is_plat("macosx") or get_config("leak_check") then - set_configvar("SUPPORT_SERIALIZATION", 0) -else - set_configvar("SUPPORT_SERIALIZATION", is_mode("release") and 1 or 0) -end +set_configvar("SUPPORT_SERIALIZATION", get_config("serialize") and 1 or 0) set_configvar("SUPPORT_TEXT_ARCHIVE", 0) set_configvar("SUPPORT_XML_ARCHIVE", 1) set_configvar("SUPPORT_BINARY_ARCHIVE", 1) @@ -112,14 +120,6 @@ set_configvar("HKU_ENABLE_HTTP_CLIENT", 1) set_configvar("HKU_ENABLE_HTTP_CLIENT_SSL", 0) set_configvar("HKU_ENABLE_HTTP_CLIENT_ZIP", 0) -if is_plat("windows") then - if is_mode("release") then - set_runtimes("MD") - else - set_runtimes("MDd") - end -end - local boost_version = "1.85.0" local hdf5_version = "1.12.2" local fmt_version = "11.0.1" From 9c833ad79243c52cc61438db623f36053f0579a7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 17:18:55 +0800 Subject: [PATCH 421/601] update --- hikyuu_cpp/hikyuu/xmake.lua | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index ca87483d..af06700f 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -99,17 +99,17 @@ target("hikyuu") add_files("./utilities/db_connect/mysql/**.cpp") end - on_config(function(target) - -- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 - -- windows下如果使用 serialize 且希望使用动态库,需要自行设置 runtimes 参数为 "MD" - -- "MT" 方式下,serialize 会挂 - if is_plat("windows") and has_config("serialize") and target:kind() == "shared" then - local runtime = get_config("runtimes") - if runtime == nil or runtime == "MT" then - raise('${red}Not support the option "--serialize=y" and "--runtimes=MT" for DLL on Windows at same time!') - end - end - end) + -- on_config(function(target) + -- -- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 + -- -- windows下如果使用 serialize 且希望使用动态库,需要自行设置 runtimes 参数为 "MD" + -- -- "MT" 方式下,serialize 会挂 + -- if is_plat("windows") and has_config("serialize") and target:kind() == "shared" then + -- local runtime = get_config("runtimes") + -- if runtime == nil or runtime == "MT" then + -- raise('${red}Not support the option "--serialize=y" and "--runtimes=MT" for DLL on Windows at same time!') + -- end + -- end + -- end) after_build(function(target) local destpath = get_config("buildir") .. "/" .. get_config("mode") .. "/" .. get_config("plat") .. "/" .. get_config("arch") From b94225e1f076b82d696f1652f122561394b4cec9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 17:24:07 +0800 Subject: [PATCH 422/601] update --- hikyuu_cpp/hikyuu/xmake.lua | 22 +++++++++++----------- xmake.lua | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index af06700f..ca87483d 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -99,17 +99,17 @@ target("hikyuu") add_files("./utilities/db_connect/mysql/**.cpp") end - -- on_config(function(target) - -- -- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 - -- -- windows下如果使用 serialize 且希望使用动态库,需要自行设置 runtimes 参数为 "MD" - -- -- "MT" 方式下,serialize 会挂 - -- if is_plat("windows") and has_config("serialize") and target:kind() == "shared" then - -- local runtime = get_config("runtimes") - -- if runtime == nil or runtime == "MT" then - -- raise('${red}Not support the option "--serialize=y" and "--runtimes=MT" for DLL on Windows at same time!') - -- end - -- end - -- end) + on_config(function(target) + -- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 + -- windows下如果使用 serialize 且希望使用动态库,需要自行设置 runtimes 参数为 "MD" + -- "MT" 方式下,serialize 会挂 + if is_plat("windows") and has_config("serialize") and target:kind() == "shared" then + local runtime = get_config("runtimes") + if runtime == nil or runtime == "MT" then + raise('${red}Not support the option "--serialize=y" and "--runtimes=MT" for DLL on Windows at same time!') + end + end + end) after_build(function(target) local destpath = get_config("buildir") .. "/" .. get_config("mode") .. "/" .. get_config("plat") .. "/" .. get_config("arch") diff --git a/xmake.lua b/xmake.lua index a941437c..cfb9f1ef 100644 --- a/xmake.lua +++ b/xmake.lua @@ -61,7 +61,7 @@ option("leak_check", {description = "Enable leak check for test", default = fals -- windows下如果使用 serialize 且希望使用动态库,需要设置 runtimes 参数为 "MD" -- "MT" 方式下,serialize 会挂 option("serialize", {description = "Enable support serialize object and pickle in python", default = true}) -if is_plat("windows") then +if is_plat("windows") and get_config("runtimes") == nil then if is_mode("release") then set_runtimes("MD") else From 4a44054d95c828eea394f28f2c198d762bb087e0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 17:53:59 +0800 Subject: [PATCH 423/601] update --- .github/workflows/windows_python.yml | 2 +- xmake.lua | 7 ------- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 114af9e4..85280650 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -33,7 +33,7 @@ jobs: - name: configure shell: cmd run: | - xmake f -c -k shared -y + xmake f -c -k shared -y --runtimes="MD" - name: build shell: cmd diff --git a/xmake.lua b/xmake.lua index cfb9f1ef..fb3197f1 100644 --- a/xmake.lua +++ b/xmake.lua @@ -61,13 +61,6 @@ option("leak_check", {description = "Enable leak check for test", default = fals -- windows下如果使用 serialize 且希望使用动态库,需要设置 runtimes 参数为 "MD" -- "MT" 方式下,serialize 会挂 option("serialize", {description = "Enable support serialize object and pickle in python", default = true}) -if is_plat("windows") and get_config("runtimes") == nil then - if is_mode("release") then - set_runtimes("MD") - else - set_runtimes("MDd") - end -end if get_config("leak_check") then -- 需要 export LD_PRELOAD=libasan.so From fb03e9f129ab044e255f3ecc81b928ce7f07de49 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 8 Aug 2024 18:35:56 +0800 Subject: [PATCH 424/601] update --- setup.py | 2 ++ xmake.lua | 14 ++++++-------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 52b2ef12..f333190c 100644 --- a/setup.py +++ b/setup.py @@ -126,6 +126,8 @@ def start_build(verbose=False, mode='release', feedback=True, worker_num=2, low_ cmd = "xmake f {} -c -y -m {} --feedback={} -k {} --low_precision={} --log_level={}".format( "-v -D" if verbose else "", mode, feedback, "shared" if mode == 'release' else "static", low_precision, 2 if mode == 'release' else 0) + if sys.platform == 'win32': + cmd = f'{cmd} --runtimes=MD' print(cmd) os.system(cmd) diff --git a/xmake.lua b/xmake.lua index fb3197f1..9918c9c3 100644 --- a/xmake.lua +++ b/xmake.lua @@ -115,7 +115,6 @@ set_configvar("HKU_ENABLE_HTTP_CLIENT_ZIP", 0) local boost_version = "1.85.0" local hdf5_version = "1.12.2" -local fmt_version = "11.0.1" local flatbuffers_version = "24.3.25" local nng_version = "1.8.0" local sqlite_version = "3.46.0+0" @@ -156,7 +155,6 @@ elseif is_plat("macosx") then end add_requires("boost " .. boost_version, { - system = false, debug = is_mode("debug"), configs = { shared = is_plat("windows"), @@ -170,13 +168,13 @@ add_requires("boost " .. boost_version, { }, }) -add_requires("fmt " .. fmt_version, {system = false}) -add_requires("spdlog", {system = false, configs = {header_only = true, fmt_external = true}}) -add_requireconfs("spdlog.fmt", {override = true, version = fmt_version, system = false}) -add_requires("sqlite3 " .. sqlite_version, {system = false, configs = {shared = true, cxflags = "-fPIC"}}) +add_requires("fmt", {configs = {header_only = true}}) +add_requires("spdlog", {configs = {header_only = true, fmt_external = true}}) +add_requireconfs("spdlog.fmt", {override = true, configs = {header_only = true}}) +add_requires("sqlite3 " .. sqlite_version, {configs = {shared = true, cxflags = "-fPIC"}}) add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs= {runtimes = get_config("runtimes")}}) -add_requires("nng " .. nng_version, {system = false, configs = {cxflags = "-fPIC"}}) -add_requires("nlohmann_json", {system = false}) +add_requires("nng " .. nng_version, {configs = {cxflags = "-fPIC"}}) +add_requires("nlohmann_json") add_defines("SPDLOG_DISABLE_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger From 97281338f28cc81685157edfc4e4f086d9ba2c1a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 02:28:14 +0800 Subject: [PATCH 425/601] =?UTF-8?q?=E5=A4=84=E7=90=86=20VS2022=2017.10=20?= =?UTF-8?q?=E7=BC=96=E8=AF=91=E5=90=8E=E6=97=A0=E6=B3=95=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=20python=20=E5=BA=93?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/windows_python.yml | 7 ++++++- hikyuu/extend.py | 4 +++- hikyuu_cpp/hikyuu/xmake.lua | 12 ------------ setup.py | 2 -- xmake.lua | 5 +++-- 5 files changed, 12 insertions(+), 18 deletions(-) diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 85280650..4e1b6eba 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -38,4 +38,9 @@ jobs: - name: build shell: cmd run: | - xmake -b core \ No newline at end of file + xmake -b core + + - name: test + shell: cmd + run: | + python hikyuu\test\test.py \ No newline at end of file diff --git a/hikyuu/extend.py b/hikyuu/extend.py index 8e78ad22..bea676d6 100644 --- a/hikyuu/extend.py +++ b/hikyuu/extend.py @@ -1,10 +1,12 @@ # # 对 C++ 引出类和函数进行扩展, pybind11 对小函数到导出效率不如 python 直接执行 # + +# 优先加载 hikyuu 库,防止 windows 公共依赖库不同导致DLL初始化失败 +from .core import * import numpy as np import pandas as pd from datetime import * -from .core import * # ------------------------------------------------------------------ # 增加Datetime、Stock的hash支持,以便可做为dict的key diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index ca87483d..0b87ce53 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -99,18 +99,6 @@ target("hikyuu") add_files("./utilities/db_connect/mysql/**.cpp") end - on_config(function(target) - -- 使用 serialize 时,建议使用静态库方式编译,boost serializasion 对 dll 的方式支持不好 - -- windows下如果使用 serialize 且希望使用动态库,需要自行设置 runtimes 参数为 "MD" - -- "MT" 方式下,serialize 会挂 - if is_plat("windows") and has_config("serialize") and target:kind() == "shared" then - local runtime = get_config("runtimes") - if runtime == nil or runtime == "MT" then - raise('${red}Not support the option "--serialize=y" and "--runtimes=MT" for DLL on Windows at same time!') - end - end - end) - after_build(function(target) local destpath = get_config("buildir") .. "/" .. get_config("mode") .. "/" .. get_config("plat") .. "/" .. get_config("arch") print(destpath) diff --git a/setup.py b/setup.py index f333190c..52b2ef12 100644 --- a/setup.py +++ b/setup.py @@ -126,8 +126,6 @@ def start_build(verbose=False, mode='release', feedback=True, worker_num=2, low_ cmd = "xmake f {} -c -y -m {} --feedback={} -k {} --low_precision={} --log_level={}".format( "-v -D" if verbose else "", mode, feedback, "shared" if mode == 'release' else "static", low_precision, 2 if mode == 'release' else 0) - if sys.platform == 'win32': - cmd = f'{cmd} --runtimes=MD' print(cmd) os.system(cmd) diff --git a/xmake.lua b/xmake.lua index 9bf4a0aa..ccbc587e 100644 --- a/xmake.lua +++ b/xmake.lua @@ -182,9 +182,10 @@ add_defines("SPDLOG_DISABLE_DEFAULT_LOGGER") -- 禁用 spdlog 默认ogger set_objectdir("$(buildir)/$(mode)/$(plat)/$(arch)/.objs") set_targetdir("$(buildir)/$(mode)/$(plat)/$(arch)/lib") --- modifed to use boost static library, except boost.python, serialization +-- on windows dll, must use runtimes MD if is_plat("windows") and get_config("kind") == "shared" then - add_defines("BOOST_ALL_DYN_LINK") + set_config("runtimes", "MD") + set_runtimes("MD") end -- is release now From d3b779e84990e1ec5c1f9528478a35b907b908fc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 02:34:52 +0800 Subject: [PATCH 426/601] update --- xmake.lua | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index ccbc587e..7a043462 100644 --- a/xmake.lua +++ b/xmake.lua @@ -114,7 +114,10 @@ set_configvar("HKU_ENABLE_HTTP_CLIENT_SSL", 0) set_configvar("HKU_ENABLE_HTTP_CLIENT_ZIP", 0) local boost_version = "1.85.0" -local hdf5_version = "1.13.3" +local hdf5_version = "1.12.2" +if is_plat("windows") then + hdf5_version = "1.13.3" +end local fmt_version = "10.2.1" local flatbuffers_version = "24.3.25" local nng_version = "1.8.0" From f11b212d80dadc5515a5ecf48b98d3df57d05988 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 09:06:21 +0800 Subject: [PATCH 427/601] update --- .github/workflows/windows_python.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 4e1b6eba..18934eba 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -42,5 +42,7 @@ jobs: - name: test shell: cmd + env: + PYTHONPATH: ${{ github.workspace }} run: | python hikyuu\test\test.py \ No newline at end of file From 16053cc04bc6ba144b0d2dbf7bbda756084706e8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 09:54:04 +0800 Subject: [PATCH 428/601] update --- .github/workflows/windows_python.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/windows_python.yml b/.github/workflows/windows_python.yml index 18934eba..9afd91b7 100644 --- a/.github/workflows/windows_python.yml +++ b/.github/workflows/windows_python.yml @@ -39,10 +39,3 @@ jobs: shell: cmd run: | xmake -b core - - - name: test - shell: cmd - env: - PYTHONPATH: ${{ github.workspace }} - run: | - python hikyuu\test\test.py \ No newline at end of file From a87419176f00f9d775d8abf74a9e5bf3c680a723 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 11:27:52 +0800 Subject: [PATCH 429/601] update --- hikyuu/gui/HikyuuTDX.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index 18770211..2bd5ad0e 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -8,6 +8,10 @@ import datetime import multiprocessing from configparser import ConfigParser from logging.handlers import QueueListener + +# 优先加载,处理 VS 17.10 升级后依赖 dll 不兼容问题 +import hikyuu + import PyQt5 from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox From 861c1d13acaba724bc8ee01f61d25b5cb8808478 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 12:19:10 +0800 Subject: [PATCH 430/601] =?UTF-8?q?=E7=9B=AE=E5=89=8D=2017.10=20=E9=9C=80?= =?UTF-8?q?=E8=A6=81=E5=9C=A8=E5=85=B6=E4=BB=96=E5=8C=85=E4=B9=8B=E5=89=8D?= =?UTF-8?q?=E5=BC=95=E5=85=A5=20hikyuu=EF=BC=8C=E6=9A=82=E6=97=B6=E6=89=94?= =?UTF-8?q?=E5=88=87=E6=8D=A2=2017.9=EF=BC=8C=E7=AD=89=E4=BB=A5=E5=90=8E?= =?UTF-8?q?=20python=20=E5=8C=85=E9=80=90=E6=AD=A5=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E5=90=8E=E5=86=8D=E5=88=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- xmake.lua | 3 --- 1 file changed, 3 deletions(-) diff --git a/xmake.lua b/xmake.lua index 7a043462..2247a33f 100644 --- a/xmake.lua +++ b/xmake.lua @@ -115,9 +115,6 @@ set_configvar("HKU_ENABLE_HTTP_CLIENT_ZIP", 0) local boost_version = "1.85.0" local hdf5_version = "1.12.2" -if is_plat("windows") then - hdf5_version = "1.13.3" -end local fmt_version = "10.2.1" local flatbuffers_version = "24.3.25" local nng_version = "1.8.0" From 1928f12130db8cbb0dee8294f097318386469b23 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 14:00:10 +0800 Subject: [PATCH 431/601] =?UTF-8?q?=E6=9A=82=E6=97=B6=E9=99=90=E5=AE=9A=20?= =?UTF-8?q?numpy=20=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- requirements.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements.txt b/requirements.txt index 777ed1bd..817b4f1d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ click +numpy<=1.26.4 matplotlib pandas>=0.17.1 pytdx From 72ce10386c6edeb69b4c7f5da50d89c74cc3f259 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 14:04:53 +0800 Subject: [PATCH 432/601] release 2.1.1 --- docs/source/release.rst | 7 +++++++ xmake.lua | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 1fc3e141..79e1b7c3 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,13 @@ 版本发布说明 ======================= +2.1.1 - 2024年8月9日 +------------------------- + +1. 预加载历史财务信息 +2. fixed windows下 MySQL blob 数据读取错误导致读取历史财务信息时消耗巨大内存 + + 2.1.0 - 2024年6月18日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 2247a33f..5a898226 100644 --- a/xmake.lua +++ b/xmake.lua @@ -6,7 +6,7 @@ set_project("hikyuu") add_rules("mode.debug", "mode.release") -- version -set_version("2.1.0", {build = "%Y%m%d%H%M"}) +set_version("2.1.1", {build = "%Y%m%d%H%M"}) set_warnings("all") From f2973714eb4305142cc979d21ead2286508e1fd1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 14:14:00 +0800 Subject: [PATCH 433/601] =?UTF-8?q?=E8=AF=BB=E5=8F=96=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=94=BE=E5=9C=A8=20output=20=E9=87=8D?= =?UTF-8?q?=E5=AE=9A=E5=90=91=E4=B9=8B=E5=89=8D=EF=BC=8C=E9=98=B2=E6=AD=A2?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6=E8=AF=BB=E5=8F=96=E5=A4=B1?= =?UTF-8?q?=E8=B4=A5=E6=B2=A1=E6=9C=89=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/HikyuuTDX.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index 2bd5ad0e..9aba0f83 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -244,6 +244,13 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): self.mp_log_q_lisener.start() def initUI(self): + # 读取配置文件放在 output 重定向之前,防止配置文件读取失败没有提示 + # 读取保存的配置文件信息,如果不存在,则使用默认配置 + this_dir = self.getUserConfigDir() + import_config = ConfigParser() + if os.path.exists(this_dir + '/importdata-gui.ini'): + import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8') + self._is_sched_import_running = False self._is_collect_running = False self._stream = None @@ -276,12 +283,6 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): self.time_start_dateEdit.setMinimumDate(today - datetime.timedelta(300)) self.collect_status_label.setText("已停止") - # 读取保存的配置文件信息,如果不存在,则使用默认配置 - this_dir = self.getUserConfigDir() - import_config = ConfigParser() - if os.path.exists(this_dir + '/importdata-gui.ini'): - import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8') - # 初始化导入行情数据类型配置 self.import_stock_checkBox.setChecked(import_config.getboolean('quotation', 'stock', fallback=True)) self.import_fund_checkBox.setChecked(import_config.getboolean('quotation', 'fund', fallback=True)) From 664f60e905eca5b7088ee7f85a5ea6df66b1944f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 9 Aug 2024 14:14:39 +0800 Subject: [PATCH 434/601] update release.rst --- docs/source/release.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/source/release.rst b/docs/source/release.rst index 79e1b7c3..fd70e334 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -6,6 +6,7 @@ 1. 预加载历史财务信息 2. fixed windows下 MySQL blob 数据读取错误导致读取历史财务信息时消耗巨大内存 +3. HikyuuTdx 读取配置文件放在 output 重定向之前,防止配置文件读取失败没有提示 2.1.0 - 2024年6月18日 From 87f9fd781a7a84ac34bfbe0a4930d1bc7633e774 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 10 Aug 2024 01:50:04 +0800 Subject: [PATCH 435/601] =?UTF-8?q?fixed=20HttpClient=20=E6=9C=AA=E6=AD=A3?= =?UTF-8?q?=E7=A1=AE=E5=A4=84=E7=90=86=E5=90=AB=E6=9C=89=E5=A4=9A=E5=80=BC?= =?UTF-8?q?=E7=9A=84=20Params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp index 117e94a9..ce2865a6 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp @@ -115,6 +115,7 @@ HttpResponse HttpClient::request(const std::string& method, const std::string& p for (auto iter = params.cbegin(); iter != params.cend(); ++iter) { if (first) { buf << "?"; + first = false; } else { buf << "&"; } From a9098fed9e06c4b0816cad1bf0adbeebafd72dc5 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 11 Aug 2024 14:55:32 +0800 Subject: [PATCH 436/601] add sqlite thread safe mode to 2 --- xmake.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xmake.lua b/xmake.lua index 5a898226..f4c69673 100644 --- a/xmake.lua +++ b/xmake.lua @@ -172,7 +172,7 @@ add_requires("boost " .. boost_version, { add_requires("fmt", {configs = {header_only = true}}) add_requires("spdlog", {configs = {header_only = true, fmt_external = true}}) add_requireconfs("spdlog.fmt", {override = true, configs = {header_only = true}}) -add_requires("sqlite3 " .. sqlite_version, {configs = {shared = true, cxflags = "-fPIC"}}) +add_requires("sqlite3 " .. sqlite_version, {configs = {shared = true, safe_mode="2", cxflags = "-fPIC"}}) add_requires("flatbuffers v" .. flatbuffers_version, {system = false, configs= {runtimes = get_config("runtimes")}}) add_requires("nng " .. nng_version, {configs = {cxflags = "-fPIC"}}) add_requires("nlohmann_json") From b5b78031c8db6a738c287d17028cb77137324503 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 11 Aug 2024 18:50:05 +0800 Subject: [PATCH 437/601] =?UTF-8?q?=E4=BC=98=E5=8C=96KData=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 118 +++++++---------------------- hikyuu_cpp/hikyuu/hikyuu.cpp | 20 ++--- 2 files changed, 38 insertions(+), 100 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 0023b50f..8bc2431d 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -134,7 +134,12 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa HKU_INFO("Loading KData..."); std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); - setKDataDriver(DataDriverFactory::getKDataDriverPool(m_kdataDriverParam)); + auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); + HKU_CHECK(driver, "driver is null!"); + if (m_kdataDriverParam != driver->getPrototype()->getParameter()) { + m_kdataDriverParam = driver->getPrototype()->getParameter(); + } + setKDataDriver(driver); // 加载 block,须在 stock 的 kdatadriver 被设置之后调用 m_blockDriver->load(); @@ -164,105 +169,38 @@ void StockManager::setKDataDriver(const KDataDriverConnectPoolPtr& driver) { } void StockManager::loadAllKData() { - auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); - HKU_ERROR_IF_RETURN(!driver, void(), "kdata driver is null!"); - - if (m_kdataDriverParam != driver->getPrototype()->getParameter()) { - m_kdataDriverParam = driver->getPrototype()->getParameter(); + const auto& ktypes = KQuery::getAllKType(); + vector low_ktypes; + low_ktypes.reserve(ktypes.size()); + for (const auto& ktype : ktypes) { + auto& back = low_ktypes.emplace_back(ktype); + to_lower(back); + HKU_INFO_IF(m_preloadParam.tryGet(back, false), "Preloading all {} kdata to buffer!", + back); } - bool preload_day = m_preloadParam.tryGet("day", false); - HKU_INFO_IF(preload_day, "Preloading all day kdata to buffer!"); - - bool preload_week = m_preloadParam.tryGet("week", false); - HKU_INFO_IF(preload_week, "Preloading all week kdata to buffer!"); - - bool preload_month = m_preloadParam.tryGet("month", false); - HKU_INFO_IF(preload_month, "Preloading all month kdata to buffer!"); - - bool preload_quarter = m_preloadParam.tryGet("quarter", false); - HKU_INFO_IF(preload_quarter, "Preloading all quarter kdata to buffer!"); - - bool preload_halfyear = m_preloadParam.tryGet("halfyear", false); - HKU_INFO_IF(preload_halfyear, "Preloading all halfyear kdata to buffer!"); - - bool preload_year = m_preloadParam.tryGet("year", false); - HKU_INFO_IF(preload_year, "Preloading all year kdata to buffer!"); - - bool preload_min = m_preloadParam.tryGet("min", false); - HKU_INFO_IF(preload_min, "Preloading all 1 min kdata to buffer!"); - - bool preload_min5 = m_preloadParam.tryGet("min5", false); - HKU_INFO_IF(preload_min5, "Preloading all 5 min kdata to buffer!"); - - bool preload_min15 = m_preloadParam.tryGet("min15", false); - HKU_INFO_IF(preload_min15, "Preloading all 15 min kdata to buffer!"); - - bool preload_min30 = m_preloadParam.tryGet("min30", false); - HKU_INFO_IF(preload_min30, "Preloading all 30 min kdata to buffer!"); - - bool preload_min60 = m_preloadParam.tryGet("min60", false); - HKU_INFO_IF(preload_min60, "Preloading all 60 min kdata to buffer!"); - - bool preload_hour2 = m_preloadParam.tryGet("hour2", false); - HKU_INFO_IF(preload_hour2, "Preloading all 120 min kdata to buffer!"); - + auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); if (!driver->getPrototype()->canParallelLoad()) { for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - if (preload_day) - iter->second.loadKDataToBuffer(KQuery::DAY); - if (preload_week) - iter->second.loadKDataToBuffer(KQuery::WEEK); - if (preload_month) - iter->second.loadKDataToBuffer(KQuery::MONTH); - if (preload_quarter) - iter->second.loadKDataToBuffer(KQuery::QUARTER); - if (preload_halfyear) - iter->second.loadKDataToBuffer(KQuery::HALFYEAR); - if (preload_year) - iter->second.loadKDataToBuffer(KQuery::YEAR); - if (preload_min) - iter->second.loadKDataToBuffer(KQuery::MIN); - if (preload_min5) - iter->second.loadKDataToBuffer(KQuery::MIN5); - if (preload_min15) - iter->second.loadKDataToBuffer(KQuery::MIN15); - if (preload_min30) - iter->second.loadKDataToBuffer(KQuery::MIN30); - if (preload_min60) - iter->second.loadKDataToBuffer(KQuery::MIN60); - if (preload_hour2) - iter->second.loadKDataToBuffer(KQuery::HOUR2); + for (size_t i = 0, len = ktypes.size(); i < len; i++) { + const auto& low_ktype = low_ktypes[i]; + if (m_preloadParam.tryGet(low_ktype, false)) { + iter->second.loadKDataToBuffer(ktypes[i]); + } + } } } else { // 异步并行加载 auto& tg = *getGlobalTaskGroup(); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - if (preload_day) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::DAY); }); - if (preload_week) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::WEEK); }); - if (preload_month) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MONTH); }); - if (preload_quarter) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::QUARTER); }); - if (preload_halfyear) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::HALFYEAR); }); - if (preload_year) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::YEAR); }); - if (preload_min) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN); }); - if (preload_min5) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN5); }); - if (preload_min15) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN15); }); - if (preload_min30) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN30); }); - if (preload_min60) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN60); }); - if (preload_hour2) - tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::HOUR2); }); + for (size_t i = 0, len = ktypes.size(); i < len; i++) { + const auto& low_ktype = low_ktypes[i]; + if (m_preloadParam.tryGet(low_ktype, false)) { + tg.submit( + [=, ktype = ktypes[i]]() mutable { iter->second.loadKDataToBuffer(ktype); }); + } + } } } diff --git a/hikyuu_cpp/hikyuu/hikyuu.cpp b/hikyuu_cpp/hikyuu/hikyuu.cpp index 7fb44db6..a37c9d5d 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.cpp +++ b/hikyuu_cpp/hikyuu/hikyuu.cpp @@ -73,17 +73,17 @@ void hikyuu_init(const string& config_file_name, bool ignore_preload, for (auto iter = option->begin(); iter != option->end(); ++iter) { try { - preloadParam.set(*iter, - ignore_preload ? false : config.getBool("preload", *iter)); - } catch (...) { - if (!ignore_preload) { - // 获取预加载的最大数量 - try { - preloadParam.set(*iter, config.getInt("preload", *iter)); - } catch (...) { - HKU_WARN("Invalid option: {}", *iter); - } + auto pos = (*iter).find("max"); + if (pos == std::string::npos) { + preloadParam.set(*iter, + ignore_preload ? false : config.getBool("preload", *iter)); + } else if (!ignore_preload) { + preloadParam.set(*iter, config.getInt("preload", *iter)); } + } catch (const std::exception& e) { + HKU_ERROR("proload param ({}) error! {}!", *iter, e.what()); + } catch (...) { + HKU_ERROR("proload param ({})! Unknown error!", *iter); } } From 8edb9d0a4bbcf0e3a07183a6cfb9b2d8abe8506a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 12 Aug 2024 17:07:20 +0800 Subject: [PATCH 438/601] update --- hikyuu/strategy/strategy.py | 14 +- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 367 +++++++------------- hikyuu_cpp/hikyuu/strategy/StrategyBase.h | 57 +-- hikyuu_pywrap/strategy/_StrategyBase.cpp | 44 +-- 4 files changed, 179 insertions(+), 303 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 1f8a7efe..77390324 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -2,8 +2,8 @@ # -*- coding: utf8 -*- # cp936 -from hikyuu import StrategyBase, Query -from hikyuu import StrategyContext, StockManager +from hikyuu import StrategyBase, Query, Datetime, TimeDelta +from hikyuu import StockManager class TestStrategy(StrategyBase): @@ -22,6 +22,14 @@ class TestStrategy(StrategyBase): print(s) +def my_func(): + sm = StockManager.instance() + print("{}".format(len(sm))) + for s in sm: + print(s) + + if __name__ == '__main__': s = TestStrategy() - s.run() \ No newline at end of file + s.run_daily_at(my_func, TimeDelta(0, 17, 6)) + s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index dc0076b4..1cc9f051 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -9,7 +9,9 @@ #include #include "hikyuu/utilities/os.h" #include "hikyuu/utilities/ini_parser/IniParser.h" -#include "../global/schedule/scheduler.h" +#include "hikyuu/global/schedule/scheduler.h" +#include "hikyuu/global/GlobalTaskGroup.h" +#include "hikyuu/hikyuu.h" #include "StrategyBase.h" namespace hku { @@ -33,32 +35,18 @@ StrategyBase::StrategyBase(const string& name) { #else m_config_file = format("{}/{}", home, ".hikyuu/hikyuu.ini"); #endif - _initDefaultParam(); } StrategyBase::StrategyBase(const string& name, const string& config_file) -: m_name(name), m_config_file(config_file) { - _initDefaultParam(); -} +: m_name(name), m_config_file(config_file) {} StrategyBase::~StrategyBase() { HKU_INFO("[Strategy {}] Quit Strategy!", m_name); } -void StrategyBase::_initDefaultParam() { - setParam("enable_market_event", false); - setParam("enable_30_seconds_clock", false); - setParam("enable_1min_clock", false); - setParam("enable_3min_clock", false); - setParam("enable_5min_clock", false); - setParam("enable_10min_clock", false); - setParam("enable_15min_clock", false); - setParam("enable_30min_clock", false); - setParam("enable_60min_clock", false); - setParam("enable_2hour_clock", false); -} +void StrategyBase::run() { + HKU_IF_RETURN(m_running, void()); -void StrategyBase::_run(bool forTest) { // 调用 strategy 自身的初始化方法 init(); @@ -74,67 +62,13 @@ void StrategyBase::_run(bool forTest) { HKU_INFO("[Strategy {}] strategy is running! You can press Ctrl-C to terminte ...", m_name); - // 加载上下文指定的证券数据 - IniParser config; - try { - config.read(m_config_file); + // 初始化 + hikyuu_init(m_config_file, false, m_context); - } catch (std::exception& e) { - HKU_FATAL("[Strategy {}] Failed read configure file (\"{}\")! {}", m_name, m_config_file, - e.what()); - HKU_INFO("[Strategy {}] Exit Strategy", m_name); - exit(1); - } catch (...) { - HKU_FATAL("[Strategy {}] Failed read configure file (\"{}\")! Unknow error!", m_name, - m_config_file); - HKU_INFO("[Strategy {}] Exit Strategy", m_name); - exit(1); - } - - Parameter baseParam, blockParam, kdataParam, preloadParam, hkuParam; - - hkuParam.set("tmpdir", config.get("hikyuu", "tmpdir", ".")); - hkuParam.set("datadir", config.get("hikyuu", "datadir", ".")); - hkuParam.set("quotation_server", - config.get("hikyuu", "quotation_server", "ipc:///tmp/hikyuu_real.ipc")); - - if (!config.hasSection("baseinfo")) { - HKU_FATAL("Missing configure of baseinfo!"); - exit(1); - } - - IniParser::StringListPtr option = config.getOptionList("baseinfo"); - for (auto iter = option->begin(); iter != option->end(); ++iter) { - string value = config.get("baseinfo", *iter); - baseParam.set(*iter, value); - } - - IniParser::StringListPtr block_config = config.getOptionList("block"); - for (auto iter = block_config->begin(); iter != block_config->end(); ++iter) { - string value = config.get("block", *iter); - blockParam.set(*iter, value); - } - - option = config.getOptionList("kdata"); - for (auto iter = option->begin(); iter != option->end(); ++iter) { - kdataParam.set(*iter, config.get("kdata", *iter)); - } - - // 设置预加载参数,只加载指定的 ktype 至内存 - auto ktype_list = m_context.getKTypeList(); - if (ktype_list.empty()) { - // 如果为空,则默认加载日线数据 - ktype_list.push_back(KQuery::DAY); - } - - // 不使用默认的预加载模式 - for (auto ktype : ktype_list) { - to_lower(ktype); - preloadParam.set(ktype, false); - } - - sm.init(baseParam, blockParam, kdataParam, preloadParam, hkuParam, m_context); + // 先将行情接收代理停止,以便后面加入处理函数 + stopSpotAgent(); + // 根据上下文获取支持的 Stock 列表 const auto& stk_code_list = getStockCodeList(); m_stock_list.reserve(stk_code_list.size()); for (const auto& code : stk_code_list) { @@ -147,163 +81,133 @@ void StrategyBase::_run(bool forTest) { } HKU_WARN_IF(m_stock_list.empty(), "[Strategy {}] stock list is empty!", m_name); - // 借助 Stock.setKRecordList 方法进行预加载(同步方式,不需要异步加载) - // 只从 context 指定起始日期开始加载 - if (!forTest) { - size_t ktype_count = ktype_list.size(); - vector k_buffer(ktype_count); - for (auto& stk : m_stock_list) { - // 保留原始 KDataDriver,因为使用 stock.setKRecordList 将会把 stock 的 KDataDriver - // 设置为 DoNothing - auto old_driver = stk.getKDataDirver(); + auto& agent = *getGlobalSpotAgent(); + agent.addProcess( + [this](const SpotRecord& spot) { event([=]() { this->receivedSpot(spot); }); }); + agent.addPostProcess( + [this](Datetime revTime) { event([=]() { this->onReceivedSpot(revTime); }); }); + startSpotAgent(false); - for (size_t i = 0; i < ktype_count; i++) { - k_buffer[i] = std::move(stk.getKRecordList( - KQueryByDate(m_context.startDatetime(), Null(), ktype_list[i]))); - } - for (size_t i = 0; i < ktype_count; i++) { - stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]); - } - - // 恢复 KDataDriver - stk.setKDataDriver(old_driver); - } - } - - // 计算每个类型当前最后的日期 - for (const auto& ktype : ktype_list) { - Datetime last_date = Datetime::min(); - for (auto& stk : m_stock_list) { - size_t count = stk.getCount(ktype); - if (count > 1) { - auto kr = stk.getKRecord(count - 1, ktype); - if (kr.datetime > last_date) { - last_date = kr.datetime; - } - } - } - m_ref_last_time[ktype] = last_date == Datetime::min() ? Null() : last_date; - } - - if (!forTest) { - // 启动行情接收代理 - auto& agent = *getGlobalSpotAgent(); - agent.addProcess([this](const SpotRecord& spot) { this->receivedSpot(spot); }); - agent.addPostProcess([this](Datetime revTime) { this->finishReceivedSpot(revTime); }); - startSpotAgent(false); - - _addTimer(); - - HKU_INFO("start even loop ..."); - _startEventLoop(); - } + m_running = true; } -void StrategyBase::_loadKData(const Datetime& start, const Datetime& end) { - // 借助 Stock.setKRecordList 方法进行预加载(同步方式,不需要异步加载) - const auto& ktype_list = getKTypeList(); - size_t ktype_count = ktype_list.size(); - vector k_buffer(ktype_count); - for (auto& stk : m_stock_list) { - // 保留原始 KDataDriver,因为使用 stock.setKRecordList 将会把 stock 的 KDataDriver 设置为 - // DoNothing - auto old_driver = stk.getKDataDirver(); - - for (size_t i = 0; i < ktype_count; i++) { - k_buffer[i] = std::move(stk.getKRecordList(KQueryByDate(start, end, ktype_list[i]))); - } - for (size_t i = 0; i < ktype_count; i++) { - stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]); - } - - // 恢复 KDataDriver - stk.setKDataDriver(old_driver); - } +void StrategyBase::start() { + HKU_INFO("start even loop ..."); + _startEventLoop(); } void StrategyBase::receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { - m_spot_map[stk] = spot; + event([=]() { this->onChange(stk, spot); }); } } -void StrategyBase::finishReceivedSpot(Datetime revTime) { - HKU_IF_RETURN(m_stock_list.empty(), void()); - event([this]() { this->onTick(); }); +void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta, + const std::string& market) { + if (!m_running) { + run(); + } - Stock& ref_stk = m_stock_list[0]; - const auto& ktype_list = getKTypeList(); - for (const auto& ktype : ktype_list) { - size_t count = ref_stk.getCount(ktype); - if (count > 0) { - KRecord k = ref_stk.getKRecord(count - 1, ktype); - if (k.datetime != m_ref_last_time[ktype]) { - m_ref_last_time[ktype] = k.datetime; - event([this, ktype]() { this->onBar(ktype); }); - } + auto* scheduler = getScheduler(); + auto new_func = [=]() { + const auto& sm = StockManager::instance(); + auto today = Datetime::today(); + int day = today.dayOfWeek(); + if (day == 0 || day == 6 || sm.isHoliday(today)) { + return; } - } -} -void StrategyBase::_addTimer() { - std::unordered_set market_set; - for (auto& stk : m_stock_list) { - market_set.insert(stk.market()); - } + auto market_info = sm.getMarketInfo(market); + Datetime open1 = today + market_info.openTime1(); + Datetime close1 = today + market_info.closeTime1(); + Datetime open2 = today + market_info.openTime2(); + Datetime close2 = today + market_info.closeTime2(); + Datetime now = Datetime::now(); + if ((now > open1 && now < close1) || (now > open2 && now < close2)) { + event(func); + } + }; const auto& sm = StockManager::instance(); - TimeDelta openTime(0, 23, 59, 59, 999, 999), closeTime(0); - for (const auto& market : market_set) { - auto market_info = sm.getMarketInfo(market); - if (market_info.openTime1() < market_info.closeTime1()) { - if (market_info.openTime1() < openTime) { - openTime = market_info.openTime1(); - } - if (market_info.closeTime1() > closeTime) { - closeTime = market_info.closeTime1(); - } + auto market_info = sm.getMarketInfo(market); + auto today = Datetime::today(); + auto now = Datetime::now(); + TimeDelta now_time = now - today; + if (now_time >= market_info.closeTime2()) { + scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { + new_func(); + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); + + } else if (now_time >= market_info.openTime2()) { + int64_t ticks = now_time.ticks() - market_info.openTime2().ticks(); + int64_t delta_ticks = delta.ticks(); + if (ticks % delta_ticks == 0) { + scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + } else { + auto delay = TimeDelta::fromTicks(delta_ticks - (ticks / delta_ticks) * delta_ticks); + scheduler->addFuncAtTime(now + delay, [=]() { + new_func(); + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); } - if (market_info.openTime2() < market_info.closeTime2()) { - if (market_info.openTime2() < openTime) { - openTime = market_info.openTime2(); - } - if (market_info.closeTime2() > closeTime) { - closeTime = market_info.closeTime2(); - } + + } else if (now_time == market_info.closeTime1()) { + scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); + + } else if (now_time < market_info.closeTime1() && now_time >= market_info.openTime1()) { + int64_t ticks = now_time.ticks() - market_info.openTime1().ticks(); + int64_t delta_ticks = delta.ticks(); + if (ticks % delta_ticks == 0) { + scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + } else { + auto delay = TimeDelta::fromTicks(delta_ticks - (ticks / delta_ticks) * delta_ticks); + scheduler->addFuncAtTime(now + delay, [=]() { + new_func(); + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); } + + } else if (now_time < market_info.openTime1()) { + scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() { + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); + + } else { + HKU_ERROR("Unknown process!"); } - - HKU_ERROR_IF_RETURN(openTime >= closeTime, void(), "Invalid market openTime: {}, closeTime: {}", - openTime, closeTime); - - auto* scheduler = getScheduler(); - if (getParam("enable_market_event")) { - scheduler->addFuncAtTimeEveryDay( - openTime, [this]() { this->event([this]() { this->onMarketOpen(); }); }); - scheduler->addFuncAtTimeEveryDay( - closeTime, [this]() { this->event([this]() { this->onMarketClose(); }); }); - } - - _addClockEvent("enable_30_seconds_clock", Seconds(30), openTime, closeTime); - _addClockEvent("enable_1min_clock", Minutes(1), openTime, closeTime); - _addClockEvent("enable_3min_clock", Minutes(3), openTime, closeTime); - _addClockEvent("enable_5min_clock", Minutes(5), openTime, closeTime); - _addClockEvent("enable_10min_clock", Minutes(10), openTime, closeTime); - _addClockEvent("enable_15min_clock", Minutes(15), openTime, closeTime); - _addClockEvent("enable_30min_clock", Minutes(30), openTime, closeTime); - _addClockEvent("enable_60min_clock", Minutes(60), openTime, closeTime); - _addClockEvent("enable_2hour_clock", Hours(2), openTime, closeTime); } -void StrategyBase::_addClockEvent(const string& enable, TimeDelta delta, TimeDelta openTime, - TimeDelta closeTime) { - auto* scheduler = getScheduler(); - if (getParam(enable)) { - int repeat = static_cast((closeTime - openTime) / delta); - scheduler->addFunc(Datetime::min(), Datetime::max(), openTime, closeTime, repeat, delta, - [this, delta]() { this->onClock(delta); }); +void StrategyBase::runDailyAt(std::function&& func, const TimeDelta& delta, + bool ignoreHoliday) { + if (!m_running) { + run(); } + + auto new_func = [=]() { + if (!ignoreHoliday) { + event(func); + return; + } + + const auto& sm = StockManager::instance(); + auto today = Datetime::today(); + int day = today.dayOfWeek(); + if (day != 0 && day != 6 && !sm.isHoliday(today)) { + event(func); + } + }; + + auto* scheduler = getScheduler(); + scheduler->addFuncAtTimeEveryDay(delta, new_func); } /* @@ -321,43 +225,4 @@ void StrategyBase::_startEventLoop() { } } -void StrategyBase::backTest(const Datetime& start, const Datetime& end) { - HKU_CHECK(!m_stock_list.empty(), "The context stock list is empty!"); - HKU_CHECK(!start.isNull(), "start date can't be null!"); - HKU_CHECK(start >= m_context.startDatetime(), - "The backtest start date must be greater than the context start date!"); - - const auto& ktypes = getKTypeList(); - HKU_CHECK(!ktypes.empty(), "The ktype list is empty!"); - - _run(true); - - // 加载回测日期之前的相关K线数据 - _loadKData(m_context.startDatetime(), start); - - size_t ktype_count = ktypes.size(); - vector ktype_mintues(ktypes.size()); - for (size_t i = 0; i < ktype_count; i++) { - ktype_mintues[i] = KQuery::getKTypeInMin(ktypes[i]); - } - - const auto& level_ktype = ktypes[0]; - Stock level_stk = getStock("sh000001"); - KQuery query = KQueryByDate(start, end, level_ktype); - auto dates = level_stk.getDatetimeList(query); - - vector> krecords(m_stock_list.size()); - for (size_t i = 0, len = m_stock_list.size(); i < len; i++) { - auto& stock = m_stock_list[i]; - HKU_CHECK(!stock.isNull(), "The pos: {} stock is Null!", i); - - KRecordList ks = - stock.getKDataDirver()->getConnect()->getKRecordList(stock.market(), stock.code(), query); - krecords[i].resize(ks.size()); - std::copy(ks.begin(), ks.end(), krecords[i].begin()); - } - - vector spots; -} - } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h index c2151c15..a60f230a 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h @@ -88,42 +88,53 @@ public: return m_stock_list; } - void run() { - _run(false); - } + /** + * 每日开盘时间内,以 delta 为周期循环定时执行指定任务 + * @param func 待执行的任务 + * @param delta 间隔时间 + * @param market 指定的市场 + */ + void runDaily(std::function&& func, const TimeDelta& delta, + const std::string& market = "SH"); - void backTest(const Datetime& start, const Datetime& end); + /** + * 每日在指定时刻执行任务 + * @param func 待执行的任务 + * @param delta 指定时刻 + * @param ignoreHoliday 忽略节假日,即节假日不执行 + */ + void runDailyAt(std::function&& func, const TimeDelta& delta, + bool ignoreHoliday = true); - void receivedSpot(const SpotRecord& spot); - void finishReceivedSpot(Datetime revTime); + void start(); virtual void init() {} - virtual void onTick() {} - virtual void onBar(const KQuery::KType& ktype) {}; - virtual void onMarketOpen() {} - virtual void onMarketClose() {} - virtual void onClock(TimeDelta detla) {} + /** + * 数据发生变化,即接收到相应行情数据变更 + * @note 通常用于调试 + * @param stk 数据发生变化的 stock + * @param spot 接收到的具体数据 + */ + virtual void onChange(const Stock& stk, const SpotRecord& spot) {} + + /** + * 一批行情数据接受完毕后通知 + * @note 通常仅用于调试打印,该批行情数据中不一定含有上下文中包含的 stock + */ + virtual void onReceivedSpot(Datetime revTime) {} private: string m_name; string m_config_file; StrategyContext m_context; - TMPtr m_tm; - StockList m_stock_list; - std::unordered_map m_ref_last_time; - std::unordered_map m_spot_map; + TMPtr m_tm; + bool m_running{false}; private: - void _initDefaultParam(); - - void _addTimer(); - void _addClockEvent(const string& enable, TimeDelta delta, TimeDelta openTime, - TimeDelta closeTime); - - void _loadKData(const Datetime& start, const Datetime& end); - void _run(bool forTest); + void run(); + void receivedSpot(const SpotRecord& spot); private: static std::atomic_bool ms_keep_running; diff --git a/hikyuu_pywrap/strategy/_StrategyBase.cpp b/hikyuu_pywrap/strategy/_StrategyBase.cpp index 27e32487..e127006f 100644 --- a/hikyuu_pywrap/strategy/_StrategyBase.cpp +++ b/hikyuu_pywrap/strategy/_StrategyBase.cpp @@ -19,26 +19,6 @@ public: void init() override { PYBIND11_OVERLOAD(void, StrategyBase, init); } - - void onTick() override { - PYBIND11_OVERLOAD(void, StrategyBase, onTick); - } - - void onBar(const KQuery::KType& ktype) override { - PYBIND11_OVERLOAD(void, StrategyBase, onBar, ktype); - } - - void onMarketOpen() override { - PYBIND11_OVERLOAD(void, StrategyBase, onMarketOpen); - } - - void onMarketClose() override { - PYBIND11_OVERLOAD(void, StrategyBase, onMarketClose); - } - - void onClock(TimeDelta detla) override { - PYBIND11_OVERLOAD(void, StrategyBase, onClock, detla); - } }; void export_Strategy(py::module& m) { @@ -60,11 +40,23 @@ void export_Strategy(py::module& m) { py::overload_cast&>(&StrategyBase::setKTypeList), py::return_value_policy::copy, "需要的K线类型") - .def("run", &StrategyBase::run) .def("init", &StrategyBase::init) - .def("on_tick", &StrategyBase::onTick) - .def("on_bar", &StrategyBase::onBar) - .def("on_market_open", &StrategyBase::onMarketOpen) - .def("on_market_close", &StrategyBase::onMarketClose) - .def("on_clock", &StrategyBase::onClock); + .def("start", &PyStrategyBase::start) + .def("run_daily", + [](StrategyBase& self, py::object func, const TimeDelta& time) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + self.runDaily(c_func, time); + }) + .def( + "run_daily_at", + [](StrategyBase& self, py::object func, const TimeDelta& time, bool ignore_holiday) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + self.runDailyAt(c_func, time, ignore_holiday); + }, + py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true) + // .def("run_daily", &StrategyBase::runDaily) + // .def("run_daily_at", &StrategyBase::runDailyAt) + ; } \ No newline at end of file From 69730916cc6393e73d96b4fb3da1eba6fb606603 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 12 Aug 2024 17:25:36 +0800 Subject: [PATCH 439/601] =?UTF-8?q?python=20=E4=BD=BF=E7=94=A8=E4=B8=BB?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E5=BE=AA=E7=8E=AF=EF=BC=8CC++=E4=B8=8D?= =?UTF-8?q?=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 10 ++-------- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 20 ++++++++++++++------ 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 77390324..d7d0d2aa 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -2,7 +2,7 @@ # -*- coding: utf8 -*- # cp936 -from hikyuu import StrategyBase, Query, Datetime, TimeDelta +from hikyuu import StrategyBase, Query, Datetime, TimeDelta, Seconds from hikyuu import StockManager @@ -15,12 +15,6 @@ class TestStrategy(StrategyBase): def init(self): print("strategy init") - def on_bar(self, ktype): - print("on bar {}".format(ktype)) - print("{}".format(len(StockManager.instance()))) - for s in self.sm: - print(s) - def my_func(): sm = StockManager.instance() @@ -31,5 +25,5 @@ def my_func(): if __name__ == '__main__': s = TestStrategy() - s.run_daily_at(my_func, TimeDelta(0, 17, 6)) + s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index 1cc9f051..7d53e8a1 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -11,9 +11,17 @@ #include "hikyuu/utilities/ini_parser/IniParser.h" #include "hikyuu/global/schedule/scheduler.h" #include "hikyuu/global/GlobalTaskGroup.h" +#include "hikyuu/global/sysinfo.h" #include "hikyuu/hikyuu.h" #include "StrategyBase.h" +#define EVENT(func) \ + if (runningInPython()) { \ + event(func); \ + } else { \ + func(); \ + } + namespace hku { std::atomic_bool StrategyBase::ms_keep_running = true; @@ -83,9 +91,9 @@ void StrategyBase::run() { auto& agent = *getGlobalSpotAgent(); agent.addProcess( - [this](const SpotRecord& spot) { event([=]() { this->receivedSpot(spot); }); }); + [this](const SpotRecord& spot) { EVENT([=]() { this->receivedSpot(spot); }); }); agent.addPostProcess( - [this](Datetime revTime) { event([=]() { this->onReceivedSpot(revTime); }); }); + [this](Datetime revTime) { EVENT([=]() { this->onReceivedSpot(revTime); }); }); startSpotAgent(false); m_running = true; @@ -99,7 +107,7 @@ void StrategyBase::start() { void StrategyBase::receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { - event([=]() { this->onChange(stk, spot); }); + EVENT([=]() { this->onChange(stk, spot); }); } } @@ -125,7 +133,7 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta Datetime close2 = today + market_info.closeTime2(); Datetime now = Datetime::now(); if ((now > open1 && now < close1) || (now > open2 && now < close2)) { - event(func); + EVENT(func); } }; @@ -194,7 +202,7 @@ void StrategyBase::runDailyAt(std::function&& func, const TimeDelta& del auto new_func = [=]() { if (!ignoreHoliday) { - event(func); + EVENT(func); return; } @@ -202,7 +210,7 @@ void StrategyBase::runDailyAt(std::function&& func, const TimeDelta& del auto today = Datetime::today(); int day = today.dayOfWeek(); if (day != 0 && day != 6 && !sm.isHoliday(today)) { - event(func); + EVENT(func); } }; From 9d9e03c758a86858eec6a4d133a3c21825ee47ea Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 12 Aug 2024 17:37:53 +0800 Subject: [PATCH 440/601] =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=BB=84=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E9=9B=86=E4=B8=AD=E4=BB=BB=E5=8A=A1=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 6 +++--- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp index c3018f9c..c0b18e25 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp @@ -13,9 +13,9 @@ namespace hku { -static StealThreadPool* g_threadPool; +static ThreadPool* g_threadPool; -StealThreadPool* getGlobalTaskGroup() { +ThreadPool* getGlobalTaskGroup() { static std::once_flag oc; std::call_once(oc, [&]() { auto cpu_num = std::thread::hardware_concurrency(); @@ -24,7 +24,7 @@ StealThreadPool* getGlobalTaskGroup() { } else if (cpu_num > 1) { cpu_num--; } - g_threadPool = new StealThreadPool(cpu_num); + g_threadPool = new ThreadPool(cpu_num); }); return g_threadPool; } diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h index 69d345ba..1eb5840d 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h @@ -11,7 +11,7 @@ #ifndef HKU_GLOBAL_TASK_GROUP #define HKU_GLOBAL_TASK_GROUP -#include "../utilities/thread/StealThreadPool.h" +#include "../utilities/thread/thread.h" namespace hku { @@ -19,7 +19,7 @@ namespace hku { * 获取全局线程池任务组 * @note 请使用 future 获取任务返回 */ -StealThreadPool* getGlobalTaskGroup(); +ThreadPool* getGlobalTaskGroup(); template using task_handle = std::future; From 52f50ebdfaaf0bbc10ffbfd13ae1471950733be1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 12 Aug 2024 17:47:09 +0800 Subject: [PATCH 441/601] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index 7d53e8a1..d32e75d0 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -15,6 +15,8 @@ #include "hikyuu/hikyuu.h" #include "StrategyBase.h" +// python 中运行拉回主线程循环 +// c++ 则直接执行(通常在定时调度的工作线程中执行) #define EVENT(func) \ if (runningInPython()) { \ event(func); \ From 3d9be8f8897a63421fe9dd10ddbd439eca42f6f5 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 12 Aug 2024 17:50:44 +0800 Subject: [PATCH 442/601] update --- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 6 +++--- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h | 4 +++- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp index c0b18e25..d7b7a1f3 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp @@ -13,9 +13,9 @@ namespace hku { -static ThreadPool* g_threadPool; +static TaskGroup* g_threadPool; -ThreadPool* getGlobalTaskGroup() { +TaskGroup* getGlobalTaskGroup() { static std::once_flag oc; std::call_once(oc, [&]() { auto cpu_num = std::thread::hardware_concurrency(); @@ -24,7 +24,7 @@ ThreadPool* getGlobalTaskGroup() { } else if (cpu_num > 1) { cpu_num--; } - g_threadPool = new ThreadPool(cpu_num); + g_threadPool = new TaskGroup(cpu_num); }); return g_threadPool; } diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h index 1eb5840d..72ab4bad 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h @@ -15,11 +15,13 @@ namespace hku { +using TaskGroup = ThreadPool; + /** * 获取全局线程池任务组 * @note 请使用 future 获取任务返回 */ -ThreadPool* getGlobalTaskGroup(); +TaskGroup* getGlobalTaskGroup(); template using task_handle = std::future; From 36077b6b26ab99d5b2e465584e57c1927189f255 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 13 Aug 2024 01:59:33 +0800 Subject: [PATCH 443/601] =?UTF-8?q?=E5=90=8C=E6=AD=A5=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/utilities/datetime/TimeDelta.cpp | 4 +-- .../hikyuu/utilities/db_connect/DBUpgrade.cpp | 4 +-- hikyuu_cpp/hikyuu/utilities/exception.cpp | 16 ++++++++++ hikyuu_cpp/hikyuu/utilities/exception.h | 32 +++++++++---------- .../utilities/http_client/HttpClient.cpp | 2 +- .../hikyuu/utilities/http_client/HttpClient.h | 2 +- .../hikyuu/utilities/http_client/url.cpp | 2 +- hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h | 2 +- 8 files changed, 39 insertions(+), 25 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/utilities/exception.cpp diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp index 77304232..73c2c6b3 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/TimeDelta.cpp @@ -167,12 +167,12 @@ TimeDelta HKU_UTILS_API Milliseconds(int64_t milliseconds) { } TimeDelta TimeDelta::operator*(double p) const { - return TimeDelta::fromTicks(static_cast(roundEx(double(ticks()) * p, 0))); + return TimeDelta::fromTicks(static_cast(roundEx(double(ticks()) * p, 0))); } TimeDelta TimeDelta::operator/(double p) const { HKU_CHECK(p != 0, "Attempt to divide by 0!"); - return TimeDelta::fromTicks(static_cast(roundEx(double(ticks()) / p, 0))); + return TimeDelta::fromTicks(static_cast(roundEx(double(ticks()) / p, 0))); } TimeDelta TimeDelta::floorDiv(double p) const { diff --git a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp index d980e413..b1d3b785 100644 --- a/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp +++ b/hikyuu_cpp/hikyuu/utilities/db_connect/DBUpgrade.cpp @@ -30,7 +30,7 @@ void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name if (!driver->tableExist("module_version")) { bool need_create = true; #if HKU_ENABLE_SQLITE - if (need_create && typeid(*driver) == typeid(SQLiteConnect)) { + if (need_create && typeid(driver.get()) == typeid(SQLiteConnect *)) { driver->exec( "CREATE TABLE `module_version` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,`module` " "TEXT, " @@ -40,7 +40,7 @@ void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name #endif #if HKU_ENABLE_MYSQL - if (need_create && typeid(*driver) == typeid(MySQLConnect)) { + if (need_create && typeid(driver.get()) == typeid(MySQLConnect *)) { driver->exec( R"(CREATE TABLE `module_version` ( `id` int NOT NULL AUTO_INCREMENT, diff --git a/hikyuu_cpp/hikyuu/utilities/exception.cpp b/hikyuu_cpp/hikyuu/utilities/exception.cpp new file mode 100644 index 00000000..a16c094f --- /dev/null +++ b/hikyuu_cpp/hikyuu/utilities/exception.cpp @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-13 + * Author: fasiondog + */ + +#include "exception.h" + +namespace hku { + +const char *exception::what() const noexcept { + return m_msg.c_str(); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/utilities/exception.h b/hikyuu_cpp/hikyuu/utilities/exception.h index da0cafdf..66baf2dd 100644 --- a/hikyuu_cpp/hikyuu/utilities/exception.h +++ b/hikyuu_cpp/hikyuu/utilities/exception.h @@ -14,40 +14,38 @@ #include #include +#ifndef HKU_UTILS_API +#define HKU_UTILS_API +#endif + namespace hku { +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable : 4275) +#endif + /** * @ingroup Utilities * @addtogroup Exception Exception 异常处理 * @{ */ -#if !defined(__clang__) && !defined(__GNUC__) -class exception : public std::exception { -public: - exception() : std::exception("Unknown exception!") {} - exception(const std::string& msg) // cppcheck-suppress noExplicitConstructor - : std::exception(msg.c_str()) {} - exception(const char* msg) : std::exception(msg) {} // cppcheck-suppress noExplicitConstructor - virtual ~exception() noexcept {}; -}; - -#else -// llvm 中的 std::exception 不接受参数 -class exception : public std::exception { +class HKU_UTILS_API exception : public std::exception { public: exception() : m_msg("Unknown exception!") {} exception(const char *msg) : m_msg(msg) {} // cppcheck-suppress noExplicitConstructor exception(const std::string &msg) : m_msg(msg) {} // cppcheck-suppress noExplicitConstructor virtual ~exception() noexcept {} - virtual const char *what() const noexcept { - return m_msg.c_str(); - } + virtual const char *what() const noexcept; protected: std::string m_msg; }; -#endif /* #ifdef __clang__ */ + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp index ce2865a6..cc25828b 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.cpp @@ -98,7 +98,7 @@ void HttpClient::_connect() { if (!m_conn.valid()) { NNG_CHECK(m_aio.wait().result()); - m_conn = std::move(nng::http_conn((nng_http_conn*)m_aio.get_output(0))); + m_conn = nng::http_conn((nng_http_conn*)m_aio.get_output(0)); } } diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h index 237343b7..e20ee542 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h +++ b/hikyuu_cpp/hikyuu/utilities/http_client/HttpClient.h @@ -132,7 +132,7 @@ public: } void setUrl(const std::string& url) noexcept { - m_url = std::move(nng::url(url)); + m_url = nng::url(url); reset(); } diff --git a/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp b/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp index f5821584..26d79d16 100644 --- a/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp +++ b/hikyuu_cpp/hikyuu/utilities/http_client/url.cpp @@ -34,7 +34,7 @@ std::string url_escape(const char* istr) { if (is_unambiguous(*p)) { ostr += *p; } else { - sprintf(szHex, "%%%02X", *p); + snprintf(szHex, 4, "%%%02X", *p); ostr += szHex; } ++p; diff --git a/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h b/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h index 862d8d4f..b40be05e 100644 --- a/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h +++ b/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h @@ -588,7 +588,7 @@ public: // Beautify Output std::string fname; - unsigned int pos = infile.find_last_of(MO_PATHSEP); + size_t pos = infile.find_last_of(MO_PATHSEP); if (pos != std::string::npos) { fname = infile.substr(pos + 1, infile.length()); } else { From 761b2332049e3bd5e4f7c5147545a37e270ece4a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 13 Aug 2024 14:14:40 +0800 Subject: [PATCH 444/601] update --- hikyuu/strategy/strategy.py | 5 +- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 129 ++++++++++---------- 2 files changed, 70 insertions(+), 64 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index d7d0d2aa..dd515dbe 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -2,7 +2,7 @@ # -*- coding: utf8 -*- # cp936 -from hikyuu import StrategyBase, Query, Datetime, TimeDelta, Seconds +from hikyuu import StrategyBase, Query, Datetime, TimeDelta, Seconds, Minutes from hikyuu import StockManager @@ -25,5 +25,6 @@ def my_func(): if __name__ == '__main__': s = TestStrategy() - s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) + # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) + s.run_daily(my_func, Minutes(1)) s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index d32e75d0..72466d3e 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -119,80 +119,85 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta run(); } - auto* scheduler = getScheduler(); - auto new_func = [=]() { + try { + auto* scheduler = getScheduler(); + auto new_func = [=]() { + const auto& sm = StockManager::instance(); + auto today = Datetime::today(); + int day = today.dayOfWeek(); + if (day == 0 || day == 6 || sm.isHoliday(today)) { + return; + } + + auto market_info = sm.getMarketInfo(market); + Datetime open1 = today + market_info.openTime1(); + Datetime close1 = today + market_info.closeTime1(); + Datetime open2 = today + market_info.openTime2(); + Datetime close2 = today + market_info.closeTime2(); + Datetime now = Datetime::now(); + if ((now > open1 && now < close1) || (now > open2 && now < close2)) { + EVENT(func); + } + }; + const auto& sm = StockManager::instance(); - auto today = Datetime::today(); - int day = today.dayOfWeek(); - if (day == 0 || day == 6 || sm.isHoliday(today)) { - return; - } - auto market_info = sm.getMarketInfo(market); - Datetime open1 = today + market_info.openTime1(); - Datetime close1 = today + market_info.closeTime1(); - Datetime open2 = today + market_info.openTime2(); - Datetime close2 = today + market_info.closeTime2(); - Datetime now = Datetime::now(); - if ((now > open1 && now < close1) || (now > open2 && now < close2)) { - EVENT(func); - } - }; - - const auto& sm = StockManager::instance(); - auto market_info = sm.getMarketInfo(market); - auto today = Datetime::today(); - auto now = Datetime::now(); - TimeDelta now_time = now - today; - if (now_time >= market_info.closeTime2()) { - scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { - new_func(); - auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); - }); - - } else if (now_time >= market_info.openTime2()) { - int64_t ticks = now_time.ticks() - market_info.openTime2().ticks(); - int64_t delta_ticks = delta.ticks(); - if (ticks % delta_ticks == 0) { - scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); - } else { - auto delay = TimeDelta::fromTicks(delta_ticks - (ticks / delta_ticks) * delta_ticks); - scheduler->addFuncAtTime(now + delay, [=]() { + auto today = Datetime::today(); + auto now = Datetime::now(); + TimeDelta now_time = now - today; + if (now_time >= market_info.closeTime2()) { + scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { new_func(); auto* sched = getScheduler(); sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); }); - } - } else if (now_time == market_info.closeTime1()) { - scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { - auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); - }); + } else if (now_time >= market_info.openTime2()) { + int64_t ticks = now_time.ticks() - market_info.openTime2().ticks(); + int64_t delta_ticks = delta.ticks(); + if (ticks % delta_ticks == 0) { + scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + } else { + auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks); + scheduler->addFuncAtTime(now + delay, [=]() { + new_func(); + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); + } - } else if (now_time < market_info.closeTime1() && now_time >= market_info.openTime1()) { - int64_t ticks = now_time.ticks() - market_info.openTime1().ticks(); - int64_t delta_ticks = delta.ticks(); - if (ticks % delta_ticks == 0) { - scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); - } else { - auto delay = TimeDelta::fromTicks(delta_ticks - (ticks / delta_ticks) * delta_ticks); - scheduler->addFuncAtTime(now + delay, [=]() { - new_func(); + } else if (now_time == market_info.closeTime1()) { + scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { auto* sched = getScheduler(); sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); }); + + } else if (now_time < market_info.closeTime1() && now_time >= market_info.openTime1()) { + int64_t ticks = now_time.ticks() - market_info.openTime1().ticks(); + int64_t delta_ticks = delta.ticks(); + if (ticks % delta_ticks == 0) { + scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + } else { + auto delay = + TimeDelta::fromTicks(delta_ticks - (ticks / delta_ticks) * delta_ticks); + scheduler->addFuncAtTime(now + delay, [=]() { + new_func(); + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); + } + + } else if (now_time < market_info.openTime1()) { + scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() { + auto* sched = getScheduler(); + sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + }); + + } else { + HKU_ERROR("Unknown process!"); } - - } else if (now_time < market_info.openTime1()) { - scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() { - auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); - }); - - } else { - HKU_ERROR("Unknown process!"); + } catch (const std::exception& e) { + HKU_THROW(e.what()); } } From f2a979a92ed37d963079fc1dc560df2c010612b6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 13 Aug 2024 15:11:08 +0800 Subject: [PATCH 445/601] update --- hikyuu/strategy/strategy.py | 6 +++++- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 7 +++++-- hikyuu_pywrap/strategy/_StrategyBase.cpp | 10 ++++++++++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index dd515dbe..2856aa8a 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -15,6 +15,9 @@ class TestStrategy(StrategyBase): def init(self): print("strategy init") + def on_received_spot(self, rev_time): + print("rev_time:", rev_time) + def my_func(): sm = StockManager.instance() @@ -26,5 +29,6 @@ def my_func(): if __name__ == '__main__': s = TestStrategy() # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) + # s.on_received_spot(Datetime.now()) s.run_daily(my_func, Minutes(1)) - s.start() + # s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index 72466d3e..ff911f4a 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -96,7 +96,7 @@ void StrategyBase::run() { [this](const SpotRecord& spot) { EVENT([=]() { this->receivedSpot(spot); }); }); agent.addPostProcess( [this](Datetime revTime) { EVENT([=]() { this->onReceivedSpot(revTime); }); }); - startSpotAgent(false); + startSpotAgent(true); m_running = true; } @@ -135,7 +135,7 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta Datetime open2 = today + market_info.openTime2(); Datetime close2 = today + market_info.closeTime2(); Datetime now = Datetime::now(); - if ((now > open1 && now < close1) || (now > open2 && now < close2)) { + if ((now >= open1 && now <= close1) || (now >= open2 && now <= close2)) { EVENT(func); } }; @@ -146,6 +146,7 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta auto now = Datetime::now(); TimeDelta now_time = now - today; if (now_time >= market_info.closeTime2()) { + HKU_INFO("time: {}", today.nextDay() + market_info.openTime1()); scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { new_func(); auto* sched = getScheduler(); @@ -168,6 +169,7 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta } else if (now_time == market_info.closeTime1()) { scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { + new_func(); auto* sched = getScheduler(); sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); }); @@ -189,6 +191,7 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta } else if (now_time < market_info.openTime1()) { scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() { + new_func(); auto* sched = getScheduler(); sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); }); diff --git a/hikyuu_pywrap/strategy/_StrategyBase.cpp b/hikyuu_pywrap/strategy/_StrategyBase.cpp index e127006f..a10f87e8 100644 --- a/hikyuu_pywrap/strategy/_StrategyBase.cpp +++ b/hikyuu_pywrap/strategy/_StrategyBase.cpp @@ -19,6 +19,14 @@ public: void init() override { PYBIND11_OVERLOAD(void, StrategyBase, init); } + + void onChange(const Stock& stk, const SpotRecord& spot) override { + PYBIND11_OVERLOAD(void, StrategyBase, onChange, stk, spot); + } + + virtual void onReceivedSpot(Datetime revTime) override { + PYBIND11_OVERLOAD(void, StrategyBase, onReceivedSpot, revTime); + } }; void export_Strategy(py::module& m) { @@ -41,6 +49,8 @@ void export_Strategy(py::module& m) { py::return_value_policy::copy, "需要的K线类型") .def("init", &StrategyBase::init) + .def("on_change", &StrategyBase::onChange) + .def("on_received_spot", &StrategyBase::onReceivedSpot) .def("start", &PyStrategyBase::start) .def("run_daily", [](StrategyBase& self, py::object func, const TimeDelta& time) { From 14a420f0e325e43080a6ce533627313db6fd0c0b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 13 Aug 2024 18:53:08 +0800 Subject: [PATCH 446/601] update --- hikyuu/strategy/strategy.py | 20 ++++++--- hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 33 +++++++++++--- hikyuu_cpp/hikyuu/strategy/StrategyBase.h | 17 +++----- hikyuu_pywrap/global/_SpotRecord.cpp | 48 +++++++++++++++++++++ hikyuu_pywrap/global/agent_main.cpp | 2 + hikyuu_pywrap/strategy/_StrategyBase.cpp | 29 +++++++------ 6 files changed, 109 insertions(+), 40 deletions(-) create mode 100644 hikyuu_pywrap/global/_SpotRecord.cpp diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 2856aa8a..7a473a3a 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -12,11 +12,16 @@ class TestStrategy(StrategyBase): self.stock_list = ['sh600000', 'sz000001'] self.ktype_list = [Query.MIN, Query.DAY] - def init(self): - print("strategy init") + def initialize(self): + print("********strategy init********") - def on_received_spot(self, rev_time): - print("rev_time:", rev_time) + +def on_change(stk, spot): + print(stk, spot.bid1, spot.ask1) + + +def on_spot(rev_time): + print("rev_time:", rev_time) def my_func(): @@ -29,6 +34,7 @@ def my_func(): if __name__ == '__main__': s = TestStrategy() # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) - # s.on_received_spot(Datetime.now()) - s.run_daily(my_func, Minutes(1)) - # s.start() + s.on_change(on_change) + s.on_received_spot(on_spot) + # s.run_daily(my_func, Minutes(1)) + s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index ff911f4a..5d28966f 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -55,10 +55,11 @@ StrategyBase::~StrategyBase() { } void StrategyBase::run() { + HKU_INFO("StrategyBase::run()"); HKU_IF_RETURN(m_running, void()); // 调用 strategy 自身的初始化方法 - init(); + initialize(); StockManager& sm = StockManager::instance(); @@ -92,10 +93,12 @@ void StrategyBase::run() { HKU_WARN_IF(m_stock_list.empty(), "[Strategy {}] stock list is empty!", m_name); auto& agent = *getGlobalSpotAgent(); - agent.addProcess( - [this](const SpotRecord& spot) { EVENT([=]() { this->receivedSpot(spot); }); }); - agent.addPostProcess( - [this](Datetime revTime) { EVENT([=]() { this->onReceivedSpot(revTime); }); }); + agent.addProcess([this](const SpotRecord& spot) { receivedSpot(spot); }); + agent.addPostProcess([this](Datetime revTime) { + if (m_on_recieved_spot) { + EVENT([=]() { m_on_recieved_spot(revTime); }); + } + }); startSpotAgent(true); m_running = true; @@ -106,10 +109,27 @@ void StrategyBase::start() { _startEventLoop(); } +void StrategyBase::onChange( + std::function&& changeFunc) { + if (!m_running) { + run(); + } + m_on_change = changeFunc; +} + +void StrategyBase::onReceivedSpot(std::function&& recievedFucn) { + if (!m_running) { + run(); + } + m_on_recieved_spot = recievedFucn; +} + void StrategyBase::receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { - EVENT([=]() { this->onChange(stk, spot); }); + if (m_on_change) { + EVENT([=]() { m_on_change(stk, spot); }); + } } } @@ -146,7 +166,6 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta auto now = Datetime::now(); TimeDelta now_time = now - today; if (now_time >= market_info.closeTime2()) { - HKU_INFO("time: {}", today.nextDay() + market_info.openTime1()); scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { new_func(); auto* sched = getScheduler(); diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h index a60f230a..8dc30c43 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h @@ -40,14 +40,6 @@ public: return StockManager::instance(); } - TMPtr getTM() const { - return m_tm; - } - - void setTM(const TMPtr& tm) { - m_tm = tm; - } - const StrategyContext& context() const { return m_context; } @@ -108,7 +100,7 @@ public: void start(); - virtual void init() {} + virtual void initialize() {} /** * 数据发生变化,即接收到相应行情数据变更 @@ -116,20 +108,21 @@ public: * @param stk 数据发生变化的 stock * @param spot 接收到的具体数据 */ - virtual void onChange(const Stock& stk, const SpotRecord& spot) {} + void onChange(std::function&& changeFunc); /** * 一批行情数据接受完毕后通知 * @note 通常仅用于调试打印,该批行情数据中不一定含有上下文中包含的 stock */ - virtual void onReceivedSpot(Datetime revTime) {} + void onReceivedSpot(std::function&& recievedFucn); private: string m_name; string m_config_file; StrategyContext m_context; StockList m_stock_list; - TMPtr m_tm; + std::function m_on_recieved_spot; + std::function m_on_change; bool m_running{false}; private: diff --git a/hikyuu_pywrap/global/_SpotRecord.cpp b/hikyuu_pywrap/global/_SpotRecord.cpp new file mode 100644 index 00000000..c9d2ffd8 --- /dev/null +++ b/hikyuu_pywrap/global/_SpotRecord.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-13 + * Author: fasiondog + */ + +#include +#include "../pybind_utils.h" + +using namespace hku; +namespace py = pybind11; + +void export_SpotRecord(py::module& m) { + py::class_(m, "SpotRecord") + .def(py::init<>()) + .def_readwrite("market", &SpotRecord::market) + .def_readwrite("code", &SpotRecord::code) + .def_readwrite("name", &SpotRecord::name) + .def_readwrite("datetime", &SpotRecord::datetime) + .def_readwrite("yesterday_close", &SpotRecord::yesterday_close) + .def_readwrite("open", &SpotRecord::open) + .def_readwrite("high", &SpotRecord::high) + .def_readwrite("low", &SpotRecord::low) + .def_readwrite("close", &SpotRecord::close) + .def_readwrite("amount", &SpotRecord::amount) + .def_readwrite("volume", &SpotRecord::volume) + .def_readwrite("bid1", &SpotRecord::bid1) + .def_readwrite("bid1_amount", &SpotRecord::bid1_amount) + .def_readwrite("bid2", &SpotRecord::bid2) + .def_readwrite("bid2_amount", &SpotRecord::bid2_amount) + .def_readwrite("bid3", &SpotRecord::bid3) + .def_readwrite("bid3_amount", &SpotRecord::bid3_amount) + .def_readwrite("bid4", &SpotRecord::bid4) + .def_readwrite("bid4_amount", &SpotRecord::bid4_amount) + .def_readwrite("bid5", &SpotRecord::bid5) + .def_readwrite("bid5_amount", &SpotRecord::bid5_amount) + .def_readwrite("ask1", &SpotRecord::ask1) + .def_readwrite("ask1_amount", &SpotRecord::ask1_amount) + .def_readwrite("ask2", &SpotRecord::ask2) + .def_readwrite("ask2_amount", &SpotRecord::ask2_amount) + .def_readwrite("ask3", &SpotRecord::ask3) + .def_readwrite("ask3_amount", &SpotRecord::ask3_amount) + .def_readwrite("ask4", &SpotRecord::ask4) + .def_readwrite("ask4_amount", &SpotRecord::ask4_amount) + .def_readwrite("ask5", &SpotRecord::ask5) + .def_readwrite("ask5_amount", &SpotRecord::ask5_amount); +} \ No newline at end of file diff --git a/hikyuu_pywrap/global/agent_main.cpp b/hikyuu_pywrap/global/agent_main.cpp index bf39131a..2722e0cf 100644 --- a/hikyuu_pywrap/global/agent_main.cpp +++ b/hikyuu_pywrap/global/agent_main.cpp @@ -9,8 +9,10 @@ namespace py = pybind11; +void export_SpotRecord(py::module& m); void export_SpotAgent(py::module& m); void export_global_main(py::module& m) { + export_SpotRecord(m); export_SpotAgent(m); } diff --git a/hikyuu_pywrap/strategy/_StrategyBase.cpp b/hikyuu_pywrap/strategy/_StrategyBase.cpp index a10f87e8..59614925 100644 --- a/hikyuu_pywrap/strategy/_StrategyBase.cpp +++ b/hikyuu_pywrap/strategy/_StrategyBase.cpp @@ -16,16 +16,8 @@ class PyStrategyBase : public StrategyBase { public: using StrategyBase::StrategyBase; - void init() override { - PYBIND11_OVERLOAD(void, StrategyBase, init); - } - - void onChange(const Stock& stk, const SpotRecord& spot) override { - PYBIND11_OVERLOAD(void, StrategyBase, onChange, stk, spot); - } - - virtual void onReceivedSpot(Datetime revTime) override { - PYBIND11_OVERLOAD(void, StrategyBase, onReceivedSpot, revTime); + void initialize() override { + PYBIND11_OVERLOAD(void, StrategyBase, initialize); } }; @@ -38,7 +30,6 @@ void export_Strategy(py::module& m) { .def_property_readonly("sm", &StrategyBase::getSM, py::return_value_policy::reference, "获取 StockManager 实例") - .def_property("tm", &StrategyBase::getTM, &StrategyBase::setTM, "账户管理") .def_property("start_datetime", py::overload_cast<>(&StrategyBase::startDatetime, py::const_), py::overload_cast(&StrategyBase::startDatetime), "起始日期") .def_property("stock_list", py::overload_cast<>(&StrategyBase::getStockCodeList, py::const_), @@ -48,10 +39,20 @@ void export_Strategy(py::module& m) { py::overload_cast&>(&StrategyBase::setKTypeList), py::return_value_policy::copy, "需要的K线类型") - .def("init", &StrategyBase::init) - .def("on_change", &StrategyBase::onChange) - .def("on_received_spot", &StrategyBase::onReceivedSpot) + .def("initialize", &StrategyBase::initialize) .def("start", &PyStrategyBase::start) + .def("on_change", + [](StrategyBase& self, py::object func) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + self.onChange(c_func); + }) + .def("on_received_spot", + [](StrategyBase& self, py::object func) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + self.onReceivedSpot(c_func); + }) .def("run_daily", [](StrategyBase& self, py::object func, const TimeDelta& time) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); From 818f29e8fecabceae6c463d71a7d45b4ce663c0a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 14 Aug 2024 01:24:32 +0800 Subject: [PATCH 447/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy=20continu?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 14 ++------ hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp | 40 ++++++++++++--------- hikyuu_cpp/hikyuu/strategy/StrategyBase.h | 14 ++------ hikyuu_pywrap/strategy/_StrategyBase.cpp | 25 ++++--------- 4 files changed, 36 insertions(+), 57 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 7a473a3a..cebc762f 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -6,16 +6,6 @@ from hikyuu import StrategyBase, Query, Datetime, TimeDelta, Seconds, Minutes from hikyuu import StockManager -class TestStrategy(StrategyBase): - def __init__(self): - super(self.__class__, self).__init__() - self.stock_list = ['sh600000', 'sz000001'] - self.ktype_list = [Query.MIN, Query.DAY] - - def initialize(self): - print("********strategy init********") - - def on_change(stk, spot): print(stk, spot.bid1, spot.ask1) @@ -32,9 +22,9 @@ def my_func(): if __name__ == '__main__': - s = TestStrategy() + s = StrategyBase(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) - s.on_change(on_change) + # s.on_change(on_change) s.on_received_spot(on_spot) # s.run_daily(my_func, Minutes(1)) s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp index 5d28966f..1fb5072c 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp @@ -35,31 +35,38 @@ void StrategyBase::sig_handler(int sig) { } } -StrategyBase::StrategyBase() : StrategyBase("Strategy") {} - -StrategyBase::StrategyBase(const string& name) { - string home = getUserDir(); - HKU_ERROR_IF(home == "", "Failed get user home path!"); -#if HKU_OS_WINOWS - m_config_file = format("{}\\{}", home, ".hikyuu\\hikyuu.ini"); -#else - m_config_file = format("{}/{}", home, ".hikyuu/hikyuu.ini"); -#endif -} +StrategyBase::StrategyBase() : StrategyBase("Strategy", "") {} StrategyBase::StrategyBase(const string& name, const string& config_file) -: m_name(name), m_config_file(config_file) {} +: m_name(name), m_config_file(config_file) { + if (m_config_file.empty()) { + string home = getUserDir(); + HKU_ERROR_IF(home == "", "Failed get user home path!"); +#if HKU_OS_WINOWS + m_config_file = format("{}\\{}", home, ".hikyuu\\hikyuu.ini"); +#else + m_config_file = format("{}/{}", home, ".hikyuu/hikyuu.ini"); +#endif + } +} + +StrategyBase::StrategyBase(const vector& codeList, const vector& ktypeList, + const string& name, const string& config_file) +: StrategyBase(name, config_file) { + m_context.setStockCodeList(codeList); + m_context.setKTypeList(ktypeList); +} StrategyBase::~StrategyBase() { + ms_keep_running = false; HKU_INFO("[Strategy {}] Quit Strategy!", m_name); } void StrategyBase::run() { - HKU_INFO("StrategyBase::run()"); HKU_IF_RETURN(m_running, void()); - // 调用 strategy 自身的初始化方法 - initialize(); + HKU_CHECK(!getStockCodeList().empty(), "The context does not contain any stocks!"); + HKU_CHECK(!getKTypeList().empty(), "The K type list was empty!"); StockManager& sm = StockManager::instance(); @@ -105,7 +112,8 @@ void StrategyBase::run() { } void StrategyBase::start() { - HKU_INFO("start even loop ..."); + HKU_CHECK(m_running, "No handler functions are registered!"); + HKU_INFO("start strategy even loop ..."); _startEventLoop(); } diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h index 8dc30c43..7919d5df 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/StrategyBase.h @@ -10,7 +10,6 @@ #include #include "../DataType.h" #include "../StrategyContext.h" -#include "../utilities/Parameter.h" #include "../utilities/thread/FuncWrapper.h" #include "../utilities/thread/ThreadSafeQueue.h" #include "../global/GlobalSpotAgent.h" @@ -19,12 +18,11 @@ namespace hku { class HKU_API StrategyBase { - PARAMETER_SUPPORT - public: StrategyBase(); - explicit StrategyBase(const string& name); - StrategyBase(const string& name, const string& config_file); + explicit StrategyBase(const string& name, const string& config_file = ""); + StrategyBase(const vector& codeList, const vector& ktypeList, + const string& name = "Strategy", const string& config_file = ""); virtual ~StrategyBase(); @@ -36,10 +34,6 @@ public: m_name = name; } - StockManager& getSM() { - return StockManager::instance(); - } - const StrategyContext& context() const { return m_context; } @@ -100,8 +94,6 @@ public: void start(); - virtual void initialize() {} - /** * 数据发生变化,即接收到相应行情数据变更 * @note 通常用于调试 diff --git a/hikyuu_pywrap/strategy/_StrategyBase.cpp b/hikyuu_pywrap/strategy/_StrategyBase.cpp index 59614925..761a2116 100644 --- a/hikyuu_pywrap/strategy/_StrategyBase.cpp +++ b/hikyuu_pywrap/strategy/_StrategyBase.cpp @@ -12,24 +12,17 @@ namespace py = pybind11; using namespace hku; -class PyStrategyBase : public StrategyBase { -public: - using StrategyBase::StrategyBase; - - void initialize() override { - PYBIND11_OVERLOAD(void, StrategyBase, initialize); - } -}; - void export_Strategy(py::module& m) { - py::class_(m, "StrategyBase") + py::class_(m, "StrategyBase") .def(py::init<>()) + .def(py::init&, const vector&, const std::string&, + const std::string&>(), + py::arg("code_list"), py::arg("ktype_list"), py::arg("name") = "Strategy", + py::arg("config") = "") .def_property("name", py::overload_cast<>(&StrategyBase::name, py::const_), py::overload_cast(&StrategyBase::name), py::return_value_policy::copy, "策略名称") - .def_property_readonly("sm", &StrategyBase::getSM, py::return_value_policy::reference, - "获取 StockManager 实例") .def_property("start_datetime", py::overload_cast<>(&StrategyBase::startDatetime, py::const_), py::overload_cast(&StrategyBase::startDatetime), "起始日期") .def_property("stock_list", py::overload_cast<>(&StrategyBase::getStockCodeList, py::const_), @@ -39,8 +32,7 @@ void export_Strategy(py::module& m) { py::overload_cast&>(&StrategyBase::setKTypeList), py::return_value_policy::copy, "需要的K线类型") - .def("initialize", &StrategyBase::initialize) - .def("start", &PyStrategyBase::start) + .def("start", &StrategyBase::start) .def("on_change", [](StrategyBase& self, py::object func) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); @@ -66,8 +58,5 @@ void export_Strategy(py::module& m) { py::object c_func = func.attr("__call__"); self.runDailyAt(c_func, time, ignore_holiday); }, - py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true) - // .def("run_daily", &StrategyBase::runDaily) - // .def("run_daily_at", &StrategyBase::runDailyAt) - ; + py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true); } \ No newline at end of file From 7feaf6c1f1879fe1524f96f0a6c394e2f747fbc2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 14 Aug 2024 01:47:28 +0800 Subject: [PATCH 448/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy=20continu?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 4 +- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 4 +- .../{StrategyBase.cpp => Strategy.cpp} | 70 ++++++++----------- .../strategy/{StrategyBase.h => Strategy.h} | 31 +++----- .../{_StrategyBase.cpp => _Strategy.cpp} | 28 ++++---- 5 files changed, 54 insertions(+), 83 deletions(-) rename hikyuu_cpp/hikyuu/strategy/{StrategyBase.cpp => Strategy.cpp} (75%) rename hikyuu_cpp/hikyuu/strategy/{StrategyBase.h => Strategy.h} (84%) rename hikyuu_pywrap/strategy/{_StrategyBase.cpp => _Strategy.cpp} (63%) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index cebc762f..ff978f6f 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -2,7 +2,7 @@ # -*- coding: utf8 -*- # cp936 -from hikyuu import StrategyBase, Query, Datetime, TimeDelta, Seconds, Minutes +from hikyuu import Strategy, Query, Datetime, TimeDelta, Seconds, Minutes from hikyuu import StockManager @@ -22,7 +22,7 @@ def my_func(): if __name__ == '__main__': - s = StrategyBase(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) + s = Strategy(['sh600000', 'sz000001', 'xxx'], [Query.MIN, Query.DAY]) # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) # s.on_change(on_change) s.on_received_spot(on_spot) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 86325e79..e6f72726 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -156,9 +156,7 @@ void SpotAgent::work_thread() { while (!m_stop && rv != 0) { rv = nng_dial(sock, ms_pubUrl.c_str(), nullptr, 0); HKU_WARN_IF(m_print && rv != 0, - "Faied connect quotation server {}, will retry after 5 seconds! You Maybe need " - "start the collection service first.", - ms_pubUrl); + "Faied connect quotation server {}, will retry after 5 seconds!", ms_pubUrl); std::this_thread::sleep_for(std::chrono::seconds(5)); } diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp similarity index 75% rename from hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp rename to hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 1fb5072c..f21569cb 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -13,7 +13,7 @@ #include "hikyuu/global/GlobalTaskGroup.h" #include "hikyuu/global/sysinfo.h" #include "hikyuu/hikyuu.h" -#include "StrategyBase.h" +#include "Strategy.h" // python 中运行拉回主线程循环 // c++ 则直接执行(通常在定时调度的工作线程中执行) @@ -26,18 +26,18 @@ namespace hku { -std::atomic_bool StrategyBase::ms_keep_running = true; +std::atomic_bool Strategy::ms_keep_running = true; -void StrategyBase::sig_handler(int sig) { +void Strategy::sig_handler(int sig) { if (sig == SIGINT) { ms_keep_running = false; exit(0); } } -StrategyBase::StrategyBase() : StrategyBase("Strategy", "") {} +Strategy::Strategy() : Strategy("Strategy", "") {} -StrategyBase::StrategyBase(const string& name, const string& config_file) +Strategy::Strategy(const string& name, const string& config_file) : m_name(name), m_config_file(config_file) { if (m_config_file.empty()) { string home = getUserDir(); @@ -50,23 +50,23 @@ StrategyBase::StrategyBase(const string& name, const string& config_file) } } -StrategyBase::StrategyBase(const vector& codeList, const vector& ktypeList, - const string& name, const string& config_file) -: StrategyBase(name, config_file) { +Strategy::Strategy(const vector& codeList, const vector& ktypeList, + const string& name, const string& config_file) +: Strategy(name, config_file) { m_context.setStockCodeList(codeList); m_context.setKTypeList(ktypeList); } -StrategyBase::~StrategyBase() { +Strategy::~Strategy() { ms_keep_running = false; - HKU_INFO("[Strategy {}] Quit Strategy!", m_name); + CLS_INFO("Quit Strategy {}!", m_name); } -void StrategyBase::run() { - HKU_IF_RETURN(m_running, void()); +void Strategy::run() { + CLS_IF_RETURN(m_running, void()); - HKU_CHECK(!getStockCodeList().empty(), "The context does not contain any stocks!"); - HKU_CHECK(!getKTypeList().empty(), "The K type list was empty!"); + CLS_CHECK(!getStockCodeList().empty(), "The context does not contain any stocks!"); + CLS_CHECK(!getKTypeList().empty(), "The K type list was empty!"); StockManager& sm = StockManager::instance(); @@ -78,7 +78,7 @@ void StrategyBase::run() { // 注册 ctrl-c 终止信号 std::signal(SIGINT, sig_handler); - HKU_INFO("[Strategy {}] strategy is running! You can press Ctrl-C to terminte ...", m_name); + CLS_INFO("{} is running! You can press Ctrl-C to terminte ...", m_name); // 初始化 hikyuu_init(m_config_file, false, m_context); @@ -86,19 +86,6 @@ void StrategyBase::run() { // 先将行情接收代理停止,以便后面加入处理函数 stopSpotAgent(); - // 根据上下文获取支持的 Stock 列表 - const auto& stk_code_list = getStockCodeList(); - m_stock_list.reserve(stk_code_list.size()); - for (const auto& code : stk_code_list) { - Stock stk = getStock(code); - if (!stk.isNull()) { - m_stock_list.push_back(stk); - } else { - HKU_WARN("[Strategy {}] Invalid code: {}, can't find the stock!", m_name, code); - } - } - HKU_WARN_IF(m_stock_list.empty(), "[Strategy {}] stock list is empty!", m_name); - auto& agent = *getGlobalSpotAgent(); agent.addProcess([this](const SpotRecord& spot) { receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { @@ -111,28 +98,27 @@ void StrategyBase::run() { m_running = true; } -void StrategyBase::start() { - HKU_CHECK(m_running, "No handler functions are registered!"); - HKU_INFO("start strategy even loop ..."); +void Strategy::start() { + CLS_CHECK(m_running, "No handler functions are registered!"); + CLS_INFO("start even loop ..."); _startEventLoop(); } -void StrategyBase::onChange( - std::function&& changeFunc) { +void Strategy::onChange(std::function&& changeFunc) { if (!m_running) { run(); } m_on_change = changeFunc; } -void StrategyBase::onReceivedSpot(std::function&& recievedFucn) { +void Strategy::onReceivedSpot(std::function&& recievedFucn) { if (!m_running) { run(); } m_on_recieved_spot = recievedFucn; } -void StrategyBase::receivedSpot(const SpotRecord& spot) { +void Strategy::receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { if (m_on_change) { @@ -141,8 +127,8 @@ void StrategyBase::receivedSpot(const SpotRecord& spot) { } } -void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta, - const std::string& market) { +void Strategy::runDaily(std::function&& func, const TimeDelta& delta, + const std::string& market) { if (!m_running) { run(); } @@ -224,15 +210,15 @@ void StrategyBase::runDaily(std::function&& func, const TimeDelta& delta }); } else { - HKU_ERROR("Unknown process!"); + CLS_ERROR("Unknown process!"); } } catch (const std::exception& e) { - HKU_THROW(e.what()); + CLS_THROW(e.what()); } } -void StrategyBase::runDailyAt(std::function&& func, const TimeDelta& delta, - bool ignoreHoliday) { +void Strategy::runDailyAt(std::function&& func, const TimeDelta& delta, + bool ignoreHoliday) { if (!m_running) { run(); } @@ -258,7 +244,7 @@ void StrategyBase::runDailyAt(std::function&& func, const TimeDelta& del /* * 在主线程中处理事件队列,避免 python GIL */ -void StrategyBase::_startEventLoop() { +void Strategy::_startEventLoop() { while (ms_keep_running) { event_type task; m_event_queue.wait_and_pop(task); diff --git a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h similarity index 84% rename from hikyuu_cpp/hikyuu/strategy/StrategyBase.h rename to hikyuu_cpp/hikyuu/strategy/Strategy.h index 7919d5df..23b12a4b 100644 --- a/hikyuu_cpp/hikyuu/strategy/StrategyBase.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -17,14 +17,16 @@ namespace hku { -class HKU_API StrategyBase { -public: - StrategyBase(); - explicit StrategyBase(const string& name, const string& config_file = ""); - StrategyBase(const vector& codeList, const vector& ktypeList, - const string& name = "Strategy", const string& config_file = ""); +class HKU_API Strategy { + CLASS_LOGGER_IMP(Strategy) - virtual ~StrategyBase(); +public: + Strategy(); + explicit Strategy(const string& name, const string& config_file = ""); + Strategy(const vector& codeList, const vector& ktypeList, + const string& name = "Strategy", const string& config_file = ""); + + virtual ~Strategy(); const string& name() const { return m_name; @@ -42,14 +44,6 @@ public: m_context = context; } - Datetime startDatetime() const { - return m_context.startDatetime(); - } - - void startDatetime(const Datetime& d) { - m_context.startDatetime(d); - } - void setStockCodeList(vector&& stockList) { m_context.setStockCodeList(std::move(stockList)); } @@ -70,10 +64,6 @@ public: return m_context.getKTypeList(); } - const StockList& getStockList() const { - return m_stock_list; - } - /** * 每日开盘时间内,以 delta 为周期循环定时执行指定任务 * @param func 待执行的任务 @@ -112,7 +102,6 @@ private: string m_name; string m_config_file; StrategyContext m_context; - StockList m_stock_list; std::function m_on_recieved_spot; std::function m_on_change; bool m_running{false}; @@ -154,6 +143,6 @@ private: void _startEventLoop(); }; -typedef shared_ptr StrategyPtr; +typedef shared_ptr StrategyPtr; } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_StrategyBase.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp similarity index 63% rename from hikyuu_pywrap/strategy/_StrategyBase.cpp rename to hikyuu_pywrap/strategy/_Strategy.cpp index 761a2116..63eaf333 100644 --- a/hikyuu_pywrap/strategy/_StrategyBase.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -5,7 +5,7 @@ * Author: fasiondog */ -#include +#include #include #include @@ -13,47 +13,45 @@ namespace py = pybind11; using namespace hku; void export_Strategy(py::module& m) { - py::class_(m, "StrategyBase") + py::class_(m, "Strategy") .def(py::init<>()) .def(py::init&, const vector&, const std::string&, const std::string&>(), py::arg("code_list"), py::arg("ktype_list"), py::arg("name") = "Strategy", py::arg("config") = "") - .def_property("name", py::overload_cast<>(&StrategyBase::name, py::const_), - py::overload_cast(&StrategyBase::name), + .def_property("name", py::overload_cast<>(&Strategy::name, py::const_), + py::overload_cast(&Strategy::name), py::return_value_policy::copy, "策略名称") - .def_property("start_datetime", py::overload_cast<>(&StrategyBase::startDatetime, py::const_), - py::overload_cast(&StrategyBase::startDatetime), "起始日期") - .def_property("stock_list", py::overload_cast<>(&StrategyBase::getStockCodeList, py::const_), - py::overload_cast&>(&StrategyBase::setStockCodeList), + .def_property("stock_list", py::overload_cast<>(&Strategy::getStockCodeList, py::const_), + py::overload_cast&>(&Strategy::setStockCodeList), py::return_value_policy::copy, "股票代码列表") - .def_property("ktype_list", py::overload_cast<>(&StrategyBase::getKTypeList, py::const_), - py::overload_cast&>(&StrategyBase::setKTypeList), + .def_property("ktype_list", py::overload_cast<>(&Strategy::getKTypeList, py::const_), + py::overload_cast&>(&Strategy::setKTypeList), py::return_value_policy::copy, "需要的K线类型") - .def("start", &StrategyBase::start) + .def("start", &Strategy::start) .def("on_change", - [](StrategyBase& self, py::object func) { + [](Strategy& self, py::object func) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); self.onChange(c_func); }) .def("on_received_spot", - [](StrategyBase& self, py::object func) { + [](Strategy& self, py::object func) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); self.onReceivedSpot(c_func); }) .def("run_daily", - [](StrategyBase& self, py::object func, const TimeDelta& time) { + [](Strategy& self, py::object func, const TimeDelta& time) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); self.runDaily(c_func, time); }) .def( "run_daily_at", - [](StrategyBase& self, py::object func, const TimeDelta& time, bool ignore_holiday) { + [](Strategy& self, py::object func, const TimeDelta& time, bool ignore_holiday) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); self.runDailyAt(c_func, time, ignore_holiday); From b87d70a17b08a9fa142ba1e07b81db530cf425b4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 14 Aug 2024 16:21:05 +0800 Subject: [PATCH 449/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy=20continu?= =?UTF-8?q?e?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 10 ++--- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 4 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 15 ++++--- hikyuu_pywrap/strategy/_Strategy.cpp | 44 ++++++++++++++++++-- 4 files changed, 57 insertions(+), 16 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index ff978f6f..a7d1b866 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -7,7 +7,7 @@ from hikyuu import StockManager def on_change(stk, spot): - print(stk, spot.bid1, spot.ask1) + print(stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1) def on_spot(rev_time): @@ -15,16 +15,16 @@ def on_spot(rev_time): def my_func(): + print("calculate:", Datetime.now()) sm = StockManager.instance() - print("{}".format(len(sm))) for s in sm: print(s) if __name__ == '__main__': - s = Strategy(['sh600000', 'sz000001', 'xxx'], [Query.MIN, Query.DAY]) + s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) - # s.on_change(on_change) + s.on_change(on_change) s.on_received_spot(on_spot) - # s.run_daily(my_func, Minutes(1)) + s.run_daily(my_func, Minutes(1)) s.start() diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index e6f72726..3089e1d7 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -160,7 +160,7 @@ void SpotAgent::work_thread() { std::this_thread::sleep_for(std::chrono::seconds(5)); } - HKU_INFO_IF(m_print, "Ready to receive quotation ..."); + HKU_INFO_IF(m_print, "Ready to receive quotation from {} ...", ms_pubUrl); while (!m_stop) { char* buf = nullptr; @@ -184,7 +184,7 @@ void SpotAgent::work_thread() { for (auto& task : m_process_task_list) { task.get(); } - HKU_INFO_IF(m_print, "received count: {}", m_batch_count); + HKU_TRACE_IF(m_print, "received count: {}", m_batch_count); m_batch_count = 0; // 执行后处理 for (const auto& postProcess : m_postProcessList) { diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index f21569cb..5750537c 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -180,7 +180,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, }); } - } else if (now_time == market_info.closeTime1()) { + } else if (now_time >= market_info.closeTime1()) { scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { new_func(); auto* sched = getScheduler(); @@ -193,8 +193,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, if (ticks % delta_ticks == 0) { scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); } else { - auto delay = - TimeDelta::fromTicks(delta_ticks - (ticks / delta_ticks) * delta_ticks); + auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks); scheduler->addFuncAtTime(now + delay, [=]() { new_func(); auto* sched = getScheduler(); @@ -210,7 +209,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, }); } else { - CLS_ERROR("Unknown process!"); + CLS_ERROR("Unknown process! now_time: {}", now_time); } } catch (const std::exception& e) { CLS_THROW(e.what()); @@ -251,7 +250,13 @@ void Strategy::_startEventLoop() { if (task.isNullTask()) { ms_keep_running = false; } else { - task(); + try { + task(); + } catch (const std::exception& e) { + CLS_ERROR("Failed run task! {}", e.what()); + } catch (...) { + CLS_ERROR("Failed run task! Unknow error!"); + } } } } diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 63eaf333..e0acea3e 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -35,26 +35,62 @@ void export_Strategy(py::module& m) { [](Strategy& self, py::object func) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); - self.onChange(c_func); + auto new_func = [=](const Stock& stk, const SpotRecord& spot) { + try { + c_func(stk, spot); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.onChange(new_func); }) .def("on_received_spot", [](Strategy& self, py::object func) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); - self.onReceivedSpot(c_func); + auto new_func = [=](Datetime revTime) { + try { + c_func(revTime); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.onReceivedSpot(new_func); }) .def("run_daily", [](Strategy& self, py::object func, const TimeDelta& time) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); - self.runDaily(c_func, time); + auto new_func = [=]() { + try { + c_func(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.runDaily(new_func, time); }) .def( "run_daily_at", [](Strategy& self, py::object func, const TimeDelta& time, bool ignore_holiday) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); py::object c_func = func.attr("__call__"); - self.runDailyAt(c_func, time, ignore_holiday); + auto new_func = [=]() { + try { + c_func(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.runDailyAt(new_func, time, ignore_holiday); }, py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true); } \ No newline at end of file From 63e782c973328f5793c1ea4f441c539d5756d3a2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 14 Aug 2024 18:10:21 +0800 Subject: [PATCH 450/601] add c++ strategy demo --- hikyuu_cpp/demo/{demo.cpp => demo1.cpp} | 1 + hikyuu_cpp/demo/demo2.cpp | 51 +++ hikyuu_cpp/demo/xmake.lua | 31 +- .../base_info/table/HistoryFinanceTable.h | 4 +- hikyuu_cpp/hikyuu/doc.h | 4 +- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 1 + hikyuu_cpp/hikyuu/hikyuu.h | 1 + .../hikyuu/strategy/AccountTradeManager.cpp | 86 ---- .../hikyuu/strategy/AccountTradeManager.h | 431 ------------------ hikyuu_cpp/hikyuu/strategy/Strategy.h | 6 +- hikyuu_cpp/unit_test/xmake.lua | 6 +- .../strategy/_AccountTradeManager.cpp | 18 - hikyuu_pywrap/strategy/_strategy_main.cpp | 2 - 13 files changed, 92 insertions(+), 550 deletions(-) rename hikyuu_cpp/demo/{demo.cpp => demo1.cpp} (94%) create mode 100644 hikyuu_cpp/demo/demo2.cpp delete mode 100644 hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp delete mode 100644 hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h delete mode 100644 hikyuu_pywrap/strategy/_AccountTradeManager.cpp diff --git a/hikyuu_cpp/demo/demo.cpp b/hikyuu_cpp/demo/demo1.cpp similarity index 94% rename from hikyuu_cpp/demo/demo.cpp rename to hikyuu_cpp/demo/demo1.cpp index 49550c7b..83bd837e 100644 --- a/hikyuu_cpp/demo/demo.cpp +++ b/hikyuu_cpp/demo/demo1.cpp @@ -35,6 +35,7 @@ int main(int argc, char* argv[]) { std::cout << k[i] << std::endl; } + // 启动行情接收(只是计算回测可以不需要) startSpotAgent(true); while (true) { diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp new file mode 100644 index 00000000..c7404f44 --- /dev/null +++ b/hikyuu_cpp/demo/demo2.cpp @@ -0,0 +1,51 @@ +// demo.cpp : 定义控制台应用程序的入口点。 +// + +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +using namespace hku; + +static void changed(const Stock& stk, const SpotRecord& spot) { + HKU_INFO("{} {} 当前收盘价: {}", stk.market_code(), stk.name(), spot.close); +} + +static void my_process1() { + HKU_INFO("{}", getStock("sh000001")); +} + +static void my_process2() { + HKU_INFO("run at time: {} {}", Datetime::now(), getStock("sh000001").name()); +} + +int main(int argc, char* argv[]) { +#if defined(_WIN32) + // Windows 下设置控制台程序输出代码页为 UTF8 + auto old_cp = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); +#endif + + Strategy stg({"sh000001", "sz000001"}, {KQuery::DAY}, "test"); + + // stock 数据变化接收,通常用于调测,直接一般不需要 + stg.onChange(changed); + + // 每日开盘期间,按间隔时间循环执行 + stg.runDaily(my_process1, Minutes(1)); + + // 每日定点执行 + stg.runDailyAt(my_process2, Datetime::now() - Datetime::today() + Seconds(20)); + + // 启动策略 + stg.start(); + +#if defined(_WIN32) + SetConsoleOutputCP(old_cp); +#endif + return 0; +} diff --git a/hikyuu_cpp/demo/xmake.lua b/hikyuu_cpp/demo/xmake.lua index 4dbb2feb..1b7e4ae9 100644 --- a/hikyuu_cpp/demo/xmake.lua +++ b/hikyuu_cpp/demo/xmake.lua @@ -1,4 +1,4 @@ -target("demo") +target("demo1") set_kind("binary") set_default(false) @@ -16,10 +16,31 @@ target("demo") add_defines("SQLITE_API=__declspec(dllimport)") end - -- add files - add_files("./*.cpp") - add_deps("hikyuu") - + + add_files("./demo1.cpp") target_end() + +target("demo2") + set_kind("binary") + set_default(false) + + add_packages("boost", "spdlog", "fmt", "flatbuffers") + add_includedirs("..") + + if is_plat("windows") then + add_cxflags("-wd4267") + add_cxflags("-wd4251") + end + + if is_plat("windows") and get_config("kind") == "shared" then + add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") + add_defines("SQLITE_API=__declspec(dllimport)") + end + + add_deps("hikyuu") + + add_files("./demo2.cpp") +target_end() diff --git a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h index 2120830c..1f469e76 100644 --- a/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h +++ b/hikyuu_cpp/hikyuu/data_driver/base_info/table/HistoryFinanceTable.h @@ -13,8 +13,8 @@ namespace hku { struct HistoryFinanceTable { TABLE_BIND4(HistoryFinanceTable, HistoryFinance, file_date, report_date, market_code, values) - uint64_t file_date; - uint64_t report_date; + uint64_t file_date{0}; + uint64_t report_date{0}; std::string market_code; // std::vector values; std::vector values; diff --git a/hikyuu_cpp/hikyuu/doc.h b/hikyuu_cpp/hikyuu/doc.h index 42d488ec..b61d9b2b 100644 --- a/hikyuu_cpp/hikyuu/doc.h +++ b/hikyuu_cpp/hikyuu/doc.h @@ -137,8 +137,8 @@ * @details 合成多因子 * @ingroup TradeSystem * - * @defgroup SystemInstance SystemInstance 系统实例 - * @details 系统实例 + * @defgroup Stratgy Strategy 策略运行时 + * @details 策略运行时 * @ingroup Hikyuu * * @defgroup Agent Agent 对外数据接收发送代理 diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index f87a0050..5df25ed9 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -5,6 +5,7 @@ * Author: fasiondog */ +#pragma once #include "agent/SpotAgent.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/hikyuu.h b/hikyuu_cpp/hikyuu/hikyuu.h index da871a1c..600e0baa 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.h +++ b/hikyuu_cpp/hikyuu/hikyuu.h @@ -16,6 +16,7 @@ #include "indicator/build_in.h" #include "trade_manage/build_in.h" #include "trade_sys/all.h" +#include "strategy/Strategy.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp deleted file mode 100644 index 8b4e67af..00000000 --- a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.cpp +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright(C) 2021 hikyuu.org - * - * Create on: 2021-03-23 - * Author: fasiondog - */ - -#if 0 -#include -#include "../utilities/arithmetic.h" -#include "AccountTradeManager.h" - -using nlohmann::json; - -namespace hku { - -#define HKU_SERVER_URL "http://localhost:9001" -#define HKU_SERVER_LOGIN_API "/hku/account/v1/login" - -#define HTTP_STATUS_OK 200 -#define HTTP_STATUS_BAD_REQUEST 400 - -static string getHttpClientErrorMsg(httplib::Error err) { - string result; - if (httplib::Error::Success == err) { - result = "Success"; - } else if (httplib::Error::Unknown == err) { - result = "Unknonw error"; - } else if (httplib::Error::Connection == err) { - result = "Connection error"; - } else if (httplib::Error::BindIPAddress == err) { - result = "BindIPAddress error"; - } else if (httplib::Error::Read == err) { - result = "Read error"; - } else if (httplib::Error::Write == err) { - result = "Write error"; - } else if (httplib::Error::ExceedRedirectCount == err) { - result = "ExceedRedirectCount error"; - } else if (httplib::Error::Canceled == err) { - result = "Canceled error"; - } else if (httplib::Error::SSLConnection == err) { - result = "SSLConnection error"; - } else if (httplib::Error::SSLLoadingCerts == err) { - result = "SSLLoadingCerts error"; - } else if (httplib::Error::SSLServerVerification == err) { - result = "SSLServerVerification error"; - } else if (httplib::Error::UnsupportedMultipartBoundaryChars == err) { - result = "UnsupportedMultipartBoundaryChars error"; - } else if (httplib::Error::Compression == err) { - result = "Compression error"; - } else { - result = "Other unknonw error"; - } - return result; -} - -AccountTradeManager::AccountTradeManager(const string& name, const string& pwd) -: TradeManagerBase(name, TC_Zero()), - m_client(std::make_unique(HKU_SERVER_URL)), - m_user(name), - m_password(pwd) { - trim(m_user); - if (m_user.empty()) { - HKU_ERROR("User name is empty."); - return; - } - - string req(fmt::format(R"({{"user":"{}", "password":"{}"}})", name, pwd)); - auto res = m_client->Post(HKU_SERVER_LOGIN_API, req.c_str(), req.size(), "application/json"); - if (!res) { - HKU_ERROR("http client err: {}", getHttpClientErrorMsg(res.error())); - return; - } - - if (HTTP_STATUS_OK == res->status) { - auto data = json::parse(res->body); - m_token = data["hku_token"].get(); - } else if (HTTP_STATUS_BAD_REQUEST == res->status) { - HKU_ERROR("Bad request: {}", res->body); - } else { - HKU_ERROR("http response status: {}", res->status); - } -} - -} // namespace hku -#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h b/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h deleted file mode 100644 index 51435190..00000000 --- a/hikyuu_cpp/hikyuu/strategy/AccountTradeManager.h +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright(C) 2021 hikyuu.org - * - * Create on: 2021-03-23 - * Author: fasiondog - */ - -#pragma once - -#if 0 -#include -#include "../trade_manage/TradeManagerBase.h" - -namespace hku { - -class HKU_API AccountTradeManager : public TradeManagerBase { -public: - AccountTradeManager() = default; - AccountTradeManager(const string& name, const string& pwd); - virtual ~AccountTradeManager() = default; - - virtual void _reset() override {} - - virtual shared_ptr _clone() override { - return std::make_shared(); - } - - /** - * 获取指定对象的保证金比率 - * @param datetime 日期 - * @param stock 指定对象 - */ - virtual double getMarginRate(const Datetime& datetime, const Stock& stock) override { - HKU_WARN("The subclass does not implement a getMarginRate method"); - return 0.0; - } - - /** 初始资金 */ - virtual price_t initCash() const override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** 账户建立日期 */ - virtual Datetime initDatetime() const override { - HKU_WARN("The subclass does not implement this method"); - return Datetime(); - } - - /** 第一笔买入交易发生日期,如未发生交易返回Null() */ - virtual Datetime firstDatetime() const override { - HKU_WARN("The subclass does not implement this method"); - return Datetime(); - } - - /** 最后一笔交易日期,注意和交易类型无关,如未发生交易返回账户建立日期 */ - virtual Datetime lastDatetime() const override { - HKU_WARN("The subclass does not implement this method"); - return Datetime(); - } - - /** - * 返回当前现金 - * @note 仅返回当前信息,不会根据权息进行调整 - */ - virtual price_t currentCash() const override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** - * 获取指定日期的现金 - * @note 如果不带日期参数,无法根据权息信息调整持仓 - */ - virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** - * 当前是否持有指定的证券 - * @note 这里未使用日期参数,必须保证是按日期顺序执行 - * @param stock 指定证券 - * @return true 是 | false 否 - */ - virtual bool have(const Stock& stock) const override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 当前空头仓位是否持有指定的证券 - * @note 这里未使用日期参数,必须保证是按日期顺序执行 - * @param stock 指定证券 - * @return true 是 | false 否 - */ - virtual bool haveShort(const Stock& stock) const override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** 当前持有的证券种类数量 */ - virtual size_t getStockNumber() const override { - HKU_WARN("The subclass does not implement this method"); - return 0; - } - - /** 当前空头持有的证券种类数量 */ - virtual size_t getShortStockNumber() const override { - HKU_WARN("The subclass does not implement this method"); - return 0; - } - - /** 获取指定时刻的某证券持有数量 */ - virtual double getHoldNumber(const Datetime& datetime, const Stock& stock) override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** 获取指定时刻的空头某证券持有数量 */ - virtual double getShortHoldNumber(const Datetime& datetime, const Stock& stock) override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** 获取指定时刻已借入的股票数量 */ - virtual double getDebtNumber(const Datetime& datetime, const Stock& stock) override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** 获取指定时刻已借入的现金额 */ - virtual price_t getDebtCash(const Datetime& datetime) override { - HKU_WARN("The subclass does not implement this method"); - return 0.0; - } - - /** 获取全部交易记录 */ - virtual TradeRecordList getTradeList() const override { - HKU_WARN("The subclass does not implement this method"); - return TradeRecordList(); - } - - /** - * 获取指定日期范围内的交易记录[start, end) - * @param start 起始日期 - * @param end 结束日期 - * @return 交易记录列表 - */ - virtual TradeRecordList getTradeList(const Datetime& start, - const Datetime& end) const override { - HKU_WARN("The subclass does not implement this method"); - return TradeRecordList(); - } - - /** 获取当前全部持仓记录 */ - virtual PositionRecordList getPositionList() const override { - HKU_WARN("The subclass does not implement this method"); - return PositionRecordList(); - } - - /** 获取全部历史持仓记录,即已平仓记录 */ - virtual PositionRecordList getHistoryPositionList() const override { - HKU_WARN("The subclass does not implement this method"); - return PositionRecordList(); - } - - /** 获取当前全部空头仓位记录 */ - virtual PositionRecordList getShortPositionList() const override { - HKU_WARN("The subclass does not implement this method"); - return PositionRecordList(); - } - - /** 获取全部空头历史仓位记录 */ - virtual PositionRecordList getShortHistoryPositionList() const override { - HKU_WARN("The subclass does not implement this method"); - return PositionRecordList(); - } - - /** 获取指定证券的当前持仓记录,如当前未持有该票,返回Null() */ - virtual PositionRecord getPosition(const Datetime&, const Stock&) override { - HKU_WARN("The subclass does not implement this method"); - return PositionRecord(); - } - - /** 获取指定证券的当前空头仓位持仓记录,如当前未持有该票,返回Null() */ - virtual PositionRecord getShortPosition(const Stock&) const override { - HKU_WARN("The subclass does not implement this method"); - return PositionRecord(); - } - - /** 获取当前借入的股票列表 */ - virtual BorrowRecordList getBorrowStockList() const override { - HKU_WARN("The subclass does not implement this method"); - return BorrowRecordList(); - } - - /** - * 存入资金 - * @param datetime 存入时间 - * @param cash 存入的资金量 - * @return true | false - */ - virtual bool checkin(const Datetime& datetime, price_t cash) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 取出资金 - * @param datetime 取出时间 - * @param cash 取出的资金量 - * @return true | false - */ - virtual bool checkout(const Datetime& datetime, price_t cash) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 存入资产 - * @param datetime 存入日期 - * @param stock 待存入的股票 - * @param price 存入股票的每股价格 - * @param number 存入股票的数量 - * @return true | false - */ - virtual bool checkinStock(const Datetime& datetime, const Stock& stock, price_t price, - double number) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 取出当前资产 - * @param datetime 取出日期 - * @param stock 待取出的股票 - * @param price 取出的每股价格 - * @param number 取出的数量 - * @return true | false - * @note 应该不会被用到 - */ - virtual bool checkoutStock(const Datetime& datetime, const Stock& stock, price_t price, - double number) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 买入操作 - * @param datetime 买入时间 - * @param stock 买入的证券 - * @param realPrice 实际买入价格 - * @param number 买入数量 - * @param stoploss 止损价 - * @param goalPrice 目标价格 - * @param planPrice 计划买入价格 - * @param from 记录是哪个系统部件发出的买入指示 - * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID - */ - virtual TradeRecord buy(const Datetime& datetime, const Stock& stock, price_t realPrice, - double number, price_t stoploss = 0.0, price_t goalPrice = 0.0, - price_t planPrice = 0.0, SystemPart from = PART_INVALID) override { - HKU_WARN("The subclass does not implement this method"); - return TradeRecord(); - } - - /** - * 卖出操作 - * @param datetime 卖出时间 - * @param stock 卖出的证券 - * @param realPrice 实际卖出价格 - * @param number 卖出数量,如果是 MAX_DOUBLE, 表示全部卖出 - * @param stoploss 新的止损价 - * @param goalPrice 新的目标价格 - * @param planPrice 原计划卖出价格 - * @param from 记录是哪个系统部件发出的卖出指示 - * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID - */ - virtual TradeRecord sell(const Datetime& datetime, const Stock& stock, price_t realPrice, - double number = MAX_DOUBLE, price_t stoploss = 0.0, - price_t goalPrice = 0.0, price_t planPrice = 0.0, - SystemPart from = PART_INVALID) override { - HKU_WARN("The subclass does not implement this method"); - return TradeRecord(); - } - - /** - * 卖空 - * @param datetime 卖空时间 - * @param stock 卖空的证券 - * @param realPrice 实际卖空价格 - * @param number 卖出数量 - * @param stoploss 止损价 - * @param goalPrice 目标价格 - * @param planPrice 计划卖空价格 - * @param from 记录是哪个系统部件发出的买入指示 - * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID - */ - virtual TradeRecord sellShort(const Datetime& datetime, const Stock& stock, price_t realPrice, - double number, price_t stoploss = 0.0, price_t goalPrice = 0.0, - price_t planPrice = 0.0, - SystemPart from = PART_INVALID) override { - HKU_WARN("The subclass does not implement this method"); - return TradeRecord(); - } - - /** - * 卖空后回补 - * @param datetime 买入时间 - * @param stock 买入的证券 - * @param realPrice 实际买入价格 - * @param number 卖出数量,如果是 MAX_DOUBLE, 表示全部卖出 - * @param stoploss 止损价 - * @param goalPrice 目标价格 - * @param planPrice 计划买入价格 - * @param from 记录是哪个系统部件发出的卖出指示 - * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID - */ - virtual TradeRecord buyShort(const Datetime& datetime, const Stock& stock, price_t realPrice, - double number = MAX_DOUBLE, price_t stoploss = 0.0, - price_t goalPrice = 0.0, price_t planPrice = 0.0, - SystemPart from = PART_INVALID) override { - HKU_WARN("The subclass does not implement this method"); - return TradeRecord(); - } - - /** - * 借入资金,从其他来源借取的资金,如融资 - * @param datetime 借入时间 - * @param cash 借入的现金 - * @return true | false - */ - virtual bool borrowCash(const Datetime& datetime, price_t cash) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 归还资金 - * @param datetime 归还日期 - * @param cash 归还现金 - * @return true | false - */ - virtual bool returnCash(const Datetime& datetime, price_t cash) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 借入证券 - * @param datetime 借入时间 - * @param stock 借入的stock - * @param price 借入时单股价格 - * @param number 借入时数量 - * @return true | false - */ - virtual bool borrowStock(const Datetime& datetime, const Stock& stock, price_t price, - double number) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 归还证券 - * @param datetime 归还时间 - * @param stock 归还的stock - * @param price 归还时单股价格 - * @param number 归还数量 - * @return true | false - */ - virtual bool returnStock(const Datetime& datetime, const Stock& stock, price_t price, - double number) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** - * 获取指定时刻的资产市值详情 - * @param datetime 必须大于帐户建立的初始日期,或为Null() - * @param ktype 日期的类型 - * @return 资产详情 - * @note 当datetime等于Null()时,与getFunds(KType)同 - */ - virtual FundsRecord getFunds(const Datetime& datetime, - KQuery::KType ktype = KQuery::DAY) override { - HKU_WARN("The subclass does not implement this method"); - return FundsRecord(); - } - - /** - * 直接加入交易记录 - * @note 如果加入初始化账户记录,将清除全部已有交易及持仓记录 - * @param tr 待加入的交易记录 - * @return bool true 成功 | false 失败 - */ - virtual bool addTradeRecord(const TradeRecord& tr) override { - HKU_WARN("The subclass does not implement this method"); - return false; - } - - /** 字符串输出 */ - virtual string str() const override { - HKU_WARN("The subclass does not implement this method"); - return string(); - } - - /** - * 以csv格式输出交易记录、未平仓记录、已平仓记录、资产净值曲线 - * @param path 输出文件所在目录 - */ - virtual void tocsv(const string& path) override { - HKU_WARN("The subclass does not implement this method"); - } - -private: - string getToken(); - -private: - std::unique_ptr m_client; - string m_user; - string m_password; - string m_token; -}; - -inline TMPtr crtAccountTM(const string& name, const string& pwd) { - return std::make_shared(name, pwd); -} - -} // namespace hku -#endif \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 23b12a4b..828eb203 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -17,6 +17,10 @@ namespace hku { +/** + * @brief 策略运行时 + * @ingroup Stratgy + */ class HKU_API Strategy { CLASS_LOGGER_IMP(Strategy) @@ -85,7 +89,7 @@ public: void start(); /** - * 数据发生变化,即接收到相应行情数据变更 + * 正确数据发生变化调用,即接收到相应行情数据变更 * @note 通常用于调试 * @param stk 数据发生变化的 stock * @param spot 接收到的具体数据 diff --git a/hikyuu_cpp/unit_test/xmake.lua b/hikyuu_cpp/unit_test/xmake.lua index 40108e33..0a2f7206 100644 --- a/hikyuu_cpp/unit_test/xmake.lua +++ b/hikyuu_cpp/unit_test/xmake.lua @@ -59,7 +59,7 @@ target("unit-test") set_kind("binary") set_default(false) - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3", "flatbuffers") if get_config("mysql") then if is_plat("macosx") then add_packages("mysqlclient") @@ -105,7 +105,7 @@ target("small-test") set_kind("binary") set_default(false) - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3", "flatbuffers") if get_config("mysql") then if is_plat("macosx") then add_packages("mysqlclient") @@ -150,7 +150,7 @@ target("real-test") set_kind("binary") set_default(false) - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3", "flatbuffers") if get_config("mysql") then if is_plat("macosx") then add_packages("mysqlclient") diff --git a/hikyuu_pywrap/strategy/_AccountTradeManager.cpp b/hikyuu_pywrap/strategy/_AccountTradeManager.cpp deleted file mode 100644 index ff9ee90d..00000000 --- a/hikyuu_pywrap/strategy/_AccountTradeManager.cpp +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright(C) 2021 hikyuu.org - * - * Create on: 2021-03-28 - * Author: fasiondog - */ - -#if 0 -#include "../pybind_utils.h" -#include - -using namespace hku; -namespace py = pybind11; - -void export_AccountTradeManger(py::module& m) { - m.def("crtAccountTM", crtAccountTM); -} -#endif \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_strategy_main.cpp b/hikyuu_pywrap/strategy/_strategy_main.cpp index 8ac8c992..2dbe2bab 100644 --- a/hikyuu_pywrap/strategy/_strategy_main.cpp +++ b/hikyuu_pywrap/strategy/_strategy_main.cpp @@ -10,9 +10,7 @@ namespace py = pybind11; void export_Strategy(py::module& m); -// void export_AccountTradeManger(py::module& m); void export_strategy_main(py::module& m) { export_Strategy(m); - // export_AccountTradeManger(m); } \ No newline at end of file From 1ae22379bfa453e9c21521795921f96b4a619af9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 14 Aug 2024 18:31:27 +0800 Subject: [PATCH 451/601] update --- hikyuu/strategy/demo/__init__.py | 3 --- hikyuu/strategy/strategy.py | 1 + sub_setup.py | 35 -------------------------------- 3 files changed, 1 insertion(+), 38 deletions(-) delete mode 100644 hikyuu/strategy/demo/__init__.py diff --git a/hikyuu/strategy/demo/__init__.py b/hikyuu/strategy/demo/__init__.py deleted file mode 100644 index 24133630..00000000 --- a/hikyuu/strategy/demo/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf8 -*- -# cp936 diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index a7d1b866..5a0f54e3 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -21,6 +21,7 @@ def my_func(): print(s) +# 以 Strategy 方式运行示例 if __name__ == '__main__': s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) diff --git a/sub_setup.py b/sub_setup.py index 9b6866f8..1cda3cb5 100644 --- a/sub_setup.py +++ b/sub_setup.py @@ -53,41 +53,6 @@ with open("./readme.md", encoding='utf-8') as f: hku_data_files = [] -# packages = [ -# 'hikyuu', -# 'hikyuu/analysis', -# 'hikyuu/config', -# 'hikyuu/config/block', -# 'hikyuu/cpp', -# 'hikyuu/data', -# 'hikyuu/data/mysql_upgrade', -# 'hikyuu/data/sqlite_upgrade', -# 'hikyuu/data/sqlite_mem_sql', -# 'hikyuu/data_driver', -# 'hikyuu/examples', -# 'hikyuu/examples/notebook', -# 'hikyuu/examples/notebook/images', -# 'hikyuu/examples/notebook/Demo', -# 'hikyuu/flat', -# 'hikyuu/fetcher', -# 'hikyuu/fetcher/proxy', -# 'hikyuu/fetcher/stock', -# 'hikyuu/gui', -# 'hikyuu/gui/data', -# 'hikyuu/indicator', -# 'hikyuu/draw', -# 'hikyuu/draw/drawplot', -# 'hikyuu/shell', -# 'hikyuu/strategy', -# 'hikyuu/strategy/demo', -# 'hikyuu/test', -# 'hikyuu/tools', -# 'hikyuu/trade_manage', -# 'hikyuu/trade_sys', -# 'hikyuu/util', -# 'hikyuu/include', -# ] - packages = ['hikyuu'] for root, dirs, files in os.walk('hikyuu'): for p in dirs: From 8b1019bd62635e98835a2fb3f5e595fe80b5731f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 15 Aug 2024 01:17:17 +0800 Subject: [PATCH 452/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E5=8C=85=E5=90=AB=E5=87=8F=E5=B0=91=E5=A4=96=E9=83=A8=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E4=BE=9D=E8=B5=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/xmake.lua | 4 +- hikyuu_cpp/hikyuu/global/SpotRecord.h | 52 ++++++++++++++++++++ hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 1 + hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 46 +++-------------- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 1 + hikyuu_cpp/hikyuu/strategy/Strategy.h | 2 +- hikyuu_cpp/unit_test/xmake.lua | 6 +-- 7 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/global/SpotRecord.h diff --git a/hikyuu_cpp/demo/xmake.lua b/hikyuu_cpp/demo/xmake.lua index 1b7e4ae9..59fb328a 100644 --- a/hikyuu_cpp/demo/xmake.lua +++ b/hikyuu_cpp/demo/xmake.lua @@ -2,7 +2,7 @@ target("demo1") set_kind("binary") set_default(false) - add_packages("boost", "spdlog", "fmt", "flatbuffers") + add_packages("boost", "spdlog", "fmt") add_includedirs("..") if is_plat("windows") then @@ -26,7 +26,7 @@ target("demo2") set_kind("binary") set_default(false) - add_packages("boost", "spdlog", "fmt", "flatbuffers") + add_packages("boost", "spdlog", "fmt") add_includedirs("..") if is_plat("windows") then diff --git a/hikyuu_cpp/hikyuu/global/SpotRecord.h b/hikyuu_cpp/hikyuu/global/SpotRecord.h new file mode 100644 index 00000000..ef69983b --- /dev/null +++ b/hikyuu_cpp/hikyuu/global/SpotRecord.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-15 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/DataType.h" + +namespace hku { + +/** + * 接收外部实时数据结构 + * @ingroup Agent + */ +struct HKU_API SpotRecord { + string market; ///< 市场标识 + string code; ///< 证券代码 + string name; ///< 证券名称 + Datetime datetime; ///< 数据时间 + price_t yesterday_close; ///< 昨日收盘价 + price_t open; ///< 开盘价 + price_t high; ///< 最高价 + price_t low; ///< 最低价 + price_t close; ///< 收盘价 + price_t amount; ///< 成交金额 (千元) + price_t volume; ///< 成交量(手) + price_t bid1; ///< 买一价 + price_t bid1_amount; ///< 买一数量(手) + price_t bid2; ///< 买二价 + price_t bid2_amount; ///< 买二数量 + price_t bid3; ///< 买三价 + price_t bid3_amount; ///< 买三数量 + price_t bid4; ///< 买四价 + price_t bid4_amount; ///< 买四数量 + price_t bid5; ///< 买五价 + price_t bid5_amount; ///< 买五数量 + price_t ask1; ///< 卖一价 + price_t ask1_amount; ///< 卖一数量 + price_t ask2; ///< 卖二价 + price_t ask2_amount; ///< 卖二数量 + price_t ask3; ///< 卖三价 + price_t ask3_amount; ///< 卖三数量 + price_t ask4; ///< 卖四价 + price_t ask4_amount; ///< 卖四数量 + price_t ask5; ///< 卖五价 + price_t ask5_amount; ///< 卖五数量 +}; + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 3089e1d7..3334114e 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "spot_generated.h" #include "SpotAgent.h" using namespace hikyuu::flat; diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index eb4558d6..03a49c6d 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -9,50 +9,18 @@ #include #include -#include "spot_generated.h" #include "../../DataType.h" #include "../../utilities/thread/ThreadPool.h" +#include "../SpotRecord.h" + +namespace hikyuu { +namespace flat { +struct Spot; +} +} // namespace hikyuu namespace hku { -/** - * 接收外部实时数据结构 - * @ingroup Agent - */ -struct HKU_API SpotRecord { - string market; ///< 市场标识 - string code; ///< 证券代码 - string name; ///< 证券名称 - Datetime datetime; ///< 数据时间 - price_t yesterday_close; ///< 昨日收盘价 - price_t open; ///< 开盘价 - price_t high; ///< 最高价 - price_t low; ///< 最低价 - price_t close; ///< 收盘价 - price_t amount; ///< 成交金额 (千元) - price_t volume; ///< 成交量(手) - price_t bid1; ///< 买一价 - price_t bid1_amount; ///< 买一数量(手) - price_t bid2; ///< 买二价 - price_t bid2_amount; ///< 买二数量 - price_t bid3; ///< 买三价 - price_t bid3_amount; ///< 买三数量 - price_t bid4; ///< 买四价 - price_t bid4_amount; ///< 买四数量 - price_t bid5; ///< 买五价 - price_t bid5_amount; ///< 买五数量 - price_t ask1; ///< 卖一价 - price_t ask1_amount; ///< 卖一数量 - price_t ask2; ///< 卖二价 - price_t ask2_amount; ///< 卖二数量 - price_t ask3; ///< 卖三价 - price_t ask3_amount; ///< 卖三数量 - price_t ask4; ///< 卖四价 - price_t ask4_amount; ///< 卖四数量 - price_t ask5; ///< 卖五价 - price_t ask5_amount; ///< 卖五数量 -}; - /** * 接收外部实时数据代理 * @ingroup Agent diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 5750537c..d1377ca7 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -9,6 +9,7 @@ #include #include "hikyuu/utilities/os.h" #include "hikyuu/utilities/ini_parser/IniParser.h" +#include "hikyuu/global/GlobalSpotAgent.h" #include "hikyuu/global/schedule/scheduler.h" #include "hikyuu/global/GlobalTaskGroup.h" #include "hikyuu/global/sysinfo.h" diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 828eb203..7b2563f8 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -10,9 +10,9 @@ #include #include "../DataType.h" #include "../StrategyContext.h" +#include "../global/SpotRecord.h" #include "../utilities/thread/FuncWrapper.h" #include "../utilities/thread/ThreadSafeQueue.h" -#include "../global/GlobalSpotAgent.h" #include "../trade_sys/portfolio/Portfolio.h" namespace hku { diff --git a/hikyuu_cpp/unit_test/xmake.lua b/hikyuu_cpp/unit_test/xmake.lua index 0a2f7206..40108e33 100644 --- a/hikyuu_cpp/unit_test/xmake.lua +++ b/hikyuu_cpp/unit_test/xmake.lua @@ -59,7 +59,7 @@ target("unit-test") set_kind("binary") set_default(false) - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3", "flatbuffers") + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then if is_plat("macosx") then add_packages("mysqlclient") @@ -105,7 +105,7 @@ target("small-test") set_kind("binary") set_default(false) - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3", "flatbuffers") + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then if is_plat("macosx") then add_packages("mysqlclient") @@ -150,7 +150,7 @@ target("real-test") set_kind("binary") set_default(false) - add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3", "flatbuffers") + add_packages("boost", "fmt", "spdlog", "doctest", "sqlite3") if get_config("mysql") then if is_plat("macosx") then add_packages("mysqlclient") From 200864087582dd65ae2cbf88d2700bbae1915129 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 15 Aug 2024 13:48:06 +0800 Subject: [PATCH 453/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 4 ++-- hikyuu_cpp/hikyuu/strategy/Strategy.h | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 5a0f54e3..4cd8d76e 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -3,7 +3,7 @@ # cp936 from hikyuu import Strategy, Query, Datetime, TimeDelta, Seconds, Minutes -from hikyuu import StockManager +from hikyuu import sm def on_change(stk, spot): @@ -16,11 +16,11 @@ def on_spot(rev_time): def my_func(): print("calculate:", Datetime.now()) - sm = StockManager.instance() for s in sm: print(s) +# 注意:每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! # 以 Strategy 方式运行示例 if __name__ == '__main__': s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 7b2563f8..e5154129 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -86,8 +86,6 @@ public: void runDailyAt(std::function&& func, const TimeDelta& delta, bool ignoreHoliday = true); - void start(); - /** * 正确数据发生变化调用,即接收到相应行情数据变更 * @note 通常用于调试 @@ -102,6 +100,11 @@ public: */ void onReceivedSpot(std::function&& recievedFucn); + /** + * 启动策略执行,必须在已注册相关处理函数后执行 + */ + void start(); + private: string m_name; string m_config_file; From 8189538924f31ad1fc3d19171c1f70db67908f06 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 15 Aug 2024 13:48:22 +0800 Subject: [PATCH 454/601] =?UTF-8?q?TradeManagerBase=E8=A1=A5=E5=85=85?= =?UTF-8?q?=E5=86=BB=E7=BB=93=E8=B5=84=E9=87=91=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h | 7 +++++++ hikyuu_pywrap/trade_manage/_TradeManager.cpp | 8 ++++++++ 2 files changed, 15 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index 46f7584d..16d255c3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -322,6 +322,13 @@ public: return 0.0; } + /** + * 当前冻结现金 + */ + virtual price_t currentFrozen() const { + return 0.0; + } + /** * 获取指定日期的现金 * @note 如果不带日期参数,无法根据权息信息调整持仓 diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index b24d730f..9b65c428 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -51,6 +51,10 @@ public: PYBIND11_OVERRIDE_NAME(price_t, TradeManagerBase, "current_cash", currentCash); } + price_t currentFrozen() const override { + PYBIND11_OVERRIDE_NAME(price_t, TradeManagerBase, "current_frozen", currentFrozen); + } + price_t cash(const Datetime& datetime, KQuery::KType ktype) override { PYBIND11_OVERLOAD(price_t, TradeManagerBase, cash, datetime, ktype); } @@ -281,6 +285,10 @@ void export_TradeManager(py::module& m) { 默认情况下,TradeManager会在执行买入/卖出操作时,调用订单代理执行代理的买入/卖出动作,但这样在实盘操作时会存在问题。因为系统在计算信号指示时,需要回溯历史数据才能得到最新的信号,这样TradeManager会在历史时刻就执行买入/卖出操作,此时如果订单代理本身没有对发出买入/卖出指令的时刻进行控制,会导致代理发送错误的指令。此时,需要指定在某一个时刻之后,才允许指定订单代理的买入/卖出操作。属性 brokeLastDatetime 即用于指定该时刻。)") + .def("current_frozen", &TradeManagerBase::currentFrozen, R"(frozen(self) + + 获取当前冻结资金)") + .def("getParam", &TradeManagerBase::getParam, R"(get_param(self, name) 获取指定的参数 From 3e1b0430a5ccf91c849afa8232cfb1d1016a0105 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 15 Aug 2024 15:53:50 +0800 Subject: [PATCH 455/601] =?UTF-8?q?TradeManager=20=E5=A2=9E=E5=8A=A0=20add?= =?UTF-8?q?Position=20=E7=94=A8=E4=BA=8E=E6=9E=84=E5=BB=BA=E5=AD=98?= =?UTF-8?q?=E5=9C=A8=E5=88=9D=E5=A7=8B=E6=8C=81=E4=BB=93=E7=9A=84=E8=B4=A6?= =?UTF-8?q?=E6=88=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_manage/TradeManager.cpp | 20 +++++++++++++++++++ hikyuu_cpp/hikyuu/trade_manage/TradeManager.h | 9 +++++++++ .../hikyuu/trade_manage/TradeManagerBase.h | 11 ++++++++++ hikyuu_pywrap/trade_manage/_TradeManager.cpp | 11 ++++++++++ 4 files changed, 51 insertions(+) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index bc2212b2..0c11e489 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -1709,6 +1709,26 @@ void TradeManager::tocsv(const string& path) { file.close(); } +bool TradeManager::addPosition(const PositionRecord& pr) { + HKU_ERROR_IF_RETURN(pr.stock.isNull(), false, "Invalid postion record! stock is null!"); + HKU_ERROR_IF_RETURN(pr.cleanDatetime != Null(), false, + "Position cleanDatetime({}) must be Null!", pr.cleanDatetime); + HKU_ERROR_IF_RETURN(pr.takeDatetime < initDatetime(), false, + "Poistion takeDatetime({}) > initDatetime({})", pr.takeDatetime, + initDatetime()); + HKU_ERROR_IF_RETURN(!m_trade_list.empty(), false, "Exist trade list!"); + + auto iter = m_position.find(pr.stock.id()); + HKU_ERROR_IF_RETURN(iter != m_position.end(), false, "The stock({}) has position!", + pr.stock.market_code()); + + m_position[pr.stock.id()] = pr; + if (pr.takeDatetime > m_init_datetime) { + m_init_datetime = pr.takeDatetime; + } + return true; +} + bool TradeManager::addTradeRecord(const TradeRecord& tr) { HKU_IF_RETURN(BUSINESS_INIT == tr.business, _add_init_tr(tr)); HKU_ERROR_IF_RETURN(tr.datetime < lastDatetime(), false, diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h index 8700d69a..2e4d5bd1 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.h @@ -344,6 +344,15 @@ public: */ virtual bool addTradeRecord(const TradeRecord& tr) override; + /** + * 直接加入持仓记录 + * @note 特殊用途构建初始持仓,可能引起混乱 + * @param pr 持仓记录 + * @return true 成功 + * @return false 失败 + */ + virtual bool addPosition(const PositionRecord& pr) override; + /** 字符串输出 */ virtual string str() const override; diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index 16d255c3..595f1de1 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -675,6 +675,17 @@ public: return false; } + /** + * 直接加入持仓记录 + * @param pr 持仓记录 + * @return true 成功 + * @return false 失败 + */ + virtual bool addPosition(const PositionRecord& pr) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + /** 字符串输出 */ virtual string str() const { HKU_WARN("The subclass does not implement this method"); diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index 9b65c428..732cf355 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -220,6 +220,10 @@ public: PYBIND11_OVERRIDE_NAME(bool, TradeManagerBase, "add_trade_record", addTradeRecord, tr); } + bool addPosition(const PositionRecord& pr) override { + PYBIND11_OVERRIDE_NAME(bool, TradeManagerBase, "add_position", addPosition, pr); + } + string str() const override { PYBIND11_OVERRIDE_NAME(string, TradeManagerBase, "__str__", str, ); } @@ -544,6 +548,13 @@ void export_TradeManager(py::module& m) { :return: True(成功) | False(失败) :rtype: bool)") + .def("add_position", &TradeManagerBase::addPosition, R"(add_postion(self, position) + + 建立初始账户后,直接加入持仓记录,仅用于构建初始有持仓的账户 + + :param PositionRecord position: 持仓记录 + return True | False)") + .def("tocsv", &TradeManagerBase::tocsv, R"(tocsv(self, path) 以csv格式输出交易记录、未平仓记录、已平仓记录、资产净值曲线 From 468c97e960eccb280815d5e1eb059362b05ccf36 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 16 Aug 2024 09:18:44 +0800 Subject: [PATCH 456/601] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=89=93=E5=8D=B0?= =?UTF-8?q?=E5=A4=9A=E4=BD=99=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index e3038991..daab49f0 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -74,11 +74,11 @@ void GlobalInitializer::init() { } void GlobalInitializer::clean() { - if (CanUpgrade()) { + if (runningInPython() && CanUpgrade()) { fmt::print( "\n====================================================================\n" "The new version of Hikyuu is {}, and you can run the upgrade command:\n" - "Hikyuu 的最新新版本是 {}, 您可以运行升级命令:\n" + "Hikyuu 的最新版本是 {}, 您可以运行升级命令:\n" "pip install hikyuu --upgrade\n" "========================================================\n\n", getLatestVersion(), getLatestVersion()); From ba66be0705350f1c773d194ee8e733c22dbf4600 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 16 Aug 2024 09:44:27 +0800 Subject: [PATCH 457/601] update --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index daab49f0..2552143b 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -74,6 +74,7 @@ void GlobalInitializer::init() { } void GlobalInitializer::clean() { +#if HKU_ENABLE_SEND_FEEDBACK if (runningInPython() && CanUpgrade()) { fmt::print( "\n====================================================================\n" @@ -83,6 +84,7 @@ void GlobalInitializer::clean() { "========================================================\n\n", getLatestVersion(), getLatestVersion()); } +#endif releaseGlobalTaskGroup(); releaseScheduler(); From 7bf435defff49b7582eaadcaa22474b233e4341e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 16 Aug 2024 16:45:26 +0800 Subject: [PATCH 458/601] upate --- .../hikyuu/strategy/OrderTradeManager.cpp | 207 ++++++++ .../hikyuu/strategy/OrderTradeManager.h | 487 ++++++++++++++++++ 2 files changed, 694 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp create mode 100644 hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h diff --git a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp new file mode 100644 index 00000000..b51553a0 --- /dev/null +++ b/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp @@ -0,0 +1,207 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-16 + * Author: fasiondog + */ + +#include "hikyuu/trade_manage/crt/TC_Zero.h" +#include "OrderTradeManager.h" + +namespace hku { + +OrderTradeManager::OrderTradeManager(const Datetime& datetime, price_t initcash, + const TradeCostPtr& costfunc, const string& name) +: TradeManagerBase(name, costfunc), + m_init_datetime(datetime), + m_first_datetime(datetime), + m_last_datetime(datetime) { + m_init_cash = roundEx(initcash, 2); + m_cash = m_init_cash; + m_broker_last_datetime = Datetime::now(); +} + +void OrderTradeManager::_reset() { + HKU_WARN("The subclass does not implement a reset method"); + m_first_datetime = m_init_datetime; + m_last_datetime = m_init_datetime; + m_cash = m_init_cash; + m_frozen_cash = 0.0; + m_position.clear(); +} + +shared_ptr OrderTradeManager::_clone() { + OrderTradeManager* p = new OrderTradeManager(m_init_datetime, m_init_cash, m_costfunc, m_name); + p->m_init_datetime = m_init_datetime; + p->m_first_datetime = m_first_datetime; + p->m_last_datetime = m_last_datetime; + p->m_init_cash = m_init_cash; + p->m_cash = m_cash; + p->m_frozen_cash = m_frozen_cash; + p->m_position = m_position; + return shared_ptr(p); +} + +PositionRecordList OrderTradeManager::getPositionList() const { + PositionRecordList result; + position_map_type::const_iterator iter = m_position.begin(); + for (; iter != m_position.end(); ++iter) { + result.push_back(iter->second); + } + return result; +} + +bool OrderTradeManager::checkin(const Datetime& datetime, price_t cash) { + HKU_IF_RETURN(datetime < m_last_datetime, false); + m_cash += cash; + return true; +} + +TradeRecord OrderTradeManager::buy(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number, price_t stoploss, price_t goalPrice, + price_t planPrice, SystemPart from) { + TradeRecord result; + result.business = BUSINESS_INVALID; + + HKU_ERROR_IF_RETURN(stock.isNull(), result, "{} Stock is Null!", datetime); + HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result, + "{} {} datetime must be >= lastDatetime({})!", datetime, + stock.market_code(), lastDatetime()); + HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} numer is zero!", datetime, + stock.market_code()); + HKU_ERROR_IF_RETURN(number < stock.minTradeNumber(), result, + "{} {} Buy number({}) must be >= minTradeNumber({})!", datetime, + stock.market_code(), number, stock.minTradeNumber()); + HKU_ERROR_IF_RETURN(number > stock.maxTradeNumber(), result, + "{} {} Buy number({}) must be <= maxTradeNumber({})!", datetime, + stock.market_code(), number, stock.maxTradeNumber()); + + CostRecord cost = getBuyCost(datetime, stock, realPrice, number); + + // 实际交易需要的现金=交易数量*实际交易价格+交易总成本 + int precision = getParam("precision"); + // price_t money = roundEx(realPrice * number * stock.unit() + cost.total, precision); + price_t money = roundEx(realPrice * number * stock.unit(), precision); + + HKU_WARN_IF_RETURN(m_cash < roundEx(money + cost.total, precision), result, + "{} {} Can't buy, need cash({:<.4f}) > current cash({:<.4f})!", datetime, + stock.market_code(), roundEx(money + cost.total, precision), m_cash); + + // 更新现金 + m_cash = roundEx(m_cash - money - cost.total, precision); + + // 加入交易记录 + result = TradeRecord(stock, datetime, BUSINESS_BUY, planPrice, realPrice, goalPrice, number, + cost, stoploss, m_cash, from); + + // 更新当前持仓记录 + position_map_type::iterator pos_iter = m_position.find(stock.id()); + if (pos_iter == m_position.end()) { + m_position[stock.id()] = PositionRecord( + stock, datetime, Null(), number, stoploss, goalPrice, number, money, cost.total, + roundEx((realPrice - stoploss) * number * stock.unit(), precision), 0.0); + } else { + PositionRecord& position = pos_iter->second; + position.number += number; + position.stoploss = stoploss; + position.goalPrice = goalPrice; + position.totalNumber += number; + position.buyMoney = roundEx(money + position.buyMoney, precision); + position.totalCost = roundEx(cost.total + position.totalCost, precision); + position.totalRisk = + roundEx(position.totalRisk + (realPrice - stoploss) * number * stock.unit(), precision); + } + + if (result.datetime > m_broker_last_datetime) { + list::const_iterator broker_iter = m_broker_list.begin(); + Datetime realtime, nulltime; + for (; broker_iter != m_broker_list.end(); ++broker_iter) { + realtime = + (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + if (realtime != nulltime && realtime > m_broker_last_datetime) { + m_broker_last_datetime = realtime; + } + } + } + + return result; +} + +TradeRecord OrderTradeManager::sell(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number, price_t stoploss, price_t goalPrice, + price_t planPrice, SystemPart from) { + HKU_CHECK(!std::isnan(number), "sell number should be a valid double!"); + TradeRecord result; + + HKU_ERROR_IF_RETURN(stock.isNull(), result, "{} Stock is Null!", datetime); + HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result, + "{} {} datetime must be >= lastDatetime({})!", datetime, + stock.market_code(), lastDatetime()); + HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} number is zero!", datetime, + stock.market_code()); + + // 对于分红扩股造成不满足最小交易量整数倍的情况,只能通过number=MAX_DOUBLE的方式全仓卖出 + HKU_ERROR_IF_RETURN(number < stock.minTradeNumber(), result, + "{} {} Sell number({}) must be >= minTradeNumber({})!", datetime, + stock.market_code(), number, stock.minTradeNumber()); + HKU_ERROR_IF_RETURN(number != MAX_DOUBLE && number > stock.maxTradeNumber(), result, + "{} {} Sell number({}) must be <= maxTradeNumber({})!", datetime, + stock.market_code(), number, stock.maxTradeNumber()); + + // 未持仓 + position_map_type::iterator pos_iter = m_position.find(stock.id()); + HKU_TRACE_IF_RETURN(pos_iter == m_position.end(), result, + "{} {} This stock was not bought never! ({}, {:<.4f}, {}, {})", datetime, + stock.market_code(), datetime, realPrice, number, getSystemPartName(from)); + + PositionRecord& position = pos_iter->second; + + // 调整欲卖出的数量,如果卖出数量等于MAX_DOUBLE,则表示卖出全部 + double real_number = number == MAX_DOUBLE ? position.number : number; + + // 欲卖出的数量大于当前持仓的数量 + HKU_ERROR_IF_RETURN(position.number < real_number, result, + "{} {} Try to sell number({}) > number of position({})!", datetime, + stock.market_code(), real_number, position.number); + + CostRecord cost = getSellCost(datetime, stock, realPrice, real_number); + + int precision = getParam("precision"); + price_t money = roundEx(realPrice * real_number * stock.unit(), precision); + + // 更新现金余额 + m_cash = roundEx(m_cash + money - cost.total, precision); + + // 更新交易记录 + result = TradeRecord(stock, datetime, BUSINESS_SELL, planPrice, realPrice, goalPrice, + real_number, cost, stoploss, m_cash, from); + + // 更新当前持仓情况 + position.number -= real_number; + position.stoploss = stoploss; + position.goalPrice = goalPrice; + // position.buyMoney = position.buyMoney; + position.totalCost = roundEx(position.totalCost + cost.total, precision); + position.sellMoney = roundEx(position.sellMoney + money, precision); + + if (position.number == 0) { + // 删除当前持仓 + m_position.erase(stock.id()); + } + + if (result.datetime > m_broker_last_datetime) { + list::const_iterator broker_iter = m_broker_list.begin(); + Datetime realtime, nulltime; + for (; broker_iter != m_broker_list.end(); ++broker_iter) { + realtime = + (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); + if (realtime != nulltime && realtime > m_broker_last_datetime) { + m_broker_last_datetime = realtime; + } + } + } + + return result; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h b/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h new file mode 100644 index 00000000..3579cff1 --- /dev/null +++ b/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h @@ -0,0 +1,487 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-16 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_manage/TradeManagerBase.h" + +namespace hku { + +class HKU_API OrderTradeManager : public TradeManagerBase { +public: + explicit OrderTradeManager(const Datetime& datetime = Datetime(199001010000LL), + price_t initcash = 100000.0, + const TradeCostPtr& costfunc = TC_Zero(), + const string& name = "SYS"); + virtual ~OrderTradeManager() {} + + virtual void _reset() override; + + virtual shared_ptr _clone() override; + + /** + * 根据权息信息更新当前持仓与交易情况 + * @note 必须按时间顺序调用 + * @param datetime 当前时刻 + */ + virtual void updateWithWeight(const Datetime& datetime) override {} + + /** + * 获取指定对象的保证金比率 + * @param datetime 日期 + * @param stock 指定对象 + */ + virtual double getMarginRate(const Datetime& datetime, const Stock& stock) { + HKU_WARN("The subclass does not implement a getMarginRate method"); + return 0.0; + } + + /** 初始资金 */ + virtual price_t initCash() const override { + return m_init_cash; + } + + /** 账户建立日期 */ + virtual Datetime initDatetime() const override { + return m_init_datetime; + } + + /** 第一笔买入交易发生日期,如未发生交易返回Null() */ + virtual Datetime firstDatetime() const { + HKU_WARN("The subclass does not implement this method"); + return Datetime(); + } + + /** 最后一笔交易日期,注意和交易类型无关,如未发生交易返回账户建立日期 */ + virtual Datetime lastDatetime() const { + return m_last_datetime; + } + + /** + * 返回当前现金 + * @note 仅返回当前信息,不会根据权息进行调整 + */ + virtual price_t currentCash() const override { + return m_cash; + } + + /** + * 当前冻结现金 + */ + virtual price_t currentFrozen() const override { + return m_frozen_cash; + } + + /** + * 获取指定日期的现金 + * @note 如果不带日期参数,无法根据权息信息调整持仓 + */ + virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) { + HKU_WARN("The subclass does not implement this method"); + return 0.0; + } + + /** + * 当前是否持有指定的证券 + * @note 这里未使用日期参数,必须保证是按日期顺序执行 + * @param stock 指定证券 + * @return true 是 | false 否 + */ + virtual bool have(const Stock& stock) const override { + return m_position.count(stock.id()) ? true : false; + } + + /** + * 当前空头仓位是否持有指定的证券 + * @note 这里未使用日期参数,必须保证是按日期顺序执行 + * @param stock 指定证券 + * @return true 是 | false 否 + */ + virtual bool haveShort(const Stock& stock) const { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** 当前持有的证券种类数量 */ + virtual size_t getStockNumber() const { + return m_position.size(); + } + + /** 当前空头持有的证券种类数量 */ + virtual size_t getShortStockNumber() const { + HKU_WARN("The subclass does not implement this method"); + return 0; + } + + /** 获取指定时刻的某证券持有数量 */ + virtual double getHoldNumber(const Datetime& datetime, const Stock& stock) { + HKU_WARN("The subclass does not implement this method"); + return 0.0; + } + + /** 获取指定时刻的空头某证券持有数量 */ + virtual double getShortHoldNumber(const Datetime& datetime, const Stock& stock) { + HKU_WARN("The subclass does not implement this method"); + return 0.0; + } + + /** 获取指定时刻已借入的股票数量 */ + virtual double getDebtNumber(const Datetime& datetime, const Stock& stock) { + HKU_WARN("The subclass does not implement this method"); + return 0.0; + } + + /** 获取指定时刻已借入的现金额 */ + virtual price_t getDebtCash(const Datetime& datetime) { + HKU_WARN("The subclass does not implement this method"); + return 0.0; + } + + /** 获取全部交易记录 */ + virtual TradeRecordList getTradeList() const { + HKU_WARN("The subclass does not implement this method"); + return TradeRecordList(); + } + + /** + * 获取指定日期范围内的交易记录[start, end) + * @param start 起始日期 + * @param end 结束日期 + * @return 交易记录列表 + */ + virtual TradeRecordList getTradeList(const Datetime& start, const Datetime& end) const { + HKU_WARN("The subclass does not implement this method"); + return TradeRecordList(); + } + + /** 获取当前全部持仓记录 */ + virtual PositionRecordList getPositionList() const override; + + /** 获取全部历史持仓记录,即已平仓记录 */ + virtual PositionRecordList getHistoryPositionList() const { + HKU_WARN("The subclass does not implement this method"); + return PositionRecordList(); + } + + /** 获取当前全部空头仓位记录 */ + virtual PositionRecordList getShortPositionList() const { + HKU_WARN("The subclass does not implement this method"); + return PositionRecordList(); + } + + /** 获取全部空头历史仓位记录 */ + virtual PositionRecordList getShortHistoryPositionList() const { + HKU_WARN("The subclass does not implement this method"); + return PositionRecordList(); + } + + /** + * 获取指定证券的持仓记录 + * @param date 指定日期 + * @param stock 指定的证券 + */ + virtual PositionRecord getPosition(const Datetime& date, const Stock& stock) { + HKU_WARN("The subclass does not implement this method"); + return PositionRecord(); + } + + /** + * 获取指定证券的空头持仓记录 + * @param date 指定日期 + * @param stock 指定的证券 + */ + virtual PositionRecord getShortPosition(const Stock&) const { + HKU_WARN("The subclass does not implement this method"); + return PositionRecord(); + } + + /** 获取当前借入的股票列表 */ + virtual BorrowRecordList getBorrowStockList() const { + HKU_WARN("The subclass does not implement this method"); + return BorrowRecordList(); + } + + /** + * 存入资金 + * @param datetime 存入时间 + * @param cash 存入的资金量 + * @return true | false + */ + virtual bool checkin(const Datetime& datetime, price_t cash); + + /** + * 取出资金 + * @param datetime 取出时间 + * @param cash 取出的资金量 + * @return true | false + */ + virtual bool checkout(const Datetime& datetime, price_t cash) { + m_cash = (cash > m_cash) ? 0.0 : m_cash - cash; + return true; + } + + /** + * 存入资产 + * @param datetime 存入日期 + * @param stock 待存入的股票 + * @param price 存入股票的每股价格 + * @param number 存入股票的数量 + * @return true | false + */ + virtual bool checkinStock(const Datetime& datetime, const Stock& stock, price_t price, + double number) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 取出当前资产 + * @param datetime 取出日期 + * @param stock 待取出的股票 + * @param price 取出的每股价格 + * @param number 取出的数量 + * @return true | false + * @note 应该不会被用到 + */ + virtual bool checkoutStock(const Datetime& datetime, const Stock& stock, price_t price, + double number) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 买入操作 + * @param datetime 买入时间 + * @param stock 买入的证券 + * @param realPrice 实际买入价格 + * @param number 买入数量 + * @param stoploss 止损价 + * @param goalPrice 目标价格 + * @param planPrice 计划买入价格 + * @param from 记录是哪个系统部件发出的买入指示 + * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID + */ + virtual TradeRecord buy(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number, price_t stoploss = 0.0, price_t goalPrice = 0.0, + price_t planPrice = 0.0, SystemPart from = PART_INVALID) override; + + /** + * 卖出操作 + * @param datetime 卖出时间 + * @param stock 卖出的证券 + * @param realPrice 实际卖出价格 + * @param number 卖出数量,如果是 MAX_DOUBLE, 表示全部卖出 + * @param stoploss 新的止损价 + * @param goalPrice 新的目标价格 + * @param planPrice 原计划卖出价格 + * @param from 记录是哪个系统部件发出的卖出指示 + * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID + */ + virtual TradeRecord sell(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number = MAX_DOUBLE, price_t stoploss = 0.0, + price_t goalPrice = 0.0, price_t planPrice = 0.0, + SystemPart from = PART_INVALID); + + /** + * 卖空 + * @param datetime 卖空时间 + * @param stock 卖空的证券 + * @param realPrice 实际卖空价格 + * @param number 卖出数量 + * @param stoploss 止损价 + * @param goalPrice 目标价格 + * @param planPrice 计划卖空价格 + * @param from 记录是哪个系统部件发出的买入指示 + * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID + */ + virtual TradeRecord sellShort(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number, price_t stoploss = 0.0, price_t goalPrice = 0.0, + price_t planPrice = 0.0, SystemPart from = PART_INVALID) { + HKU_WARN("The subclass does not implement this method"); + return TradeRecord(); + } + + /** + * 卖空后回补 + * @param datetime 买入时间 + * @param stock 买入的证券 + * @param realPrice 实际买入价格 + * @param number 卖出数量,如果是 MAX_DOUBLE, 表示全部卖出 + * @param stoploss 止损价 + * @param goalPrice 目标价格 + * @param planPrice 计划买入价格 + * @param from 记录是哪个系统部件发出的卖出指示 + * @return 返回对应的交易记录,如果操作失败,business等于BUSINESS_INVALID + */ + virtual TradeRecord buyShort(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number = MAX_DOUBLE, price_t stoploss = 0.0, + price_t goalPrice = 0.0, price_t planPrice = 0.0, + SystemPart from = PART_INVALID) { + HKU_WARN("The subclass does not implement this method"); + return TradeRecord(); + } + + /** + * 借入资金,从其他来源借取的资金,如融资 + * @param datetime 借入时间 + * @param cash 借入的现金 + * @return true | false + */ + virtual bool borrowCash(const Datetime& datetime, price_t cash) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 归还资金 + * @param datetime 归还日期 + * @param cash 归还现金 + * @return true | false + */ + virtual bool returnCash(const Datetime& datetime, price_t cash) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 借入证券 + * @param datetime 借入时间 + * @param stock 借入的stock + * @param price 借入时单股价格 + * @param number 借入时数量 + * @return true | false + */ + virtual bool borrowStock(const Datetime& datetime, const Stock& stock, price_t price, + double number) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 归还证券 + * @param datetime 归还时间 + * @param stock 归还的stock + * @param price 归还时单股价格 + * @param number 归还数量 + * @return true | false + */ + virtual bool returnStock(const Datetime& datetime, const Stock& stock, price_t price, + double number) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 获取账户当前时刻的资产详情 + * @param ktype 日期的类型 + * @return 资产详情 + */ + virtual FundsRecord getFunds(KQuery::KType ktype = KQuery::DAY) const { + HKU_WARN("The subclass does not implement this method"); + return FundsRecord(); + } + + /** + * 获取指定时刻的资产市值详情 + * @param datetime 必须大于帐户建立的初始日期,或为Null() + * @param ktype 日期的类型 + * @return 资产详情 + * @note 当datetime等于Null()时,与getFunds(KType)同 + */ + virtual FundsRecord getFunds(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) { + HKU_WARN("The subclass does not implement this method"); + return FundsRecord(); + } + + /** + * 直接加入交易记录 + * @note 如果加入初始化账户记录,将清除全部已有交易及持仓记录 + * @param tr 待加入的交易记录 + * @return bool true 成功 | false 失败 + */ + virtual bool addTradeRecord(const TradeRecord& tr) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** + * 直接加入持仓记录 + * @param pr 持仓记录 + * @return true 成功 + * @return false 失败 + */ + virtual bool addPosition(const PositionRecord& pr) { + HKU_WARN("The subclass does not implement this method"); + return false; + } + + /** 字符串输出 */ + virtual string str() const { + HKU_WARN("The subclass does not implement this method"); + return string(); + } + + /** + * 以csv格式输出交易记录、未平仓记录、已平仓记录、资产净值曲线 + * @param path 输出文件所在目录 + */ + virtual void tocsv(const string& path) { + HKU_WARN("The subclass does not implement this method"); + } + +private: + Datetime m_init_datetime; // 账户建立日期 + Datetime m_first_datetime; // 第一次交易时间 + Datetime m_last_datetime; // 最后一次交易时间 + + price_t m_init_cash{0.0}; // 初始资金 + price_t m_cash{0.0}; // 当前可用现金 + price_t m_frozen_cash{0.0}; // 当前冻结资金 + + typedef map position_map_type; + position_map_type m_position; // 当前持仓交易对象的持仓记录 + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void save(Archive& ar, const unsigned int version) const { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(TradeManagerBase); + ar& BOOST_SERIALIZATION_NVP(m_init_datetime); + ar& BOOST_SERIALIZATION_NVP(m_first_datetime); + ar& BOOST_SERIALIZATION_NVP(m_last_datetime); + ar& BOOST_SERIALIZATION_NVP(m_init_cash); + ar& BOOST_SERIALIZATION_NVP(m_cash); + ar& BOOST_SERIALIZATION_NVP(m_frozen_cash); + PositionRecordList position = getPositionList(); + ar& bs::make_nvp("m_position", position); + } + + template + void load(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(TradeManagerBase); + ar& BOOST_SERIALIZATION_NVP(m_init_datetime); + ar& BOOST_SERIALIZATION_NVP(m_first_datetime); + ar& BOOST_SERIALIZATION_NVP(m_last_datetime); + ar& BOOST_SERIALIZATION_NVP(m_init_cash); + ar& BOOST_SERIALIZATION_NVP(m_cash); + ar& BOOST_SERIALIZATION_NVP(m_frozen_cash); + PositionRecordList position; + ar& bs::make_nvp("m_position", position); + PositionRecordList::const_iterator iter = position.begin(); + for (; iter != position.end(); ++iter) { + m_position[iter->stock.id()] = *iter; + } + } + +#endif /* HKU_SUPPORT_SERIALIZATION */ +}; + +} // namespace hku \ No newline at end of file From 55a57ea9ee9ea3a3b88028d649f42392b8599b3a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 17 Aug 2024 01:40:18 +0800 Subject: [PATCH 459/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20OrderBroker=20?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_manage/broker.py | 8 ++-- .../hikyuu/strategy/OrderTradeManager.cpp | 20 +++++----- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 24 +++++------ .../hikyuu/trade_manage/OrderBrokerBase.h | 16 ++++---- .../hikyuu/trade_manage/TradeManager.cpp | 40 +++++++++---------- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 13 +++--- 6 files changed, 58 insertions(+), 63 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index d6195ecb..b2004417 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -46,13 +46,13 @@ class OrderBrokerWrap(OrderBrokerBase): def _buy(self, datetime, market, code, price, num): """实现 OrderBrokerBase 的 _buy 接口""" - self._broker.buy('{}{}'.format(market, code), price, num) - return datetime + ret = self._broker.buy('{}{}'.format(market, code), price, num) + return str(datetime) if ret is not None else str(ret) def _sell(self, datetime, market, code, price, num): """实现 OrderBrokerBase 的 _sell 接口""" - self._broker.sell('{}{}'.format(market, code), price, num) - return datetime + ret = self._broker.sell('{}{}'.format(market, code), price, num) + return str(datetime) if ret is not None else str(ret) class TestOrderBroker: diff --git a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp index b51553a0..a484a50d 100644 --- a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp @@ -112,14 +112,14 @@ TradeRecord OrderTradeManager::buy(const Datetime& datetime, const Stock& stock, roundEx(position.totalRisk + (realPrice - stoploss) * number * stock.unit(), precision); } - if (result.datetime > m_broker_last_datetime) { + if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - Datetime realtime, nulltime; + string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - realtime = + broker_ret = (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); - if (realtime != nulltime && realtime > m_broker_last_datetime) { - m_broker_last_datetime = realtime; + if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } } @@ -189,14 +189,14 @@ TradeRecord OrderTradeManager::sell(const Datetime& datetime, const Stock& stock m_position.erase(stock.id()); } - if (result.datetime > m_broker_last_datetime) { + if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - Datetime realtime, nulltime; + string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - realtime = + broker_ret = (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); - if (realtime != nulltime && realtime > m_broker_last_datetime) { - m_broker_last_datetime = realtime; + if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } } diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 7ea38c6e..f3e85df3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -25,34 +25,30 @@ OrderBrokerBase::OrderBrokerBase(const string& name) : m_name(name) {} OrderBrokerBase::~OrderBrokerBase() {} -Datetime OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code, - price_t price, double num) { - Datetime tradetime; +string OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code, + price_t price, double num) { + string ret; try { - tradetime = _buy(datetime, market, code, price, num); + ret = _buy(datetime, market, code, price, num); } catch (const std::exception& e) { HKU_ERROR(e.what()); - tradetime = Null(); } catch (...) { HKU_ERROR_UNKNOWN; - tradetime = Null(); } - return tradetime; + return ret; } -Datetime OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code, - price_t price, double num) { - Datetime tradetime; +string OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code, + price_t price, double num) { + string ret; try { - tradetime = _sell(datetime, market, code, price, num); + ret = _sell(datetime, market, code, price, num); } catch (const std::exception& e) { HKU_ERROR(e.what()); - tradetime = Null(); } catch (...) { HKU_ERROR_UNKNOWN; - tradetime = Null(); } - return tradetime; + return ret; } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 2c2055b6..7a578df1 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -51,8 +51,8 @@ public: * @param num 买入数量 * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 */ - Datetime buy(Datetime datetime, const string& market, const string& code, price_t price, - double num); + string buy(Datetime datetime, const string& market, const string& code, price_t price, + double num); /** * 执行卖出操作 @@ -63,8 +63,8 @@ public: * @param num 卖出数量 * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 */ - Datetime sell(Datetime datetime, const string& market, const string& code, price_t price, - double num); + string sell(Datetime datetime, const string& market, const string& code, price_t price, + double num); /** * 执行实际买入操作 @@ -75,8 +75,8 @@ public: * @param num 买入数量 * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 */ - virtual Datetime _buy(Datetime datetime, const string& market, const string& code, - price_t price, double num) = 0; + virtual string _buy(Datetime datetime, const string& market, const string& code, price_t price, + double num) = 0; /** * 执行实际卖出操作 @@ -87,8 +87,8 @@ public: * @param num 卖出数量 * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 */ - virtual Datetime _sell(Datetime datetime, const string& market, const string& code, - price_t price, double num) = 0; + virtual string _sell(Datetime datetime, const string& market, const string& code, price_t price, + double num) = 0; protected: string m_name; diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index 0c11e489..abead7c1 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -865,14 +865,14 @@ TradeRecord TradeManager::buy(const Datetime& datetime, const Stock& stock, pric roundEx(position.totalRisk + (realPrice - stoploss) * number * stock.unit(), precision); } - if (result.datetime > m_broker_last_datetime) { + if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - Datetime realtime, nulltime; + string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - realtime = + broker_ret = (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); - if (realtime != nulltime && realtime > m_broker_last_datetime) { - m_broker_last_datetime = realtime; + if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } } @@ -955,14 +955,14 @@ TradeRecord TradeManager::sell(const Datetime& datetime, const Stock& stock, pri returnCash(datetime, m_borrow_cash < m_cash ? m_borrow_cash : m_cash); } - if (result.datetime > m_broker_last_datetime) { + if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - Datetime realtime, nulltime; + string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - realtime = + broker_ret = (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); - if (realtime != nulltime && realtime > m_broker_last_datetime) { - m_broker_last_datetime = realtime; + if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } } @@ -1070,14 +1070,14 @@ TradeRecord TradeManager::sellShort(const Datetime& datetime, const Stock& stock position.sellMoney = roundEx(position.sellMoney + money, precision); } - if (result.datetime > m_broker_last_datetime) { + if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - Datetime realtime, nulltime; + string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - realtime = + broker_ret = (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, number); - if (realtime != nulltime && realtime > m_broker_last_datetime) { - m_broker_last_datetime = realtime; + if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } } @@ -1144,14 +1144,14 @@ TradeRecord TradeManager::buyShort(const Datetime& datetime, const Stock& stock, m_short_position.erase(stock.id()); } - if (result.datetime > m_broker_last_datetime) { + if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - Datetime realtime, nulltime; + string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - realtime = + broker_ret = (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); - if (realtime != nulltime && realtime > m_broker_last_datetime) { - m_broker_last_datetime = realtime; + if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } } diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index 40344702..3cd294fb 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -15,15 +15,14 @@ class PyOrderBrokerBase : public OrderBrokerBase { public: using OrderBrokerBase::OrderBrokerBase; - Datetime _buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) override { - PYBIND11_OVERLOAD_PURE(Datetime, OrderBrokerBase, _buy, datetime, market, code, price, num); + string _buy(Datetime datetime, const string& market, const string& code, price_t price, + double num) override { + PYBIND11_OVERLOAD_PURE(string, OrderBrokerBase, _buy, datetime, market, code, price, num); } - Datetime _sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) override { - PYBIND11_OVERLOAD_PURE(Datetime, OrderBrokerBase, _sell, datetime, market, code, price, - num); + string _sell(Datetime datetime, const string& market, const string& code, price_t price, + double num) override { + PYBIND11_OVERLOAD_PURE(string, OrderBrokerBase, _sell, datetime, market, code, price, num); } }; From f6e55aad4bb486f9c86e1da9fb02a8fc7045161b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 17 Aug 2024 18:20:40 +0800 Subject: [PATCH 460/601] =?UTF-8?q?=E5=AE=8C=E5=96=84=20OrderBrokerBase=20?= =?UTF-8?q?continue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 89 ++++++++++++++++++- .../hikyuu/trade_manage/OrderBrokerBase.h | 46 ++++++++-- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 18 +++- 3 files changed, 144 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index f3e85df3..a67b96b9 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -5,10 +5,13 @@ * Author: fasiondog */ +#include #include "OrderBrokerBase.h" namespace hku { +using json = nlohmann::json; + HKU_API std::ostream& operator<<(std::ostream& os, const OrderBrokerBase& broker) { os << "OrderBroker(" << broker.name() << ")"; return os; @@ -26,7 +29,7 @@ OrderBrokerBase::OrderBrokerBase(const string& name) : m_name(name) {} OrderBrokerBase::~OrderBrokerBase() {} string OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code, - price_t price, double num) { + price_t price, double num) noexcept { string ret; try { ret = _buy(datetime, market, code, price, num); @@ -39,7 +42,7 @@ string OrderBrokerBase::buy(Datetime datetime, const string& market, const strin } string OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code, - price_t price, double num) { + price_t price, double num) noexcept { string ret; try { ret = _sell(datetime, market, code, price, num); @@ -51,4 +54,86 @@ string OrderBrokerBase::sell(Datetime datetime, const string& market, const stri return ret; } +Parameter OrderBrokerBase::balance() noexcept { + Parameter ret; + ret.set("cash", 0.0); + ret.set("frozen", 0.0); + + try { + auto brk_ret = _balance(); + HKU_IF_RETURN(brk_ret.empty(), ret); + + json x(brk_ret); + ret.set("cash", x["cash"].get()); + if (x.contains("forzen")) { + ret.set("forzen", x["frozen"].get()); + } else { + ret.set("frozen", 0.0); + } + + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } + return ret; +} + +vector OrderBrokerBase::position() noexcept { + vector ret; + + vector brk_positions; + try { + brk_positions = _position(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } + + HKU_IF_RETURN(brk_positions.empty(), ret); + + for (size_t i = 0, len = brk_positions.size(); i < len; i++) { + try { + json brk_pos(brk_positions[i]); + if (brk_pos.empty()) { + continue; + } + + Parameter pos; + auto market = brk_pos["market"].get(); + auto code = brk_pos["code"].get(); + auto stock = getStock(fmt::format("{}{}", market, code)); + if (stock.isNull()) { + // 策略的上下文可能并不包含该股,此时忽略 + HKU_DEBUG("Not found the stock: {}{}", market, code); + continue; + } + + pos.set("stock", stock); + pos.set("num", brk_pos["number"].get()); + if (brk_pos.contains("buy_frozen_num")) { + pos.set("buy_frozen_num", brk_pos["buy_frozen_num"].get()); + } else { + pos.set("buy_frozen_num", 0.0); + } + + if (brk_pos.contains("sell_frozon_num")) { + pos.set("sell_frozon_num", brk_pos["sell_frozon_num"].get()); + } else { + pos.set("sell_frozon_num", 0.0); + } + + ret.emplace_back(pos); + + } catch (const std::exception& e) { + HKU_ERROR("Failed parser the [{}] record ({})! {}", i, brk_positions[i], e.what()); + } catch (...) { + HKU_ERROR("Failed parser the [{}] record ({})! Unknown error!", i, brk_positions[i]); + } + } + + return ret; +} + } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 7a578df1..3c98ba79 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -49,10 +49,10 @@ public: * @param code 证券代码 * @param price 买入价格 * @param num 买入数量 - * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 + * @return 委托单号,否则返回空字符串 */ string buy(Datetime datetime, const string& market, const string& code, price_t price, - double num); + double num) noexcept; /** * 执行卖出操作 @@ -61,13 +61,21 @@ public: * @param code 证券代码 * @param price 卖出价格 * @param num 卖出数量 - * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 + * @return 委托单号,否则返回空字符串 */ string sell(Datetime datetime, const string& market, const string& code, price_t price, - double num); + double num) noexcept; /** - * 执行实际买入操作 + * 获取资金状况 + * @return {"cash"(double): 可用资金,"frozen"(double): 冻结资金} + */ + Parameter balance() noexcept; + + vector position() noexcept; + + /** + * 子类实现接口,执行实际买入操作 * @param datetime 策略指示时间 * @param market 市场标识 * @param code 证券代码 @@ -79,7 +87,7 @@ public: double num) = 0; /** - * 执行实际卖出操作 + * 子类实现接口,执行实际卖出操作 * @param datetime 策略指示时间 * @param market 市场标识 * @param code 证券代码 @@ -90,6 +98,32 @@ public: virtual string _sell(Datetime datetime, const string& market, const string& code, price_t price, double num) = 0; + /** + * 子类获取资产信息实现 + * @return string json字符串,需包含 number: "cash"(可用资金,必须), "frozen"(冻结资金,可选) + */ + virtual string _balance() { + return string(); + } + + /** + * 子类获取持仓信息实现 + * @return vector json 字符串组成的持仓信息列表 + *
+     * 其中:market, code, num 为必须
+     * 示例:
+     * [{"market": "SZ",
+     *   "code": "000001",
+     *   "num": 100,
+     *   "buy_frozen_num", 0,
+     *   "sell_frozen_num", 0
+     * }]
+     * 
_position() { + return vector(); + } + protected: string m_name; }; diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index 3cd294fb..7ecee917 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -24,6 +24,14 @@ public: double num) override { PYBIND11_OVERLOAD_PURE(string, OrderBrokerBase, _sell, datetime, market, code, price, num); } + + string _balance() override { + PYBIND11_OVERLOAD(string, OrderBrokerBase, _balance); + } + + vector _position() override { + PYBIND11_OVERLOAD(vector, OrderBrokerBase, _position); + } }; void export_OrderBroker(py::module& m) { @@ -69,6 +77,11 @@ void export_OrderBroker(py::module& m) { :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 "rtype: Datetime)") + .def("balance", &OrderBrokerBase::balance) + + .def("position", + [](OrderBrokerBase& self) { return vector_to_python_list(self.position()); }) + .def("_buy", &OrderBrokerBase::_buy, R"(_buy(self, datetime, market, code, price, num) @@ -93,5 +106,8 @@ void export_OrderBroker(py::module& m) { :param float price: 卖出价格 :param float num: 卖出数量 :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 - "rtype: Datetime)"); + "rtype: Datetime)") + + .def("_balance", &OrderBrokerBase::_balance) + .def("_position", &OrderBrokerBase::_position); } From 8f3b38c54f25f0a6c10fc08e5c790ca0ba515542 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 17 Aug 2024 18:31:07 +0800 Subject: [PATCH 461/601] =?UTF-8?q?=E5=AE=8C=E5=96=84=20OrderBrokerWrap?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_manage/broker.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index b2004417..dc60a9a3 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -54,6 +54,16 @@ class OrderBrokerWrap(OrderBrokerBase): ret = self._broker.sell('{}{}'.format(market, code), price, num) return str(datetime) if ret is not None else str(ret) + def _balance(self): + if hasattr(self._broker, "balance"): + return self._broker.balance() + return str() + + def _position(self): + if hasattr(self._broker, "position"): + return self._broker.position() + return list() + class TestOrderBroker: """用于测试的订单代理,仅在执行买入/卖出时打印信息""" From 2566e5c7703c1e92bcdf6fe9e354ab4772c64392 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 18 Aug 2024 02:58:05 +0800 Subject: [PATCH 462/601] BrokerTradeManager continue --- ...radeManager.cpp => BrokerTradeManager.cpp} | 92 +++++++++++++++---- ...derTradeManager.h => BrokerTradeManager.h} | 53 ++--------- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 1 + .../hikyuu/trade_manage/PositionRecord.h | 22 ++--- 4 files changed, 93 insertions(+), 75 deletions(-) rename hikyuu_cpp/hikyuu/strategy/{OrderTradeManager.cpp => BrokerTradeManager.cpp} (73%) rename hikyuu_cpp/hikyuu/strategy/{OrderTradeManager.h => BrokerTradeManager.h} (88%) diff --git a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp similarity index 73% rename from hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp rename to hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index a484a50d..5631f442 100644 --- a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -6,22 +6,41 @@ */ #include "hikyuu/trade_manage/crt/TC_Zero.h" -#include "OrderTradeManager.h" +#include "BrokerTradeManager.h" namespace hku { -OrderTradeManager::OrderTradeManager(const Datetime& datetime, price_t initcash, - const TradeCostPtr& costfunc, const string& name) -: TradeManagerBase(name, costfunc), - m_init_datetime(datetime), - m_first_datetime(datetime), - m_last_datetime(datetime) { - m_init_cash = roundEx(initcash, 2); +BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, + const string& name) +: TradeManagerBase(name, costfunc) { + HKU_ASSERT(broker); + m_broker_list.emplace_back(broker); + + auto balance = broker->balance(); + m_init_cash = balance.get("cash"); m_cash = m_init_cash; - m_broker_last_datetime = Datetime::now(); + m_frozen_cash = balance.get("frozen"); + + auto now = Datetime::now(); + auto brk_positions = broker->position(); + for (const auto& brk_pos : brk_positions) { + PositionRecord pos; + pos.takeDatetime = now; + pos.stock = brk_pos.get("stock"); + pos.number = brk_pos.get("num"); + pos.totalNumber = pos.number; + pos.buyMoney = brk_pos.get("cost"); + pos.totalRisk = pos.buyMoney; + m_position[pos.stock.id()] = pos; + } + + m_init_datetime = Datetime::now(); + m_first_datetime = m_init_datetime; + m_last_datetime = m_init_datetime; + m_broker_last_datetime = m_init_datetime; } -void OrderTradeManager::_reset() { +void BrokerTradeManager::_reset() { HKU_WARN("The subclass does not implement a reset method"); m_first_datetime = m_init_datetime; m_last_datetime = m_init_datetime; @@ -30,8 +49,8 @@ void OrderTradeManager::_reset() { m_position.clear(); } -shared_ptr OrderTradeManager::_clone() { - OrderTradeManager* p = new OrderTradeManager(m_init_datetime, m_init_cash, m_costfunc, m_name); +shared_ptr BrokerTradeManager::_clone() { + BrokerTradeManager* p = new BrokerTradeManager(); p->m_init_datetime = m_init_datetime; p->m_first_datetime = m_first_datetime; p->m_last_datetime = m_last_datetime; @@ -42,7 +61,40 @@ shared_ptr OrderTradeManager::_clone() { return shared_ptr(p); } -PositionRecordList OrderTradeManager::getPositionList() const { +void BrokerTradeManager::getCurrentBrokerPosition() { + auto& broker = m_broker_list.front(); + + auto balance = broker->balance(); + m_cash = m_init_cash; + m_frozen_cash = balance.get("frozen"); + + auto now = Datetime::now(); + auto brk_positions = broker->position(); + for (const auto& brk_pos : brk_positions) { + PositionRecord pos; + pos.takeDatetime = now; + pos.stock = brk_pos.get("stock"); + pos.number = brk_pos.get("num"); + pos.totalNumber = pos.number; + pos.buyMoney = brk_pos.get("cost"); + pos.totalRisk = pos.buyMoney; + auto iter = m_position.find(pos.stock.id()); + if (iter == m_position.end()) { + m_position[pos.stock.id()] = pos; + } else { + iter->number = pos.number; + iter->totalNumber = pos.totalNumber; + iter->buyMoney = pos.buyMoney; + } + } + + m_init_datetime = Datetime::now(); + m_first_datetime = m_init_datetime; + m_last_datetime = m_init_datetime; + m_broker_last_datetime = m_init_datetime; +} + +PositionRecordList BrokerTradeManager::getPositionList() const { PositionRecordList result; position_map_type::const_iterator iter = m_position.begin(); for (; iter != m_position.end(); ++iter) { @@ -51,15 +103,15 @@ PositionRecordList OrderTradeManager::getPositionList() const { return result; } -bool OrderTradeManager::checkin(const Datetime& datetime, price_t cash) { +bool BrokerTradeManager::checkin(const Datetime& datetime, price_t cash) { HKU_IF_RETURN(datetime < m_last_datetime, false); m_cash += cash; return true; } -TradeRecord OrderTradeManager::buy(const Datetime& datetime, const Stock& stock, price_t realPrice, - double number, price_t stoploss, price_t goalPrice, - price_t planPrice, SystemPart from) { +TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock, price_t realPrice, + double number, price_t stoploss, price_t goalPrice, + price_t planPrice, SystemPart from) { TradeRecord result; result.business = BUSINESS_INVALID; @@ -127,9 +179,9 @@ TradeRecord OrderTradeManager::buy(const Datetime& datetime, const Stock& stock, return result; } -TradeRecord OrderTradeManager::sell(const Datetime& datetime, const Stock& stock, price_t realPrice, - double number, price_t stoploss, price_t goalPrice, - price_t planPrice, SystemPart from) { +TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stock, + price_t realPrice, double number, price_t stoploss, + price_t goalPrice, price_t planPrice, SystemPart from) { HKU_CHECK(!std::isnan(number), "sell number should be a valid double!"); TradeRecord result; diff --git a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h similarity index 88% rename from hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h rename to hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index 3579cff1..087cc042 100644 --- a/hikyuu_cpp/hikyuu/strategy/OrderTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -11,13 +11,13 @@ namespace hku { -class HKU_API OrderTradeManager : public TradeManagerBase { +class HKU_API BrokerTradeManager : public TradeManagerBase { public: - explicit OrderTradeManager(const Datetime& datetime = Datetime(199001010000LL), - price_t initcash = 100000.0, - const TradeCostPtr& costfunc = TC_Zero(), - const string& name = "SYS"); - virtual ~OrderTradeManager() {} + BrokerTradeManager() = default; + explicit BrokerTradeManager(const OrderBrokerPtr& broker, + const TradeCostPtr& costfunc = TC_Zero(), + const string& name = "SYS"); + virtual ~BrokerTradeManager() {} virtual void _reset() override; @@ -433,6 +433,9 @@ public: HKU_WARN("The subclass does not implement this method"); } +private: + void getCurrentBrokerPosition(); + private: Datetime m_init_datetime; // 账户建立日期 Datetime m_first_datetime; // 第一次交易时间 @@ -444,44 +447,6 @@ private: typedef map position_map_type; position_map_type m_position; // 当前持仓交易对象的持仓记录 - -//============================================ -// 序列化支持 -//============================================ -#if HKU_SUPPORT_SERIALIZATION -private: - friend class boost::serialization::access; - template - void save(Archive& ar, const unsigned int version) const { - ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(TradeManagerBase); - ar& BOOST_SERIALIZATION_NVP(m_init_datetime); - ar& BOOST_SERIALIZATION_NVP(m_first_datetime); - ar& BOOST_SERIALIZATION_NVP(m_last_datetime); - ar& BOOST_SERIALIZATION_NVP(m_init_cash); - ar& BOOST_SERIALIZATION_NVP(m_cash); - ar& BOOST_SERIALIZATION_NVP(m_frozen_cash); - PositionRecordList position = getPositionList(); - ar& bs::make_nvp("m_position", position); - } - - template - void load(Archive& ar, const unsigned int version) { - ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(TradeManagerBase); - ar& BOOST_SERIALIZATION_NVP(m_init_datetime); - ar& BOOST_SERIALIZATION_NVP(m_first_datetime); - ar& BOOST_SERIALIZATION_NVP(m_last_datetime); - ar& BOOST_SERIALIZATION_NVP(m_init_cash); - ar& BOOST_SERIALIZATION_NVP(m_cash); - ar& BOOST_SERIALIZATION_NVP(m_frozen_cash); - PositionRecordList position; - ar& bs::make_nvp("m_position", position); - PositionRecordList::const_iterator iter = position.begin(); - for (; iter != position.end(); ++iter) { - m_position[iter->stock.id()] = *iter; - } - } - -#endif /* HKU_SUPPORT_SERIALIZATION */ }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index a67b96b9..1a656578 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -112,6 +112,7 @@ vector OrderBrokerBase::position() noexcept { pos.set("stock", stock); pos.set("num", brk_pos["number"].get()); + pos.set("cost", brk_pos["cost"].get()); // 总成本 if (brk_pos.contains("buy_frozen_num")) { pos.set("buy_frozen_num", brk_pos["buy_frozen_num"].get()); } else { diff --git a/hikyuu_cpp/hikyuu/trade_manage/PositionRecord.h b/hikyuu_cpp/hikyuu/trade_manage/PositionRecord.h index ea4247df..c62a4f72 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/PositionRecord.h +++ b/hikyuu_cpp/hikyuu/trade_manage/PositionRecord.h @@ -28,17 +28,17 @@ public: /** 仅用于python的__str__ */ string toString() const; - Stock stock; ///< 交易对象 - Datetime takeDatetime; ///< 初次建仓日期 - Datetime cleanDatetime; ///< 平仓日期,当前持仓记录中为Null() - double number; ///< 当前持仓数量 - price_t stoploss; ///< 当前止损价 - price_t goalPrice; ///< 当前的目标价格 - double totalNumber; ///< 累计持仓数量 - price_t buyMoney; ///< 累计买入资金 - price_t totalCost; ///< 累计交易总成本 - price_t totalRisk; ///< 累计交易风险 = 各次 (买入价格-止损)*买入数量, 不包含交易成本 - price_t sellMoney; ///< 累计卖出资金 + Stock stock; ///< 交易对象 + Datetime takeDatetime; ///< 初次建仓日期 + Datetime cleanDatetime; ///< 平仓日期,当前持仓记录中为Null() + double number{0.0}; ///< 当前持仓数量 + price_t stoploss{0.0}; ///< 当前止损价 + price_t goalPrice{0.0}; ///< 当前的目标价格 + double totalNumber{0.0}; ///< 累计持仓数量 + price_t buyMoney{0.0}; ///< 累计买入资金 + price_t totalCost{0.0}; ///< 累计交易总成本 + price_t totalRisk{0.0}; ///< 累计交易风险 = 各次 (买入价格-止损)*买入数量, 不包含交易成本 + price_t sellMoney{0.0}; ///< 累计卖出资金 //=================== // 序列化支持 From 628ceaef5a67f58afd6a3974abe1e2323b8eb951 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 18 Aug 2024 17:45:25 +0800 Subject: [PATCH 463/601] BrokerTradeManager continue --- hikyuu/trade_manage/broker.py | 8 ++-- .../hikyuu/strategy/BrokerTradeManager.cpp | 45 ++++++++++++++----- .../hikyuu/strategy/BrokerTradeManager.h | 43 ++++++------------ .../hikyuu/trade_manage/FundsRecord.cpp | 15 ++----- hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h | 16 +++---- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 18 ++------ .../hikyuu/trade_manage/OrderBrokerBase.h | 12 +++-- .../hikyuu/trade_manage/TradeManagerBase.h | 7 --- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 8 ++-- hikyuu_pywrap/trade_manage/_TradeManager.cpp | 8 ---- 10 files changed, 75 insertions(+), 105 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index dc60a9a3..a80b3ca8 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -54,10 +54,10 @@ class OrderBrokerWrap(OrderBrokerBase): ret = self._broker.sell('{}{}'.format(market, code), price, num) return str(datetime) if ret is not None else str(ret) - def _balance(self): - if hasattr(self._broker, "balance"): - return self._broker.balance() - return str() + def _cash(self): + if hasattr(self._broker, "cash"): + return self._broker.cash() + return 0.0 def _position(self): if hasattr(self._broker, "position"): diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 5631f442..98cf1ee8 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -16,10 +16,8 @@ BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const Trade HKU_ASSERT(broker); m_broker_list.emplace_back(broker); - auto balance = broker->balance(); - m_init_cash = balance.get("cash"); + m_init_cash = broker->cash(); m_cash = m_init_cash; - m_frozen_cash = balance.get("frozen"); auto now = Datetime::now(); auto brk_positions = broker->position(); @@ -45,7 +43,6 @@ void BrokerTradeManager::_reset() { m_first_datetime = m_init_datetime; m_last_datetime = m_init_datetime; m_cash = m_init_cash; - m_frozen_cash = 0.0; m_position.clear(); } @@ -56,7 +53,6 @@ shared_ptr BrokerTradeManager::_clone() { p->m_last_datetime = m_last_datetime; p->m_init_cash = m_init_cash; p->m_cash = m_cash; - p->m_frozen_cash = m_frozen_cash; p->m_position = m_position; return shared_ptr(p); } @@ -64,9 +60,7 @@ shared_ptr BrokerTradeManager::_clone() { void BrokerTradeManager::getCurrentBrokerPosition() { auto& broker = m_broker_list.front(); - auto balance = broker->balance(); - m_cash = m_init_cash; - m_frozen_cash = balance.get("frozen"); + m_cash = broker->cash(); auto now = Datetime::now(); auto brk_positions = broker->position(); @@ -82,9 +76,9 @@ void BrokerTradeManager::getCurrentBrokerPosition() { if (iter == m_position.end()) { m_position[pos.stock.id()] = pos; } else { - iter->number = pos.number; - iter->totalNumber = pos.totalNumber; - iter->buyMoney = pos.buyMoney; + iter->second.number = pos.number; + iter->second.totalNumber = pos.totalNumber; + iter->second.buyMoney = pos.buyMoney; } } @@ -256,4 +250,33 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc return result; } +FundsRecord BrokerTradeManager::getFunds(KQuery::KType inktype) const { + FundsRecord funds; + int precision = getParam("precision"); + + string ktype(inktype); + to_upper(ktype); + + price_t value{0.0}; // 当前市值 + position_map_type::const_iterator iter = m_position.begin(); + for (; iter != m_position.end(); ++iter) { + const PositionRecord& record = iter->second; + auto price = record.stock.getMarketValue(lastDatetime(), ktype); + value = roundEx((value + record.number * price * record.stock.unit()), precision); + } + + funds.cash = m_cash; + funds.market_value = value; + funds.short_market_value = 0.0; + funds.base_cash = m_init_cash; + funds.base_asset = 0.0; + funds.borrow_cash = 0.0; + funds.borrow_asset = 0.0; + return funds; +} + +FundsRecord BrokerTradeManager::getFunds(const Datetime& datetime, KQuery::KType ktype) { + return (datetime >= m_last_datetime) ? getFunds(ktype) : FundsRecord(); +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index 087cc042..ba649f44 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -35,7 +35,7 @@ public: * @param datetime 日期 * @param stock 指定对象 */ - virtual double getMarginRate(const Datetime& datetime, const Stock& stock) { + virtual double getMarginRate(const Datetime& datetime, const Stock& stock) override { HKU_WARN("The subclass does not implement a getMarginRate method"); return 0.0; } @@ -51,13 +51,12 @@ public: } /** 第一笔买入交易发生日期,如未发生交易返回Null() */ - virtual Datetime firstDatetime() const { - HKU_WARN("The subclass does not implement this method"); - return Datetime(); + virtual Datetime firstDatetime() const override { + return m_first_datetime; } /** 最后一笔交易日期,注意和交易类型无关,如未发生交易返回账户建立日期 */ - virtual Datetime lastDatetime() const { + virtual Datetime lastDatetime() const override { return m_last_datetime; } @@ -69,20 +68,12 @@ public: return m_cash; } - /** - * 当前冻结现金 - */ - virtual price_t currentFrozen() const override { - return m_frozen_cash; - } - /** * 获取指定日期的现金 * @note 如果不带日期参数,无法根据权息信息调整持仓 */ - virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) { - HKU_WARN("The subclass does not implement this method"); - return 0.0; + virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override { + return (datetime >= m_last_datetime) ? m_cash : 0.0; } /** @@ -211,7 +202,7 @@ public: * @param cash 存入的资金量 * @return true | false */ - virtual bool checkin(const Datetime& datetime, price_t cash); + virtual bool checkin(const Datetime& datetime, price_t cash) override; /** * 取出资金 @@ -219,7 +210,7 @@ public: * @param cash 取出的资金量 * @return true | false */ - virtual bool checkout(const Datetime& datetime, price_t cash) { + virtual bool checkout(const Datetime& datetime, price_t cash) override { m_cash = (cash > m_cash) ? 0.0 : m_cash - cash; return true; } @@ -284,7 +275,7 @@ public: virtual TradeRecord sell(const Datetime& datetime, const Stock& stock, price_t realPrice, double number = MAX_DOUBLE, price_t stoploss = 0.0, price_t goalPrice = 0.0, price_t planPrice = 0.0, - SystemPart from = PART_INVALID); + SystemPart from = PART_INVALID) override; /** * 卖空 @@ -380,10 +371,7 @@ public: * @param ktype 日期的类型 * @return 资产详情 */ - virtual FundsRecord getFunds(KQuery::KType ktype = KQuery::DAY) const { - HKU_WARN("The subclass does not implement this method"); - return FundsRecord(); - } + virtual FundsRecord getFunds(KQuery::KType ktype = KQuery::DAY) const override; /** * 获取指定时刻的资产市值详情 @@ -392,10 +380,8 @@ public: * @return 资产详情 * @note 当datetime等于Null()时,与getFunds(KType)同 */ - virtual FundsRecord getFunds(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) { - HKU_WARN("The subclass does not implement this method"); - return FundsRecord(); - } + virtual FundsRecord getFunds(const Datetime& datetime, + KQuery::KType ktype = KQuery::DAY) override; /** * 直接加入交易记录 @@ -441,9 +427,8 @@ private: Datetime m_first_datetime; // 第一次交易时间 Datetime m_last_datetime; // 最后一次交易时间 - price_t m_init_cash{0.0}; // 初始资金 - price_t m_cash{0.0}; // 当前可用现金 - price_t m_frozen_cash{0.0}; // 当前冻结资金 + price_t m_init_cash{0.0}; // 初始资金 + price_t m_cash{0.0}; // 当前可用现金 typedef map position_map_type; position_map_type m_position; // 当前持仓交易对象的持仓记录 diff --git a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.cpp b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.cpp index dffda018..2542e5ca 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.cpp @@ -23,18 +23,9 @@ HKU_API std::ostream& operator<<(std::ostream& os, const FundsRecord& funds) { return os; } -FundsRecord::FundsRecord() -: cash(0.0), - market_value(0.0), - short_market_value(0.0), - base_cash(0.0), - base_asset(0.0), - borrow_cash(0.0), - borrow_asset(0.0) {} - -FundsRecord ::FundsRecord(price_t cash, price_t market_value, price_t short_market_value, - price_t base_cash, price_t base_asset, price_t borrow_cash, - price_t borrow_asset) +FundsRecord::FundsRecord(price_t cash, price_t market_value, price_t short_market_value, + price_t base_cash, price_t base_asset, price_t borrow_cash, + price_t borrow_asset) : cash(cash), market_value(market_value), short_market_value(short_market_value), diff --git a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h index c9e2f582..00c12d6c 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h +++ b/hikyuu_cpp/hikyuu/trade_manage/FundsRecord.h @@ -20,17 +20,17 @@ namespace hku { */ class HKU_API FundsRecord { public: - FundsRecord(); + FundsRecord() = default; FundsRecord(price_t cash, price_t market_value, price_t short_market_value, price_t base_cash, price_t base_asset, price_t borrow_cash, price_t borrow_asset); - price_t cash; /**< 当前现金 */ - price_t market_value; /**< 当前多头市值 */ - price_t short_market_value; /**< 当前空头仓位市值 */ - price_t base_cash; /**< 当前投入本金principal */ - price_t base_asset; /**< 当前投入的资产价值 */ - price_t borrow_cash; /**< 当前借入的资金,即负债 */ - price_t borrow_asset; /**< 当前借入证券资产价值 */ + price_t cash{0.0}; /**< 当前现金 */ + price_t market_value{0.0}; /**< 当前多头市值 */ + price_t short_market_value{0.0}; /**< 当前空头仓位市值 */ + price_t base_cash{0.0}; /**< 当前投入本金principal */ + price_t base_asset{0.0}; /**< 当前投入的资产价值 */ + price_t borrow_cash{0.0}; /**< 当前借入的资金,即负债 */ + price_t borrow_asset{0.0}; /**< 当前借入证券资产价值 */ // 当前总资产 = 现金 + 多头市值 + 空头数量×(借入价格 - 当前价格) // = cash + market_value + borrow_asset - short_market_value diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 1a656578..04aa67de 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -54,22 +54,10 @@ string OrderBrokerBase::sell(Datetime datetime, const string& market, const stri return ret; } -Parameter OrderBrokerBase::balance() noexcept { - Parameter ret; - ret.set("cash", 0.0); - ret.set("frozen", 0.0); - +price_t OrderBrokerBase::cash() noexcept { + price_t ret = 0.0; try { - auto brk_ret = _balance(); - HKU_IF_RETURN(brk_ret.empty(), ret); - - json x(brk_ret); - ret.set("cash", x["cash"].get()); - if (x.contains("forzen")) { - ret.set("forzen", x["frozen"].get()); - } else { - ret.set("frozen", 0.0); - } + return _cash(); } catch (const std::exception& e) { HKU_ERROR(e.what()); diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 3c98ba79..40b0bd0e 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -67,10 +67,9 @@ public: double num) noexcept; /** - * 获取资金状况 - * @return {"cash"(double): 可用资金,"frozen"(double): 冻结资金} + * 获取当前可用资金 */ - Parameter balance() noexcept; + price_t cash() noexcept; vector position() noexcept; @@ -99,11 +98,10 @@ public: double num) = 0; /** - * 子类获取资产信息实现 - * @return string json字符串,需包含 number: "cash"(可用资金,必须), "frozen"(冻结资金,可选) + * 子类获取当前可用现金接口 */ - virtual string _balance() { - return string(); + virtual price_t _cash() { + return 0.0; } /** diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index 595f1de1..c9375583 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -322,13 +322,6 @@ public: return 0.0; } - /** - * 当前冻结现金 - */ - virtual price_t currentFrozen() const { - return 0.0; - } - /** * 获取指定日期的现金 * @note 如果不带日期参数,无法根据权息信息调整持仓 diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index 7ecee917..bdaaba0e 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -25,8 +25,8 @@ public: PYBIND11_OVERLOAD_PURE(string, OrderBrokerBase, _sell, datetime, market, code, price, num); } - string _balance() override { - PYBIND11_OVERLOAD(string, OrderBrokerBase, _balance); + price_t _cash() override { + PYBIND11_OVERLOAD(price_t, OrderBrokerBase, _cash); } vector _position() override { @@ -77,7 +77,7 @@ void export_OrderBroker(py::module& m) { :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 "rtype: Datetime)") - .def("balance", &OrderBrokerBase::balance) + .def("cash", &OrderBrokerBase::cash) .def("position", [](OrderBrokerBase& self) { return vector_to_python_list(self.position()); }) @@ -108,6 +108,6 @@ void export_OrderBroker(py::module& m) { :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 "rtype: Datetime)") - .def("_balance", &OrderBrokerBase::_balance) + .def("_cash", &OrderBrokerBase::_cash) .def("_position", &OrderBrokerBase::_position); } diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index 732cf355..5b449a6f 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -51,10 +51,6 @@ public: PYBIND11_OVERRIDE_NAME(price_t, TradeManagerBase, "current_cash", currentCash); } - price_t currentFrozen() const override { - PYBIND11_OVERRIDE_NAME(price_t, TradeManagerBase, "current_frozen", currentFrozen); - } - price_t cash(const Datetime& datetime, KQuery::KType ktype) override { PYBIND11_OVERLOAD(price_t, TradeManagerBase, cash, datetime, ktype); } @@ -289,10 +285,6 @@ void export_TradeManager(py::module& m) { 默认情况下,TradeManager会在执行买入/卖出操作时,调用订单代理执行代理的买入/卖出动作,但这样在实盘操作时会存在问题。因为系统在计算信号指示时,需要回溯历史数据才能得到最新的信号,这样TradeManager会在历史时刻就执行买入/卖出操作,此时如果订单代理本身没有对发出买入/卖出指令的时刻进行控制,会导致代理发送错误的指令。此时,需要指定在某一个时刻之后,才允许指定订单代理的买入/卖出操作。属性 brokeLastDatetime 即用于指定该时刻。)") - .def("current_frozen", &TradeManagerBase::currentFrozen, R"(frozen(self) - - 获取当前冻结资金)") - .def("getParam", &TradeManagerBase::getParam, R"(get_param(self, name) 获取指定的参数 From 7af1c6b0cbc0221aff801fb3df0bad4d0a3201e9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 18 Aug 2024 18:20:03 +0800 Subject: [PATCH 464/601] update --- hikyuu/trade_manage/broker_easytrader.py | 7 +++++++ hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index ebf39f1c..fd67197d 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -15,3 +15,10 @@ class EasyTraderOrderBroker: def sell(self, code, price, num): self.user.sell(code[2:], price=price, amount=num) print("卖出:%s %.3f %i" % (code, price, num)) + + def cash(self): + balance = self.user.balance + ret = 0.0 + for i in range(len(balance)): + ret += balance[i]['可用资金'] + return ret diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 04aa67de..6ac4a32d 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -57,8 +57,7 @@ string OrderBrokerBase::sell(Datetime datetime, const string& market, const stri price_t OrderBrokerBase::cash() noexcept { price_t ret = 0.0; try { - return _cash(); - + ret = _cash(); } catch (const std::exception& e) { HKU_ERROR(e.what()); } catch (...) { From 336dc29ac46906db8e6985552b3a3f309883086f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 18 Aug 2024 21:58:35 +0800 Subject: [PATCH 465/601] =?UTF-8?q?OrderBrokerBase=20buy/sell=20=E5=8E=BB?= =?UTF-8?q?=E9=99=A4=E8=BF=94=E5=9B=9E=E5=80=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_manage/broker.py | 6 ++-- .../hikyuu/strategy/BrokerTradeManager.cpp | 12 +++----- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 16 ++++------- .../hikyuu/trade_manage/OrderBrokerBase.h | 20 ++++++------- .../hikyuu/trade_manage/TradeManager.cpp | 24 ++++++---------- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 28 +++++++------------ 6 files changed, 38 insertions(+), 68 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index a80b3ca8..5a2ee31f 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -46,13 +46,11 @@ class OrderBrokerWrap(OrderBrokerBase): def _buy(self, datetime, market, code, price, num): """实现 OrderBrokerBase 的 _buy 接口""" - ret = self._broker.buy('{}{}'.format(market, code), price, num) - return str(datetime) if ret is not None else str(ret) + self._broker.buy('{}{}'.format(market, code), price, num) def _sell(self, datetime, market, code, price, num): """实现 OrderBrokerBase 的 _sell 接口""" - ret = self._broker.sell('{}{}'.format(market, code), price, num) - return str(datetime) if ret is not None else str(ret) + self._broker.sell('{}{}'.format(market, code), price, num) def _cash(self): if hasattr(self._broker, "cash"): diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 98cf1ee8..4fa172b4 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -160,11 +160,9 @@ TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - broker_ret = - (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); - if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } } @@ -237,11 +235,9 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - broker_ret = - (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); - if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); + if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } } diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 6ac4a32d..497a4ea7 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -28,30 +28,26 @@ OrderBrokerBase::OrderBrokerBase(const string& name) : m_name(name) {} OrderBrokerBase::~OrderBrokerBase() {} -string OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code, - price_t price, double num) noexcept { - string ret; +void OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code, + price_t price, double num) noexcept { try { - ret = _buy(datetime, market, code, price, num); + _buy(datetime, market, code, price, num); } catch (const std::exception& e) { HKU_ERROR(e.what()); } catch (...) { HKU_ERROR_UNKNOWN; } - return ret; } -string OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code, - price_t price, double num) noexcept { - string ret; +void OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code, + price_t price, double num) noexcept { try { - ret = _sell(datetime, market, code, price, num); + _sell(datetime, market, code, price, num); } catch (const std::exception& e) { HKU_ERROR(e.what()); } catch (...) { HKU_ERROR_UNKNOWN; } - return ret; } price_t OrderBrokerBase::cash() noexcept { diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 40b0bd0e..7e4d5820 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -49,10 +49,9 @@ public: * @param code 证券代码 * @param price 买入价格 * @param num 买入数量 - * @return 委托单号,否则返回空字符串 */ - string buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) noexcept; + void buy(Datetime datetime, const string& market, const string& code, price_t price, + double num) noexcept; /** * 执行卖出操作 @@ -61,10 +60,9 @@ public: * @param code 证券代码 * @param price 卖出价格 * @param num 卖出数量 - * @return 委托单号,否则返回空字符串 */ - string sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) noexcept; + void sell(Datetime datetime, const string& market, const string& code, price_t price, + double num) noexcept; /** * 获取当前可用资金 @@ -80,10 +78,9 @@ public: * @param code 证券代码 * @param price 买入价格 * @param num 买入数量 - * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 */ - virtual string _buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) = 0; + virtual void _buy(Datetime datetime, const string& market, const string& code, price_t price, + double num) = 0; /** * 子类实现接口,执行实际卖出操作 @@ -92,10 +89,9 @@ public: * @param code 证券代码 * @param price 卖出价格 * @param num 卖出数量 - * @return 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 */ - virtual string _sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) = 0; + virtual void _sell(Datetime datetime, const string& market, const string& code, price_t price, + double num) = 0; /** * 子类获取当前可用现金接口 diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index abead7c1..fe5421e4 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -867,11 +867,9 @@ TradeRecord TradeManager::buy(const Datetime& datetime, const Stock& stock, pric if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - broker_ret = - (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); - if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } } @@ -957,11 +955,9 @@ TradeRecord TradeManager::sell(const Datetime& datetime, const Stock& stock, pri if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - broker_ret = - (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); - if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); + if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } } @@ -1072,11 +1068,9 @@ TradeRecord TradeManager::sellShort(const Datetime& datetime, const Stock& stock if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - broker_ret = - (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, number); - if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, number); + if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } } @@ -1146,11 +1140,9 @@ TradeRecord TradeManager::buyShort(const Datetime& datetime, const Stock& stock, if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); - string broker_ret; for (; broker_iter != m_broker_list.end(); ++broker_iter) { - broker_ret = - (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); - if (!broker_ret.empty() && datetime > m_broker_last_datetime) { + (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } } diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index bdaaba0e..d06075e8 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -15,14 +15,14 @@ class PyOrderBrokerBase : public OrderBrokerBase { public: using OrderBrokerBase::OrderBrokerBase; - string _buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) override { - PYBIND11_OVERLOAD_PURE(string, OrderBrokerBase, _buy, datetime, market, code, price, num); + void _buy(Datetime datetime, const string& market, const string& code, price_t price, + double num) override { + PYBIND11_OVERLOAD_PURE(void, OrderBrokerBase, _buy, datetime, market, code, price, num); } - string _sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) override { - PYBIND11_OVERLOAD_PURE(string, OrderBrokerBase, _sell, datetime, market, code, price, num); + void _sell(Datetime datetime, const string& market, const string& code, price_t price, + double num) override { + PYBIND11_OVERLOAD_PURE(void, OrderBrokerBase, _sell, datetime, market, code, price, num); } price_t _cash() override { @@ -61,9 +61,7 @@ void export_OrderBroker(py::module& m) { :param str market: 市场标识 :param str code: 证券代码 :param float price: 买入价格 - :param float num: 买入数量 - :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 - :rtype: Datetime)") + :param float num: 买入数量)") .def("sell", &OrderBrokerBase::sell, R"(sell(self, datetime, market, code, price, num) @@ -73,9 +71,7 @@ void export_OrderBroker(py::module& m) { :param str market: 市场标识 :param str code: 证券代码 :param float price: 卖出价格 - :param float num: 卖出数量 - :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 - "rtype: Datetime)") + :param float num: 卖出数量)") .def("cash", &OrderBrokerBase::cash) @@ -91,9 +87,7 @@ void export_OrderBroker(py::module& m) { :param str market: 市场标识 :param str code: 证券代码 :param float price: 买入价格 - :param float num: 买入数量 - :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 - :rtype: Datetime)") + :param float num: 买入数量)") .def("_sell", &OrderBrokerBase::_sell, R"(_sell(self, datetime, market, code, price, num) @@ -104,9 +98,7 @@ void export_OrderBroker(py::module& m) { :param str market: 市场标识 :param str code: 证券代码 :param float price: 卖出价格 - :param float num: 卖出数量 - :return: 操作执行的时刻。实盘时,应返回委托单时间或服务器交易时间。 - "rtype: Datetime)") + :param float num: 卖出数量)") .def("_cash", &OrderBrokerBase::_cash) .def("_position", &OrderBrokerBase::_position); From f56402d855f10bc3ed54b62ded891d690019c7a2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 19 Aug 2024 01:52:37 +0800 Subject: [PATCH 466/601] BrokerTradeManager continue --- hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp | 8 +++++--- hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h | 3 ++- hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp | 14 ++++++++++++++ hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h | 12 ++++++++++++ 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 4fa172b4..482d0745 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -57,10 +57,12 @@ shared_ptr BrokerTradeManager::_clone() { return shared_ptr(p); } -void BrokerTradeManager::getCurrentBrokerPosition() { - auto& broker = m_broker_list.front(); +void BrokerTradeManager::getCashFromBroker() { + m_cash = m_broker_list.front()->cash(); +} - m_cash = broker->cash(); +void BrokerTradeManager::getPositionFromBroker() { + auto& broker = m_broker_list.front(); auto now = Datetime::now(); auto brk_positions = broker->position(); diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index ba649f44..5974c0a5 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -420,7 +420,8 @@ public: } private: - void getCurrentBrokerPosition(); + void getCashFromBroker(); + void getPositionFromBroker(); private: Datetime m_init_datetime; // 账户建立日期 diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 497a4ea7..28bcfbb0 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -12,6 +12,20 @@ namespace hku { using json = nlohmann::json; +BrokerPositionRecord::BrokerPositionRecord(BrokerPositionRecord&& rv) +: stock(std::move(rv.stock)), number(rv.number) { + rv.number = 0.0; +} + +BrokerPositionRecord& BrokerPositionRecord::operator=(BrokerPositionRecord&& rv) { + if (this != &rv) { + stock = std::move(rv.stock); + number = rv.number; + rv.number = 0.0; + } + return *this; +} + HKU_API std::ostream& operator<<(std::ostream& os, const OrderBrokerBase& broker) { os << "OrderBroker(" << broker.name() << ")"; return os; diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 7e4d5820..77396d30 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -14,6 +14,18 @@ namespace hku { +struct HKU_API BrokerPositionRecord { + Stock stock; + price_t number{0.0}; + + BrokerPositionRecord() = default; + BrokerPositionRecord(const BrokerPositionRecord&) = default; + BrokerPositionRecord(BrokerPositionRecord&& rv); + + BrokerPositionRecord& operator=(const BrokerPositionRecord&) = default; + BrokerPositionRecord& operator=(BrokerPositionRecord&& rv); +}; + /** * 订单代理基类,实现实际的订单操作及程序化的订单。 * @details 可通过向 TradeManager.regBroker 向 TradeManager 注册多个订单代理实例。 From 7b87a213db1be334009d029b58016855928d6cb9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 19 Aug 2024 02:29:22 +0800 Subject: [PATCH 467/601] BrokerTradeManager continue --- .../hikyuu/strategy/BrokerTradeManager.cpp | 20 ++++----- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 44 ++++++++++--------- .../hikyuu/trade_manage/OrderBrokerBase.h | 16 ++++--- .../hikyuu/trade_manage/PositionRecord.cpp | 2 +- .../hikyuu/trade_manage/PositionRecord.h | 2 +- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 13 +++++- .../trade_manage/_PositionRecord.cpp | 4 +- 7 files changed, 60 insertions(+), 41 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 482d0745..fc37897e 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -24,11 +24,11 @@ BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const Trade for (const auto& brk_pos : brk_positions) { PositionRecord pos; pos.takeDatetime = now; - pos.stock = brk_pos.get("stock"); - pos.number = brk_pos.get("num"); - pos.totalNumber = pos.number; - pos.buyMoney = brk_pos.get("cost"); - pos.totalRisk = pos.buyMoney; + pos.stock = brk_pos.stock; + pos.number = brk_pos.number; + pos.totalNumber = brk_pos.number; + pos.buyMoney = brk_pos.money; + pos.totalRisk = brk_pos.money; m_position[pos.stock.id()] = pos; } @@ -69,11 +69,11 @@ void BrokerTradeManager::getPositionFromBroker() { for (const auto& brk_pos : brk_positions) { PositionRecord pos; pos.takeDatetime = now; - pos.stock = brk_pos.get("stock"); - pos.number = brk_pos.get("num"); - pos.totalNumber = pos.number; - pos.buyMoney = brk_pos.get("cost"); - pos.totalRisk = pos.buyMoney; + pos.stock = brk_pos.stock; + pos.number = brk_pos.number; + pos.totalNumber = brk_pos.number; + pos.buyMoney = brk_pos.money; + pos.totalRisk = brk_pos.money; auto iter = m_position.find(pos.stock.id()); if (iter == m_position.end()) { m_position[pos.stock.id()] = pos; diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 28bcfbb0..589c76c4 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -12,20 +12,36 @@ namespace hku { using json = nlohmann::json; +BrokerPositionRecord::BrokerPositionRecord(const Stock& stock_, price_t number_, price_t money_) +: stock(stock_), number(number_), money(money_) {} + BrokerPositionRecord::BrokerPositionRecord(BrokerPositionRecord&& rv) -: stock(std::move(rv.stock)), number(rv.number) { +: stock(std::move(rv.stock)), number(rv.number), money(rv.money) { rv.number = 0.0; + rv.money = 0.0; } BrokerPositionRecord& BrokerPositionRecord::operator=(BrokerPositionRecord&& rv) { if (this != &rv) { stock = std::move(rv.stock); number = rv.number; + money = rv.money; rv.number = 0.0; + rv.money = 0.0; } return *this; } +string BrokerPositionRecord::str() const { + return fmt::format("BrokerPositionRecord({}, {:<.4f}, {:<.4f})", stock.market_code(), number, + money); +} + +HKU_API std::ostream& operator<<(std::ostream& os, const BrokerPositionRecord& pos) { + os << pos.str(); + return os; +} + HKU_API std::ostream& operator<<(std::ostream& os, const OrderBrokerBase& broker) { os << "OrderBroker(" << broker.name() << ")"; return os; @@ -76,8 +92,8 @@ price_t OrderBrokerBase::cash() noexcept { return ret; } -vector OrderBrokerBase::position() noexcept { - vector ret; +vector OrderBrokerBase::position() noexcept { + vector ret; vector brk_positions; try { @@ -97,31 +113,19 @@ vector OrderBrokerBase::position() noexcept { continue; } - Parameter pos; + BrokerPositionRecord pos; auto market = brk_pos["market"].get(); auto code = brk_pos["code"].get(); auto stock = getStock(fmt::format("{}{}", market, code)); if (stock.isNull()) { // 策略的上下文可能并不包含该股,此时忽略 - HKU_DEBUG("Not found the stock: {}{}", market, code); + HKU_WARN("Not found the stock: {}{}", market, code); continue; } - pos.set("stock", stock); - pos.set("num", brk_pos["number"].get()); - pos.set("cost", brk_pos["cost"].get()); // 总成本 - if (brk_pos.contains("buy_frozen_num")) { - pos.set("buy_frozen_num", brk_pos["buy_frozen_num"].get()); - } else { - pos.set("buy_frozen_num", 0.0); - } - - if (brk_pos.contains("sell_frozon_num")) { - pos.set("sell_frozon_num", brk_pos["sell_frozon_num"].get()); - } else { - pos.set("sell_frozon_num", 0.0); - } - + pos.stock = stock; + pos.number = brk_pos["number"].get(); + pos.money = brk_pos["money"].get(); ret.emplace_back(pos); } catch (const std::exception& e) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 77396d30..1c0fed54 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -16,16 +16,22 @@ namespace hku { struct HKU_API BrokerPositionRecord { Stock stock; - price_t number{0.0}; + price_t number{0.0}; // 数量 + price_t money{0.0}; // 买入花费总资金 BrokerPositionRecord() = default; + BrokerPositionRecord(const Stock& stock, price_t number, price_t money); BrokerPositionRecord(const BrokerPositionRecord&) = default; BrokerPositionRecord(BrokerPositionRecord&& rv); BrokerPositionRecord& operator=(const BrokerPositionRecord&) = default; BrokerPositionRecord& operator=(BrokerPositionRecord&& rv); + + string str() const; }; +HKU_API std::ostream& operator<<(std::ostream& os, const BrokerPositionRecord&); + /** * 订单代理基类,实现实际的订单操作及程序化的订单。 * @details 可通过向 TradeManager.regBroker 向 TradeManager 注册多个订单代理实例。 @@ -81,7 +87,7 @@ public: */ price_t cash() noexcept; - vector position() noexcept; + vector position() noexcept; /** * 子类实现接口,执行实际买入操作 @@ -116,13 +122,11 @@ public: * 子类获取持仓信息实现 * @return vector json 字符串组成的持仓信息列表 *
-     * 其中:market, code, num 为必须
      * 示例:
      * [{"market": "SZ",
      *   "code": "000001",
-     *   "num": 100,
-     *   "buy_frozen_num", 0,
-     *   "sell_frozen_num", 0
+     *   "number": 100,
+     *   "money": 0,
      * }]
      * 
(m, "BrokerPositionRecord") + .def(py::init<>()) + .def(py::init()) + .def("__str__", &BrokerPositionRecord::str) + .def("__repr__", &BrokerPositionRecord::str) + .def_readwrite("stock", &BrokerPositionRecord::stock, "持仓对象") + .def_readwrite("number", &BrokerPositionRecord::number, "持仓数量") + .def_readwrite("money", &BrokerPositionRecord::money, "买入花费总资金"); + py::class_( m, "OrderBrokerBase", R"(订单代理包装基类,用户可以参考自定义自己的订单代理,加入额外的处理 @@ -76,7 +85,9 @@ void export_OrderBroker(py::module& m) { .def("cash", &OrderBrokerBase::cash) .def("position", - [](OrderBrokerBase& self) { return vector_to_python_list(self.position()); }) + [](OrderBrokerBase& self) { + return vector_to_python_list(self.position()); + }) .def("_buy", &OrderBrokerBase::_buy, R"(_buy(self, datetime, market, code, price, num) diff --git a/hikyuu_pywrap/trade_manage/_PositionRecord.cpp b/hikyuu_pywrap/trade_manage/_PositionRecord.cpp index fc7f529a..d2b6937b 100644 --- a/hikyuu_pywrap/trade_manage/_PositionRecord.cpp +++ b/hikyuu_pywrap/trade_manage/_PositionRecord.cpp @@ -21,8 +21,8 @@ void export_PositionRecord(py::module& m) { .def(py::init()) - .def("__str__", &PositionRecord::toString) - .def("__repr__", &PositionRecord::toString) + .def("__str__", &PositionRecord::str) + .def("__repr__", &PositionRecord::str) .def_readwrite("stock", &PositionRecord::stock, "交易对象(Stock)") .def_readwrite("take_datetime", &PositionRecord::takeDatetime, "初次建仓时刻(Datetime)") From 854a3c3ed8108f8592f416ae3922c0a07c24d478 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 19 Aug 2024 03:03:54 +0800 Subject: [PATCH 468/601] BrokerTradeManager continue --- hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp | 8 ++++++++ hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index fc37897e..6f4cd3c9 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -124,6 +124,10 @@ TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock "{} {} Buy number({}) must be <= maxTradeNumber({})!", datetime, stock.market_code(), number, stock.maxTradeNumber()); + // 同步资金与账户信息 + getCashFromBroker(); + getPositionFromBroker(); + CostRecord cost = getBuyCost(datetime, stock, realPrice, number); // 实际交易需要的现金=交易数量*实际交易价格+交易总成本 @@ -194,6 +198,10 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc "{} {} Sell number({}) must be <= maxTradeNumber({})!", datetime, stock.market_code(), number, stock.maxTradeNumber()); + // 同步资金与账户信息 + getCashFromBroker(); + getPositionFromBroker(); + // 未持仓 position_map_type::iterator pos_iter = m_position.find(stock.id()); HKU_TRACE_IF_RETURN(pos_iter == m_position.end(), result, diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index 5974c0a5..5f0994eb 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -73,7 +73,7 @@ public: * @note 如果不带日期参数,无法根据权息信息调整持仓 */ virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override { - return (datetime >= m_last_datetime) ? m_cash : 0.0; + return (datetime >= m_last_datetime) ? currentCash() : 0.0; } /** From 24784fc404c72a08cf9ebb81b53431c0e5d76a4e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 19 Aug 2024 10:42:23 +0800 Subject: [PATCH 469/601] update --- .../hikyuu/strategy/BrokerTradeManager.cpp | 28 +++++++++++++------ .../hikyuu/trade_manage/OrderBrokerBase.cpp | 6 ++++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 6f4cd3c9..cda8b1a7 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -66,24 +66,36 @@ void BrokerTradeManager::getPositionFromBroker() { auto now = Datetime::now(); auto brk_positions = broker->position(); + + position_map_type new_positions; for (const auto& brk_pos : brk_positions) { PositionRecord pos; pos.takeDatetime = now; pos.stock = brk_pos.stock; - pos.number = brk_pos.number; - pos.totalNumber = brk_pos.number; - pos.buyMoney = brk_pos.money; - pos.totalRisk = brk_pos.money; auto iter = m_position.find(pos.stock.id()); if (iter == m_position.end()) { - m_position[pos.stock.id()] = pos; + pos.number = brk_pos.number; + pos.totalNumber = brk_pos.number; + pos.buyMoney = brk_pos.money; + pos.totalRisk = brk_pos.money; + new_positions[pos.stock.id()] = pos; } else { - iter->second.number = pos.number; - iter->second.totalNumber = pos.totalNumber; - iter->second.buyMoney = pos.buyMoney; + auto& cur_pos = iter->second; + if (cur_pos.number != 0.0) { + pos.totalCost = cur_pos.totalCost / cur_pos.number * brk_pos.number; + pos.totalRisk = cur_pos.totalRisk / cur_pos.number * brk_pos.number; + } else { + pos.totalRisk = brk_pos.money; + } + pos.number = brk_pos.number; + pos.totalNumber = brk_pos.number; + pos.buyMoney = pos.buyMoney; + new_positions[pos.stock.id()] = pos; } } + m_position.swap(new_positions); + m_init_datetime = Datetime::now(); m_first_datetime = m_init_datetime; m_last_datetime = m_init_datetime; diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 589c76c4..91c94413 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -126,6 +126,12 @@ vector OrderBrokerBase::position() noexcept { pos.stock = stock; pos.number = brk_pos["number"].get(); pos.money = brk_pos["money"].get(); + if (pos.number == 0.0 || pos.money == 0.0) { + HKU_WARN( + "Fetched position number({:<.4f}) or money({:<.4f}) was zero! It's ignored!", + pos.number, pos.money); + continue; + } ret.emplace_back(pos); } catch (const std::exception& e) { From 8696f58db382e94774ceeb19c506c896aaae12f5 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 19 Aug 2024 11:02:33 +0800 Subject: [PATCH 470/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20broker=20=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_manage/broker.py | 12 +++---- hikyuu/trade_manage/broker_easytrader.py | 4 +-- hikyuu/trade_manage/broker_mail.py | 33 +++++++++---------- .../hikyuu/strategy/BrokerTradeManager.cpp | 8 +++-- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 10 +++--- .../hikyuu/trade_manage/OrderBrokerBase.h | 20 ++++++++--- .../hikyuu/trade_manage/TradeManager.cpp | 16 ++++++--- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 10 +++--- 8 files changed, 68 insertions(+), 45 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index 5a2ee31f..eb8252d2 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -44,13 +44,13 @@ class OrderBrokerWrap(OrderBrokerBase): super(OrderBrokerWrap, self).__init__(name) self._broker = broker - def _buy(self, datetime, market, code, price, num): + def _buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from): """实现 OrderBrokerBase 的 _buy 接口""" - self._broker.buy('{}{}'.format(market, code), price, num) + self._broker.buy('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) - def _sell(self, datetime, market, code, price, num): + def _sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from): """实现 OrderBrokerBase 的 _sell 接口""" - self._broker.sell('{}{}'.format(market, code), price, num) + self._broker.sell('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) def _cash(self): if hasattr(self._broker, "cash"): @@ -69,10 +69,10 @@ class TestOrderBroker: def __init__(self): pass - def buy(self, code, price, num): + def buy(self, code, price, num, stoploss, goal_price, part_from): print("买入:%s %.3f %i" % (code, price, num)) - def sell(self, code, price, num): + def sell(self, code, price, num, stoploss, goal_price, part_from): print("卖出:%s %.3f %i" % (code, price, num)) diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index fd67197d..85db85f7 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -8,11 +8,11 @@ class EasyTraderOrderBroker: def __init__(self, user): self.user = user - def buy(self, code, price, num): + def buy(self, code, price, num, stoploss, goal_price, part_from): self.user.buy(code[2:], price=price, amount=num) print("买入:%s %.3f %i" % (code, price, num)) - def sell(self, code, price, num): + def sell(self, code, price, num, stoploss, goal_price, part_from): self.user.sell(code[2:], price=price, amount=num) print("卖出:%s %.3f %i" % (code, price, num)) diff --git a/hikyuu/trade_manage/broker_mail.py b/hikyuu/trade_manage/broker_mail.py index 64be7f1a..83889bf8 100644 --- a/hikyuu/trade_manage/broker_mail.py +++ b/hikyuu/trade_manage/broker_mail.py @@ -24,24 +24,25 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE # SOFTWARE. -#=============================================================================== +# =============================================================================== # History # 1. 20170704, Added by fasiondog -#=============================================================================== +# =============================================================================== import smtplib from email.mime.text import MIMEText from email.header import Header - + + class MailOrderBroker: """ 邮件订单代理 """ - + def __init__(self, host, sender, pwd, receivers): """ 邮件订单代理,执行买入/卖出操作时发送 Email - + :param str host: smtp服务器地址 :param int port: smtp服务器端口 :param str sender: 发件邮箱(既用户名) @@ -52,10 +53,10 @@ class MailOrderBroker: self._pwd = pwd self._sender = sender self._receivers = receivers - + def _sendmail(self, title, msg): """发送邮件 - + :param str title: 邮件标题 :param str msg: 邮件内容 """ @@ -70,14 +71,13 @@ class MailOrderBroker: smtpObj.connect(self._host, 25) smtpObj.login(self._sender, self._pwd) smtpObj.sendmail(self._sender, self._receivers, message.as_string()) - - - def buy(self, code, price, num): + + def buy(self, code, price, num, stoploss, goal_price, part_from): """执行买入操作,向指定的邮箱发送邮件,格式如下:: - + 邮件标题:【Hkyuu提醒】买入 证券代码 邮件内容:买入:证券代码,价格:买入的价格,数量:买入数量 - + :param str code: 证券代码 :param float price: 买入价格 :param int num: 买入数量 @@ -85,14 +85,13 @@ class MailOrderBroker: action = "买入:{},价格:{},数量:{} ".format(code, price, num) title = "【Hkyuu提醒】买入 {}".format(code) self._sendmail(title, action) - - def sell(self, code, price, num): + def sell(self, code, price, num, stoploss, goal_price, part_from): """执行卖出操作,向指定的邮箱发送邮件,格式如下:: - + 邮件标题:【Hkyuu提醒】卖出 证券代码 邮件内容:卖出:证券代码,价格:卖出的价格,数量:卖出数量 - + :param str code: 证券代码 :param float price: 卖出价格 :param int num: 卖出数量 @@ -100,5 +99,3 @@ class MailOrderBroker: title = "【Hkyuu提醒】卖出 {}".format(code) action = "卖出:{},价格:{},数量:{} ".format(code, price, num) self._sendmail(title, action) - - \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index cda8b1a7..3a5e47ac 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -179,7 +179,9 @@ TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + (*broker_iter) + ->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice, + from); if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } @@ -258,7 +260,9 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); + (*broker_iter) + ->sell(datetime, stock.market(), stock.code(), realPrice, real_number, stoploss, + goalPrice, from); if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 91c94413..b8115cc3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -59,9 +59,10 @@ OrderBrokerBase::OrderBrokerBase(const string& name) : m_name(name) {} OrderBrokerBase::~OrderBrokerBase() {} void OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code, - price_t price, double num) noexcept { + price_t price, double num, price_t stoploss, price_t goalPrice, + SystemPart from) noexcept { try { - _buy(datetime, market, code, price, num); + _buy(datetime, market, code, price, num, stoploss, goalPrice, from); } catch (const std::exception& e) { HKU_ERROR(e.what()); } catch (...) { @@ -70,9 +71,10 @@ void OrderBrokerBase::buy(Datetime datetime, const string& market, const string& } void OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code, - price_t price, double num) noexcept { + price_t price, double num, price_t stoploss, price_t goalPrice, + SystemPart from) noexcept { try { - _sell(datetime, market, code, price, num); + _sell(datetime, market, code, price, num, stoploss, goalPrice, from); } catch (const std::exception& e) { HKU_ERROR(e.what()); } catch (...) { diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 1c0fed54..fc991442 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -11,6 +11,7 @@ #include "../DataType.h" #include "../utilities/Parameter.h" +#include "../trade_sys/system/SystemPart.h" namespace hku { @@ -67,9 +68,12 @@ public: * @param code 证券代码 * @param price 买入价格 * @param num 买入数量 + * @param stoploss 预期的止损价 + * @param goalPrice 预期的目标价位 + * @param from 系统部件来源 */ - void buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) noexcept; + void buy(Datetime datetime, const string& market, const string& code, price_t price, double num, + price_t stoploss, price_t goalPrice, SystemPart from) noexcept; /** * 执行卖出操作 @@ -78,9 +82,12 @@ public: * @param code 证券代码 * @param price 卖出价格 * @param num 卖出数量 + * @param stoploss 新的预期止损价 + * @param goalPrice 新的预期目标价位 + * @param from 系统部件来源 */ void sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) noexcept; + double num, price_t stoploss, price_t goalPrice, SystemPart from) noexcept; /** * 获取当前可用资金 @@ -96,9 +103,12 @@ public: * @param code 证券代码 * @param price 买入价格 * @param num 买入数量 + * @param stoploss 预期的止损价 + * @param goalPrice 预期的目标价位 + * @param from 系统部件来源 */ virtual void _buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) = 0; + double num, price_t stoploss, price_t goalPrice, SystemPart from) = 0; /** * 子类实现接口,执行实际卖出操作 @@ -109,7 +119,7 @@ public: * @param num 卖出数量 */ virtual void _sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) = 0; + double num, price_t stoploss, price_t goalPrice, SystemPart from) = 0; /** * 子类获取当前可用现金接口 diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index fe5421e4..f1e1be52 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -868,7 +868,9 @@ TradeRecord TradeManager::buy(const Datetime& datetime, const Stock& stock, pric if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + (*broker_iter) + ->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice, + from); if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } @@ -956,7 +958,9 @@ TradeRecord TradeManager::sell(const Datetime& datetime, const Stock& stock, pri if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number); + (*broker_iter) + ->sell(datetime, stock.market(), stock.code(), realPrice, real_number, stoploss, + goalPrice, from); if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } @@ -1069,7 +1073,9 @@ TradeRecord TradeManager::sellShort(const Datetime& datetime, const Stock& stock if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, number); + (*broker_iter) + ->sell(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice, + from); if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } @@ -1141,7 +1147,9 @@ TradeRecord TradeManager::buyShort(const Datetime& datetime, const Stock& stock, if (datetime > m_broker_last_datetime) { list::const_iterator broker_iter = m_broker_list.begin(); for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number); + (*broker_iter) + ->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice, + from); if (datetime > m_broker_last_datetime) { m_broker_last_datetime = datetime; } diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index 96675142..56dacdad 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -16,13 +16,15 @@ public: using OrderBrokerBase::OrderBrokerBase; void _buy(Datetime datetime, const string& market, const string& code, price_t price, - double num) override { - PYBIND11_OVERLOAD_PURE(void, OrderBrokerBase, _buy, datetime, market, code, price, num); + double num, price_t stoploss, price_t goalPrice, SystemPart from) override { + PYBIND11_OVERLOAD_PURE(void, OrderBrokerBase, _buy, datetime, market, code, price, num, + stoploss, goalPrice, from); } void _sell(Datetime datetime, const string& market, const string& code, price_t price, - double num) override { - PYBIND11_OVERLOAD_PURE(void, OrderBrokerBase, _sell, datetime, market, code, price, num); + double num, price_t stoploss, price_t goalPrice, SystemPart from) override { + PYBIND11_OVERLOAD_PURE(void, OrderBrokerBase, _sell, datetime, market, code, price, num, + stoploss, goalPrice, from); } price_t _cash() override { From 04ac7da82737cda4cb8793fcf1078eae6aafd3e9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 19 Aug 2024 18:21:49 +0800 Subject: [PATCH 471/601] =?UTF-8?q?SpotAgent::start=20=E5=85=88=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=20stop?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 3334114e..b3c90b80 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -34,6 +34,7 @@ SpotAgent::~SpotAgent() { } void SpotAgent::start() { + stop(); if (m_stop) { m_stop = false; m_receiveThread = std::thread([this]() { work_thread(); }); From a86ba248dffa5e25ba1a377c93aeb73528b6e7a7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 00:11:11 +0800 Subject: [PATCH 472/601] BrokerTradeManager continue --- .../hikyuu/strategy/BrokerTradeManager.cpp | 119 +++++++++--------- .../hikyuu/strategy/BrokerTradeManager.h | 23 ++-- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 12 ++ .../hikyuu/trade_manage/OrderBrokerBase.h | 28 +++++ .../hikyuu/trade_manage/TradeManagerBase.h | 8 ++ hikyuu_cpp/hikyuu/trade_sys/system/System.cpp | 9 +- 6 files changed, 121 insertions(+), 78 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 3a5e47ac..09270dfa 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -5,19 +5,21 @@ * Author: fasiondog */ +#include #include "hikyuu/trade_manage/crt/TC_Zero.h" #include "BrokerTradeManager.h" namespace hku { +using json = nlohmann::json; + BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, const string& name) : TradeManagerBase(name, costfunc) { HKU_ASSERT(broker); m_broker_list.emplace_back(broker); - m_init_cash = broker->cash(); - m_cash = m_init_cash; + m_cash = broker->cash(); auto now = Datetime::now(); auto brk_positions = broker->position(); @@ -32,74 +34,73 @@ BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const Trade m_position[pos.stock.id()] = pos; } - m_init_datetime = Datetime::now(); - m_first_datetime = m_init_datetime; - m_last_datetime = m_init_datetime; - m_broker_last_datetime = m_init_datetime; + m_datetime = Datetime::now(); + m_broker_last_datetime = m_datetime; } void BrokerTradeManager::_reset() { HKU_WARN("The subclass does not implement a reset method"); - m_first_datetime = m_init_datetime; - m_last_datetime = m_init_datetime; - m_cash = m_init_cash; + m_datetime = Datetime::max(); + m_cash = 0.0; m_position.clear(); } shared_ptr BrokerTradeManager::_clone() { BrokerTradeManager* p = new BrokerTradeManager(); - p->m_init_datetime = m_init_datetime; - p->m_first_datetime = m_first_datetime; - p->m_last_datetime = m_last_datetime; - p->m_init_cash = m_init_cash; + p->m_datetime = m_datetime; p->m_cash = m_cash; p->m_position = m_position; return shared_ptr(p); } -void BrokerTradeManager::getCashFromBroker() { - m_cash = m_broker_list.front()->cash(); -} +void BrokerTradeManager::fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) { + HKU_CHECK(broker, "broker is null!"); -void BrokerTradeManager::getPositionFromBroker() { - auto& broker = m_broker_list.front(); - - auto now = Datetime::now(); - auto brk_positions = broker->position(); - - position_map_type new_positions; - for (const auto& brk_pos : brk_positions) { - PositionRecord pos; - pos.takeDatetime = now; - pos.stock = brk_pos.stock; - auto iter = m_position.find(pos.stock.id()); - if (iter == m_position.end()) { - pos.number = brk_pos.number; - pos.totalNumber = brk_pos.number; - pos.buyMoney = brk_pos.money; - pos.totalRisk = brk_pos.money; - new_positions[pos.stock.id()] = pos; - } else { - auto& cur_pos = iter->second; - if (cur_pos.number != 0.0) { - pos.totalCost = cur_pos.totalCost / cur_pos.number * brk_pos.number; - pos.totalRisk = cur_pos.totalRisk / cur_pos.number * brk_pos.number; - } else { - pos.totalRisk = brk_pos.money; - } - pos.number = brk_pos.number; - pos.totalNumber = brk_pos.number; - pos.buyMoney = pos.buyMoney; - new_positions[pos.stock.id()] = pos; - } + auto brk_asset = broker->getAssetInfo(); + if (brk_asset.empty()) { + m_datetime = Datetime::now(); + m_cash = 0.0; + m_position.clear(); + return; } - m_position.swap(new_positions); + try { + json asset(brk_asset); + m_datetime = asset.contains("datetime") + ? m_datetime = Datetime(asset["datetime"].get()) + : m_datetime = Datetime::now(); - m_init_datetime = Datetime::now(); - m_first_datetime = m_init_datetime; - m_last_datetime = m_init_datetime; - m_broker_last_datetime = m_init_datetime; + m_cash = asset["cash"]; + + auto& positions = asset["positions"]; + for (auto iter = positions.cbegin(); iter != positions.cend(); ++iter) { + const auto& jpos = *iter; + auto market = jpos["market"].get(); + auto code = jpos["code"].get(); + Stock stock = getStock(fmt::format("{}{}", market, code)); + if (stock.isNull()) { + HKU_WARN("Not found stock: {}{}", market, code); + continue; + } + + PositionRecord pos; + pos.stock = stock; + pos.takeDatetime = m_datetime; + pos.number = jpos["number"].get(); + pos.stoploss = jpos["stoploss"].get(); + pos.goalPrice = jpos["goal_price"].get(); + pos.totalNumber = pos.number; + price_t cost_price = jpos["cost_price"].get(); + pos.buyMoney = pos.number * cost_price; + pos.totalRisk = (pos.stoploss - cost_price) * pos.number; + m_position[stock.id()] = pos; + } + + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } + + m_broker_last_datetime = m_datetime; } PositionRecordList BrokerTradeManager::getPositionList() const { @@ -112,7 +113,7 @@ PositionRecordList BrokerTradeManager::getPositionList() const { } bool BrokerTradeManager::checkin(const Datetime& datetime, price_t cash) { - HKU_IF_RETURN(datetime < m_last_datetime, false); + HKU_IF_RETURN(datetime < m_datetime, false); m_cash += cash; return true; } @@ -136,10 +137,6 @@ TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock "{} {} Buy number({}) must be <= maxTradeNumber({})!", datetime, stock.market_code(), number, stock.maxTradeNumber()); - // 同步资金与账户信息 - getCashFromBroker(); - getPositionFromBroker(); - CostRecord cost = getBuyCost(datetime, stock, realPrice, number); // 实际交易需要的现金=交易数量*实际交易价格+交易总成本 @@ -212,10 +209,6 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc "{} {} Sell number({}) must be <= maxTradeNumber({})!", datetime, stock.market_code(), number, stock.maxTradeNumber()); - // 同步资金与账户信息 - getCashFromBroker(); - getPositionFromBroker(); - // 未持仓 position_map_type::iterator pos_iter = m_position.find(stock.id()); HKU_TRACE_IF_RETURN(pos_iter == m_position.end(), result, @@ -290,7 +283,7 @@ FundsRecord BrokerTradeManager::getFunds(KQuery::KType inktype) const { funds.cash = m_cash; funds.market_value = value; funds.short_market_value = 0.0; - funds.base_cash = m_init_cash; + funds.base_cash = m_cash; funds.base_asset = 0.0; funds.borrow_cash = 0.0; funds.borrow_asset = 0.0; @@ -298,7 +291,7 @@ FundsRecord BrokerTradeManager::getFunds(KQuery::KType inktype) const { } FundsRecord BrokerTradeManager::getFunds(const Datetime& datetime, KQuery::KType ktype) { - return (datetime >= m_last_datetime) ? getFunds(ktype) : FundsRecord(); + return (datetime >= m_datetime) ? getFunds(ktype) : FundsRecord(); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index 5f0994eb..a43ddd00 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -23,6 +23,8 @@ public: virtual shared_ptr _clone() override; + virtual void fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) override; + /** * 根据权息信息更新当前持仓与交易情况 * @note 必须按时间顺序调用 @@ -42,22 +44,22 @@ public: /** 初始资金 */ virtual price_t initCash() const override { - return m_init_cash; + return m_cash; } /** 账户建立日期 */ virtual Datetime initDatetime() const override { - return m_init_datetime; + return m_datetime; } /** 第一笔买入交易发生日期,如未发生交易返回Null() */ virtual Datetime firstDatetime() const override { - return m_first_datetime; + return m_datetime; } /** 最后一笔交易日期,注意和交易类型无关,如未发生交易返回账户建立日期 */ virtual Datetime lastDatetime() const override { - return m_last_datetime; + return m_datetime; } /** @@ -73,7 +75,7 @@ public: * @note 如果不带日期参数,无法根据权息信息调整持仓 */ virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override { - return (datetime >= m_last_datetime) ? currentCash() : 0.0; + return (datetime >= m_datetime) ? currentCash() : 0.0; } /** @@ -420,16 +422,9 @@ public: } private: - void getCashFromBroker(); - void getPositionFromBroker(); + Datetime m_datetime; // 当前日期 -private: - Datetime m_init_datetime; // 账户建立日期 - Datetime m_first_datetime; // 第一次交易时间 - Datetime m_last_datetime; // 最后一次交易时间 - - price_t m_init_cash{0.0}; // 初始资金 - price_t m_cash{0.0}; // 当前可用现金 + price_t m_cash{0.0}; // 当前可用现金 typedef map position_map_type; position_map_type m_position; // 当前持仓交易对象的持仓记录 diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index b8115cc3..2ce41a29 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -82,6 +82,18 @@ void OrderBrokerBase::sell(Datetime datetime, const string& market, const string } } +string OrderBrokerBase::getAssetInfo() noexcept { + string ret; + try { + ret = _getAssetInfo(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } + return ret; +} + price_t OrderBrokerBase::cash() noexcept { price_t ret = 0.0; try { diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index fc991442..13ab26f3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -96,6 +96,30 @@ public: vector position() noexcept; + /** + * 获取当前资产信息 + * @return string json字符串 + *
+     * 接口规范:
+     * {
+     *   "datetime": "2001-01-01 18:00:00.12345",
+     *   "cash": 0.0,
+     *   "positions": [
+     *       {"market": "SZ", "code": "000001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0,
+     *        "cost_price": 0.0},
+     *       {"market": "SH", "code": "600001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0,
+     *        "cost_price": 0.0},
+     *    ]
+     * }
+     *
+     * 说明:
+     * cash: 当前可用资金
+     * number 应该为:现有持仓 + 正在买入 - 正在卖出
+     * cost_price: 每股买入成本价
+     * 
+ */ + string getAssetInfo() noexcept; + /** * 子类实现接口,执行实际买入操作 * @param datetime 策略指示时间 @@ -144,6 +168,10 @@ public: return vector(); } + virtual string _getAssetInfo() { + return string(); + } + protected: string m_name; }; diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index c9375583..0ab8d5a1 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -693,6 +693,14 @@ public: HKU_WARN("The subclass does not implement this method"); } + /** + * 从订单代理实例同步当前账户资产信息(包含资金、持仓等) + * @param broker 订单代理实例 + */ + virtual void fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) { + HKU_WARN("The subclass does not implement this method"); + } + protected: string m_name; // 账户名称 TradeCostPtr m_costfunc; // 成本算法 diff --git a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp index bef25c41..df1bc232 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/system/System.cpp @@ -382,8 +382,15 @@ void System::run(const KData& kdata, bool reset, bool resetAll) { size_t total = kdata.size(); auto const* ks = kdata.data(); auto const* src_ks = m_src_kdata.data(); + + // 适应 strategy 模式下运行时同步资产信息可能造成的偏差 + Datetime tm_init_datetime = m_tm->initDatetime(); + if (KQuery::getKTypeInMin(kdata.getQuery().kType()) >= 1440) { + tm_init_datetime = tm_init_datetime.startOfDay(); + } + for (size_t i = 0; i < total; ++i) { - if (ks[i].datetime >= m_tm->initDatetime()) { + if (ks[i].datetime >= tm_init_datetime) { auto tr = _runMoment(ks[i], src_ks[i]); if (trace) { HKU_INFO_IF(!tr.isNull(), "{}", tr); From b7068e7ed045041dccf816734fbd77f65197b5b6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 00:38:00 +0800 Subject: [PATCH 473/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy,=20SpotAg?= =?UTF-8?q?ent=20=E4=BB=A5=E4=BE=BF=E8=83=BD=E7=94=A8=E4=BB=A5=E5=A4=9A?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=96=B9=E5=BC=8F=E6=89=A7=E8=A1=8C=20strate?= =?UTF-8?q?gy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 102 ++++++++++--------- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 4 + hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 9 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 20 ++-- 4 files changed, 74 insertions(+), 61 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index f2707b5c..2a6637ff 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -171,68 +171,74 @@ void HKU_API startSpotAgent(bool print) { agent.setPrintFlag(print); - const auto& preloadParam = sm.getPreloadParameter(); - if (preloadParam.tryGet("min", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN)); - } + // 防止调用 stopSpotAgent 后重新 startSpotAgent + static std::atomic_bool g_init_spot_agent{false}; + if (!g_init_spot_agent) { + const auto& preloadParam = sm.getPreloadParameter(); + if (preloadParam.tryGet("min", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN)); + } - if (preloadParam.tryGet("day", false)) { - agent.addProcess(updateStockDayData); - } + if (preloadParam.tryGet("day", false)) { + agent.addProcess(updateStockDayData); + } - if (preloadParam.tryGet("week", false)) { - agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::WEEK)); - } + if (preloadParam.tryGet("week", false)) { + agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::WEEK)); + } - if (preloadParam.tryGet("month", false)) { - agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::MONTH)); - } + if (preloadParam.tryGet("month", false)) { + agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::MONTH)); + } - if (preloadParam.tryGet("quarter", false)) { - agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::QUARTER)); - } + if (preloadParam.tryGet("quarter", false)) { + agent.addProcess( + std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::QUARTER)); + } - if (preloadParam.tryGet("halfyear", false)) { - agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::HALFYEAR)); - } + if (preloadParam.tryGet("halfyear", false)) { + agent.addProcess( + std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::HALFYEAR)); + } - if (preloadParam.tryGet("year", false)) { - agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::YEAR)); - } + if (preloadParam.tryGet("year", false)) { + agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::YEAR)); + } - if (preloadParam.tryGet("min5", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN5)); - } + if (preloadParam.tryGet("min5", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN5)); + } - if (preloadParam.tryGet("min15", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN15)); - } + if (preloadParam.tryGet("min15", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN15)); + } - if (preloadParam.tryGet("min30", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN30)); - } + if (preloadParam.tryGet("min30", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN30)); + } - if (preloadParam.tryGet("min60", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN60)); - } - if (preloadParam.tryGet("min3", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN3)); - } + if (preloadParam.tryGet("min60", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN60)); + } + if (preloadParam.tryGet("min3", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN3)); + } - if (preloadParam.tryGet("hour2", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR2)); - } + if (preloadParam.tryGet("hour2", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR2)); + } - if (preloadParam.tryGet("hour4", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR4)); - } + if (preloadParam.tryGet("hour4", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR4)); + } - if (preloadParam.tryGet("hour6", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR6)); - } + if (preloadParam.tryGet("hour6", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR6)); + } - if (preloadParam.tryGet("hour12", false)) { - agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR12)); + if (preloadParam.tryGet("hour12", false)) { + agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR12)); + } } agent.start(); diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index b3c90b80..5de1f3f6 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -215,21 +215,25 @@ void SpotAgent::work_thread() { void SpotAgent::addProcess(std::function process) { HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!"); + std::lock_guard lock(m_mutex); m_processList.push_back(process); } void SpotAgent::addPostProcess(std::function func) { HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!"); + std::lock_guard lock(m_mutex); m_postProcessList.push_back(func); } void SpotAgent::clearProcessList() { HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!"); + std::lock_guard lock(m_mutex); m_processList.clear(); } void SpotAgent::clearPostProcessList() { HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!"); + std::lock_guard lock(m_mutex); m_postProcessList.clear(); } diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index 03a49c6d..cafc4b4e 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -45,6 +45,7 @@ public: /** 设置是否打印数据接收进展情况,主要用于在交互环境下关闭打印 */ void setPrintFlag(bool print) { + std::lock_guard lock(m_mutex); m_print = print; } @@ -104,14 +105,18 @@ private: enum STATUS { WAITING, RECEIVING }; // 等待新的批次数据,正在接收批次数据中 enum STATUS m_status = WAITING; // 当前内部状态 std::atomic_bool m_stop = true; // 结束代理工作标识 - bool m_print = true; // 是否打印接收进度,防止的交互模式的影响 + int m_revTimeout = 100; // 连接数据服务超时时长(毫秒) size_t m_batch_count = 0; // 记录本次批次接收的数据数量 std::thread m_receiveThread; // 数据接收线程 ThreadPool m_tg; // 数据处理任务线程池 + vector> m_process_task_list; + + // 下面属性被修改时需要加锁,以便可以使用多线程方式运行 strategy + std::mutex m_mutex; + bool m_print = true; // 是否打印接收进度,防止的交互模式的影响 list> m_processList; // 已注册的 spot 处理函数列表 list> m_postProcessList; // 已注册的批次后处理函数列表 - vector> m_process_task_list; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index d1377ca7..e3b4033e 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -71,19 +71,17 @@ void Strategy::run() { StockManager& sm = StockManager::instance(); - // 非独立进程方式运行 Stratege 或 重复执行,则直接返回 - if (sm.thread_id() == std::this_thread::get_id()) { - return; + // sm 尚未初始化,则初始化 + if (sm.thread_id() == std::thread::id()) { + // 注册 ctrl-c 终止信号 + std::signal(SIGINT, sig_handler); + + CLS_INFO("{} is running! You can press Ctrl-C to terminte ...", m_name); + + // 初始化 + hikyuu_init(m_config_file, false, m_context); } - // 注册 ctrl-c 终止信号 - std::signal(SIGINT, sig_handler); - - CLS_INFO("{} is running! You can press Ctrl-C to terminte ...", m_name); - - // 初始化 - hikyuu_init(m_config_file, false, m_context); - // 先将行情接收代理停止,以便后面加入处理函数 stopSpotAgent(); From 24f56e72a9f070c0876b64adc4495d7db81433d1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 15:43:08 +0800 Subject: [PATCH 474/601] update --- hikyuu/trade_manage/broker.py | 20 +++--- .../hikyuu/strategy/BrokerTradeManager.cpp | 15 ----- .../hikyuu/trade_manage/OrderBrokerBase.cpp | 64 ------------------- .../hikyuu/trade_manage/OrderBrokerBase.h | 30 --------- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 18 +----- 5 files changed, 14 insertions(+), 133 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index eb8252d2..6fc2dc47 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -29,7 +29,9 @@ # 1. 20170704, Added by fasiondog # =============================================================================== +import json from hikyuu import OrderBrokerBase +from hikyuu.util import hku_error class OrderBrokerWrap(OrderBrokerBase): @@ -52,15 +54,15 @@ class OrderBrokerWrap(OrderBrokerBase): """实现 OrderBrokerBase 的 _sell 接口""" self._broker.sell('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) - def _cash(self): - if hasattr(self._broker, "cash"): - return self._broker.cash() - return 0.0 - - def _position(self): - if hasattr(self._broker, "position"): - return self._broker.position() - return list() + def _get_asset_info(self): + try: + if hasattr(self._broker, "get_asset_info"): + ret = self._broker.get_asset_info() + return json.dumps(ret) if type(ret) == dict else str(ret) + return str() + except Exception as e: + hku_error(e) + return str() class TestOrderBroker: diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 09270dfa..e62e2053 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -19,21 +19,6 @@ BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const Trade HKU_ASSERT(broker); m_broker_list.emplace_back(broker); - m_cash = broker->cash(); - - auto now = Datetime::now(); - auto brk_positions = broker->position(); - for (const auto& brk_pos : brk_positions) { - PositionRecord pos; - pos.takeDatetime = now; - pos.stock = brk_pos.stock; - pos.number = brk_pos.number; - pos.totalNumber = brk_pos.number; - pos.buyMoney = brk_pos.money; - pos.totalRisk = brk_pos.money; - m_position[pos.stock.id()] = pos; - } - m_datetime = Datetime::now(); m_broker_last_datetime = m_datetime; } diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp index 2ce41a29..ff135130 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.cpp @@ -94,68 +94,4 @@ string OrderBrokerBase::getAssetInfo() noexcept { return ret; } -price_t OrderBrokerBase::cash() noexcept { - price_t ret = 0.0; - try { - ret = _cash(); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR_UNKNOWN; - } - return ret; -} - -vector OrderBrokerBase::position() noexcept { - vector ret; - - vector brk_positions; - try { - brk_positions = _position(); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR_UNKNOWN; - } - - HKU_IF_RETURN(brk_positions.empty(), ret); - - for (size_t i = 0, len = brk_positions.size(); i < len; i++) { - try { - json brk_pos(brk_positions[i]); - if (brk_pos.empty()) { - continue; - } - - BrokerPositionRecord pos; - auto market = brk_pos["market"].get(); - auto code = brk_pos["code"].get(); - auto stock = getStock(fmt::format("{}{}", market, code)); - if (stock.isNull()) { - // 策略的上下文可能并不包含该股,此时忽略 - HKU_WARN("Not found the stock: {}{}", market, code); - continue; - } - - pos.stock = stock; - pos.number = brk_pos["number"].get(); - pos.money = brk_pos["money"].get(); - if (pos.number == 0.0 || pos.money == 0.0) { - HKU_WARN( - "Fetched position number({:<.4f}) or money({:<.4f}) was zero! It's ignored!", - pos.number, pos.money); - continue; - } - ret.emplace_back(pos); - - } catch (const std::exception& e) { - HKU_ERROR("Failed parser the [{}] record ({})! {}", i, brk_positions[i], e.what()); - } catch (...) { - HKU_ERROR("Failed parser the [{}] record ({})! Unknown error!", i, brk_positions[i]); - } - } - - return ret; -} - } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 13ab26f3..0597eed3 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -89,13 +89,6 @@ public: void sell(Datetime datetime, const string& market, const string& code, price_t price, double num, price_t stoploss, price_t goalPrice, SystemPart from) noexcept; - /** - * 获取当前可用资金 - */ - price_t cash() noexcept; - - vector position() noexcept; - /** * 获取当前资产信息 * @return string json字符串 @@ -145,29 +138,6 @@ public: virtual void _sell(Datetime datetime, const string& market, const string& code, price_t price, double num, price_t stoploss, price_t goalPrice, SystemPart from) = 0; - /** - * 子类获取当前可用现金接口 - */ - virtual price_t _cash() { - return 0.0; - } - - /** - * 子类获取持仓信息实现 - * @return vector json 字符串组成的持仓信息列表 - *
-     * 示例:
-     * [{"market": "SZ",
-     *   "code": "000001",
-     *   "number": 100,
-     *   "money": 0,
-     * }]
-     * 
_position() { - return vector(); - } - virtual string _getAssetInfo() { return string(); } diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index 56dacdad..edebc1ec 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -27,12 +27,8 @@ public: stoploss, goalPrice, from); } - price_t _cash() override { - PYBIND11_OVERLOAD(price_t, OrderBrokerBase, _cash); - } - - vector _position() override { - PYBIND11_OVERLOAD(vector, OrderBrokerBase, _position); + string _getAssetInfo() override { + PYBIND11_OVERLOAD_NAME(string, OrderBrokerBase, "_get_asset_info", _getAssetInfo); } }; @@ -84,13 +80,6 @@ void export_OrderBroker(py::module& m) { :param float price: 卖出价格 :param float num: 卖出数量)") - .def("cash", &OrderBrokerBase::cash) - - .def("position", - [](OrderBrokerBase& self) { - return vector_to_python_list(self.position()); - }) - .def("_buy", &OrderBrokerBase::_buy, R"(_buy(self, datetime, market, code, price, num) @@ -113,6 +102,5 @@ void export_OrderBroker(py::module& m) { :param float price: 卖出价格 :param float num: 卖出数量)") - .def("_cash", &OrderBrokerBase::_cash) - .def("_position", &OrderBrokerBase::_position); + .def("_get_asset_info", &OrderBrokerBase::_getAssetInfo); } From 6fe37d6dbecaefa67bbd9f46243c4a8ed3ddde25 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 16:03:50 +0800 Subject: [PATCH 475/601] update --- .../examples/notebook/006-TradeManager.ipynb | 80 ++++++++++--------- hikyuu/examples/notebook/008-Pickle.ipynb | 62 +++++++------- hikyuu/trade_manage/broker.py | 4 +- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 2 + 4 files changed, 72 insertions(+), 76 deletions(-) diff --git a/hikyuu/examples/notebook/006-TradeManager.ipynb b/hikyuu/examples/notebook/006-TradeManager.ipynb index f0c18449..d9d1dc5e 100644 --- a/hikyuu/examples/notebook/006-TradeManager.ipynb +++ b/hikyuu/examples/notebook/006-TradeManager.ipynb @@ -9,25 +9,25 @@ "name": "stderr", "output_type": "stream", "text": [ - "2024-04-06 01:24:41,451 [INFO] hikyuu version: 2.0.0_202404040113_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:93) [hikyuu::hku_info]\n" + "2024-08-20 16:00:57,364 [INFO] hikyuu version: 2.1.1_202408182226_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:97) [hikyuu::hku_info]\n" ] }, { "name": "stdout", "output_type": "stream", "text": [ - "2024-04-06 01:24:41.867 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2024-04-06 01:24:41.890 [HKU-I] - Loading market information... (StockManager.cpp:532)\n", - "2024-04-06 01:24:41.902 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n", - "2024-04-06 01:24:41.914 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n", - "2024-04-06 01:24:42.064 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n", - "2024-04-06 01:24:43.250 [HKU-I] - Loading KData... (StockManager.cpp:133)\n", - "2024-04-06 01:24:43.958 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n", - "2024-04-06 01:24:43.959 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n", - "2024-04-06 01:24:43.959 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n", - "2024-04-06 01:24:43.963 [HKU-I] - 0.71s Loaded Data. (StockManager.cpp:150)\n", - "CPU times: total: 500 ms\n", - "Wall time: 3.35 s\n" + "2024-08-20 16:00:57.865 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-08-20 16:00:57.883 [HKU-I] - Loading market information... (StockManager.cpp:481)\n", + "2024-08-20 16:00:57.890 [HKU-I] - Loading stock type information... (StockManager.cpp:494)\n", + "2024-08-20 16:00:57.897 [HKU-I] - Loading stock information... (StockManager.cpp:409)\n", + "2024-08-20 16:00:58.045 [HKU-I] - Loading stock weight... (StockManager.cpp:511)\n", + "2024-08-20 16:00:59.275 [HKU-I] - Loading KData... (StockManager.cpp:134)\n", + "2024-08-20 16:01:00.831 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:179)\n", + "2024-08-20 16:01:00.832 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:179)\n", + "2024-08-20 16:01:00.832 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:179)\n", + "2024-08-20 16:01:00.902 [HKU-I] - 1.63s Loaded Data. (StockManager.cpp:159)\n", + "CPU times: total: 625 ms\n", + "Wall time: 4.01 s\n" ] } ], @@ -78,7 +78,7 @@ " current borrow_cash: 0.00,\n", " current borrow_asset: 0.00,\n", " Position: \n", - " SZ000001 平安银行 2017-01-03 00:00:00 1762 100.00 911.00 1046.00 135.00 14.82% 0.14%\n", + " SZ000001 平安银行 2017-01-03 00:00:00 1854 100.00 911.00 1029.00 118.00 12.95% 0.12%\n", " Short Position: \n", " Borrow Stock: \n", "}\n" @@ -148,21 +148,21 @@ " SZ000001\n", " 平安银行\n", " 2017-01-03\n", - " 1762\n", + " 1854\n", " 100\n", " 911.0\n", - " 1046.0\n", - " 135.0\n", - " 14.81888\n", + " 1029.0\n", + " 118.0\n", + " 12.952799\n", " \n", " \n", "\n", "" ], "text/plain": [ - " 证券名称 买入日期 已持仓天数 持仓数量 投入金额 当前市值 盈亏金额 盈亏比例\n", - "证券代码 \n", - "SZ000001 平安银行 2017-01-03 1762 100 911.0 1046.0 135.0 14.81888" + " 证券名称 买入日期 已持仓天数 持仓数量 投入金额 当前市值 盈亏金额 盈亏比例\n", + "证券代码 \n", + "SZ000001 平安银行 2017-01-03 1854 100 911.0 1029.0 118.0 12.952799" ] }, "execution_count": 3, @@ -259,7 +259,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -271,7 +271,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -298,28 +298,30 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "买入:SZ000001 11.470 1000\n", - "卖出:SZ000001 11.140 1000\n", - "买入:SZ000001 11.500 1000\n", - "卖出:SZ000001 11.290 1000\n", - "买入:SZ000001 10.580 1000\n", - "卖出:SZ000001 10.460 1000\n", - "买入:SZ000001 9.420 1000\n", - "卖出:SZ000001 9.100 1000\n", - "买入:SZ000001 9.330 1000\n", - "卖出:SZ000001 9.160 1000\n", - "买入:SZ000001 9.330 1000\n", - "卖出:SZ000001 10.550 1000\n", - "买入:SZ000001 10.560 1000\n", - "卖出:SZ000001 10.350 1000\n", - "买入:SZ000001 10.560 1000\n" + "买入:SZ000001, 价格: 9.33, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 9.16, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 9.33, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 10.55, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.56, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 10.35, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.56, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 10.43, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.58, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 11.12, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.4, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 9.94, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.31, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 10.12, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.22, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n", + "卖出:SZ000001, 价格: 10.11, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n", + "买入:SZ000001, 价格: 10.13, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n" ] } ], diff --git a/hikyuu/examples/notebook/008-Pickle.ipynb b/hikyuu/examples/notebook/008-Pickle.ipynb index ca4707a5..fd7e0f39 100644 --- a/hikyuu/examples/notebook/008-Pickle.ipynb +++ b/hikyuu/examples/notebook/008-Pickle.ipynb @@ -5,24 +5,29 @@ "execution_count": 1, "metadata": {}, "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "2024-08-20 15:45:43,093 [INFO] hikyuu version: 2.1.1_202408182226_RELEASE_windows_x64 [] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:97) [hikyuu::hku_info]\n" + ] + }, { "name": "stdout", "output_type": "stream", "text": [ - "warning: can't import TA-Lib, will be ignored! You can fetch ta-lib from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib\n", - "std::cout are redirected to python::stdout\n", - "std::cerr are redirected to python::stderr\n", - "2023-10-14 02:24:48.199 [HKU-I] - Using SQLITE3 BaseInfoDriver (BaseInfoDriver.cpp:58)\n", - "2023-10-14 02:24:48.200 [HKU-I] - Loading market information... (StockManager.cpp:499)\n", - "2023-10-14 02:24:48.200 [HKU-I] - Loading stock type information... (StockManager.cpp:512)\n", - "2023-10-14 02:24:48.200 [HKU-I] - Loading stock information... (StockManager.cpp:426)\n", - "2023-10-14 02:24:48.252 [HKU-I] - Loading stock weight... (StockManager.cpp:529)\n", - "2023-10-14 02:24:48.630 [HKU-I] - Loading KData... (StockManager.cpp:134)\n", - "2023-10-14 02:24:48.638 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:157)\n", - "2023-10-14 02:24:48.639 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:160)\n", - "2023-10-14 02:24:48.639 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:163)\n", - "2023-10-14 02:24:48.659 [HKU-I] - 0.03s Loaded Data. (StockManager.cpp:145)\n", - "Wall time: 1.16 s\n" + "2024-08-20 15:45:43.596 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n", + "2024-08-20 15:45:43.615 [HKU-I] - Loading market information... (StockManager.cpp:481)\n", + "2024-08-20 15:45:43.621 [HKU-I] - Loading stock type information... (StockManager.cpp:494)\n", + "2024-08-20 15:45:43.628 [HKU-I] - Loading stock information... (StockManager.cpp:409)\n", + "2024-08-20 15:45:43.785 [HKU-I] - Loading stock weight... (StockManager.cpp:511)\n", + "2024-08-20 15:45:45.041 [HKU-I] - Loading KData... (StockManager.cpp:134)\n", + "2024-08-20 15:45:46.483 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:179)\n", + "2024-08-20 15:45:46.483 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:179)\n", + "2024-08-20 15:45:46.484 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:179)\n", + "2024-08-20 15:45:46.552 [HKU-I] - 1.51s Loaded Data. (StockManager.cpp:159)\n", + "CPU times: total: 516 ms\n", + "Wall time: 3.78 s\n" ] } ], @@ -42,21 +47,9 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, - "outputs": [ - { - "ename": "UnicodeDecodeError", - "evalue": "'utf-8' codec can't decode byte 0x9c in position 133: invalid start byte", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mUnicodeDecodeError\u001b[0m Traceback (most recent call last)", - "\u001b[1;32m~\\AppData\\Local\\Temp\\ipykernel_7616\\6354363.py\u001b[0m in \u001b[0;36m\u001b[1;34m\u001b[0m\n\u001b[0;32m 2\u001b[0m \u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0;32m 3\u001b[0m \u001b[1;32mwith\u001b[0m \u001b[0mopen\u001b[0m\u001b[1;33m(\u001b[0m\u001b[1;34m\"temp\"\u001b[0m\u001b[1;33m,\u001b[0m \u001b[1;34m'wb'\u001b[0m\u001b[1;33m)\u001b[0m \u001b[1;32mas\u001b[0m \u001b[0mf\u001b[0m\u001b[1;33m:\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[1;32m----> 4\u001b[1;33m \u001b[0mpickle\u001b[0m\u001b[1;33m.\u001b[0m\u001b[0mdump\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0mk\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mf\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n\u001b[0m", - "\u001b[1;31mUnicodeDecodeError\u001b[0m: 'utf-8' codec can't decode byte 0x9c in position 133: invalid start byte" - ] - } - ], + "outputs": [], "source": [ "import pickle\n", "\n", @@ -66,7 +59,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -75,22 +68,21 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ - "k2 = KData()\n", - "hku_load(k2, \"temp\")" + "k2 = hku_load(\"temp\")" ] }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 6, "metadata": {}, "outputs": [ { "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9cAAAMTCAYAAACrDuSLAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjYuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8o6BhiAAAACXBIWXMAAA9hAAAPYQGoP6dpAACl3ElEQVR4nOzdeXgTdeLH8U8PaAhHQIIIhYCIlnqhrYCuCN4CP3QFb7bifVQBLe6i4gEi4CKuqLBWwRMryCqeqAgiiLgCQqXIWqsIEiiHBDAgJaXH/P4YE5o2PdM2Sft+PU+eJjOTyXeSTJpPvleUYRiGAAAAAABAjUWHugAAAAAAAEQ6wjUAAAAAAEEiXAMAAAAAECTCNQAAAAAAQSJcAwAAAAAQJMI1AAAAAABBIlwDAAAAABAkwjUAAKhz+fn5Onz4cKiLAQBAnSFcAwAQRrZv367x48dr48aNlW5bUFCg2bNnB/2Yv/32m5555hkZhiFJcjqdevLJJ6t8/yVLlqiwsFCSdNppp/nKtHz5cm3atEmS9Prrr+vYY48NuqyhsHXrVqWlpcntdvstX7Roke677z5J0oYNG7Rq1apQFA8AECYI1wAAhJHt27frscceq1K4XrlypW655Rbdc889kqRt27YpKiqqSpe+ffv69uN0OpWWlqaioiJJ0qZNmzR27Fi/x9q6das++OADPfzww7r44ouVmJio/Px8FRQUaOTIkb6QWVhYqOLiYm3evFmXX365PvroI0nSqlWrdN5559XKcxRIfn6+nnrqKX3//fd+y8ePH1/l5yQqKkq//vprmX0/88wzWrhwoVq0aOG3/JdfftGcOXMkSd99953OO+88jRs3TsXFxb5tDh48qCeffFI///xz7R80ACCsEK4BAD7Lly/XkCFD5HA41KRJE7Vs2VLJycl+gePcc89V165dy91HZesLCgp0zDHHaPTo0b5lr7zyik455RRZLBZ16tRJY8eOLdOE2OPxaOzYsXI4HIqLi9NJJ50UsNY2NzdXw4YNU9u2bWW1WtWvXz+tXr26zHb//e9/de6556pFixZq06aNhg0bph07dpTZriplK/nY1113nW688cZyj782nXPOOXrllVc0ffp0PfjggzrmmGOUnZ1d5tK+fXuNHz/eb5k3FJanuLhYt9xyi84880zZbDb95S9/0axZsxQTE6N77rlHM2bM0IEDB9SkSRO9+eabev/99/Xbb7/57v/QQw9pyJAhuueee2QYhhYuXKiLLrqozp6Le++9V2+++aZOOOGEMuvat28f8HkpeXn99dcD7nfPnj2aNWuWHn/8ccXExPits1qtys/PlyRdf/31+uKLL5SRkaGcnBzfNs2bN5fL5dKQIUOUl5dXi0cMAAg3saEuAAAgPLz44ou688471bdvX40bN05t27bV9u3b9dFHH2nv3r0VBubqmD9/vn777TelpqZKkp577jndc889uvXWW/X444/rq6++0hNPPKG9e/fqhRde8N1v2LBh+vTTT/Xoo48qISFBL7/8sm644QY1bdpU1157rSTJ7XbrnHPOUVFRkaZMmSKLxaJJkybpggsuUGZmpo4//nhJ0urVq3X++eerT58+evXVV7V9+3Y98sgj+v7775WZmakmTZpUq2wrVqzQ7NmzlZGRoUOHDumGG26oleeqKq6//nr9+uuvWr9+vaKiotSjR48y28TGxqp9+/Zl1r322mu66aabfLe9x/3GG28oKipKf/nLX3TLLbfoxBNPVOvWrf3ue9FFF6l37966+uqr9csvv+if//ynli9frv3792vNmjW6+OKL1bRpU/3www9yuVzavn27brnlFt1yyy1++/n666/Vp0+foJ6DL774Qi+++KLWrVunuLi4gMcf6HkpaefOnQGXT5kyRYmJibryyivldDrlcDh865o3by6PxyPJfO+1bNlSzz77rFasWKH9+/f7jmvSpEl6//33NW7cOE2dOrWmhwkACHcGAACGYXTs2NHo0qWLUVBQUOF2/fv3N7p06VLj9X379jUuueQSwzAMY//+/UbLli2NoUOH+m0zfPhwIzo62nA6nYZhGMbSpUsNScZzzz3n2+bw4cNG9+7dje7du/uWjRs3zoiKijKysrJ8y37++WcjOjrauO222/zK2LlzZ+PgwYO+Za+88oohyXjzzTerVTbDMAybzWaccsopxoIFCwxJxg033FDu8Vfm22+/NSQZn376abXuV1xcXO66+Ph4Iz09vczy33//3cjOzjbeeecdQ5KxYcMGIzs72/j888+NqKioCt8LHTt2NObMmWM8//zzxgUXXGBccMEFxoknnmhIMk488UTfstdff90YOnSo0bdvX2Pz5s3G5s2bjSVLlhiSjC+//NLweDzVOs5A+vXrZwwZMiTgunHjxhnx8fGV7sP7Htu8ebNv2aZNmwyLxWKsWLHCOHjwoNGxY0fjtttuMx5//HHj9ttvN3r37m1IMlq2bGlIMqKjo40OHToYffr0MV5//XW//b/88suG1Wo1du3aFdSxAgDCF83CAQCSzL6hdW39+vVasWKF7r77bknmgFAHDhzQrbfe6rfd9ddfr+LiYi1atEiSWdsdExPj19y6SZMmuuaaa7Rx40b98ssvvu169eqlU0891bdd9+7dddZZZ+nTTz+VJLlcLi1fvlzXXXedrFarb7trr71WTZo08W1X1bJJ0rJly7R+/Xr93//9X7BPkc/AgQMD9gm2WCySpD/++EMPP/yw71JysK0LL7xQDz/8cKWPYbPZ1KNHD3Xp0kWSlJCQoB49esjhcMgwDL344ovauXOn32XHjh164YUXtGvXLp177rlKTU3V559/rscee0x79+6VxWLR0UcfrVtvvVWff/65TjrpJL3//vsqKipS165d1bVrV0VHRysqKkq9e/cOWNNcHT/99JOWL1+u22+/Paj9lGYYhm6//XYNGDBA3bp105QpU1RUVKS//e1vWrlypWJiYpScnCxJ+s9//qNNmzbJ4/Fo+/btWrlypYYPH+63v5SUFEnSW2+9VavlBACED5qFAwAkSddcc41mzpypyy+/XM8995y6detW7raGYeiPP/4IuM47KFYg//73v9WlSxdfCM3KypJkjjBd0imnnCJJvr6rWVlZOu6449SyZctyt+vcubOys7PLNDv2bvf111/r4MGDWr9+vQzDKPOYzZo103HHHef3mFUpW6BtasOsWbP8Bh3zio42fxcvKirSxo0bdfDgQS1YsEC33nprmabbNXX88cfr3nvv1QMPPKARI0aUWd++fXs9//zz6tChgw4fPqypU6dq4sSJ+ve//62nn35aXbt21d13362vvvpKbrdbxx57rF+//V9//VXt27f3/VAQjMWLFysuLk79+vULel8lFRUVafXq1dq/f78++ugjFRUV6c0331T//v3Vv39/SeaAZunp6erRo0eZbhMFBQW+ZvaS1LRpU/Xr10+LFy/WqFGjarWsAIDwQLgGAEgyR0QuLi7Wyy+/rM8++0wpKSl66KGH1L179zLbOp3OMkG3JG9NaElut1tvvvmmHn74YV9A9A6AZbfb/bY96qijJEm///67b7vS25Tebu/evSoqKqpwO7fbXe5jerfbvXt3tcpWVzp16lRhP2Gbzaa33npLv/76qxYsWFDp/lJTU3393CVzdOvSPwr8+uuv2rt3r3r37q0nnnhCTzzxRLn7i401v0IMHDhQ3377rWbPnq2rrrpKTz/9tPr376+xY8fq6quv1rPPPquHHnpIJ598snbu3KljjjlGWVlZtfaDRGZmpk488US/Vgil5ebmKioqqlr7jY2N1U8//aQWLVro5ptv1t69ezVs2LAy20hSRkaGJDNsb9q0Sb/88osKCwvL9ONOSkrSG2+8Ua1yAAAiB+EaACDJrLmdNWuWRowYoUmTJmn27NmaM2eOJk6cqH/84x9+2x5zzDF6++23A+5n5MiR2rdvX5nlr7/+ugoLC/1qlr1zI3vDtpc3CHmXFxYWltmm9Hbl7au625V8zKqULVKMHz9e11xzje/2scceK6fTqY8//liffPKJJLPG+pprrtEzzzyjDh06VLi/cePGafz48fr73/+u448/Xt27d9fQoUM1efJkXXbZZZLM4Ot9vk444QR99dVXuuqqq/Tf//631kYO/+2333T00UdXuE379u21bNmyCrdZvXp1mYHo2rdvr3fffVcLFy70TfE1b948/fOf/9SmTZu0f/9+SeaI8klJSTruuOP0l7/8Rd26dQs4anm7du18P94AABoewjUAwE/Pnj31n//8R99//72uueYajRkzRl26dNHVV1/t2yYuLi5gk2XJrFENFK7T09N19dVXq127dn7bSmYtcNu2bX3Lvff31hrbbLaANcUltyu5r0DbRUdHq02bNpVuV/Ixq1K2cGIYRrk1tIFGC58/f74effRRnXzyyZKkzZs3q2vXrr4a12+++SZgy4UrrrjCd/0vf/mLCgoKlJWVpffff1833HCDXC6Xb33btm19I49/8MEHOvfcc7VmzRo99dRTQR+vZI4V0KZNmwq3qelo4Zs2bdKtt96qp59+WoZh6NNPP1WTJk2Ulpam4447Tt27d9fVV1+tc845RxMnTpQkPfDAA/r444/1/vvvl9lfs2bNlJ+fr+Li4oj7cQYAUDk+2QEAAZ1yyilauHChJGnu3LlB7evzzz/Xjz/+qLvuustvuTfw/O9///Nb7r3ds2dP33YbN270zSlccruoqCidcsopatmypTp27FhmX97tevToobi4uHIf0+Px6JdffvF7zKqULZzcfffd5U4pFchNN92kPXv2aPr06ZLMpugltW7dWna7vcylZF/iv/71r2rXrp1OO+00GYahyy+/XO3atfNdvLW7w4cP1/z58/XUU0/Jbrfr7LPProUjNsN7yTBfm+677z7t27dPI0aMUNeuXXXLLbeoQ4cOGj58uM4++2y1b99effr00YoVKySZU249/fTTZQbB89q9e7eOOuoogjUANFB8ugMAJEk7duwos+zw4cOSzBq3YDz//PNKSkrSmWee6bd84MCBiomJ0Wuvvea3/PXXX1fLli01YMAASdLgwYPl8Xg0b9483zYFBQWaO3eu+vXrp/bt2/u2++qrr7Rp0ybfdr/88otWrFihq666SpLZ9DkhIUFz5sxRQUGBb7t58+YpPz/ft11VyxZqhmFIkv7xj39o3rx5vnmXq6J169Zq2rRpuesTExMDjli+ZMkS3zbLli3T119/LavVqpycHBmGIcMwNGHCBPXq1cvXAqB///7q0qWLnnzySd155521FjA7deqkrVu31sq+Srvtttv0+uuva/ny5XK73dq+fbvOOussv20GDhyoFStW6Nprr9U///lPzZ8/X+ecc07AH3mcTqc6d+5cJ2UFAIQezcIBAJLMaZiuueYanXPOOWrdurWcTqdmzJihpk2b+qbOqolt27bpww8/1MyZM8us69Spk0aOHKlnn31WrVq10nnnnadly5bptdde0/Tp032Dpl199dV6+umnNWLECLlcLnXt2lUvvfSSnE6n3nzzTd/+xo4dq//85z8aOHCgHnroIUVFRWnSpEnq0qWLRo8e7dvun//8p4YOHarLLrtMt912m5xOpx599FFdccUVOvfcc6tVtlDbsmWLJGnJkiVasmRJmVGrg7F27dqAfYcvvfRSv9uvvfaa4uLi9OGHH+qOO+7Qd999pylTpvj6cktScXGxOnfurJycnFodXb1///565plntGXLloAD6QVj0KBBvusHDhzQ4sWL9ccff2jIkCG+5YcOHVJxcbGWL1+ub775RieffLKWLFmiiy66SAcOHFDz5s192y5durRWp2sDAIQXwjUAQJJ0xx13aMmSJXr33XeVl5enjh07qm/fvpo3b15QTaBfeOEFtWrVStddd13A9U899ZTatGmjl156Senp6erevbtefvll3Xzzzb5tYmJi9Omnn+rvf/+7Jk6cKI/Ho169emnJkiU644wzfNt16dJFy5Yt0+jRo3XnnXcqLi5OAwYM0FNPPaVWrVr5trv88ss1b948Pf7447ruuuvUrl073X333Ro3bly1y1ZThw8f9rUMKOnQoUOSzGbq5U13JpmtCWJiYpSTk6MWLVpo4cKFOv3002tUFm/td2lWq1UtWrQoszwmJsbv9syZMzVs2DCNHz9eEydOVFFRkcaMGeM3PdY999yjpUuXqmfPnrrxxhvVtWvXGpe3pPPPP1/NmzfXhx9+qJEjRwbcprCwUD/++GOF+3E6nX63i4qKNG/ePH399df673//q/Xr18tms+m+++7TkCFDtHLlSj388MP68ssv1b9/f61cudJ335ycHB133HF+wfrHH3/Uxo0by/wwAQBoQAwAAOrI4cOHjfbt2xujR48OdVHCzt13321IqvFl6dKlvn1lZ2cbhmFUex/Z2dnGiy++aPztb38zWrZs6dvfjh07Kr3vuHHj/I7nyy+/NC6++GKjTZs2Rq9evYymTZsao0aNMjwej3H77bcbUVFRxmuvvWYcOHDASEpKMlq1amXMnj27Vp7Lv//970bXrl2NgoKCMuvGjRtXredk8+bNvvsmJycbQ4YMMZ555hnju+++Mw4ePGi8+OKLRlJSkhEVFWVcfvnlxo8//mgUFxcbgwYNMo4++mjj9ddfN3r37m3cfvvtfuW44YYbjOTk5Fo5XgBAeKLmGgBQZ9555x399ttvfvMrwzRx4kQ98MADNb5/yVHXvYOvZWdnV2sfBw8e1LPPPqsmTZoEHL37q6++CjhaeMmR4++//37NnTtXLpdLN998s+bMmaO2bdvqvffeU25urvr27asNGzbo9ddf1/XXXy9JWrhwoa6++moNHz5cxx57bLkjz1fVmDFj9MYbb2jGjBm69957y6yPj4/Xtm3bKtzHsmXLdN555/ktW7Nmjd/tgoICvf322zrzzDP15ptv+o1A/u677+rRRx/V/fffr+bNm2vMmDG+devWrdPcuXP9mskDABqeKMMopy0YAABBeuedd7R7927CdQM2a9YsNW3aVEOHDg3YD/1f//qXLrjggjL9rIuKijR//ny/oB6MpUuXavDgwVqxYkWtNDevLX/88YeSk5N11VVX+abrAgA0TIRrAAAAAACCxFRcAAAAAAAEiXANAAAAAECQCNcAAAAAAASpQY0WXlxc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dJNCQkIqvNq1a1dt3+pqLisrS0OGDHHv7/Tp03XhwoWa/KgAvwvG+pOkDz/8UF26dFFkZKQ6d+6sjRs31nifSktLtWzZMvXo0aPGfYCaCNZ6kaTz58/rmWee0dChQyssy87O1rBhw3TZZZepVatWeuGFF3za78qOERctWqSOHTsqKipK7du319KlS33ariTJAGjQ/vznP5t58+aZzMxMs379ehMXF2eSkpKMMcasXbvWREZGmpUrV5odO3aYvn37ms6dO5uSkhJjjDGPPfaYefDBB80333xjvv76azNw4EDTsmVLk5eXZ4wxZsuWLeapp54y27dvN59//rnp0qWL6dWrV43GtWLFCnPZZZeZRYsWme+//95s2LDB/Pjjj1X22b17t+natatZt26dyczMNM8++6wJCQkxa9euda+zbt0607t3bxMREWGaNGlSmx8ZYEkw1lxOTo7Zv3+/x6tr165m1qxZVfarruZKSkpM27ZtzbBhw0xGRob54IMPjNPpNHPmzLHwEwRqLxjrb/v27aZJkybmmWeeMTt27DAPP/ywsdlsZt++fdX2Xb58ubnuuutMRESEadeunYWfDFBRMNbLhQsXzMKFC43L5TIRERFmwIABFZZ37drV3HLLLSYjI8MsX77chIaGmjVr1lS77eqOEadOnWo+/PBDs2vXLvP0008bSebjjz+udrsXI1wDDdyRI0c83r/99tsmNDTUnDt3zvTs2dNMmTLFvWzPnj1Gkvniiy+89j169KiRZD799FOvn7V27VojyZw+fbrKMZ04ccJER0ebN954w6d9OXHihDl79qxH26BBg8zdd9/tfj906FAza9Yss2jRIsI1AiIYa+6PPvvsMxMdHW1OnDhR5XrV1dxvv/1mJJndu3e7l6ekpLgPzoC6Foz1d9ddd3nURGlpqenYsaNJSUmpdn/i4+PNc889Z2bNmkW4ht8FY72cOXPGdOrUyaxYscKMHTu2Qrj+8MMPTXh4uDl27Ji7bdSoUSYxMbHa/fX1GPG6666rUZ1ejMvCgQauTZs2Hu9tNptKS0t1+vRpfffddx6X23Tq1EktW7ZUenp6pX0lqaSkxOtnlZSUyGaz6bLLLqtyTO+++66aNm2q0aNH+7Qvl19+eYVt22w2j/H885//1Lx589S0aVOftg34SzDW3B899dRTSk5O1uWXX17letXVXIsW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\n", + "image/png": "iVBORw0KGgoAAAANSUhEUgAAA9MAAAMTCAYAAACi5UTxAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjguMCwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy81sbWrAAAACXBIWXMAAA9hAAAPYQGoP6dpAACdi0lEQVR4nOzdeXhU5eH28TsQyTASBmVcIHAAxYa4ICQCLggoLsCPqlhcG1HrgoFIDbYUcUFRoVTf4oJGsbgVxLVVSyuuKGrFJRHQGiMIOBCxMqITIA6Q5Lx/PJ0tmSyTbSbJ93Ndc5E5z5kzz5mNuefZkmzbtgUAAAAAAOqtQ7wrAAAAAABAa0OYBgAAAAAgRoRpAAAAAABiRJgGAAAAACBGhGkAAAAAAGJEmAYAAAAAIEaEaQAAAAAAYkSYBgAAzW7Pnj3au3dvvKsBAECTIUwDAJBAvv32W916663asGFDnfvu27dPTz75ZKPv8/vvv9c999wj27YlSR6PR3/605/qffs333xT5eXlkqRBgwYF67Rq1Spt3LhRkvTEE0+oX79+ja5rPGzZskV5eXny+XwR21977TVdf/31kqTPP/9cH374YTyqBwCIE8I0AAAJ5Ntvv9Vtt91WrzC9evVqXXHFFfrtb38rSdq6dauSkpLqdRk+fHjwOB6PR3l5eaqoqJAkbdy4UbNmzYq4ry1btuill17STTfdpDPOOEMZGRnas2eP9u3bp2uvvTYYKsvLy1VZWalNmzbpnHPO0T/+8Q9J0ocffqhTTjmlSR6jaPbs2aO7775bn332WcT2W2+9td6PSVJSkjZv3lzt2Pfcc49WrFihLl26RGz/+uuv9dRTT0mSPv30U51yyimaPXu2Kisrg/vs3r1bf/rTn7R+/fqmP2kAQFwRpgEAQatWrdKECRNkWZb2228/paamKisrKyJgjBo1Sn379q3xGHWV79u3T4ceeqimT58e3Pboo4/qmGOOkcPhUK9evTRr1qxqXYL9fr9mzZoly7KUkpKio446KmqrbElJiS6++GJ1795dTqdTI0aM0EcffVRtv3//+98aNWqUunTpogMOOEAXX3yxtm3bVm2/+tQt/L4vuugiXXbZZTWef1M6+eST9eijj+r+++/XDTfcoEMPPVRFRUXVLocccohuvfXWiG2BEFiTyspKXXHFFTr++OPlcrl04okn6pFHHlHHjh3129/+VgsXLtTOnTu13377aenSpXrxxRf1/fffB29/4403asKECfrtb38r27a1YsUKnX766c32WFx33XVaunSpfvGLX1QrO+SQQ6I+LuGXJ554Iupxf/jhBz3yyCO6/fbb1bFjx4gyp9OpPXv2SJIuueQSvfXWW1qyZImKi4uD++y///7yer2aMGGCysrKmvCMAQDxlhzvCgAAEsPDDz+sa665RsOHD9fs2bPVvXt3ffvtt/rHP/6hHTt21BqQY/HCCy/o+++/V05OjiTpvvvu029/+1tdeeWVuv322/Xuu+9q3rx52rFjhx566KHg7S6++GK98soruuWWW5Senq7Fixfr0ksvVadOnXThhRdKknw+n04++WRVVFRo/vz5cjgcuvPOOzV69GgVFhbqiCOOkCR99NFHOvXUUzVs2DA99thj+vbbb3XzzTfrs88+U2Fhofbbb7+Y6vbee+/pySef1JIlS/Tzzz/r0ksvbZLHqj4uueQSbd68WevWrVNSUpIGDBhQbZ/k5GQdcsgh1coef/xxXX755cHrgfP+61//qqSkJJ144om64oordOSRR6pbt24Rtz399NM1dOhQnX/++fr666/1xz/+UatWrVJpaak++eQTnXHGGerUqZO++OILeb1effvtt7riiit0xRVXRBzn/fff17Bhwxr1GLz11lt6+OGHtWbNGqWkpEQ9/2iPS7jvvvsu6vb58+crIyNDEydOlMfjkWVZwbL9999ffr9fknntpaam6t5779V7772n0tLS4HndeeedevHFFzV79mzdddddDT1NAECisQEAsG27Z8+edp8+fex9+/bVut/IkSPtPn36NLh8+PDh9plnnmnbtm2Xlpbaqamp9rnnnhuxz6RJk+wOHTrYHo/Htm3bXrlypS3Jvu+++4L77N271+7fv7/dv3//4LbZs2fbSUlJ9tq1a4Pb1q9fb3fo0MG+6qqrIurYu3dve/fu3cFtjz76qC3JXrp0aUx1s23bdrlc9jHHHGMvX77clmRfeumlNZ5/XT7++GNbkv3KK6/EdLvKysoay9LS0uz8/Pxq23/66Se7qKjIfv75521J9ueff24XFRXZb7zxhp2UlFTra6Fnz572U089ZT/44IP26NGj7dGjR9tHHnmkLck+8sgjg9ueeOIJ+9xzz7WHDx9ub9q0yd60aZP95ptv2pLsd955x/b7/TGdZzQjRoywJ0yYELVs9uzZdlpaWp3HCLzGNm3aFNy2ceNG2+Fw2O+99569e/duu2fPnvZVV11l33777fbVV19tDx061JZkp6am2pLsDh062D169LCHDRtmP/HEExHHX7x4se10Ou3//ve/jTpXAEDioJs3AECSGdvZ3NatW6f33ntPU6dOlWQmcNq5c6euvPLKiP0uueQSVVZW6rXXXpNkWrM7duwY0X16v/320wUXXKANGzbo66+/Du43ZMgQDRw4MLhf//79dcIJJ+iVV16RJHm9Xq1atUoXXXSRnE5ncL8LL7xQ++23X3C/+tZNkt5++22tW7dO//d//9fYhyho7NixUcf0OhwOSdKuXbt00003BS/hk2Oddtppuummm+q8D5fLpQEDBqhPnz6SpPT0dA0YMECWZcm2bT388MP67rvvIi7btm3TQw89pP/+978aNWqUcnJy9MYbb+i2227Tjh075HA4dPDBB+vKK6/UG2+8oaOOOkovvviiKioq1LdvX/Xt21cdOnRQUlKShg4dGrUlORZfffWVVq1apauvvrpRx6nKtm1dffXVGjNmjA477DDNnz9fFRUV+vWvf63Vq1erY8eOysrKkiQ9++yz2rhxo/x+v7799lutXr1akyZNijhedna2JOnpp59u0noCAOKHbt4AAEnSBRdcoEWLFumcc87Rfffdp8MOO6zGfW3b1q5du6KWBSaxiuaBBx5Qnz59gqFz7dq1kswM0OGOOeYYSQqOPV27dq0OP/xwpaam1rhf7969VVRUVK0bcWC/999/X7t379a6detk23a1++zcubMOP/zwiPusT92i7dMUHnnkkYhJwgI6dDC/g1dUVGjDhg3avXu3li9friuvvLJaV+yGOuKII3Tddddp5syZys3NrVZ+yCGH6MEHH1SPHj20d+9e3XXXXbrjjjv0wAMP6M9//rP69u2rqVOn6t1335XP51O/fv0ixt1v3rxZhxxySPCHgcZ4/fXXlZKSohEjRjT6WOEqKir00UcfqbS0VP/4xz9UUVGhpUuXauTIkRo5cqQkMwFZfn6+BgwYUG0YxL59+4Ld5iWpU6dOGjFihF5//XVNmzatSesKAIgPwjQAQJKZsbiyslKLFy/Wq6++quzsbN14443q379/tX09Hk+1YBsu0NIZzufzaenSpbrpppuCgTAwYZXb7Y7Y98ADD5Qk/fTTT8H9qu5Tdb8dO3aooqKi1v18Pl+N9xnYb/v27THVrbn06tWr1nG+LpdLTz/9tDZv3qzly5fXebycnJzgOHXJzD5d9UeAzZs3a8eOHRo6dKjmzZunefPm1Xi85GTzFWLs2LH6+OOP9eSTT+q8887Tn//8Z40cOVKzZs3S+eefr3vvvVc33nijjj76aH333Xc69NBDtXbt2ib7AaKwsFBHHnlkRC+DqkpKSpSUlBTTcZOTk/XVV1+pS5cu+s1vfqMdO3bo4osvrraPJC1ZskSSCdcbN27U119/rfLy8mrjsDMzM/XXv/41pnoAABIXYRoAIMm0zD7yyCPKzc3VnXfeqSeffFJPPfWU7rjjDv3+97+P2PfQQw/Vc889F/U41157rX788cdq25944gmVl5dHtBwH1iYOhOuAQPAJbC8vL6+2T9X9ajpWrPuF32d96tZa3HrrrbrggguC1/v16yePx6N//vOf+te//iXJtEhfcMEFuueee9SjR49ajzd79mzdeuut+t3vfqcjjjhC/fv317nnnqu5c+fqrLPOkmSCbuDx+sUvfqF3331X5513nv7973832cze33//vQ4++OBa9znkkEP09ttv17rPRx99VG3iuEMOOUR/+9vftGLFiuCSW88884z++Mc/auPGjSotLZVkZnzPzMzU4YcfrhNPPFGHHXZY1FnFDzrooOCPNQCA1o8wDQCIcOyxx+rZZ5/VZ599pgsuuEAzZsxQnz59dP755wf3SUlJidoFWTItptHCdH5+vs4//3wddNBBEftKppW3e/fuwe2B2wdahV0uV9SW4PD9wo8Vbb8OHTrogAMOqHO/8PusT90SiW3bNbbARpvN+4UXXtAtt9yio48+WpK0adMm9e3bN9ii+sEHH0TtmfCrX/0q+PeJJ56offv2ae3atXrxxRd16aWXyuv1Bsu7d+8enBn8pZde0qhRo/TJJ5/o7rvvbvT5Smas/wEHHFDrPg2dzXvjxo268sor9ec//1m2beuVV17Rfvvtp7y8PB1++OHq37+/zj//fJ188sm64447JEkzZ87UP//5T7344ovVjte5c2ft2bNHlZWVre7HGABAdXySAwCiOuaYY7RixQpJ0rJlyxp1rDfeeENffvmlpkyZErE9EHD+85//RGwPXD/22GOD+23YsCG4pm/4fklJSTrmmGOUmpqqnj17VjtWYL8BAwYoJSWlxvv0+/36+uuvI+6zPnVLJFOnTq1xiadoLr/8cv3www+6//77JZmu5eG6desmt9td7RI+Fvjss8/WQQcdpEGDBsm2bZ1zzjk66KCDgpdA6+2kSZP0wgsv6O6775bb7dZJJ53UBGdswnp4eG9K119/vX788Ufl5uaqb9++uuKKK9SjRw9NmjRJJ510kg455BANGzZM7733niSzBNaf//znapPWBWzfvl0HHnggQRoA2gg+zQEAkqRt27ZV27Z3715JpkWtMR588EFlZmbq+OOPj9g+duxYdezYUY8//njE9ieeeEKpqakaM2aMJGn8+PHy+/165plngvvs27dPy5Yt04gRI3TIIYcE93v33Xe1cePG4H5ff/213nvvPZ133nmSTFfm9PR0PfXUU9q3b19wv2eeeUZ79uwJ7lffusWbbduSpN///vd65plnguse10e3bt3UqVOnGsszMjKizij+5ptvBvd5++239f7778vpdKq4uFi2bcu2bc2ZM0dDhgwJtvCPHDlSffr00Z/+9Cddc801TRYoe/XqpS1btjTJsaq66qqr9MQTT2jVqlXy+Xz69ttvdcIJJ0TsM3bsWL333nu68MIL9cc//lEvvPCCTj755Kg/6ng8HvXu3btZ6goAaHl08wYASDLLIl1wwQU6+eST1a1bN3k8Hi1cuFCdOnUKLmXVEFu3btXLL7+sRYsWVSvr1auXrr32Wt17773q2rWrTjnlFL399tt6/PHHdf/99wcnOTv//PP15z//Wbm5ufJ6verbt6/+8pe/yOPxaOnSpcHjzZo1S88++6zGjh2rG2+8UUlJSbrzzjvVp08fTZ8+PbjfH//4R5177rk666yzdNVVV8nj8eiWW27Rr371K40aNSqmusXbN998I0l688039eabb1abVboxCgoKoo79/eUvfxlx/fHHH1dKSopefvllTZ48WZ9++qnmz58fHIstSZWVlerdu7eKi4ubdPbzkSNH6p577tE333wTdeK7xhg3blzw7507d+r111/Xrl27NGHChOD2n3/+WZWVlVq1apU++OADHX300XrzzTd1+umna+fOndp///2D+65cubJJl08DAMQXYRoAIEmaPHmy3nzzTf3tb39TWVmZevbsqeHDh+uZZ55pVJfmhx56SF27dtVFF10Utfzuu+/WAQccoL/85S/Kz89X//79tXjxYv3mN78J7tOxY0e98sor+t3vfqc77rhDfr9fQ4YM0ZtvvqnjjjsuuF+fPn309ttva/r06brmmmuUkpKiMWPG6O6771bXrl2D+51zzjl65plndPvtt+uiiy7SQQcdpKlTp2r27Nkx162h9u7dG2z5D/fzzz9LMt3Oa1p+TDK9BTp27Kji4mJ16dJFK1as0ODBgxtUl0DrdlVOp1NdunSptr1jx44R1xctWqSLL75Yt956q+644w5VVFRoxowZEctV/fa3v9XKlSt17LHH6rLLLlPfvn0bXN9wp556qvbff3+9/PLLuvbaa6PuU15eri+//LLW43g8nojrFRUVeuaZZ/T+++/r3//+t9atWyeXy6Xrr79eEyZM0OrVq3XTTTfpnXfe0ciRI7V69ergbYuLi3X44YdHBOkvv/xSGzZsqPZDBACgFbMBAGgme/futQ855BB7+vTp8a5Kwpk6daotqcGXlStXBo9VVFRk27Yd8zGKiorshx9+2P71r39tp6amBo+3bdu2Om87e/bsiPN555137DPOOMM+4IAD7CFDhtidOnWyp02bZvv9fvvqq6+2k5KS7Mcff9zeuXOnnZmZaXft2tV+8sknm+Sx/N3vfmf37dvX3rdvX7Wy2bNnx/SYbNq0KXjbrKwse8KECfY999xjf/rpp/bu3bvthx9+2M7MzLSTkpLsc845x/7yyy/tyspKe9y4cfbBBx9sP/HEE/bQoUPtq6++OqIel156qZ2VldUk5wsASAy0TAMAms3zzz+v77//PmJ9Yxh33HGHZs6c2eDbh8+KHpgsraioKKZj7N69W/fee6/222+/qLNrv/vuu1Fn8w6f2f0Pf/iDli1bJq/Xq9/85jd66qmn1L17d/39739XSUmJhg8frs8//1xPPPGELrnkEknSihUrdP7552vSpEnq169fjTPD19eMGTP017/+VQsXLtR1111XrTwtLU1bt26t9Rhvv/22TjnllIhtn3zyScT1ffv26bnnntPxxx+vpUuXRswQ/re//U233HKL/vCHP2j//ffXjBkzgmVr1qzRsmXLIrq9AwBavyTbrqFvFwAAjfT8889r+/bthOk27JFHHlGnTp107rnnRh1H/v/+3//T6NGjq42Trqio0AsvvBARzBtj5cqVGj9+vN57770m6T7eVHbt2qWsrCydd955weWzAABtA2EaAAAAAIAYsTQWAAAAAAAxIkwDAAAAABAjwjQAAAAAADFqU7N5V1ZW6ttvv1VqaqqSkpLiXR0AAAAAQAuxbVs7d+5Uz5491aFD87cbt6kw/e2336p3797xrgYAAAAAIE62bNmiXr16Nfv9tKkwHViSY8uWLeratWucawMAAAAAaCmlpaXq3bt31KUam0ObCtOBrt1du3YlTAMAAABAO9RSQ36ZgAwAAAAAgBgRpgEAAAAAiBFhGgAAAACAGBGmAQAAAACIEWEaAAAAAIAYEaYBAAAAAIgRYRoAAAAAgBgRpgEAAAAAiBFhGgAAAACAGBGmAQAAAACIEWEaAAAAAIAYEaYBAAAAAIgRYRoAAAAAgBgRpgEAAAAAiBFhGgAAAACAGBGmAQAAAACIEWEaAAAAAIAYEaYBAAAAAIgRYRoAAABA/O3bF+8aADEhTAMAAKBh9u6V3n473rVITBMnSsXF0ct27JA++0xasUIqK2vZeiWSDRukXr1C16++Wrr11rhVJ6EUFUmlpdHLRo2SVq8OXS8vl2w7dH3DBrMPmh1hGgCAtmDdOumMMySnUzr0UOnyy6UffojcJz9f6tdP6txZOvVUaePGUNmOHdJvfiN17y65XNLpp5tjRvPTT2a/006L3P7559KIEeb4hx0m/fWvkeUffiiddJLkcEh9+kh/+lP047/wgtSzZ/XtJSXShAlSly6S2y394Q9SZWXNj8kLL0gZGeb+hgyRCgpq3veyy6SkpPpffve76Md56CHpF78w9zlggPTww5HlxcXSmDGh52nGDPNFWJIef7zm+3v3XRNaayqv+lgH/OUv0uGHSykp0tCh1R8Dy6p+LK+35sepqh9/lM45p+bXSjTvvCOdcIJ5nfTuLU2fHhkofT7zfHTrJnXtKl15ZfXAWdtreePG6ud09NG116my0jxXgwZVL5s0qfrxnn++9uMVFkpvvGFex8cfLx17rHTEESY4du4sHXKIeb/+4Q9m36q++Ub61a/M+XfvLp17rtkW7k9/Mu8jh0M68cTIcNW3b/TXyejRpnzUqOjlhx9evS7N8bqMZt8+6aWXzGdEffz8s5SXJ/XoYT4TTjzRvLbC/etf5jl1OKT0dHMu4d5/Xxo2zLw/DjtMWrIksvzGG83rzOk0r6G//a32Ou3aJV13nXlvOxxSZmaorKbH6Ioroh/r+efNayc/33zeud3muNH89rfSzTfXXK++fWu+HHVU9NvU9XlekzlzzHm9915oW13v6VdeMc9P167Sr38t+f2hsrfeMmV79tTv/lua3Yb4fD5bku3z+eJdFQAAWtbJJ9v2nXfa9tq1tr18uW3362fb48aFyp991rZTUmz7ySdt+5NPbHv4cNs+6ijbrqgw5ddfb9tXXGHbH3xg2//+t22fdppt9+hh29H+T50xw7Yl2x49OrTN57PtQw+17UmTTB1uv922O3Qwx7Nt2/7xR9t2ucx9rFlj6uFw2PZjj4WO8cILtj1smG136mTbHTtG3md5uW0PHmzbI0fa9urVtv3Xv9p2ly62PWdO9Mfj3/+27eRk216wwNzfuefa9sEH23ZpafT9d+607e3bzWXsWNu+447Q9e3bbXvRItvOygpd3727+jHWrbPtY44x57F2rW3/8Y+2nZRkHnvbNo/1YYfZ9vjxtl1QYNsvvmjbbrdtz55tyktLbXv9+sjL7Nm23aePbe/da9tlZdXLH3nEtlNTbfuHH6rX529/M49lfr5tf/ihbZ9zjrm/HTtC++y/v20vXRp5zMBroqrDD7fttLTYLoFzD/eLX9j2woXm8Xr6ads+8EDbnjIlVH7WWbZ99NG2/c47tv3yy+Z19ZvfhMrrei1//LF57X31VeicPJ7o52Tb5rk98kjzWB1+ePXy//s/2542LfIx2rWr5uPZtm1PnWrbV19t2z//bN4rH35o259/btt//7upa2Vl7be/4ALb/t3vzOvkrbfMa/+YY0Ln+Nhjtt25s20vWWJe35ddZt5fP/5oyjdvjqxvUZF5/S9aZMq3bq3+WjrmGNueNat6XZr6dWnb5j22aZPZNy3NbFuxwrwW9u2r/bEJuO8+85peudK8Di66yLyeN20y5V99ZT4DbrzRvB8XLDCvi7feMuUej9k/N9fc/uabTfk774Tu4/LLbfvVV23700/Nc5qcbF630ZSX2/aIEeZz4tVXzX5//WuovOpj9NFH5nX82ms1n+Of/mTb8+aZv3futO1DDjF/jxwZ+mx98knbHjDAtn/6KfK+Ro6sz6MYXV2f5zX573/N8y7Z9rvvhrbX9p4uLbXt7t3N59Ann9j20KHmubVt296zx7bT02t/jKpVvWXzIGEaAIC2oGpYWLrUfPkJhL7Bg00gCCgqMl94wr9YhispMeUrVkRu/89/TCA744zIMH3vvbZ90EHmy0/AsGHmy5htmy+OUmSYnTAhMkSNHWu+zN9/f/Uw/c9/mm3ffhvaduedpi7Rwt+ECeYLXMCPP5ovro8+Wn3fqs4+23zxDrdsmTmf2vzwQ/WQdfrptn3hhebv7783j0H4l/G8vMgfPcLt22d+FAkEoGhOPjl6ALJt2x4yJPLx3bXLtg84wDxXtm2eq6r1qY9XXzXPUW2XQKiLpupr7c47zZdr2zavL8m8XgKWLjUhJhDM6notv/qqCWX1lZlpQsusWdHD9AknhL7c10dZmbn/NWtCYTpg3ToTDupS9TF6/31znC+/NNenTDE/EAX4fNUft3B/+Uso/Ebzxhu1h99wjX1d2rapS1FRZJieMMGcQ7TL//1f9WNUfYz27rVtp9O2H3rIXH/22eqvg8GDzXNt27b9+99X/2Hj9NNNPaKpqDCPUeD9U9WiReb9VdtrP9xNN9n2iSfWXB7+WWrb0cP0W2/Zds+etr1hQ2i/Pn1s27LMj5U9e9r2pZfatt9v/j7qqMhLz57R77uuz/OaXHyxbU+cGBmm63pPf/SR+awKeOCB0OfWHXfY9vnn136fVbR0HqSbNwAAbUHv3pHXHY5QF+iffpI+/VQaOzZUPmCA6R4Z6Boa7faSVFER2lZZabrn3XijuW24lStNd9tOnULbTj01dPxjjzX3+eCDpjtnQYHpYjlxYmj/f/5TuvNO02Wzqi++MF0cw+931CjTJTm8i294fcLPt1s30+UyvCtsUzj/fNP1XJIOPFDaf//Icocj9BgedJB0yinSokWmG+OGDdI//iFdcEH0Yz/xhHnML7ssevmbb5rn9frro5d/8YU0eHDo+v77S8cdJ330kbm+Y4f51+2u8zQjPPqotG2b6T4b7XLnndJ334X2X7jQdCffvdtcj/ZaCzxGX3xh/g3vbj1qlOkKX1hYv9fyjh2xndMnn0i//720337Ry2M93l13me6qxx5bvWz//aN3Vy0slA44wAyFkOp+P06caN4/a9aY99P995vHIdp9lpeb52TWrJrP8dZbpdxc8xquS2Nfl9EUF5su3vPnhyL0nj2mu7DPJy1fbq736SPdc4+5TdXHKDnZXAKP0SmnmOtPPWW2vfaatHmz9MtfmvIvvjCvs6Sk0DFGjQq9P6qybXOc7t2jlz/2mOmy3a1b3ef744/SffdJs2dHL//2W9Pl/t13pa+/Nq+/vn0j99mxw3x2LFtWvXv+m2+a7utLl4bq3rmz6bodfgk/96FDTbd5qe7P82heecV0s696TnW9p3v2lNavN6+Bn3+WXn3VDM/ZtMk8RgsW1HyfCYAwDQBAW2Pb0uLF5suU02m+lEhm7F84y5K2bo1+jEceMV++jj8+tG3uXBMCc3Or779xY+3H79TJfKm97TYzPvG448xkQ6ecEto//ItdVd27m+AcPs4uMDnP999H7vvjjyZ0xXK+DXXYYWY8XzSbN5txs6eeGtr2xBPSs8+a5+WII8zjO2lS9Nv/v/8nTZlScwC66y7pkktqDkDdu1cfZ1taGnq8AmPq+/WT0tKks86q/9jnRx81P6xEu1Qdc33ooaGx61Xt3Ss9+WToMQoEFY8nss6SqXd9Xss//GC+nHfuLPXvL11zjbR9e83nUtvrLnC8Sy81dTvxROnFF2vet6TEjGXu2DF6eZcuoR8VwnXtal5HNQW1Rx4x461/8Qtz/ZRTzLwIgweb99O8edIzz0SGn4Dnnzfvh0suiX7s1avNZerUms8rXGNfl9HMn28em8DzK5kfbPbf3zw2kgnGAwZUD9EBzz5rXiuBzxS324yRvuQSU9czzzR1GzDAlNf1/gj3/fdmXLJlhX48C1debn4g7NXLvI8OPNB8xr36avS6Pvywec+dcUb08p49zY8fZ55pfkjIzDQB++qrQ/sceKD08cdmXHN9eDzm3MMv//1vqDw93XyeSXV/nlfl9Zo5N+67r/qPoXW9p9PSzLwJRx5pnu+9e6WrrpKmTZNmzow+f0YiaZH27xZCN28AQLu3d69tX3mlGU/58cdm26pVpq2narfIU04xY5ireuQR06V64cLQtvfeM10c//Mfc/3SSyO7eR92WPXxy088EequvW2b6VJ45ZVmzPOiRWbM87Jl1e//sceqd/P+9luz/zXXmO6OmzaZsYmSOV44j8dsX7Uqcvvll0fWOeCDD2ruXlrbJTA2M5riYts+4gjbPumkUFfJ3bvNuMGzzjJjup9+2oxj/eMfq9/+zTdNt/Tvv49+/A0bzHjstWtrrsP06aYb/AcfmDo8+KDp+j9mjCkvLTWP3bp1ZhzjiSfadrdutr1lS83HtG3TPXX58prLf/lLM2a3LqWlZvy42x16LHfvtu1evcxj5PWaMZhjxpjH++mn6/da3rLFjL0sLDSvpV69TBfV8vLa6zN7dvRu3oHHaOVKMzZZsu1//CP6MbKzbfv440PHCXTz7tgxdKl6PXw8eFWVlbZ9663meXv55dD2J56w7a5dbXvxYlO/Sy+17d69o79eTj7ZvBZqcsklkV3Ga9MUr0vbrt7N+4orzNjfgQND+/ztb6aLfX384x9m/PPvfx/a9vnn5jNr1izTlXj+fPO5GPhcePll81w89ph5f7zzjukW7nCEjvHOO7a9335mvyOOMGPYo/nuO7PP4YebLvUffmjOab/9zGdBuIoK2+7bt35DBx56yHxGBoaK2LYZ4921q3msTjop8vL99+axDYyZXrnSvDb27DHdr6v65S+j329dn+fhKitNN/xAF/BNmyK7edf1ng4oLQ29rv7+d3N+331n27/6lXmNnH22ma+iDoyZbgTCNACgXduyxQSiQw+NnCjmww/NF5fwcXW2bb6oXntt6PrPP9v2VVeZiZgefjjyuIccYr4kBlQN0xkZJmSFe/hh82XWts2kZVXHJ95+e2i8ZLhoYdq2zbjpgw82X9a7dLHtG24w57VxY+R+//2v2f7GG5HbL7oo+pfHffsiJxs77TTb/n//r/YJyLZvr3mirr/9zUwG9ctfRo4Rf/BBExzLykLb/vpXM86z6neX884zk1DV5Pe/r3sM986dZrxhUpK5nHKKObeaxj2WlpowHRhTWtWWLaZO9b3cfXfNdfv8czNp0i9+UT1sfPih+TIvmeB2yy2hMdH1fS2HCwTwmsYTB9QUpqsaPbrmce7Z2SbIVw3T4bp2rTmMhvvxRxNAUlMjg3RlpXmPhz9P5eXmsbzxxshjBMarfvFF9PvYscM8xq+8Und9bLtpXpe2XT1MV1aawNetm21//bXZJyfHtv/wh9qPU1FhJg7r2NE8f+GfL+efX/15uuIKEzoD7rjDnH9Skgm4V15pxhsH7N5tHrtVq0xdHI7I5yJg61bzON92W2jbvn3meQrfZtvmc6xTp/qPrf7LXyLnPli61IxZnzLFPB+jRplJyubNM+/haGH6mmvMWP1ol6qf27Zd9+d5uFmzzHt5505zvWqYtu3a39NV7d5tzuG998xn1Z/+ZB7L6dMjH4catHQeTI5vuzgAAGgSX31lujcec4y0dq108MGhsrQ08++WLZFj67ZsMWN+JTNW7cwzTdfKDz6IXNJl8WLTHXDq1FBX0H37zL8OhxmLmJZmjhduy5ZQt8F168x4zvAutZmZplvsjh316xI6bpwZi7t1qxl//I9/mHGmVccSut2m62u0+mRlVT9ucnLkmNjdu013zfBtqanV94tm4UIzVnTePDP+MPx8160z3XQ7dw5ty8w0Xdc3bAg95j//bMaPP/ZYzffz/POm+3JtunQxXX8fecScU48eZnmf//u/6Punpppu0VW7vgb89JMZp/vcc7Xfr2S6t7/7bvRxs//+txnzfPbZZgx91W6hQ4eacaIlJWaZtqIi6Y47pIEDQ0vm1PZarirwuH7zjVkirbEGDzbjQ6N59FFT79pYlulGe9BBNe+zfbsZV+p0mvHH4ef6/ffmfRA+BrVjR/P+qtpN/7nnTBf7jIzo9/Pyy+Y9HFgyqzZN9boM9+675t+kJNNF/bzzTFfhO++Unn669jXMKyuliy4y+7zyilnOL9y6dWZJsXCZmaY7eMCNN5rx8tu2me7j114bOc+A0xl67E4+2ew3b15o3HVA9+5Shw7m/ROQnGw+/8K7UkvmORk9un5jq/PzzWvhmWfM2Gi323zWL1pkuq9XVkp//KPpDl2bLVvM4zlokHkv9eplxryvWBF9mbe6Ps/DzZ0b+dkYWO969Ghp5Ejz/0Nt7+mqbrvN3Pakk0yX+oULzfF/8xuzbFaCIUwDANAWXHyxWbv32WfNl7pwaWkmcL7+uvmCLpkvZFu3hr5E33yzGZP30UfVg+3UqdUnybrhBvMl8dFHTTgYPtyE7oqK0HjRN98MHT8trfoax599ZsbIuVz1P8+kpNCYyfx8E6Cqjnnt0ME8Fq+/HpokyeczE03NmFH78W3bTIQT7UtjXdauNQH6uefM2stVpaWZdWr37g2Nbf3sM1P/8InVXn3VjMEMn2Qr3Jo1ZmxptPuIpmtXc3nnHfO8/+pX0ff76SdTXtsX1pQUM8771VelnBwTNgIqK834xjfeMOPFP/mk+u39fvOcXXGF9Oc/117vwI9ADz5oglJg7GVdr+WqAhNKhQedxvjoo5qPVdM44nCBH7yGDat5n5wc8754663qY80POMD8IPPFF6EAadvSf/5Tffzs3/9e++vk7383P1LVp95N+brcudME3R07TFAKmDHDBL7vvjPBN1rYCnjwQfMZ89FH0d+vaWmhya8CPvss9LoK6NTJTGzm85l5HfLza77P8AnOwgXWlF692nwWS+Z9vmFD5I88FRXmR8C5c2u+j4CyMjNu2OORbrnFzANQ9QejQYPM+ZeVRb4XA2w79Pn47rvmfVJcbOYBWL48+hrnUt2f5+GKiiKvl5RIp51mfnQZPjyyrKb3dMAXX5g5FD77zFz3+0M/3JaV1e912tJapP27hdDNGwDQLhUXmy5zzz1XfS3TwNqj991nxhQ++6wZSz18uBmvGtCjh1nXturtt22Lfp9Vu3lv3Wq6AF5zjW1/9pkZb9elS2js7CefmKVQrrvOjGUNjPn83e+qH7umbt5/+Yu57UcfmbGrhx5qlvCybXOeJ51kxiLbtumK2bGjGXO4Zo1Z7ubYY+seN7tihalX1WVpaloaa+ZMc7Ft0+28X7/qj+H69ab7qcdjjn3RReY5+PvfzTjyqmMZf/tbs9ZqTRYsMN3dq6qosO0zzzTrXNu2GUv7yiumq+9TT5nn+JZbQvsvX266sxcUmC7xw4ebLrfh61CH++yzUPflv//djGUMV1RkuvIHHq+zzzZ/v/CCqVdFhVm2SjLPU9XHKLCM29Kl5jn+9FPTbbhLl8jlu+p6Ld91lznG2rWmHr17m2XXAsKfs3DRunlv3Wqej/feM4/nVVeZ1/H770d/jGzbdHON1s3b5zNjjh98MDRG+fvvTV03bDBjrTdsMI9DYI30qo+R12tud911ZnzvsmXm+Zs82YzP/fTTUD1+/NHc97/+VXNdu3Uz9akq/DkLaOjrsqpNm0xX35kzzWuq6lCPKVOiL8tX9fV9wglmLHHVxygw5v/FF0337blzzeNy773mMQrMBbFtm3lffPaZbb/+unn8R44MnfMbb5iuzu++a27/xz+az5RHHjHl4c+ZbZvxvykpZl33Tz4xXf4POijy/fTpp7V3uw/39tuma3RA4LVu25HrTJ93XuRzGOjmffzxZpz3ww+bMc2XX266qp98sun+/oc/mK7wgbkGJk0yy9rZdt2f59FeHwHRunnX9Z4OGDkycsm1sWNNHQoLzbCZ2pZb+x/GTDcCYRpAu3TNNfGuAeLtnXdqniQr8OWostKMLXS7zReZX/86csxeUlL021cNTAFVw7Rtmy9/Rx9txgMOHlx9ArBXX7Xt444zXzh79TLBbt++6seuKUyPGWPGF3ftatvnnBM5bvbbb024eOml0LaFC01Y7dzZfJmsa2Ktb7814yajBa2awvR555mLbZsvqzU9D4HxhB9/bL4wdu5svmjn5lZfm3r4cBOOapKdbb7IVuX3m/Geged8+XITVDp1MuNp77svckzpe++ZsY4Oh9nv178O/TgRTdUwnZoaOfayf//QhFHhYfr++029/H7zI0pNj1FgUq+rrjLH3n9/8xr75JPIetT1Wn7gAXM+KSkmTNx4owm1AeHPWbhoYfqHH8w8BF26mDWETz21+oR3VYWH6V27zLktXmzmHbjxRjOm3+k0r98PPjDHLygw4+wLCmz7m29qfoyuv94cd+9eE/TS0szzN2SIbb/2WmQ93njD3KamH8Q2bDDl4fMrBIQ/ZwENfV1GEwhl4etM27Z5nFJTzY8jBx0UOUFh1dd3v37RH6OsrNBtliyx7SOPNO+Bww+PXCN6y5ZQ2aGHmrXLw+c4KC428wy4XKZOQ4fa9jPPhMrDn7OABx4wdezUyTxehYWR5/2Xv5jnvq4f9WzbrL9+ySWh66+9FprzIRCmt241P5YceGBoUr7i4tCY6YD/+7/QDy23324+Y23b/NgWCNNDhkT+uFnb53m010dAtDBd13vats1nw7BhkZ9RX39tPlNSU82PkFU/K6No6TyYZNuBju2tX2lpqVwul3w+n7oGptEHgLburLPMuDcADffOO2YMYnq6GRdadYmhp58269s29TrVrcnnn5suvBs2mK6da9dKF14YKt+3z3Rhzcw0Xbh37JCWLIlbdeNm82bTzXXDBtPl/fTTzbI/d95plmr661/NWryvv272+/FHs7092rDBdNdftUq67jrz/nrhBTM++fHHzTJ86elm6aQEHC/brE491XTrPu888zPBnDlmmMVdd5nXU8+eppv2W29Jf/mL9K9/mc+x3r3N43rllaEx5+PHm2EAKSmmi3dyshlGsHu3mSvjL3+J66k2pZbOg4yZBgAA7VtFhRnrPH68CczR1upFpGiTWu23n1kbd8cOM6730UfjU7dEkpFh5he49Vbzutq2zUzKdv/9ZmKpRx6JnBCrvZo500xgVVQUGkd72WVmPPftt5txvu1JRYV5LE44wYyXfugh81pavtyM7d6500zQ9dxz5r12771mArrAmOSkpMi1zjt2NOujH3ts5P3UNAEZ6o2WaQBo7WiZBhrP768+0RPQHMInhYJRWVl94sT2bt8+04Ic7bUSPokhItAyDQAA0NII0mgpBOnqCNLV1TZzNUE6YRCmAaA18ngkr9f87fOFlrdwu80yRQAAAGhWhGkAaG08HjN2qqwstC0ry/zrdJpxVgRqAACAZkWYBoDWxus1QXrJksgJgIqKpOxsU06YBgAAaFaEaQBorTIyzBI0AAAAaHGM9gcAAAAAIEaEaQAAAAAAYkSYBgAAAAAgRoRpAAAAAABiRJgGAAAAACBGhGkAgJGTE+8aAAAAtBqEaQCAUVIS7xoAAAC0GoRpAAAAAABiRJgGAAAAACBGhGkAaKM8Po8KtxWqcFuhJj47UYXbCuXxeeJdLQAAgDYhOd4VAAA0PY/Po/SF6fKX+4PbXih6QY5kh4pzi2W5rDjWDgAAoPWjZRoA2iBvmTciSAf4y/3ylnnjUCMAAIC2hTANAAAAAECMCNMA2pWc5aylDAAAgMYjTANoV0p2spYyAAAAGo8wDQAAAABAjAjTAAAAAADEiDANAAAAAECMCNMAAAAAAMSIMA0AAAAAQIwI0wAAAAAAxCg53hUAgObm8XnkLfNKknx+nwq3FUqS3E63LJcVz6rFn8cjec1jI59PKiyU3G7JauePCwAAQB0I0wDajpwcKT8/YpPH51H6wnT5y/3BbVmLsiRJjmSHinOL22+g9nikjAyprCy0LStLcjqloiICNQAAQC0I0wDajpKSapu8Zd6IIB3OX+6Xt8zbJsO02+mWI9lR7dwdyQ65nW5zxes1Qfqmm6Q+fcy2b76R7rjDlBGmAQAAakSYBoCqorRwtzaWy1JxbnGwe3veijwtGLMgsmu7221aoe+4I/LGTqcpAwAAQI0I0wBQVZQW7oRUVFTrdctlBYOzy+FSZo/MyP0ty9wmMGY6L09asIAx0wAAAPVAmAaA1ibQopydXb0s1lZlywoFZ5dLysysfX8AAABIYmksAGiQnOU58bvzQItyQYG5/OpXob+ZOAwAAKBF0DINAA1QsjPOXcHDW5Sffz6+dQEAAGiHCNMAWrdo6yRLjPsFAABAsyJMA2i9alonWQqulex2RV8iSqqyTFQiCf+BIBw/EAAAACQMwjSA1iuwTvKSJSZUBxQVmcm5vF5ZVmbUJaIkRS4TlSii/UAQ8L8fCAjUAAAA8UeYBtD6ZWTUOgt1nUtEJZJ6/EBAmAYAAIg/wjQAJKI6fiAAAABAfMW8NNYzzzyjgQMHyul0yrIszZkzR7ZtR+yzceNGnXbaaVqyZEm17UlJSRGXo48+OmKfVatWKSsrSw6HQ0cddZReffXVBpwWACBmaWnxrgEAAECrEXOY/vLLLzVr1iytXr1aN954o+bMmaOHHnpIkrRhwwZdeeWVGjRokN58881qt92xY4c6dOigr776SuvXr9f69ev1yiuvBMs3bdqkcePG6bTTTtPHH3+skSNHasKECdq8eXPDzxAAUD/5+fGuAQAAQKsRc5iePXu2LrzwQg0cOFCTJ0/WmWeeqddff12S9MYbb2j79u1atWpV1Nvu2LFD3bp10xFHHKH+/furf//+6t27d7D8/vvvV//+/TV//nwdc8wxuu+++3TggQfqsccea+DpAWgVcnLiXQMAAAAgJjGH6aoqKirUvXt3SdLkyZP10ksvadCgQVH33bFjh9zumpehWblypcaMGRO8npycrBEjRmj16tVR99+zZ49KS0sjLgBaoZKSeNcA9ZCznB89AAAAAhocpnfv3q3Fixfrww8/VG5uriQpKSmp1tv88MMPWr9+vTp37qz+/fvrmmuu0fbt24PlGzduVL9+/SJuY1mWtm7dGvV48+bNk8vlCl7CW7kBAE3D4/OocFuhvtj+hQq3FapwW6E8Pk+8qwUAABBXDZrN2+FwaM+ePeratavy8/N17LHH1ut2Z599to4//nh16NBBa9eu1c0336w1a9bo/fffV8eOHbVr1y45nc6I2zidTu3Zsyfq8W644QZNnz49eL20tJRADQBNyOPzKH1huvzlfklS1qIsSZIj2aHi3OLEW6cbAACghTQoTK9Zs0Y+n0+ffPKJpk2bps8//1xz586t83a9evVSr169JEmDBw/W4YcfrhEjRqiwsFBDhgxRSkqK9u7dG3Ebv99fLWAHpKSkKCUlpSGnAAAx8/g88pZ5JUk+v0+F2wrldrrbdKD0lnmDQTqcv9wvb5m3TZ87AABAbRoUpgcMGCBJGjZsmJxOp6666irdfPPN6ty5c0zHyfzfGqrffPONhgwZorS0NG3ZsiViny1btuiwww5rSDUBoP48HslrgrJ8PqmwUHK7JcuExaottJJppaWFFgAAoH1q9ARkycnJsm1bFRUVMd/2o48+kiT1799fkjR8+PDgzOCSmdzs7bff1ujRoxtbTQCJyOMxoTUQXgsLzbZ41CMjQ8rK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Vq9eLUlauXJlRHm3bt2UmZkZUT5mzJhgeXJyskaMGBEsBwAAAAAgETR4nendu3fr6aef1ocffqj58+dr06ZNkqR+/fpF7GdZlrZu3aoff/xRP/30U43lkrRx48ao5WvXro1ahz179mjPnj3B66WlpQ09HQAAAAAA6q1BLdMOh0NdunTR9OnT9cADD+jYY4/Vrl27JElOpzNiX6fTqT179tRZLkm7du2qtbyqefPmyeVyBS+9e/duyOkAAAAAABCTBrVMr1mzRj6fT5988ommTZumzz//XOecc44kae/evRH7+v1+OZ1OpaSk1FouSSkpKbWWV3XDDTdo+vTpweulpaUEagAAAABAs2tQmB4wYIAkadiwYXI6nbrqqqs0depUSdKWLVt0+OGHB/fdsmWLzj//fLndbqWkpGjLli0Rx9qyZYuysrIkSWlpaVHLDzvssKj1SElJCYZ0AACAqrbt3KZtu7ZJknx+nwq3FapHlx7qkdojzjVLHDnLc5jADQAaoNETkCUnJ8u2bblcLvXt2zc4s7ckffXVV9q6datGjx6tDh066IQTTogoD7Rujx49WpI0fPjwiPKKigq9/fbbwXIAAIBYPFzwsLIWZSlrUZZWeVYpa1GWHi54ON7VSiglO0viXQUAaJViapkuLS1Vbm6usrOz1bNnT61du1YzZszQRRddFBxDfcMNN2jQoEHq16+f8vLyNH78eB1zzDGSpOnTp2vChAkaMWKEjj/+eN12221KT0/XuHHjJEnTpk3T0KFDNWfOHJ177rl68MEHVVlZqcsuu6zJTxwAALR9k7Mm66z0syRJeSvytGDMAvXo0g5apT0eyes1f/t8UmGh5HZLlhXfegFAGxJTmHY4HNq3b58mTZokn8+nPn366Nprr9X1118vScrNzdX27ds1ZcoU+f1+nX322Vq4cGHw9r/85S917733as6cOfrxxx916qmnavny5erYsaMkafDgwVq2bJlmzpypuXPnaujQoXrttdeUmprahKcMAADaix6poS7dLodLmT0y41yjFuDxSBkZUllZaFtWluR0SkVFBGoAaCIxhelOnTpp2bJlNZYnJSVpzpw5mjNnTo37TJ06NTi+OpqJEydq4sSJsVQLAAAAAV6vCdJLlphQLZkQnZ1tygjTANAkGrzONAAAAOIsJ0fKr2HysIwMKbPmlniPzyNvmTc4MZskuZ1uWS7CNgDUB2EarVtaWrxrAABA/JQ0bPIwj8+j9IXp8pf7JUlZi8zKKo5kh4pzi6sFamb8BoDqCNNo3Wr6NR4AgLaqCSYX85Z5g0E6nL/cL2+Zt1qYZsZvAKiOMA0AANBaMLkYACSMRq8zDQAAgBYSPrlYQYG5LFlitgVaqwEALYKWaQAAgNamjsnFAADNjzAdA7fTLUeyo9oYI0eyQ26nO061AgAAiKKoKPrfAIAmQZiOgeWyVJxbLG9ZZDcqlpEAAAAJw+02Y6izsyO3O52mDADQJAjTMbJcFsEZAAAkLssyLdGBMdR5edKCBTHP+A0AqB1hGgAAoK2xrFBwdrkYXw0AzYDZvAEAAAAAiBFhGgAA4H9ylufEuwoAgFaCMA0AAPA/JTtL4l0FAEArQZgGAABoy9LS4l0DAGiTmICsubC2IwAASAT5+fGuAQC0SYTppsbajgAAAADQ5tHNu6kF1nYsKDCXESPMv0VFrO0IAAAAAG0ELdPNgbUdAQAAAKBNo2UaAAAAAIAYEaYBAAAAAIgRYRoAALQLaaksEYVWLCcn3jUAUAVhGgAAtAv541vZElEJEJ48Po8KtxXK5/epcFuhCrcVyuPzxLta7VNJSbxrAKAKJiADAABIRHEOTx6fR+kL0+Uv90uSshZlSZIcyQ4V5xbLcrFKCYD2jZZpAAAAVOMt8waDdDh/uV/eMm8cagQAiYUw3dzSGJ8FAAAAAG0N3bybW34rG58FAEA74/F5gi2tgbHBbqebbswAgFoRpgEAQLtVdVywZMYGMy4YCcPjkbxeyeeTCgvNNrdbsnhtAvFGmAYAAO1WXeOC22qYdjvdciQ7qp27I9kht9Mdp1qhGo9HysiQysrM9SwzCZycTqmoiEANxBlhGgAAoJ2xXJaKc4vlLfMqb0WeFoxZIEl0b080Xq8J0kuWmFAtmRCdnW3KCNNAXBGmAQAA2iHLZclyWXI5XMrskRnv6qA2GRlSJs8RkGiYzRsAAAAAgBgRpgEAAAAAiBFhGgAAAACAGBGmAQAAAACIEROQAQAAAK2Ux+eRt8wrSZr77lzNOnmWJGZmB1oCYRoAAKAdS0tNi3cVkJMj5efHfDOPz6P0hekR64W/UPSCJLNmeHFuMYEaaEaEaQAAgHYsf3zsIQ5NxOMx60V/8YVUWGi2ud31Xj/aW+aNCNLh/OV+ecu8hGmgGTFmGgAAAO1PTk7L7RONx2PWj87KklatMv9mZZltHk/DjgmgRRGmAQAAmlJDwxVaVklJw/fxeExLcmFhqFU51gDs9UplZdKSJVJBgbksWWK2eb2xHQtAXNDNGwAAoCnVJ6Sh9Qq0KJeVhbZlZUlOp1RUVO8u2kEZGVJmZtPWEUCLoGUaAAAAqC9alAH8Dy3TAAAAQKxoUQbaPcI0AAAAEBCYYVuSfL4GzbINoH0gTAMAAABSzeOhpdCYaAD4H8I0AAAAIEWOh87ICG0vKpKysxkTDSACYRoAAKApBLoH0zW49WM8NIB6IEwDAAA0VtXuwVW7BhOo27Wc5TnKH58f72oAaGIsjQUAANBYLJeEWpTsZO1xoC2iZRoAAKCe6mxhpHtw4qM7PoAmQpgGAACoJ1oYW4mcHCk/yo8edXXHb0Ien0feMtMrwef3qXBbodxOtywXoR1oKwjTAAAAaBsCrc5ffBG91TnabN3NMFO3x+dR+sJ0+cv9wW1Zi7LkSHaoOLeYQA20EYRpAAAAtH6xTALXzN3xvWXeiCAd4C/3y1vmJUwDbQQTkAEAAKD1YxI4AC2MlmkAAAC0HUwCB6CF0DINAAAAAECMaJkGAABIFIEJtCSWbgKABEeYBgAASARVJ9CSmnXpJgBA4xCmAQAAEkG0ZZukZlm6CQDQeIRpAACARMIEWgDQKhCmAQAA0L6Ed5mn+zyABiJMAwAAtDaEwYZxu8348+zsyO1OpymjKz2AGBCmAQAAWou6wiBqZ1nmxwevV8rLkxYsMNsDs6UTpgHEgDANAADQWoSHQSkUCFk6q/4sy1xcrprHpldt7Y/W+k/vAKDdI0wDAAC0JoEwKNUeCFurnBwpPz8+911Ty78U2frf0r0DagjubqdbjmSH/OX+ajdxJDvkdtJbAWhOhGkAAIA6eHweecu88vl9KtxWKMkEGctFa3CTKylpmftJS6u+raaWfymy9b+legfU0a3fclkqzi2Wt8zUJW9FnhaMMfXl9Qk0P8I0AABot2pq2Qtv1fP4PEpfmB7cJ2tRVnCf4tziegeWQCCXpLnvztWsk2cF60DoiYOaWr/r0/Jfxz71eV3VS11jvCVZLiv4+nE5XMrs0cZ6KgAJjDANAADarZpa9sIDrrfMG7Ubrb/cL2+Zt15BuGogl6QXil6QFBbKm+KEkBDq87qq/8HqMcYbQFwQpgEAQLvWEi17NQVyKSyUN+TA0boqIyHQYgy0fR3iXQEAAAA0ULwm6gIA0DINAAAAtGXh4/XDMV4faBzCNAAAANBGRRuvHxDrJHoAIhGmAQAA/ictlTHIceHxhJaa8vmkwsLmWWqqOST4uPV6jdcnTAMNQpgGAAD4n/zxjEFucR6PlJEhlZWFtmVlmbWUi4oSP1Azbh1ot5iADAAAAPHj9ZogvWSJVFBgLkuWmG3e6uN8JSlneU7L1C3BW50BxBct0wAAAIi/jIx6r6NcsrOkmSvzP7Q6A6gFYRoAAABtR1FR9L/jiLH4QNtEmAYAAEA1bqdbjmRHtcmrHMkOuZ3uavvnLM+J75hzt9uMs87OjtzudJqyOGIsPtA2EaYBAABQjeWyVJxbLG+ZV3kr8rRgzAJJNa9N3GJdr2tiWaYl2uuV8vKkBaa+rWZWcACtDmEaAAAAUVkuS5bLksvhUmaP+o1njivLMheXq97jrwGgoZjNGwAAAACAGBGmAQAAUCsm0EoALNMFJBy6eQMAAKBWNU2g5fF55C0za0H7/D4VbiuUVPO4ajQCy3QBCYcwDQAAgJh5fB6lL0yPmO07a1GWJDPjd3FuMYEaQJtGN28AAADEzFvmrbZsVoC/3B9ssQaAtoowDQAAAABAjAjTAAAAAADEKOYwvW7dOp1xxhlyOp069NBDdfnll+uHH36QJO3du1fTp0/XwQcfrP3331+/+tWvtH379uBtN27cqKSkpIjL0UcfHXH8VatWKSsrSw6HQ0cddZReffXVRp4iAAAAAABNK+YwnZubq1GjRmn16tVavHix3nnnHU2aNEmS9Pvf/17PPfecHn/8cS1fvlz/+c9/dNFFFwVvu2PHDnXo0EFfffWV1q9fr/Xr1+uVV14Jlm/atEnjxo3Taaedpo8//lgjR47UhAkTtHnz5safKQAAAJBoioqkwkJzKSqKd20AxCDm2byXLl2q3r17S5IGDhwon8+nSy65RKWlpXrggQe0bNkyjRs3TpL06KOP6qSTTtK6des0cOBA7dixQ926ddMRRxwR9dj333+/+vfvr/nz50uS7rvvPr388st67LHHdNtttzX0HAEAAIDE4nZLTqeUnR253ek0ZQASXswt04EgHeBwOFRZWak1a9aooqJCgwcPDpadcMIJSklJ0UcffSTJtEy7a/lwWLlypcaMGRO8npycrBEjRmj16tVR99+zZ49KS0sjLgAAAEDCsyzTEl1QII0YYf4tKDDbLJYUA1qDRk1AZtu2Fi9erGHDhgVD9jfffBMsLysrU3l5ub7//ntJ0g8//KD169erc+fO6t+/v6655ppqY6r79esXcR+WZWnr1q1R73/evHlyuVzBS9WgDwAA0Fhup1uOZEe17Y5kh9zO9tuCWNPjIvHY1JtlSZmZkstl/s3MJEgDrUjM3bwD9u3bpylTpmjlypVatWqV+vbtq+OOO0433XSTfvGLX8jlcunaa6+Vbdvq2LGjJOnss8/W8ccfrw4dOmjt2rW6+eabtWbNGr3//vvq2LGjdu3aJafTGXE/TqdTe/bsiVqHG264QdOnTw9eLy0tJVADAIAmZbksFecWy1vmVd6KPC0Ys0CSCZOWq/0Gn/DHRVKzPzYenyd4Xz6/T4XbCtv9cwAgvhoUprdu3aoLLrhAGzdu1FtvvaXjjjtOkvTUU0/pggsukGVZSk5O1rRp09S1a1cdfPDBkqRevXqpV69ekqTBgwfr8MMP14gRI1RYWKghQ4YoJSVFe/fujbgvv99fLWAHpKSkKCUlpSGnAAAAUG+Wy5LlsuRyuJTZIzPe1UkYgcdFUrM+Nh6fR+kL0+Uv9we3ZS3KkiPZoeLcYgI1gLiIuZv3V199pWHDhik1NVVr167V8ccfHyw74ogjVFhYqO+++07bt2/XzJkz9dNPP0WMow6XmWk+cANdw9PS0rRly5aIfbZs2aLDDjss1moCAACgjfCWeSOCdIC/3B9srQaAlhZzmL744ot1wgkn6F//+lewxbmqQw45RN26ddODDz6oAQMGaNCgQVH3C0xM1r9/f0nS8OHD9frrrwfLKyoq9Pbbb2v06NGxVhMAAAAAJEk5y3PiXQW0QTF18/7qq69UUFCgmTNnauPGjRFlBx10kFauXKmDDz5YXbt21T//+U/Nnz9fL730UnCfu+++Wz179tTRRx+tL774QjNmzNDYsWODYXvatGkaOnSo5syZo3PPPVcPPvigKisrddlllzX6RAEAABBfOctzlD8+v/nvKC2t+e8DrUrJzpJ4VwFtUExh+rvvvpMknXfeedXK7r//fu3YsUP33HOPfv75Zw0aNEgvvfSSTj/99OA+TqdTM2bMkNfrlWVZmjRpkm666aZg+eDBg7Vs2TLNnDlTc+fO1dChQ/Xaa68pNTW1oecHAACABNFigSa/BQI7gHYvpjA9YsQI2bZd6z633HJLjWVTpkzRlClTar39xIkTNXHixFiqBQAAALR7aam0yAMtqVHrTAMAAKBurMmMltAiXegBBDV4nWkAAADUT/3WZGZWagBoTQjTAAAALSB8TeYjDzqS9aoBoJWjmzcAAEALozsuALR+tEwDAAA0laKi6H8DANocwjQAAEBjud2S0yllZ0dudzpNGQCgzaGbNwAAQGNZlmmJLiiQRoww/xYUmG2WFe/aAQCaAS3TAAAATcGyzMXlkjKZXAwA2jpapgEAAAAAiBFhGgAAAACAGNHNGwAAIJFUnQWcWcEBICERpgEAABJBTTOCS616VnCPzyNvmVeS5PP7VLitUJLkdrpluZicDU0gJ0fKr752e7TXHq87NCXCNAAAQCIIzAjuNV/+NXeuNGuW+dvtbpWzgnt8HqUvTJe/3B/clrUoS5LkSHaoOLdYre+skHBKSqptqum1F3zdEajRBAjTAAAAiSIwI7gkPf98fOvSBLxl3ogwE85f7pe3zFuvMO12uuVIdlQ7liPZIbezdbbYo5E8ntAPTz6fVGh6PAR+eKrptRd83RGm0QQI0wAAAEholstScW5xsMtu3oo8LRizgC677ZXHI2VkSGVloW1ZpseDnE7Tw2O/+FQN7QthGgAAAAnPclnB4OxyuJTZg7W82y2v1wTpJUtMqA4oKjJzDni9Uo/4VQ/tB2EaAAAAiKe0tHjXoHXKyJAy+VEF8cM60wAAAEA8RZmJGkDiI0wDAAAAABAjwjQAAAAAADEiTAMAAAAAECPCNAAAQD2lpTJRVCLgeQCQCAjTAAAA9ZQ/nomiEgHPA4BEQJgGAAAAACBGhGkAAAAAAGJEmAYAAGhKaYznBYD2gDANAADQlPIZzwsA7QFhGgAAAECb4na65Uh2VNvuSHbI7XTHoUZoi5LjXQEAAAAAaEqWy1JxbrG8ZV5JUt6KPC0Ys0Bup1uWy4pz7dBW0DINAAAAoM2xXJYye2Qqs0emXA6XMntk1hqkc5bntGDt0BYQpgEAAAC0eyU7S+JdBbQyhGkAAAAAAGJEmAYAAAAAIEaEaQAAAAAAYkSYBgAAANCmpaWmxbsKaIMI0wAAAADatPzx+fGuAtogwjQAAAAAADEiTAMAAAAAECPCNAAAAAAAMSJMAwAAAAAQI8I0AAAAAAAxIkwDAAAAABAjwjQAAAAAADEiTAMAAAAAECPCNAAAAAAAMSJMAwAAAAAQI8I0AAAAmoXb6ZYj2RG1zJHskNvpbuEaAUDTSY53BQAAANA2WS5LxbnF8pZ5JUl5K/K0YMwCSSZoWy5LkjeONUQ0OctzlD8+P97VABIeYRoAAADNxnJZ/wvNksvhUmaPzDjXCHUp2VkS7yoArQLdvAEAAAAAiBFhGgAAAACAGNHNGwAAAI2WlprWuAMUFUX/GwASFGEaAAAAjdbgCavcbsnplLKzI7c7naYMABIU3bwBAAAQP5ZlWqILCsxlxAjzb1GRKQMSRM7ynHhXAQmGlmkAAADEl2WFgrPLJWUy4zcSD7OcoypapgEAAAAAiBFhGgAAAC2i0ZOUAUACIUwDAACgRTR4kjIASECEaQAAAAAAYkSYBgAAAAAgRoRpAAAAoI1yO91yJDuiljmSHXI7WcsbaCiWxgIAAADaKMtlqTi3WN4yb7Uyt9Mty8Va3kBDEaYBAACANsxyWYRmoBnQzRsAAAAAgBjRMg0AAACgXfL4PMEu8D6/T4XbCun+jnojTAMAAABodzw+j9IXpstf7g9uy1qUJUeyQ8W5xQRq1Ilu3gAAAADaHW+ZNyJIB/jL/VEnbAOqIkwDAAAAABAjwjQAAAAAADEiTAMAAAAAECPCNAAAAAAAMWI2bwAAAKCdY4koIHaEaQAAAKAdY4kooGHo5g0AAAC0YywRBTQMYRoAAAAAgBgRpgEAAAAAiBFhGgAAAACAGBGmAQAAAACIEWEaAAAAAIAYEaYBAAAAAIgRYRoAAABAu+N2uuVIdlTb7kh2yO10x6FGUE5OvGsQk+R4VwAAAAAAWprlslScWxxcSztvRZ4WjFkgt9Mty2XFuXbtVElJvGsQE8I0AAAAEkdaWrxrgHbEclnB4OxyuJTZIzPONUJrQjdvAAAAJI78/HjXAADqhTANAAAAAECMYg7T69at0xlnnCGn06lDDz1Ul19+uX744QdJ0t69ezV9+nQdfPDB2n///fWrX/1K27dvj7j9Cy+8oIyMDDkcDg0ZMkQFBQUR5atWrVJWVpYcDoeOOuoovfrqq404PQAAAACoW1oqQwwQm5jDdG5urkaNGqXVq1dr8eLFeueddzRp0iRJ0u9//3s999xzevzxx7V8+XL95z//0UUXXRS87QcffKALL7xQkydP1ocffijLsjRu3Djt3LlTkrRp0yaNGzdOp512mj7++GONHDlSEyZM0ObNm5vmbAEAAABEYFZrI388QwwQm5gnIFu6dKl69+4tSRo4cKB8Pp8uueQSlZaW6oEHHtCyZcs0btw4SdKjjz6qk046SevWrdPAgQN11113ady4cbruuuskSYsXL9ahhx6q559/Xpdffrnuv/9+9e/fX/Pnz5ck3XfffXr55Zf12GOP6bbbbmuiUwYAAAAQwKzWQMPE3DIdCNIBDodDlZWVWrNmjSoqKjR48OBg2QknnKCUlBR99NFHkqSVK1dq7NixwfJu3bopMzNTq1evDpaPGTMmWJ6cnKwRI0YEywEAAAA0PctlKbNHpjJ7ZAZntSZIA7Vr1ARktm1r8eLFGjZsWDBkf/PNN8HysrIylZeX6/vvv9ePP/6on376Sf369Ys4hmVZ2rp1qyRp48aNtZZXtWfPHpWWlkZcAAAAAABobg0O0/v27dPVV1+tlStXauHCherbt6+OO+443XTTTdqyZYtKS0s1ZcoU2batjh07ateuXZIkp9MZcRyn06k9e/ZIknbt2lVreVXz5s2Ty+UKXqq2mgMAAAAA0BwaFKa3bt2qUaNGafny5Xrrrbd03HHHKSkpSU899ZT27Nkjy7LUvXt3ud1ude3aVQcffLBSUlIkmRm/w/n9/mCATklJqbW8qhtuuEE+ny942bJlS0NOBwAAAACAmMQcpr/66isNGzZMqampWrt2rY4//vhg2RFHHKHCwkJ999132r59u2bOnKmffvpJgwcPltvtVkpKSrXAu2XLFh122GGSpLS0tFrLq0pJSVHXrl0jLgAAAADiLCenafYBEljMYfriiy/WCSecoH/96186+OCDo+5zyCGHqFu3bnrwwQc1YMAADRo0SB06dNAJJ5yg119/Pbifz+fTJ598otGjR0uShg8fHlFeUVGht99+O1gOAAAAoBUoKWmafYAEFtPSWF999ZUKCgo0c+ZMbdy4MaLsoIMO0sqVK3XwwQera9eu+uc//6n58+frpZdeCu4zffp0TZgwQSNGjNDxxx+v2267Tenp6cGltKZNm6ahQ4dqzpw5Ovfcc/Xggw+qsrJSl112WePPFAAAAECTyFmew7rMaPdiCtPfffedJOm8886rVnb//fdrx44duueee/Tzzz9r0KBBeuml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hWnfvn2OxuREsObrdrsVFhbm9Th06JBffWuavzNnzlR6erpcLpeuuOIKrVmzxq9tBirU8q1p/s6ePbvaNisfxcXFfo0rEMGasSS9/vrr6tSpk2JiYtS6dWvl5OT41a+mOVzpzTffVEpKit9jCVSo5Vvba/Do0aOrzd/Vq1f7Paa6Csa8+/bt6/PnOiMjo9a+7733nnr06KHo6GhdcsklWrFihR8p1J9Qy7O2+dvY5zhfgjFzSXrrrbfUuXNnxcTE6LLLLtOyZcv8PqYNGzaoZ8+eat68uRITE/Xqq6/63bcmoZhVTeev7du3a8CAAWrevLlSU1P15JNP+r1dD0PIueaaa+yxxx6z/Px8W7dunaWnp9v1119vZmarVq2y6Ohoe+WVV+zjjz+23r17W4cOHay8vNzMzB588EG7++677f3337ctW7bYgAED7KKLLrJvv/222n6mTJlikqx///6Oxvfcc8+ZJMvIyPBr/RdeeMEuv/xyi4qK8tln8ODB1q1bN3v//fdt9erV1qJFC3v88ccdjcmJUMq3oKDAOnbsaG+++abl5+fbggULLCwszFatWlVt3c8//9yaNWtmkqy4uNjRmJwI1nxbtGhhr776qu3cudPzqNxvTWqbv3fddZe9++679umnn9r48eMtIiLCCgoK/BpTIEIp39rmb0lJidf2du7caePGjbNevXo5icyxYM34xRdftBYtWtiiRYvs888/t3fffde2bdtWa7/a5vCbb75pPXr0sKioKGvWrJlfY6mLUMrXn9fgX/3qVzZx4kSveXzs2DF/46qzYMx737591X62O3bsaDNmzKix3969e61FixY2YcIE+/jjj23WrFkWHh5umzZtCiCZwIRSnv7M38Y+x/kSjJkXFRVZRESEzZw50/Lz8y07O9vCw8Nt48aNtfZ9++23LTo62ubMmWMFBQW2adMmy8vLc5iKb6GWVU3nr6NHj1pqaqrdcsst9tFHH9lTTz1lERER9sorr/gTlQfFdAjau3ev1/NXX33VwsPD7fjx49alSxebOHGiZ1lhYaFJ8kzIc/t++eWXJsneeecdr/Zt27ZZUlKSXXfddY6Kva+++sqSk5Nt2LBhfhfTXbt2tYULF9qMGTOq9cnPzzdJ9sknn3japk6dam632+8xORVK+ZaUlFR7UzZw4ED7zW9+49VWUVFhvXv3tptvvrnBi+lgzPf06dMmKaA3ADXN33OVl5dbXFycPfXUU473469Qytff+VuptLTU4uPjbf369Y7241QwZlxSUmJxcXG2bNkyx8dT2xwePHiwzZgxwxYtWtQoxXQo5evPHO7Zs6c9/fTTjrZbn4Ix73Nt2LDB4uLirKSkpMb1Jk+ebB06dLCKigpP28CBA2348OGO9xmoUMrT6WtwY5zjfAnGzFetWmWtWrXyauvSpYstXLiwxn5nz561iy++2GbPnl3rPgIRSlmZ1Xz+evbZZy0xMdFOnDjhabvnnnusS5cutW63Kv7MOwS1adPG63lMTIwqKip05MgRffrppxo8eLBnWWZmpi666CLl5eWdt68klZeXe9oqKio0btw4zZw5UxdddJGjsWVlZWnEiBHq1KmT330+/vhjTZ48WZGRkdWW5eTk6MILL1SXLl08bf369dPevXt14MABR2PzVyjl26pVK7Vo0aLamKqOR/r+z7hOnDihe++919F4AhGM+ZaWlkqSkpKSHB9PTfP3XGam8vJyJSYmOt6Pv0IpX3/nb6U//elP6tChgwYOHOhoP04FY8arV69WbGysRo4c6fh4apvD//znP/XYY48pNjbW8bYDEUr5+jOHS0tLA3rtqS/BmPe5Zs+erQkTJqhVq1Y1rrd9+3Z17txZYWFhnra+ffvqww8/DGi/gQilPJ2+BjfGOc6XYMz82muvVUREhF577TWVl5dr/fr12r17t2644YYa+23cuFEHDhxQVlaWX/txKpSykmo+f23fvl2ZmZlq3ry5p61v377Kz8/X6dOn/RqbxDXTIc/MtHTpUvXo0UNfffWVJCk9Pd1rHbfbfd5rYF988UU1b95cV199tadt3rx5OnXqlCZMmOBoLCtWrNCWLVs0Z84cR/2qnvTOtWvXLp/HI6lBr+utFAr5VrV7925t2LBB/fr187Tt2LFD06ZN0+LFixUe3rgvGcGSb+X1Qunp6UpNTdXQoUPPex3QuWqav1V9/fXXuv/+++V2uzV8+HC/x1YXoZBvVb7mb6WTJ0/q2Wef1aRJkxxvty6CJeO8vDx17NhRTz75pNLS0tS2bVtNnz5dZ8+erbVvbXPY3zneEEIh36p8zeGSkhKNGTNGiYmJ6tWrl/7+97872mZ9Cpa8q8rLy1NeXp7Gjx9f67qJiYnas2ePV1tZWZm+/vrrgPZdVz/0PM9V02twU5zjfAmWzJOSkrRs2TLdcccdioyM1KBBg/TEE08oMzOzxn55eXlq27at1q5dq4yMDKWmpuq3v/2tjh075ve+/fVDz0qq+fyUmJio4uJimZmnraysTBUVFV7Xidcmwu818YNz9uxZ3XfffcrJydHmzZs9P2gul8trPZfL5fM3MC+99JJmzpypp556yvPbyffee08LFy5UXl6eIiL8nz47d+5UVlaWVq9erfj4+Doclbdjx475PB5Jjn6rFIhQy7eoqEhDhgxR165dNXbsWEnSqVOndPvtt2vSpEnq3r27cnNzA9p2IIIpX7fbrby8PLlcLu3evVsLFizQL37xC33++edKS0urw1FKmzdv1oABA3T27Fm1b99er7/+erVjbAihlq+v+VvVypUrFRUV1ahv4oIp4wMHDuizzz5Tamqq1qxZo23btikrK0vR0dGaPXt24AfZhEIt3/PN4XXr1snlcqmkpETLly/X8OHD9Y9//ENDhgzxe3z1IZjyruq5557T0KFDlZqaWuu6N998s4YOHaply5ZpxIgRysvL04svvqhmzZoFtO+6CIU8qzrf/G2qc5wvwZT5tm3bdNttt2natGkaNmyYcnJylJWVpUsvvVTXXHPNefsdOHBApaWlWrNmjVasWKH9+/dr/PjxOnPmjKObctUmFLKqzfDhwzV37lw99thjeuihh1RUVKSFCxdKkrPXBEd/FI4fjOLiYuvVq5e1bt3a3n//fTMz++CDD0ySffHFF17r9uzZ07KysjzPT548affcc49FRUXZkiVLvLZ54YUX2ksvveRpGzNmjNf1Do8++qg1a9bM83j00UetrKzMfvrTn9rDDz/sWe+RRx7xunZh+fLlXv3Gjh1b7ZjO7WNmdu+991rv3r292nbs2GGSbOvWrX5lFYhQy3fNmjWWkJBgN9xwg5WVlXnaR44caX379rXvvvvOzMxycnIa/Jpps+DK15eysjJr2bKl53qdQOevmdnx48dt+/bttnnzZps6darFxMTY2rVr/YkpYKGW7/nmb1VXXXWVTZ061Z946kWwZTxgwADLyMjwuqnbQw89ZOnp6WZWtzlc6eWXX26Ua6bNQi9ff+Zwpf79+3tuCNRYgi3vSqWlpRYdHW1vv/22V3tNec+dO9eio6MtLCzM2rZta+PGjWvQ+6z4Ekp5mtU8f5viHOdLsGV+6623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", "text/plain": [ "
" ] @@ -127,7 +119,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.16" + "version": "3.11.7" } }, "nbformat": 4, diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index 6fc2dc47..a55f8770 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -72,10 +72,10 @@ class TestOrderBroker: pass def buy(self, code, price, num, stoploss, goal_price, part_from): - print("买入:%s %.3f %i" % (code, price, num)) + print(f"买入:{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}") def sell(self, code, price, num, stoploss, goal_price, part_from): - print("卖出:%s %.3f %i" % (code, price, num)) + print(f"卖出:{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}") def crtOB(broker, name="NO_NAME"): diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index edebc1ec..25560843 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -80,6 +80,8 @@ void export_OrderBroker(py::module& m) { :param float price: 卖出价格 :param float num: 卖出数量)") + .def("get_asset_info", &OrderBrokerBase::getAssetInfo) + .def("_buy", &OrderBrokerBase::_buy, R"(_buy(self, datetime, market, code, price, num) From 58b00d9edb1b1d4b287543b7281d9f1f5a83ef8f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 18:23:59 +0800 Subject: [PATCH 476/601] update broker_easytrader --- hikyuu/trade_manage/broker_easytrader.py | 41 +++++++++++++++++++++--- 1 file changed, 36 insertions(+), 5 deletions(-) diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index 85db85f7..d38a87d3 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -4,21 +4,52 @@ # Create on: 2024-01-30 # Author: fasiondog +from hikyuu import Datetime, hku_info + + class EasyTraderOrderBroker: def __init__(self, user): self.user = user + self.buffer = {} def buy(self, code, price, num, stoploss, goal_price, part_from): self.user.buy(code[2:], price=price, amount=num) print("买入:%s %.3f %i" % (code, price, num)) + self.buffer[code] = (num, stoploss, goal_price) def sell(self, code, price, num, stoploss, goal_price, part_from): self.user.sell(code[2:], price=price, amount=num) print("卖出:%s %.3f %i" % (code, price, num)) + if code in self.buffer: + old_num = self.buffer[code][0] + if old_num == num: + self.buffer.pop(code) + else: + self.buffer[code] = (old_num - num, stoploss, goal_price) - def cash(self): + def get_asset_info(self): balance = self.user.balance - ret = 0.0 - for i in range(len(balance)): - ret += balance[i]['可用资金'] - return ret + cash = 0.0 + for item in balance: + cash += item['可用资金'] + + positions = [] + for v in self.user.position: + if v["交易市场"] == "沪A": + market = "SH" + elif v["交易市场"] == "深A": + market = "SZ" + else: + hku_info(f"Ignored not supported market: {v['交易市场']}") + continue + + code = v["证券代码"] + market_code = f"{market}{code}" + if market_code in self.buffer: + stoploss, goal_price = self.buffer[market_code] + else: + stoploss, goal_price = 0.0, 0.0 + positions.append(dict(market=market, code=code, number=( + v['当前持仓'] + v['买入冻结'] - v['卖出冻结']), stoploss=stoploss, goal_price=goal_price)) + + return dict(datetime=str(Datetime.now()), cash=cash, positions=positions) From 8962fda0af0a53422674604cc759f15064176ac6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 21:08:17 +0800 Subject: [PATCH 477/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=86=85=E9=83=A8?= =?UTF-8?q?=E5=AE=9A=E6=97=B6=E8=B0=83=E5=BA=A6=EF=BC=8C=E5=85=B1=E7=94=A8?= =?UTF-8?q?=E5=86=85=E9=83=A8=E4=BB=BB=E5=8A=A1=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 8 ++++---- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 9 ++++----- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h | 2 +- hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp | 5 +++-- hikyuu_cpp/hikyuu/utilities/TimerManager.h | 14 ++++++++------ 5 files changed, 20 insertions(+), 18 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 8bc2431d..c1cf6d02 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -192,12 +192,12 @@ void StockManager::loadAllKData() { } else { // 异步并行加载 - auto& tg = *getGlobalTaskGroup(); + auto& tg = getGlobalTaskGroup(); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { for (size_t i = 0, len = ktypes.size(); i < len; i++) { const auto& low_ktype = low_ktypes[i]; if (m_preloadParam.tryGet(low_ktype, false)) { - tg.submit( + tg->submit( [=, ktype = ktypes[i]]() mutable { iter->second.loadKDataToBuffer(ktype); }); } } @@ -222,7 +222,7 @@ void StockManager::reload() { HKU_INFO("start reload kdata to buffer"); std::vector can_not_parallel_stk_list; // 记录不支持并行加载的Stock { - auto* tg = getGlobalTaskGroup(); + auto& tg = getGlobalTaskGroup(); std::lock_guard lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { auto driver = iter->second.getKDataDirver(); @@ -559,7 +559,7 @@ vector> StockManager::getHistoryFinanceAllFields() con } void StockManager::loadHistoryFinance() { - auto* tg = getGlobalTaskGroup(); + auto& tg = getGlobalTaskGroup(); std::lock_guard lock1(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { tg->submit([=]() { iter->second.getHistoryFinance(); }); diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp index d7b7a1f3..5e5e94af 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp @@ -13,9 +13,9 @@ namespace hku { -static TaskGroup* g_threadPool; +static std::shared_ptr g_threadPool; -TaskGroup* getGlobalTaskGroup() { +std::shared_ptr& getGlobalTaskGroup() { static std::once_flag oc; std::call_once(oc, [&]() { auto cpu_num = std::thread::hardware_concurrency(); @@ -24,7 +24,7 @@ TaskGroup* getGlobalTaskGroup() { } else if (cpu_num > 1) { cpu_num--; } - g_threadPool = new TaskGroup(cpu_num); + g_threadPool = std::make_shared(cpu_num); }); return g_threadPool; } @@ -33,8 +33,7 @@ void releaseGlobalTaskGroup() { HKU_TRACE("releaseGlobalTaskGroup"); if (g_threadPool) { g_threadPool->stop(); - delete g_threadPool; - g_threadPool = nullptr; + g_threadPool.reset(); } } diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h index 72ab4bad..705d3e9d 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h @@ -21,7 +21,7 @@ using TaskGroup = ThreadPool; * 获取全局线程池任务组 * @note 请使用 future 获取任务返回 */ -TaskGroup* getGlobalTaskGroup(); +std::shared_ptr& getGlobalTaskGroup(); template using task_handle = std::future; diff --git a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp index bc5ccc55..fc5bddf7 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp @@ -6,8 +6,9 @@ */ #include "hikyuu/GlobalInitializer.h" -#include "hikyuu/utilities/Log.h" #include +#include "hikyuu/utilities/Log.h" +#include "hikyuu/global/GlobalTaskGroup.h" #include "scheduler.h" namespace hku { @@ -16,7 +17,7 @@ static TimerManager *g_scheduler; TimerManager *getScheduler() { static std::once_flag oc; - std::call_once(oc, [&]() { g_scheduler = new TimerManager(2); }); + std::call_once(oc, [&]() { g_scheduler = new TimerManager(getGlobalTaskGroup()); }); return g_scheduler; } diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index 9c6aa4a9..ce6d4bcb 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -39,6 +39,12 @@ public: start(); } + /** + * 指定线程池方式构造,以便共享其他线程池 + * @param tg 指定任务组线程池 + */ + TimerManager(const std::shared_ptr& tg) : m_tg(tg) {} + /** 析构函数 */ ~TimerManager() { stop(); @@ -60,11 +66,7 @@ public: std::priority_queue new_queue; m_queue.swap(new_queue); if (!m_tg) { -#if CPP_STANDARD >= CPP_STANDARD_14 - m_tg = std::make_unique(m_work_num); -#else - m_tg = std::unique_ptr(new ThreadPool(m_work_num)); -#endif + m_tg = std::make_shared(m_work_num); } /* @@ -532,7 +534,7 @@ private: std::unordered_map m_timers; int m_current_timer_id; size_t m_work_num; // 任务执行线程池线程数量 - std::unique_ptr m_tg; + std::shared_ptr m_tg; }; } // namespace hku \ No newline at end of file From b3af90910c0d245b3c3fe3d40c43b6fa1ba27886 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 20 Aug 2024 21:17:35 +0800 Subject: [PATCH 478/601] update --- hikyuu_cpp/hikyuu/utilities/arithmetic.cpp | 7 +++++++ hikyuu_cpp/hikyuu/utilities/arithmetic.h | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp b/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp index ce64a8ea..df8bffe0 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.cpp @@ -17,6 +17,13 @@ namespace hku { +template double HKU_UTILS_API roundEx(double number, int ndigits); +template float HKU_UTILS_API roundEx(float number, int ndigits); +template double HKU_UTILS_API roundUp(double number, int ndigits); +template float HKU_UTILS_API roundUp(float number, int ndigits); +template double HKU_UTILS_API roundDown(double number, int ndigits); +template float HKU_UTILS_API roundDown(float number, int ndigits); + #if defined(_MSC_VER) /** * 将UTF8编码的字符串转换为GB2312编码的字符串 diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.h b/hikyuu_cpp/hikyuu/utilities/arithmetic.h index 2e8b228d..906191ff 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.h +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.h @@ -88,11 +88,11 @@ ValueT roundEx(ValueT number, int ndigits = 0) { ValueT pow1, pow2, y, z; ValueT x = number; if (ndigits >= 0) { - pow1 = std::pow(10.0, (ValueT)ndigits); + pow1 = pow(ValueT(10.0), ValueT(ndigits)); pow2 = 1.0; y = (x * pow1) * pow2; } else { - pow1 = std::pow(10.0, (ValueT)-ndigits); + pow1 = pow(ValueT(10.0), ValueT(-ndigits)); pow2 = 1.0; y = x / pow1; } From d6396a036652b80e27f34774a095fa550a11a155 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 00:12:43 +0800 Subject: [PATCH 479/601] =?UTF-8?q?fixed=20reload=20=E6=97=B6=E9=87=8D?= =?UTF-8?q?=E6=96=B0=E5=8A=A0=E8=BD=BD=E5=8E=86=E5=8F=B2=E8=B4=A2=E5=8A=A1?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index c1cf6d02..ab453ba3 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -472,6 +472,7 @@ void StockManager::loadAllStocks() { stock.m_data->m_precision = info.precision; stock.m_data->m_minTradeNumber = info.minTradeNumber; stock.m_data->m_maxTradeNumber = info.maxTradeNumber; + stock.m_data->m_history_finance_ready = false; } } } From bf6b2b7d16880fd01991d99ca966f743503a0ee9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 00:36:18 +0800 Subject: [PATCH 480/601] =?UTF-8?q?fixed=20=E9=87=8D=E5=A4=8D=E5=88=9D?= =?UTF-8?q?=E5=A7=8B=E5=8C=96=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index ab453ba3..e024dcf4 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -96,9 +96,11 @@ Parameter default_other_param() { void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockParam, const Parameter& kdataParam, const Parameter& preloadParam, const Parameter& hikyuuParam, const StrategyContext& context) { - HKU_WARN_IF_RETURN(m_initializing, void(), - "The last initialization has not finished. Please try again later!"); - m_initializing = true; + // 防止重复 init + if (m_thread_id != std::thread::id()) { + return; + } + m_thread_id = std::this_thread::get_id(); m_baseInfoDriverParam = baseInfoParam; m_blockDriverParam = blockParam; @@ -150,6 +152,8 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa // 加载历史财务信息 loadHistoryFinance(); + initInnerTask(); + // add special Market, for temp csv file m_marketInfoDict["TMP"] = MarketInfo("TMP", "Temp Csv file", "temp load from csv file", "000001", Null(), @@ -157,7 +161,6 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); - m_initializing = false; } void StockManager::setKDataDriver(const KDataDriverConnectPoolPtr& driver) { @@ -203,8 +206,6 @@ void StockManager::loadAllKData() { } } } - - initInnerTask(); } void StockManager::reload() { From 031f63bece0464d25b963303e7b14de45aa94958 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 01:45:31 +0800 Subject: [PATCH 481/601] =?UTF-8?q?fxied=20TimerManager=20=E5=A4=96?= =?UTF-8?q?=E9=83=A8=E5=B7=A5=E4=BD=9C=E7=BB=84=E6=9E=84=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/TimerManager.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index ce6d4bcb..517dc0c5 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -43,7 +43,10 @@ public: * 指定线程池方式构造,以便共享其他线程池 * @param tg 指定任务组线程池 */ - TimerManager(const std::shared_ptr& tg) : m_tg(tg) {} + TimerManager(const std::shared_ptr& tg) + : m_stop(true), m_current_timer_id(-1), m_work_num(1), m_tg(tg) { + start(); + } /** 析构函数 */ ~TimerManager() { From ffced4604309fcef93f48fec5838466c30f1a0dc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 11:56:52 +0800 Subject: [PATCH 482/601] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=88=9D=E5=A7=8B?= =?UTF-8?q?=E5=8C=96=E4=B8=8Ereload=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index e024dcf4..6bbdf8a6 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -96,6 +96,10 @@ Parameter default_other_param() { void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockParam, const Parameter& kdataParam, const Parameter& preloadParam, const Parameter& hikyuuParam, const StrategyContext& context) { + HKU_WARN_IF_RETURN(m_initializing, void(), + "The last initialization has not finished. Please try again later!"); + m_initializing = true; + // 防止重复 init if (m_thread_id != std::thread::id()) { return; @@ -161,6 +165,7 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); + m_initializing = false; } void StockManager::setKDataDriver(const KDataDriverConnectPoolPtr& driver) { @@ -195,7 +200,7 @@ void StockManager::loadAllKData() { } else { // 异步并行加载 - auto& tg = getGlobalTaskGroup(); + auto* tg = getGlobalTaskGroup(); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { for (size_t i = 0, len = ktypes.size(); i < len; i++) { const auto& low_ktype = low_ktypes[i]; @@ -209,8 +214,10 @@ void StockManager::loadAllKData() { } void StockManager::reload() { - loadAllHolidays(); + HKU_IF_RETURN(m_initializing, void()); + m_initializing = true; + loadAllHolidays(); loadAllMarketInfos(); loadAllStockTypeInfo(); loadAllStocks(); @@ -223,7 +230,7 @@ void StockManager::reload() { HKU_INFO("start reload kdata to buffer"); std::vector can_not_parallel_stk_list; // 记录不支持并行加载的Stock { - auto& tg = getGlobalTaskGroup(); + auto* tg = getGlobalTaskGroup(); std::lock_guard lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { auto driver = iter->second.getKDataDirver(); @@ -254,6 +261,7 @@ void StockManager::reload() { } loadHistoryFinance(); + m_initializing = false; } string StockManager::tmpdir() const { @@ -561,7 +569,7 @@ vector> StockManager::getHistoryFinanceAllFields() con } void StockManager::loadHistoryFinance() { - auto& tg = getGlobalTaskGroup(); + auto* tg = getGlobalTaskGroup(); std::lock_guard lock1(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { tg->submit([=]() { iter->second.getHistoryFinance(); }); From 266fb7210773656fefd6cf42a1f2c36a027a639b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 11:57:30 +0800 Subject: [PATCH 483/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20TimeManager=20?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=A4=96=E9=83=A8=E4=BB=BB=E5=8A=A1=E7=BB=84?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/utilities/TimerManager.h | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index 517dc0c5..93624d9e 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -35,7 +35,12 @@ public: * @param work_num 定时任务执行线程池线程数量 */ explicit TimerManager(size_t work_num = 1) - : m_stop(true), m_current_timer_id(-1), m_work_num(work_num) { + : m_stop(true), + m_current_timer_id(-1), + m_work_num(work_num), + m_tg(nullptr), + m_use_extend_tg(false) { + HKU_ASSERT(work_num >= 1); start(); } @@ -43,13 +48,15 @@ public: * 指定线程池方式构造,以便共享其他线程池 * @param tg 指定任务组线程池 */ - TimerManager(const std::shared_ptr& tg) - : m_stop(true), m_current_timer_id(-1), m_work_num(1), m_tg(tg) { + explicit TimerManager(ThreadPool* tg) + : m_stop(true), m_current_timer_id(-1), m_work_num(1), m_tg(tg), m_use_extend_tg(true) { + HKU_ASSERT(m_tg); start(); } /** 析构函数 */ ~TimerManager() { + HKU_DEBUG("~TimerManager"); stop(); for (auto iter = m_timers.begin(); iter != m_timers.end(); ++iter) { delete iter->second; @@ -69,7 +76,7 @@ public: std::priority_queue new_queue; m_queue.swap(new_queue); if (!m_tg) { - m_tg = std::make_shared(m_work_num); + m_tg = new ThreadPool(m_work_num); } /* @@ -157,9 +164,10 @@ public: m_detect_thread.join(); } - if (m_tg) { + if (!m_use_extend_tg && m_tg) { m_tg->stop(); - m_tg.reset(); + delete m_tg; + m_tg = nullptr; } } @@ -537,7 +545,8 @@ private: std::unordered_map m_timers; int m_current_timer_id; size_t m_work_num; // 任务执行线程池线程数量 - std::shared_ptr m_tg; + ThreadPool* m_tg{nullptr}; + bool m_use_extend_tg{false}; }; } // namespace hku \ No newline at end of file From 294df20d86b88439a0070e8a6a3b3de46b404872 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 12:00:57 +0800 Subject: [PATCH 484/601] =?UTF-8?q?=E5=AE=9A=E6=97=B6=E8=B0=83=E5=BA=A6?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E5=86=85=E9=83=A8=E5=85=AC=E5=85=B1=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 2 +- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 9 +++++---- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h | 2 +- hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp | 1 - hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp | 1 + 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 2552143b..0f6e0fcb 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -86,8 +86,8 @@ void GlobalInitializer::clean() { } #endif - releaseGlobalTaskGroup(); releaseScheduler(); + releaseGlobalTaskGroup(); releaseGlobalSpotAgent(); IndicatorImp::releaseDynEngine(); diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp index 5e5e94af..d7b7a1f3 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp @@ -13,9 +13,9 @@ namespace hku { -static std::shared_ptr g_threadPool; +static TaskGroup* g_threadPool; -std::shared_ptr& getGlobalTaskGroup() { +TaskGroup* getGlobalTaskGroup() { static std::once_flag oc; std::call_once(oc, [&]() { auto cpu_num = std::thread::hardware_concurrency(); @@ -24,7 +24,7 @@ std::shared_ptr& getGlobalTaskGroup() { } else if (cpu_num > 1) { cpu_num--; } - g_threadPool = std::make_shared(cpu_num); + g_threadPool = new TaskGroup(cpu_num); }); return g_threadPool; } @@ -33,7 +33,8 @@ void releaseGlobalTaskGroup() { HKU_TRACE("releaseGlobalTaskGroup"); if (g_threadPool) { g_threadPool->stop(); - g_threadPool.reset(); + delete g_threadPool; + g_threadPool = nullptr; } } diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h index 705d3e9d..72ab4bad 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h @@ -21,7 +21,7 @@ using TaskGroup = ThreadPool; * 获取全局线程池任务组 * @note 请使用 future 获取任务返回 */ -std::shared_ptr& getGlobalTaskGroup(); +TaskGroup* getGlobalTaskGroup(); template using task_handle = std::future; diff --git a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp index f899ce7d..a9622c1e 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp @@ -15,7 +15,6 @@ namespace hku { void initInnerTask() { auto* tm = getScheduler(); tm->addFuncAtTimeEveryDay(Datetime::min(), Datetime::max(), TimeDelta(), reloadHikyuuTask); - tm->start(); } void reloadHikyuuTask() { diff --git a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp index fc5bddf7..90d58f4e 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp @@ -17,6 +17,7 @@ static TimerManager *g_scheduler; TimerManager *getScheduler() { static std::once_flag oc; + // 使用内部公共任务组,减少内部线程 std::call_once(oc, [&]() { g_scheduler = new TimerManager(getGlobalTaskGroup()); }); return g_scheduler; } From bfe14c4f5abb12b29980265b31f144d1e1b62622 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 12:02:44 +0800 Subject: [PATCH 485/601] update --- hikyuu_cpp/hikyuu/Stock.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index 1561f88d..e8ee4445 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -278,7 +278,7 @@ struct HKU_API Stock::Data { mutable vector m_history_finance; // 历史财务信息 [财务报告日期, 字段1, 字段2, ...] - mutable bool m_history_finance_ready{false}; + mutable std::atomic_bool m_history_finance_ready{false}; mutable std::mutex m_history_finance_mutex; price_t m_tick; From ff79c1e061a76ad202e5dc9666b1fa084f32d9ca Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 12:34:12 +0800 Subject: [PATCH 486/601] =?UTF-8?q?=E5=8E=BB=E9=99=A4Strategy=E4=B8=8D?= =?UTF-8?q?=E5=BF=85=E8=A6=81=E7=9A=84=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/demo2.cpp | 12 +++++++++++ hikyuu_cpp/hikyuu/StockManager.cpp | 4 ++-- hikyuu_cpp/hikyuu/StrategyContext.cpp | 6 +++++- hikyuu_cpp/hikyuu/StrategyContext.h | 6 ++++-- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 11 +++++----- hikyuu_cpp/hikyuu/strategy/Strategy.h | 24 ---------------------- hikyuu_cpp/hikyuu/utilities/TimerManager.h | 2 +- hikyuu_pywrap/strategy/_Strategy.cpp | 8 ++------ 8 files changed, 32 insertions(+), 41 deletions(-) diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp index c7404f44..022c0442 100644 --- a/hikyuu_cpp/demo/demo2.cpp +++ b/hikyuu_cpp/demo/demo2.cpp @@ -15,6 +15,12 @@ static void changed(const Stock& stk, const SpotRecord& spot) { HKU_INFO("{} {} 当前收盘价: {}", stk.market_code(), stk.name(), spot.close); } +static void changed2(const Stock& stk, const SpotRecord& spot) { + if (stk.market_code() == "SZ000001") { + HKU_INFO("strategy 2 process sz000001"); + } +} + static void my_process1() { HKU_INFO("{}", getStock("sh000001")); } @@ -41,6 +47,12 @@ int main(int argc, char* argv[]) { // 每日定点执行 stg.runDailyAt(my_process2, Datetime::now() - Datetime::today() + Seconds(20)); + auto t = std::thread([]() { + Strategy stg2("stratege2"); + stg2.onChange(changed2); + stg2.start(); + }); + // 启动策略 stg.start(); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 6bbdf8a6..a7db183f 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -98,14 +98,14 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa const Parameter& hikyuuParam, const StrategyContext& context) { HKU_WARN_IF_RETURN(m_initializing, void(), "The last initialization has not finished. Please try again later!"); - m_initializing = true; // 防止重复 init if (m_thread_id != std::thread::id()) { return; } - + m_initializing = true; m_thread_id = std::this_thread::get_id(); + m_baseInfoDriverParam = baseInfoParam; m_blockDriverParam = blockParam; m_kdataDriverParam = kdataParam; diff --git a/hikyuu_cpp/hikyuu/StrategyContext.cpp b/hikyuu_cpp/hikyuu/StrategyContext.cpp index 97256913..6a730f8e 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.cpp +++ b/hikyuu_cpp/hikyuu/StrategyContext.cpp @@ -29,11 +29,15 @@ void StrategyContext::setKTypeList(const vector& ktypeList) { }); } -bool StrategyContext::isAll() const { +bool StrategyContext::isAll() const noexcept { return std::find_if(m_stockCodeList.begin(), m_stockCodeList.end(), [](string val) { to_upper(val); return val == "ALL"; }) != m_stockCodeList.end(); } +bool StrategyContext::isValid() const noexcept { + return m_stockCodeList.empty() || m_ktypeList.empty(); +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index de34b2af..8eb91a5f 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -24,9 +24,11 @@ public: virtual ~StrategyContext() = default; - bool isAll() const; + bool isAll() const noexcept; - Datetime startDatetime() const { + bool isValid() const noexcept; + + Datetime startDatetime() const noexcept { return m_startDatetime; } diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index e3b4033e..d5e5d460 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -16,8 +16,7 @@ #include "hikyuu/hikyuu.h" #include "Strategy.h" -// python 中运行拉回主线程循环 -// c++ 则直接执行(通常在定时调度的工作线程中执行) +// python 中运行拉回主线程循环,非 python 环境则直接执行 #define EVENT(func) \ if (runningInPython()) { \ event(func); \ @@ -66,9 +65,6 @@ Strategy::~Strategy() { void Strategy::run() { CLS_IF_RETURN(m_running, void()); - CLS_CHECK(!getStockCodeList().empty(), "The context does not contain any stocks!"); - CLS_CHECK(!getKTypeList().empty(), "The K type list was empty!"); - StockManager& sm = StockManager::instance(); // sm 尚未初始化,则初始化 @@ -80,8 +76,13 @@ void Strategy::run() { // 初始化 hikyuu_init(m_config_file, false, m_context); + } else { + m_context = sm.getStrategyContext(); } + CLS_CHECK(!m_context.getStockCodeList().empty(), "The context does not contain any stocks!"); + CLS_CHECK(!m_context.getKTypeList().empty(), "The K type list was empty!"); + // 先将行情接收代理停止,以便后面加入处理函数 stopSpotAgent(); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index e5154129..4d389da8 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -44,30 +44,6 @@ public: return m_context; } - void context(const StrategyContext& context) { - m_context = context; - } - - void setStockCodeList(vector&& stockList) { - m_context.setStockCodeList(std::move(stockList)); - } - - void setStockCodeList(const vector& stockList) { - m_context.setStockCodeList(stockList); - } - - const vector& getStockCodeList() const { - return m_context.getStockCodeList(); - } - - void setKTypeList(const vector& ktypeList) { - m_context.setKTypeList(ktypeList); - } - - const vector& getKTypeList() const { - return m_context.getKTypeList(); - } - /** * 每日开盘时间内,以 delta 为周期循环定时执行指定任务 * @param func 待执行的任务 diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index 93624d9e..522caf56 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -46,6 +46,7 @@ public: /** * 指定线程池方式构造,以便共享其他线程池 + * @note 请自行保证 tg 的生命周期在 TimerManager 存活期间始终有效 * @param tg 指定任务组线程池 */ explicit TimerManager(ThreadPool* tg) @@ -56,7 +57,6 @@ public: /** 析构函数 */ ~TimerManager() { - HKU_DEBUG("~TimerManager"); stop(); for (auto iter = m_timers.begin(); iter != m_timers.end(); ++iter) { delete iter->second; diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index e0acea3e..f2ea4bd9 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -23,12 +23,8 @@ void export_Strategy(py::module& m) { py::overload_cast(&Strategy::name), py::return_value_policy::copy, "策略名称") - .def_property("stock_list", py::overload_cast<>(&Strategy::getStockCodeList, py::const_), - py::overload_cast&>(&Strategy::setStockCodeList), - py::return_value_policy::copy, "股票代码列表") - .def_property("ktype_list", py::overload_cast<>(&Strategy::getKTypeList, py::const_), - py::overload_cast&>(&Strategy::setKTypeList), - py::return_value_policy::copy, "需要的K线类型") + .def_property_readonly("context", &Strategy::context, py::return_value_policy::copy, + "策略上下文") .def("start", &Strategy::start) .def("on_change", From 2494d692274eb6598b217b7dd96f14180ba02178 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 12:46:42 +0800 Subject: [PATCH 487/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/demo2.cpp | 3 +++ hikyuu_cpp/hikyuu/strategy/Strategy.h | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp index 022c0442..dc6329a7 100644 --- a/hikyuu_cpp/demo/demo2.cpp +++ b/hikyuu_cpp/demo/demo2.cpp @@ -48,6 +48,9 @@ int main(int argc, char* argv[]) { stg.runDailyAt(my_process2, Datetime::now() - Datetime::today() + Seconds(20)); auto t = std::thread([]() { + // 以线程的方式执行另一个策略 + // 注意:同一进程内的所有 strategy 共享的是同一个上下文, + // 即使后续创建的 strategy 指定了新的上下文,但不会生效!!! Strategy stg2("stratege2"); stg2.onChange(changed2); stg2.start(); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 4d389da8..2f0d13c3 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -64,7 +64,7 @@ public: /** * 正确数据发生变化调用,即接收到相应行情数据变更 - * @note 通常用于调试 + * @note 通常用于调试。且只要收到行情采集消息就会触发,不受开、闭市时间限制 * @param stk 数据发生变化的 stock * @param spot 接收到的具体数据 */ @@ -73,6 +73,7 @@ public: /** * 一批行情数据接受完毕后通知 * @note 通常仅用于调试打印,该批行情数据中不一定含有上下文中包含的 stock + * 且只要收到行情采集消息就会触发,不受开、闭市时间限制。 */ void onReceivedSpot(std::function&& recievedFucn); From 09bf7b6c776f8d12e5fcbf3b1e49f29738f3b13b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 13:54:09 +0800 Subject: [PATCH 488/601] try mocosx --- .github/workflows/macos.yml | 53 +++++++++++++++++++++++++++++++++++++ hikyuu_cpp/hikyuu/xmake.lua | 6 +++-- 2 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/macos.yml diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml new file mode 100644 index 00000000..beffab0a --- /dev/null +++ b/.github/workflows/macos.yml @@ -0,0 +1,53 @@ +name: macOS (x86_64) + +on: + push: + branches: + - main + pull_request: + branches: + - main + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [macos-12] + arch: [x86_64, arm64] + kind: [shared] + + runs-on: ${{ matrix.os }} + + concurrency: + group: ${{ github.ref }}-${{ github.base_ref }}-${{ github.head_ref }}-macOS-${{ matrix.arch }}-${{ matrix.kind }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v4 + + - name: Cache packages + id: cache-xmake-macosx + uses: actions/cache@v4 + env: + cache-name: cache-node-modules + with: + path: ~/.xmake + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: 2.9.4 + actions-cache-folder: '.xmake-cache' + actions-cache-key: 'macosx-x64' + + - name: config + run: | + xmake f -c -k ${{ matrix.kind }} -y -vD --mysql=n --hdf5=n + + - name: build + run: | + xmake -bvD core diff --git a/hikyuu_cpp/hikyuu/xmake.lua b/hikyuu_cpp/hikyuu/xmake.lua index 0b87ce53..b0c91519 100644 --- a/hikyuu_cpp/hikyuu/xmake.lua +++ b/hikyuu_cpp/hikyuu/xmake.lua @@ -72,15 +72,17 @@ target("hikyuu") -- add files -- add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp") - add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/*.cpp|utilities/mo/*.cpp") + add_files("./**.cpp|data_driver/**.cpp|utilities/db_connect/mysql/**.cpp|utilities/mo/**.cpp") add_files("./data_driver/*.cpp") if get_config("hdf5") or get_config("sqlite") then add_files("./data_driver/base_info/sqlite/**.cpp") + add_files("./data_driver/block_info/sqlite/**.cpp") end if get_config("mysql") then add_files("./data_driver/base_info/mysql/**.cpp") + add_files("./data_driver/block_info/mysql/**.cpp") end - add_files("./data_driver/block_info/**.cpp") + add_files("./data_driver/block_info/qianlong/**.cpp") add_files("./data_driver/kdata/cvs/**.cpp") if get_config("sqlite") then add_files("./data_driver/kdata/sqlite/**.cpp") From d8d6387b322d05839aeafd9becd8802f9189080b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 14:13:44 +0800 Subject: [PATCH 489/601] update --- hikyuu_cpp/demo/demo2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp index dc6329a7..ea76e49e 100644 --- a/hikyuu_cpp/demo/demo2.cpp +++ b/hikyuu_cpp/demo/demo2.cpp @@ -50,7 +50,7 @@ int main(int argc, char* argv[]) { auto t = std::thread([]() { // 以线程的方式执行另一个策略 // 注意:同一进程内的所有 strategy 共享的是同一个上下文, - // 即使后续创建的 strategy 指定了新的上下文,但不会生效!!! + // 即使后续创建的 strategy 指定了新的上下文,但不会生效!!! Strategy stg2("stratege2"); stg2.onChange(changed2); stg2.start(); From fcae79f53ee7877b88088a6c3d0f309f5980a4a3 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 14:16:05 +0800 Subject: [PATCH 490/601] update macosx.yml --- .github/workflows/macos.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index beffab0a..e399327f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -3,10 +3,10 @@ name: macOS (x86_64) on: push: branches: - - main + - master pull_request: branches: - - main + - master jobs: build: From 89b492ed797ae08cd20d0ce0e24086b4ee9c31f8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 14:45:13 +0800 Subject: [PATCH 491/601] remove unused var --- hikyuu_cpp/hikyuu/KDataImp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/KDataImp.cpp b/hikyuu_cpp/hikyuu/KDataImp.cpp index df4e5f9b..18618c0f 100644 --- a/hikyuu_cpp/hikyuu/KDataImp.cpp +++ b/hikyuu_cpp/hikyuu/KDataImp.cpp @@ -238,7 +238,6 @@ void KDataImp::_recoverBackward() { Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); StockWeightList weightList = m_stock.getWeight(start_date, end_date); StockWeightList::const_reverse_iterator weightIter = weightList.rbegin(); - StockWeightList::const_reverse_iterator pre_weightIter; size_t pre_pos = total - 1; for (; weightIter != weightList.rend(); ++weightIter) { From 5f13c8b840ca5c08fd236530719f7a9b61a8b643 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 14:49:24 +0800 Subject: [PATCH 492/601] update --- .github/workflows/macos_arm64.yml | 53 +++++++++++++++++++ .../workflows/{macos.yml => macos_x86_64.yml} | 2 +- hikyuu_pywrap/xmake.lua | 2 +- 3 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/macos_arm64.yml rename .github/workflows/{macos.yml => macos_x86_64.yml} (97%) diff --git a/.github/workflows/macos_arm64.yml b/.github/workflows/macos_arm64.yml new file mode 100644 index 00000000..c2f6409a --- /dev/null +++ b/.github/workflows/macos_arm64.yml @@ -0,0 +1,53 @@ +name: macOS (x86_64) + +on: + push: + branches: + - master + pull_request: + branches: + - master + +jobs: + build: + strategy: + fail-fast: false + matrix: + os: [macos-14] + arch: [arm64] + kind: [shared] + + runs-on: ${{ matrix.os }} + + concurrency: + group: ${{ github.ref }}-${{ github.base_ref }}-${{ github.head_ref }}-macOS-${{ matrix.arch }}-${{ matrix.kind }} + cancel-in-progress: true + steps: + - uses: actions/checkout@v4 + + - name: Cache packages + id: cache-xmake-macosx + uses: actions/cache@v4 + env: + cache-name: cache-node-modules + with: + path: ~/.xmake + key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-build- + ${{ runner.os }}- + + - uses: xmake-io/github-action-setup-xmake@v1 + with: + xmake-version: 2.9.4 + actions-cache-folder: '.xmake-cache' + actions-cache-key: 'macosx-arm64' + + - name: config + run: | + xmake f -c -k ${{ matrix.kind }} -y -vD --mysql=n --hdf5=n + + - name: build + run: | + xmake -bvD core diff --git a/.github/workflows/macos.yml b/.github/workflows/macos_x86_64.yml similarity index 97% rename from .github/workflows/macos.yml rename to .github/workflows/macos_x86_64.yml index e399327f..7c6cf07f 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos_x86_64.yml @@ -14,7 +14,7 @@ jobs: fail-fast: false matrix: os: [macos-12] - arch: [x86_64, arm64] + arch: [x86_64] kind: [shared] runs-on: ${{ matrix.os }} diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index 9a942a58..fbdf9c27 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -33,7 +33,7 @@ target("core") add_includedirs("../hikyuu_cpp") add_files("./**.cpp") - add_rpathdirs("$ORIGIN", "$ORIGIN/lib", "$ORIGIN/../lib") + add_rpathdirs("$ORIGIN") on_load("windows", "linux", "macosx", function(target) import("lib.detect.find_tool") From 2f2d989b74014af4e325de5503d70004ea409153 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 14:59:00 +0800 Subject: [PATCH 493/601] update for macosx --- hikyuu_pywrap/xmake.lua | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/hikyuu_pywrap/xmake.lua b/hikyuu_pywrap/xmake.lua index fbdf9c27..1e4ae46c 100644 --- a/hikyuu_pywrap/xmake.lua +++ b/hikyuu_pywrap/xmake.lua @@ -30,11 +30,20 @@ target("core") add_cxflags("-Wno-error=parentheses-equality -Wno-error=missing-braces") end + if is_plat("linux", "cross") then + add_rpathdirs("$ORIGIN", "$ORIGIN/cpp") + end + + if is_plat("macosx") then + add_linkdirs("/usr/lib") + + -- macosx 下不能主动链接 python,所以需要使用如下编译选项 + add_shflags("-undefined dynamic_lookup") + end + add_includedirs("../hikyuu_cpp") add_files("./**.cpp") - add_rpathdirs("$ORIGIN") - on_load("windows", "linux", "macosx", function(target) import("lib.detect.find_tool") if is_plat("windows") then From 7c980545e0199b9ef625874d7e3bdf0d19afd74d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 15:00:52 +0800 Subject: [PATCH 494/601] update for macosx github action --- .github/workflows/macos_arm64.yml | 2 +- .github/workflows/macos_x86_64.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/macos_arm64.yml b/.github/workflows/macos_arm64.yml index c2f6409a..3045d16c 100644 --- a/.github/workflows/macos_arm64.yml +++ b/.github/workflows/macos_arm64.yml @@ -1,4 +1,4 @@ -name: macOS (x86_64) +name: macOS_arm64 on: push: diff --git a/.github/workflows/macos_x86_64.yml b/.github/workflows/macos_x86_64.yml index 7c6cf07f..fd9ab682 100644 --- a/.github/workflows/macos_x86_64.yml +++ b/.github/workflows/macos_x86_64.yml @@ -1,4 +1,4 @@ -name: macOS (x86_64) +name: macOS_x86_64 on: push: From 679a52edd0e3902c4f9d745ac9300648be88828a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 15:08:22 +0800 Subject: [PATCH 495/601] =?UTF-8?q?=E7=A7=BB=E9=99=A4=E5=A4=9A=E4=BD=99?= =?UTF-8?q?=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KDataImp.cpp | 1 - xmake.lua | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/KDataImp.cpp b/hikyuu_cpp/hikyuu/KDataImp.cpp index 18618c0f..bcca0799 100644 --- a/hikyuu_cpp/hikyuu/KDataImp.cpp +++ b/hikyuu_cpp/hikyuu/KDataImp.cpp @@ -359,7 +359,6 @@ void KDataImp::_recoverEqualBackward() { Datetime end_date(m_buffer.back().datetime.date() + bd::days(1)); StockWeightList weightList = m_stock.getWeight(start_date, end_date); StockWeightList::const_reverse_iterator weightIter = weightList.rbegin(); - StockWeightList::const_reverse_iterator pre_weightIter; size_t pre_pos = total - 1; for (; weightIter != weightList.rend(); ++weightIter) { diff --git a/xmake.lua b/xmake.lua index f4c69673..4c51ea60 100644 --- a/xmake.lua +++ b/xmake.lua @@ -208,7 +208,7 @@ if is_plat("windows") then end end -if not is_plat("windows") then +if is_plat("linux", "cross", "macosx") then -- disable some compiler errors add_cxflags("-Wno-error=deprecated-declarations", "-fno-strict-aliasing") add_cxflags("-ftemplate-depth=1023", "-pthread") From d8cb10e91fe9c7f5f2cf0f4aa326ec00564fe493 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 21 Aug 2024 16:33:07 +0800 Subject: [PATCH 496/601] update docs --- docs/source/trade_sys/signal.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/trade_sys/signal.rst b/docs/source/trade_sys/signal.rst index e5e079f6..b892b8f3 100644 --- a/docs/source/trade_sys/signal.rst +++ b/docs/source/trade_sys/signal.rst @@ -177,7 +177,7 @@ PF调仓周期买入信号指示器 p._x = self._x return p - def _calculate(self): + def _calculate(self, k): self._addBuySignal(Datetime(201201210000)) self._addSellSignal(Datetime(201201300000)) From 173324773a198ce75ec74fa29055085a7d3222fc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 22 Aug 2024 12:52:34 +0800 Subject: [PATCH 497/601] =?UTF-8?q?Datetime=20=E6=96=B0=E5=A2=9E=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20"20240822=2011:30:06.230"=20=E7=9A=84=E5=AD=97?= =?UTF-8?q?=E7=AC=A6=E4=B8=B2=E6=96=B9=E5=BC=8F=E6=9E=84=E9=80=A0=EF=BC=8C?= =?UTF-8?q?=E4=BB=A5=E4=BE=BF=E6=94=AF=E6=8C=81=E8=BF=85=E6=8A=95=E6=A0=BC?= =?UTF-8?q?=E5=BC=8F=E7=9A=84=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../hikyuu/utilities/datetime/Datetime.cpp | 32 ++++++++++++++----- .../hikyuu/utilities/datetime/Datetime.h | 1 + .../utilities/datetime/test_Datetime.cpp | 1 + 3 files changed, 26 insertions(+), 8 deletions(-) diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp index b072ece4..ef4a54cf 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.cpp @@ -93,22 +93,38 @@ Datetime::Datetime(unsigned long long datetime) { } Datetime::Datetime(const std::string &ts) { + HKU_CHECK(ts.size() >= 8, "Invalid datetime str: {}", ts); + std::string timeStr(ts); trim(timeStr); if ("+infinity" == timeStr) { m_data = bt::ptime(bd::date(bd::pos_infin), bt::time_duration(0, 0, 0)); - } else if (timeStr.size() <= 10) { - auto pos1 = timeStr.rfind("-"); - auto pos2 = timeStr.rfind("/"); + return; + } + + to_upper(timeStr); + auto pos = timeStr.find('T'); + if (pos != std::string::npos) { + m_data = bt::from_iso_string(timeStr); + return; + } + + pos = timeStr.find(' '); + auto pos1 = timeStr.find('-'); + auto pos2 = timeStr.find('/'); + if (pos == std::string::npos) { m_data = (pos1 != std::string::npos || pos2 != std::string::npos) ? bt::ptime(bd::from_string(timeStr), bt::time_duration(0, 0, 0)) : bt::ptime(bd::from_undelimited_string(timeStr), bt::time_duration(0, 0, 0)); - } else { - to_upper(timeStr); - auto pos = timeStr.find("T"); - m_data = - (pos != std::string::npos) ? bt::from_iso_string(timeStr) : bt::time_from_string(timeStr); + return; } + + auto date_str = timeStr.substr(0, pos); + auto time_str = timeStr.substr(pos + 1); + m_data = + (pos1 != std::string::npos || pos2 != std::string::npos) + ? bt::time_from_string(timeStr) + : bt::ptime(bd::from_undelimited_string(date_str), bt::duration_from_string(time_str)); } bool Datetime::isNull() const { diff --git a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h index 7da8ccdc..9cd2c01b 100644 --- a/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h +++ b/hikyuu_cpp/hikyuu/utilities/datetime/Datetime.h @@ -106,6 +106,7 @@ public: * 2、"20010101" * 3、"2001-01-01 18:00:00.12345" * 4、"20010101T181159" + * 5、"20240822 11:30:06.230" * */ explicit Datetime(const std::string &); diff --git a/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp b/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp index 4f252ff5..b4a14bfa 100644 --- a/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/utilities/datetime/test_Datetime.cpp @@ -61,6 +61,7 @@ TEST_CASE("test_Datetime") { CHECK(Datetime("2001/1/2 3:4:5") == d); CHECK(Datetime("20010102T030405") == d); CHECK(Datetime("20010102t030405") == d); + CHECK(Datetime("20010102 3:4:5") == d); CHECK_THROWS(Datetime("2001")); /** @arg 非法年份 */ From 5ed61e782f92638e3f4a64cb926b4c6926c3db3e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 22 Aug 2024 18:05:35 +0800 Subject: [PATCH 498/601] =?UTF-8?q?qmt=20=E8=A1=8C=E6=83=85=E9=87=87?= =?UTF-8?q?=E9=9B=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 49 ++++ hikyuu/gui/HikyuuTDX.py | 3 +- hikyuu/gui/data/MainWindow.py | 256 ++++++++--------- hikyuu/gui/data/MainWindow.ui | 370 +++++++++++++------------ hikyuu/gui/spot_server.py | 23 +- hikyuu/gui/start_qmt.py | 36 +++ hikyuu/interactive.py | 29 +- 7 files changed, 456 insertions(+), 310 deletions(-) create mode 100644 hikyuu/fetcher/stock/zh_stock_a_qmt.py create mode 100644 hikyuu/gui/start_qmt.py diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py new file mode 100644 index 00000000..01d21575 --- /dev/null +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -0,0 +1,49 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# +# Create on: 2024-08-22 +# Author: fasiondog + +from xtquant import xtdata +from hikyuu import Datetime +from hikyuu.util import * + + +@hku_catch(trace=True, callback=lambda stk: hku_warn("Failed parse stk: {}", stk)) +def parse_one_result_qmt(stk_code: str, data: dict): + '''将 qmt tick 数据转为 spot 数据 + + :param str stk_code: qmt 风格的证券编码,如 000001.SZ + :param dict data: 对应的 qmt 全推 tick 数据 + ''' + result = {} + code, market = stk_code.split('.') + result['market'] = market + result['code'] = code + result['name'] = '' + result['datetime'] = Datetime(data['timetag']) if 'timetag' in data else xtdata.timetag_to_datetime( + data['time'], "%Y-%m-%d %H:%M:%S") + + result['yesterday_close'] = data['lastClose'] + result['open'] = data['open'] + result['high'] = data['high'] + result['low'] = data['low'] + result['close'] = data['lastPrice'] + result['amount'] = data['amount'] * 0.001 # 转千元 + result['volume'] = data['pvolume'] * 0.01 # 转手数 + + for i in range(5): + result[f'bid{i+1}'] = data['bidPrice'][i] + result[f'bid{i+1}_amount'] = data['bidVol'][i] + result[f'ask{i+1}'] = data['askPrice'][i] + result[f'ask{i+1}_amount'] = data['askVol'][i] + return result + + +def get_spot(stocklist, unused1, unused2, batch_func=None): + code_list = [f'{s.code}.{s.market}' for s in stocklist] + full_tick = xtdata.get_full_tick(code_list) + records = [parse_one_result_qmt(code, data) for code, data in full_tick.items()] + if batch_func is not None: + batch_func(records) + return records diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index 9aba0f83..4712b511 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -81,7 +81,8 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): self.hdf5_import_thread.stop() if self.escape_time_thread: self.escape_time_thread.stop() - self.mp_log_q_lisener.stop() + if hasattr(self, 'mp_log_q_lisener'): + self.mp_log_q_lisener.stop() event.accept() def getUserConfigDir(self): diff --git a/hikyuu/gui/data/MainWindow.py b/hikyuu/gui/data/MainWindow.py index 8d1014db..bed6eabd 100644 --- a/hikyuu/gui/data/MainWindow.py +++ b/hikyuu/gui/data/MainWindow.py @@ -2,7 +2,7 @@ # Form implementation generated from reading ui file 'MainWindow.ui' # -# Created by: PyQt5 UI code generator 5.15.6 +# Created by: PyQt5 UI code generator 5.15.10 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. @@ -343,122 +343,121 @@ class Ui_MainWindow(object): self.save_pushButton.setGeometry(QtCore.QRect(390, 190, 75, 23)) self.save_pushButton.setObjectName("save_pushButton") self.layoutWidget4 = QtWidgets.QWidget(self.groupBox_6) - self.layoutWidget4.setGeometry(QtCore.QRect(40, 80, 321, 282)) + self.layoutWidget4.setGeometry(QtCore.QRect(40, 80, 321, 296)) self.layoutWidget4.setObjectName("layoutWidget4") self.gridLayout_6 = QtWidgets.QGridLayout(self.layoutWidget4) self.gridLayout_6.setContentsMargins(0, 0, 0, 0) self.gridLayout_6.setObjectName("gridLayout_6") - self.preload_day_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_day_checkBox.setObjectName("preload_day_checkBox") - self.gridLayout_6.addWidget(self.preload_day_checkBox, 0, 0, 1, 1) + self.preload_week_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_week_checkBox.setObjectName("preload_week_checkBox") + self.gridLayout_6.addWidget(self.preload_week_checkBox, 1, 0, 1, 1) self.label_24 = QtWidgets.QLabel(self.layoutWidget4) self.label_24.setObjectName("label_24") self.gridLayout_6.addWidget(self.label_24, 0, 1, 1, 1) + self.preload_day_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_day_checkBox.setObjectName("preload_day_checkBox") + self.gridLayout_6.addWidget(self.preload_day_checkBox, 0, 0, 1, 1) + self.preload_min15_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_min15_checkBox.setObjectName("preload_min15_checkBox") + self.gridLayout_6.addWidget(self.preload_min15_checkBox, 8, 0, 1, 1) + self.preload_month_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_month_checkBox.setObjectName("preload_month_checkBox") + self.gridLayout_6.addWidget(self.preload_month_checkBox, 2, 0, 1, 1) self.preload_day_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) self.preload_day_spinBox.setMaximum(999999) self.preload_day_spinBox.setObjectName("preload_day_spinBox") self.gridLayout_6.addWidget(self.preload_day_spinBox, 0, 2, 1, 1) - self.preload_week_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_week_checkBox.setObjectName("preload_week_checkBox") - self.gridLayout_6.addWidget(self.preload_week_checkBox, 1, 0, 1, 1) - self.label_25 = QtWidgets.QLabel(self.layoutWidget4) - self.label_25.setObjectName("label_25") - self.gridLayout_6.addWidget(self.label_25, 1, 1, 1, 1) - self.preload_week_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_week_spinBox.setMaximum(999999) - self.preload_week_spinBox.setObjectName("preload_week_spinBox") - self.gridLayout_6.addWidget(self.preload_week_spinBox, 1, 2, 1, 1) - self.preload_month_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_month_checkBox.setObjectName("preload_month_checkBox") - self.gridLayout_6.addWidget(self.preload_month_checkBox, 2, 0, 1, 1) - self.label_26 = QtWidgets.QLabel(self.layoutWidget4) - self.label_26.setObjectName("label_26") - self.gridLayout_6.addWidget(self.label_26, 2, 1, 1, 1) - self.preload_month_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_month_spinBox.setMaximum(999999) - self.preload_month_spinBox.setObjectName("preload_month_spinBox") - self.gridLayout_6.addWidget(self.preload_month_spinBox, 2, 2, 1, 1) - self.preload_quarter_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_quarter_checkBox.setObjectName("preload_quarter_checkBox") - self.gridLayout_6.addWidget(self.preload_quarter_checkBox, 3, 0, 1, 1) - self.label_27 = QtWidgets.QLabel(self.layoutWidget4) - self.label_27.setObjectName("label_27") - self.gridLayout_6.addWidget(self.label_27, 3, 1, 1, 1) - self.preload_quarter_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_quarter_spinBox.setMaximum(999999) - self.preload_quarter_spinBox.setObjectName("preload_quarter_spinBox") - self.gridLayout_6.addWidget(self.preload_quarter_spinBox, 3, 2, 1, 1) - self.preload_halfyear_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_halfyear_checkBox.setObjectName("preload_halfyear_checkBox") - self.gridLayout_6.addWidget(self.preload_halfyear_checkBox, 4, 0, 1, 1) - self.label_28 = QtWidgets.QLabel(self.layoutWidget4) - self.label_28.setObjectName("label_28") - self.gridLayout_6.addWidget(self.label_28, 4, 1, 1, 1) + self.preload_min5_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_min5_checkBox.setObjectName("preload_min5_checkBox") + self.gridLayout_6.addWidget(self.preload_min5_checkBox, 7, 0, 1, 1) self.preload_halfyear_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) self.preload_halfyear_spinBox.setMaximum(999999) self.preload_halfyear_spinBox.setObjectName("preload_halfyear_spinBox") self.gridLayout_6.addWidget(self.preload_halfyear_spinBox, 4, 2, 1, 1) - self.preload_year_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_year_checkBox.setObjectName("preload_year_checkBox") - self.gridLayout_6.addWidget(self.preload_year_checkBox, 5, 0, 1, 1) - self.label_29 = QtWidgets.QLabel(self.layoutWidget4) - self.label_29.setObjectName("label_29") - self.gridLayout_6.addWidget(self.label_29, 5, 1, 1, 1) - self.preload_year_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_year_spinBox.setMaximum(999999) - self.preload_year_spinBox.setObjectName("preload_year_spinBox") - self.gridLayout_6.addWidget(self.preload_year_spinBox, 5, 2, 1, 1) - self.preload_min1_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_min1_checkBox.setObjectName("preload_min1_checkBox") - self.gridLayout_6.addWidget(self.preload_min1_checkBox, 6, 0, 1, 1) self.label_30 = QtWidgets.QLabel(self.layoutWidget4) self.label_30.setObjectName("label_30") self.gridLayout_6.addWidget(self.label_30, 6, 1, 1, 1) - self.preload_min1_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_min1_spinBox.setMaximum(999999) - self.preload_min1_spinBox.setObjectName("preload_min1_spinBox") - self.gridLayout_6.addWidget(self.preload_min1_spinBox, 6, 2, 1, 1) - self.preload_min5_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_min5_checkBox.setObjectName("preload_min5_checkBox") - self.gridLayout_6.addWidget(self.preload_min5_checkBox, 7, 0, 1, 1) - self.label_31 = QtWidgets.QLabel(self.layoutWidget4) - self.label_31.setObjectName("label_31") - self.gridLayout_6.addWidget(self.label_31, 7, 1, 1, 1) - self.preload_min5_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_min5_spinBox.setMaximum(999999) - self.preload_min5_spinBox.setObjectName("preload_min5_spinBox") - self.gridLayout_6.addWidget(self.preload_min5_spinBox, 7, 2, 1, 1) - self.preload_min15_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_min15_checkBox.setObjectName("preload_min15_checkBox") - self.gridLayout_6.addWidget(self.preload_min15_checkBox, 8, 0, 1, 1) self.label_32 = QtWidgets.QLabel(self.layoutWidget4) self.label_32.setObjectName("label_32") self.gridLayout_6.addWidget(self.label_32, 8, 1, 1, 1) - self.preload_min15_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) - self.preload_min15_spinBox.setMaximum(999999) - self.preload_min15_spinBox.setObjectName("preload_min15_spinBox") - self.gridLayout_6.addWidget(self.preload_min15_spinBox, 8, 2, 1, 1) + self.label_26 = QtWidgets.QLabel(self.layoutWidget4) + self.label_26.setObjectName("label_26") + self.gridLayout_6.addWidget(self.label_26, 2, 1, 1, 1) + self.preload_quarter_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_quarter_spinBox.setMaximum(999999) + self.preload_quarter_spinBox.setObjectName("preload_quarter_spinBox") + self.gridLayout_6.addWidget(self.preload_quarter_spinBox, 3, 2, 1, 1) + self.preload_min60_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_min60_checkBox.setObjectName("preload_min60_checkBox") + self.gridLayout_6.addWidget(self.preload_min60_checkBox, 10, 0, 1, 1) + self.preload_min1_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_min1_checkBox.setObjectName("preload_min1_checkBox") + self.gridLayout_6.addWidget(self.preload_min1_checkBox, 6, 0, 1, 1) + self.preload_week_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_week_spinBox.setMaximum(999999) + self.preload_week_spinBox.setObjectName("preload_week_spinBox") + self.gridLayout_6.addWidget(self.preload_week_spinBox, 1, 2, 1, 1) + self.preload_halfyear_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_halfyear_checkBox.setObjectName("preload_halfyear_checkBox") + self.gridLayout_6.addWidget(self.preload_halfyear_checkBox, 4, 0, 1, 1) self.preload_min30_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) self.preload_min30_checkBox.setObjectName("preload_min30_checkBox") self.gridLayout_6.addWidget(self.preload_min30_checkBox, 9, 0, 1, 1) - self.label_33 = QtWidgets.QLabel(self.layoutWidget4) - self.label_33.setObjectName("label_33") - self.gridLayout_6.addWidget(self.label_33, 9, 1, 1, 1) + self.label_29 = QtWidgets.QLabel(self.layoutWidget4) + self.label_29.setObjectName("label_29") + self.gridLayout_6.addWidget(self.label_29, 5, 1, 1, 1) self.preload_min30_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) self.preload_min30_spinBox.setMaximum(999999) self.preload_min30_spinBox.setObjectName("preload_min30_spinBox") self.gridLayout_6.addWidget(self.preload_min30_spinBox, 9, 2, 1, 1) - self.preload_min60_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) - self.preload_min60_checkBox.setObjectName("preload_min60_checkBox") - self.gridLayout_6.addWidget(self.preload_min60_checkBox, 10, 0, 1, 1) self.label_34 = QtWidgets.QLabel(self.layoutWidget4) self.label_34.setObjectName("label_34") self.gridLayout_6.addWidget(self.label_34, 10, 1, 1, 1) + self.preload_min5_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_min5_spinBox.setMaximum(999999) + self.preload_min5_spinBox.setObjectName("preload_min5_spinBox") + self.gridLayout_6.addWidget(self.preload_min5_spinBox, 7, 2, 1, 1) self.preload_min60_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) self.preload_min60_spinBox.setMaximum(999999) self.preload_min60_spinBox.setObjectName("preload_min60_spinBox") self.gridLayout_6.addWidget(self.preload_min60_spinBox, 10, 2, 1, 1) - + self.preload_year_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_year_spinBox.setMaximum(999999) + self.preload_year_spinBox.setObjectName("preload_year_spinBox") + self.gridLayout_6.addWidget(self.preload_year_spinBox, 5, 2, 1, 1) + self.preload_month_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_month_spinBox.setMaximum(999999) + self.preload_month_spinBox.setObjectName("preload_month_spinBox") + self.gridLayout_6.addWidget(self.preload_month_spinBox, 2, 2, 1, 1) + self.label_25 = QtWidgets.QLabel(self.layoutWidget4) + self.label_25.setObjectName("label_25") + self.gridLayout_6.addWidget(self.label_25, 1, 1, 1, 1) + self.label_33 = QtWidgets.QLabel(self.layoutWidget4) + self.label_33.setObjectName("label_33") + self.gridLayout_6.addWidget(self.label_33, 9, 1, 1, 1) + self.label_27 = QtWidgets.QLabel(self.layoutWidget4) + self.label_27.setObjectName("label_27") + self.gridLayout_6.addWidget(self.label_27, 3, 1, 1, 1) + self.label_28 = QtWidgets.QLabel(self.layoutWidget4) + self.label_28.setObjectName("label_28") + self.gridLayout_6.addWidget(self.label_28, 4, 1, 1, 1) + self.label_31 = QtWidgets.QLabel(self.layoutWidget4) + self.label_31.setObjectName("label_31") + self.gridLayout_6.addWidget(self.label_31, 7, 1, 1, 1) + self.preload_min1_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_min1_spinBox.setMaximum(999999) + self.preload_min1_spinBox.setObjectName("preload_min1_spinBox") + self.gridLayout_6.addWidget(self.preload_min1_spinBox, 6, 2, 1, 1) + self.preload_min15_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) + self.preload_min15_spinBox.setMaximum(999999) + self.preload_min15_spinBox.setObjectName("preload_min15_spinBox") + self.gridLayout_6.addWidget(self.preload_min15_spinBox, 8, 2, 1, 1) + self.preload_year_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_year_checkBox.setObjectName("preload_year_checkBox") + self.gridLayout_6.addWidget(self.preload_year_checkBox, 5, 0, 1, 1) + self.preload_quarter_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) + self.preload_quarter_checkBox.setObjectName("preload_quarter_checkBox") + self.gridLayout_6.addWidget(self.preload_quarter_checkBox, 3, 0, 1, 1) self.preload_hour2_checkBox = QtWidgets.QCheckBox(self.layoutWidget4) self.preload_hour2_checkBox.setObjectName("preload_hour2_checkBox") self.gridLayout_6.addWidget(self.preload_hour2_checkBox, 11, 0, 1, 1) @@ -467,9 +466,8 @@ class Ui_MainWindow(object): self.gridLayout_6.addWidget(self.label_42, 11, 1, 1, 1) self.preload_hour2_spinBox = QtWidgets.QSpinBox(self.layoutWidget4) self.preload_hour2_spinBox.setMaximum(999999) - self.preload_hour2_spinBox.setObjectName("preload_hour20_spinBox") + self.preload_hour2_spinBox.setObjectName("preload_hour2_spinBox") self.gridLayout_6.addWidget(self.preload_hour2_spinBox, 11, 2, 1, 1) - self.layoutWidget5 = QtWidgets.QWidget(self.groupBox_6) self.layoutWidget5.setGeometry(QtCore.QRect(40, 30, 362, 40)) self.layoutWidget5.setObjectName("layoutWidget5") @@ -499,7 +497,7 @@ class Ui_MainWindow(object): self.collect_sample_spinBox.setObjectName("collect_sample_spinBox") self.horizontalLayout_10.addWidget(self.collect_sample_spinBox) self.collect_use_zhima_checkBox = QtWidgets.QCheckBox(self.tab) - self.collect_use_zhima_checkBox.setGeometry(QtCore.QRect(40, 280, 141, 16)) + self.collect_use_zhima_checkBox.setGeometry(QtCore.QRect(40, 275, 141, 16)) self.collect_use_zhima_checkBox.setObjectName("collect_use_zhima_checkBox") self.layoutWidget7 = QtWidgets.QWidget(self.tab) self.layoutWidget7.setGeometry(QtCore.QRect(40, 190, 214, 24)) @@ -546,7 +544,7 @@ class Ui_MainWindow(object): self.horizontalLayout_7.addWidget(self.collect_phase2_last_timeEdit) self.horizontalLayout_9.addLayout(self.horizontalLayout_7) self.textBrowser = QtWidgets.QTextBrowser(self.tab) - self.textBrowser.setGeometry(QtCore.QRect(30, 321, 511, 181)) + self.textBrowser.setGeometry(QtCore.QRect(30, 311, 511, 191)) self.textBrowser.setObjectName("textBrowser") self.layoutWidget8 = QtWidgets.QWidget(self.tab) self.layoutWidget8.setGeometry(QtCore.QRect(40, 90, 141, 23)) @@ -560,6 +558,7 @@ class Ui_MainWindow(object): self.collect_source_comboBox = QtWidgets.QComboBox(self.layoutWidget8) self.collect_source_comboBox.setObjectName("collect_source_comboBox") self.collect_source_comboBox.addItem("") + self.collect_source_comboBox.addItem("") self.horizontalLayout_13.addWidget(self.collect_source_comboBox) self.collect_start_pushButton = QtWidgets.QPushButton(self.tab) self.collect_start_pushButton.setGeometry(QtCore.QRect(40, 30, 141, 23)) @@ -639,50 +638,50 @@ class Ui_MainWindow(object): self.label_9.setText(_translate("MainWindow", "导入权息数据:")) self.hdf5_weight_label.setText(_translate("MainWindow", "TextLabel")) self.import_detail_textEdit.setHtml(_translate("MainWindow", "\n" - "\n" - "

导入上证日线记录:

\n" - "

导入深证日线记录:

\n" - "

导入上证5分钟线记录:

\n" - "

导入深证5分钟线记录:

\n" - "

导入上证1分钟线记录:

\n" - "

导入深证1分钟线记录:

\n" - "

导入上证分笔记录:

\n" - "

导入深证分笔记录:

\n" - "

导入上证分时数据:

\n" - "

导入深证分时数据:

\n" - "

导入权息数据数:

\n" - "

导入完毕!

")) +"\n" +"

导入上证日线记录:

\n" +"

导入深证日线记录:

\n" +"

导入上证5分钟线记录:

\n" +"

导入深证5分钟线记录:

\n" +"

导入上证1分钟线记录:

\n" +"

导入深证1分钟线记录:

\n" +"

导入上证分笔记录:

\n" +"

导入深证分笔记录:

\n" +"

导入上证分时数据:

\n" +"

导入深证分时数据:

\n" +"

导入权息数据数:

\n" +"

导入完毕!

")) self.import_status_label.setText(_translate("MainWindow", "import_status_label")) self.sched_import_pushButton.setText(_translate("MainWindow", "启动定时导入")) self.label_40.setText(_translate("MainWindow", "导入执行时间:")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab_2), _translate("MainWindow", "执行导入")) self.groupBox_6.setTitle(_translate("MainWindow", "预加载设置")) self.save_pushButton.setText(_translate("MainWindow", "保存设置")) - self.preload_day_checkBox.setText(_translate("MainWindow", "预加载日线")) - self.label_24.setText(_translate("MainWindow", "最大缓存数量:")) self.preload_week_checkBox.setText(_translate("MainWindow", "预加载周线")) - self.label_25.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_month_checkBox.setText(_translate("MainWindow", "预加载月线")) - self.label_26.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_quarter_checkBox.setText(_translate("MainWindow", "预加载季线")) - self.label_27.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_halfyear_checkBox.setText(_translate("MainWindow", "预加载半年线")) - self.label_28.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_year_checkBox.setText(_translate("MainWindow", "预加载年线")) - self.label_29.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_min1_checkBox.setText(_translate("MainWindow", "预加载1分钟线")) - self.label_30.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_min5_checkBox.setText(_translate("MainWindow", "预加载5分钟线")) - self.label_31.setText(_translate("MainWindow", "最大缓存数量:")) + self.label_24.setText(_translate("MainWindow", "最大缓存数量:")) + self.preload_day_checkBox.setText(_translate("MainWindow", "预加载日线")) self.preload_min15_checkBox.setText(_translate("MainWindow", "预加载15分钟线")) + self.preload_month_checkBox.setText(_translate("MainWindow", "预加载月线")) + self.preload_min5_checkBox.setText(_translate("MainWindow", "预加载5分钟线")) + self.label_30.setText(_translate("MainWindow", "最大缓存数量:")) self.label_32.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_min30_checkBox.setText(_translate("MainWindow", "预加载30分钟线")) - self.label_33.setText(_translate("MainWindow", "最大缓存数量:")) + self.label_26.setText(_translate("MainWindow", "最大缓存数量:")) self.preload_min60_checkBox.setText(_translate("MainWindow", "预加载60分钟线")) + self.preload_min1_checkBox.setText(_translate("MainWindow", "预加载1分钟线")) + self.preload_halfyear_checkBox.setText(_translate("MainWindow", "预加载半年线")) + self.preload_min30_checkBox.setText(_translate("MainWindow", "预加载30分钟线")) + self.label_29.setText(_translate("MainWindow", "最大缓存数量:")) self.label_34.setText(_translate("MainWindow", "最大缓存数量:")) - self.preload_hour2_checkBox.setText(_translate("MainWindow", "预加载2小时线")) + self.label_25.setText(_translate("MainWindow", "最大缓存数量:")) + self.label_33.setText(_translate("MainWindow", "最大缓存数量:")) + self.label_27.setText(_translate("MainWindow", "最大缓存数量:")) + self.label_28.setText(_translate("MainWindow", "最大缓存数量:")) + self.label_31.setText(_translate("MainWindow", "最大缓存数量:")) + self.preload_year_checkBox.setText(_translate("MainWindow", "预加载年线")) + self.preload_quarter_checkBox.setText(_translate("MainWindow", "预加载季线")) + self.preload_hour2_checkBox.setText(_translate("MainWindow", "预加载120分钟线")) self.label_42.setText(_translate("MainWindow", "最大缓存数量:")) self.label_35.setText(_translate("MainWindow", "此处为 Hikyuu 运行时的数据预加载设置,请根据机器内存大小选择")) self.label_36.setText(_translate("MainWindow", "(目前加载全部日线数据目前需要约需900M内存)")) @@ -694,17 +693,18 @@ class Ui_MainWindow(object): self.label_37.setText(_translate("MainWindow", "执行时间段2:")) self.label_38.setText(_translate("MainWindow", "-")) self.textBrowser.setHtml(_translate("MainWindow", "\n" - "\n" - "

注:

\n" - "

1、行情采集服务仅对预加载数据有效,在行情采集服务运行期间,hikyuu.interactive运行时将自动连接采集服务获取行情数据,并更新预加载的内容数据。

\n" - "


\n" - "

2、如使用芝麻代理(http://h.zhimaruanjian.com/),请自行申请(需付费),并确保ip为其白名单。

\n" - "


\n" - "

3、此处采集为网络采集,更推荐直接运行安装目录下gui子目录下的 start_huatai_insight.py ,使用华泰 insight 的实时服务。运行前,需手工修改该文件最下方 __main__ 中的 login,放入自己的账号和密码。该程序独立运行,不用关闭,和这里的采集效果一样。

")) +"\n" +"

注:

\n" +"

1、行情采集服务仅对预加载数据有效,在行情采集服务运行期间,hikyuu.interactive运行时将自动连接采集服务获取行情数据,并更新预加载的内容数据。

\n" +"


\n" +"

2、如使用芝麻代理(http://h.zhimaruanjian.com/),请自行申请(需付费),并确保ip为其白名单。

\n" +"


\n" +"

3、此处采集为网络采集,更推荐直接运行安装目录下gui子目录下的 start_qmt.py ,使用miniqmt 实时服务。该程序独立运行,不用关闭,和这里的采集效果一样。注意:miniqmt需要QMT交易端配合,且在同一机器上执行。

")) self.label_39.setText(_translate("MainWindow", "行情数据源:")) self.collect_source_comboBox.setItemText(0, _translate("MainWindow", "qq")) + self.collect_source_comboBox.setItemText(1, _translate("MainWindow", "qmt")) self.collect_start_pushButton.setText(_translate("MainWindow", "启动采集")) self.collect_status_label.setText(_translate("MainWindow", "TextLabel")) self.tabWidget.setTabText(self.tabWidget.indexOf(self.tab), _translate("MainWindow", "行情采集服务")) diff --git a/hikyuu/gui/data/MainWindow.ui b/hikyuu/gui/data/MainWindow.ui index 837d9a34..15962a1a 100644 --- a/hikyuu/gui/data/MainWindow.ui +++ b/hikyuu/gui/data/MainWindow.ui @@ -713,21 +713,21 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入上证日线记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入深证日线记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入上证5分钟线记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入深证5分钟线记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入上证1分钟线记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入深证1分钟线记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入上证分笔记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入深证分笔记录:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入上证分时数据:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入深证分时数据:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入权息数据数:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">导入完毕!</span></p></body></html> +</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入上证日线记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入深证日线记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入上证5分钟线记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入深证5分钟线记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入上证1分钟线记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入深证1分钟线记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入上证分笔记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入深证分笔记录:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入上证分时数据:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入深证分时数据:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入权息数据数:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">导入完毕!</p></body></html>
@@ -815,14 +815,14 @@ p, li { white-space: pre-wrap; } 40 80 321 - 282 + 296 - - + + - 预加载日线 + 预加载周线 @@ -833,31 +833,17 @@ p, li { white-space: pre-wrap; } - - - - 999999 - - - - - + + - 预加载周线 + 预加载日线 - - + + - 最大缓存数量: - - - - - - - 999999 + 预加载15分钟线 @@ -868,99 +854,8 @@ p, li { white-space: pre-wrap; } - - - - 最大缓存数量: - - - - - - - 999999 - - - - - - - 预加载季线 - - - - - - - 最大缓存数量: - - - - - - - 999999 - - - - - - - 预加载半年线 - - - - - - - 最大缓存数量: - - - - - - - 999999 - - - - - - - 预加载年线 - - - - - - - 最大缓存数量: - - - - - - - 999999 - - - - - - - 预加载1分钟线 - - - - - - - 最大缓存数量: - - - - - + + 999999 @@ -973,24 +868,17 @@ p, li { white-space: pre-wrap; } - - - - 最大缓存数量: - - - - - + + 999999 - - + + - 预加载15分钟线 + 最大缓存数量: @@ -1001,29 +889,15 @@ p, li { white-space: pre-wrap; } - - - - 999999 - - - - - - - 预加载30分钟线 - - - - - + + 最大缓存数量: - - + + 999999 @@ -1036,6 +910,48 @@ p, li { white-space: pre-wrap; } + + + + 预加载1分钟线 + + + + + + + 999999 + + + + + + + 预加载半年线 + + + + + + + 预加载30分钟线 + + + + + + + 最大缓存数量: + + + + + + + 999999 + + + @@ -1043,6 +959,13 @@ p, li { white-space: pre-wrap; } + + + + 999999 + + + @@ -1050,6 +973,104 @@ p, li { white-space: pre-wrap; } + + + + 999999 + + + + + + + 999999 + + + + + + + 最大缓存数量: + + + + + + + 最大缓存数量: + + + + + + + 最大缓存数量: + + + + + + + 最大缓存数量: + + + + + + + 最大缓存数量: + + + + + + + 999999 + + + + + + + 999999 + + + + + + + 预加载年线 + + + + + + + 预加载季线 + + + + + + + 预加载120分钟线 + + + + + + + 最大缓存数量: + + + + + + + 999999 + + + @@ -1114,7 +1135,7 @@ p, li { white-space: pre-wrap; } 40 - 280 + 275 141 16 @@ -1207,22 +1228,22 @@ p, li { white-space: pre-wrap; } 30 - 321 + 311 511 - 181 + 191 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><meta charset="utf-8" /><style type="text/css"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Microsoft YaHei UI'; font-size:9pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">注:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-size:10pt; font-weight:600; color:#ff0000;">1、行情采集服务仅对预加载数据有效</span><span style=" font-family:'SimSun';">,在行情采集服务运行期间,hikyuu.interactive运行时将自动连接采集服务获取行情数据,并更新预加载的内容数据。</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun';">2、如使用芝麻代理(</span><a href="http://h.zhimaruanjian.com/"><span style=" font-family:'SimSun'; text-decoration: underline; color:#0000ff;">http://h.zhimaruanjian.com/</span></a><span style=" font-family:'SimSun';">),请自行申请(需付费),并确保ip为其白名单。</span></p> -<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'SimSun';"><br /></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'SimSun'; font-weight:700; color:#0000ff;">3、此处采集为网络采集,更推荐直接运行安装目录下gui子目录下的 start_huatai_insight.py ,使用华泰 insight 的实时服务。运行前,需手工修改该文件最下方 __main__ 中的 login,放入自己的账号和密码。该程序独立运行,不用关闭,和这里的采集效果一样。</span></p></body></html> +</style></head><body style=" font-family:'SimSun'; font-size:9pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">注:</p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:10pt; font-weight:600; color:#ff0000;">1、行情采集服务仅对预加载数据有效</span>,在行情采集服务运行期间,hikyuu.interactive运行时将自动连接采集服务获取行情数据,并更新预加载的内容数据。</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">2、如使用芝麻代理(<a href="http://h.zhimaruanjian.com/"><span style=" text-decoration: underline; color:#0000ff;">http://h.zhimaruanjian.com/</span></a>),请自行申请(需付费),并确保ip为其白名单。</p> +<p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:696; color:#0000ff;">3、此处采集为网络采集,更推荐直接运行安装目录下gui子目录下的 start_qmt.py ,使用miniqmt 实时服务。该程序独立运行,不用关闭,和这里的采集效果一样。注意:miniqmt需要QMT交易端配合,且在同一机器上执行。</span></p></body></html>
@@ -1249,6 +1270,11 @@ p, li { white-space: pre-wrap; } qq + + + qmt + + diff --git a/hikyuu/gui/spot_server.py b/hikyuu/gui/spot_server.py index 2be71e43..744445ca 100644 --- a/hikyuu/gui/spot_server.py +++ b/hikyuu/gui/spot_server.py @@ -18,7 +18,8 @@ import threading import hikyuu.flat as fb from hikyuu.util import * -from hikyuu.fetcher.stock.zh_stock_a_sina_qq import get_spot +from hikyuu.fetcher.stock.zh_stock_a_sina_qq import get_spot as qq_get_spot +from hikyuu.fetcher.stock.zh_stock_a_qmt import get_spot as qmt_get_spot from hikyuu import hikyuu_init, StockManager, constant, Datetime, TimeDelta @@ -269,14 +270,22 @@ def collect(server, use_proxy, source, seconds, phase1, phase2, ignore_weekend): hikyuu_init(config_file, ignore_preload=True) sm = StockManager.instance() - stk_list = [ - stk.market_code.lower() for stk in sm if stk.valid and stk.type in - (constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_GEM, - constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ) - ] + if source == 'qmt': + stk_list = [s for s in sm if s.valid] + else: + stk_list = [ + stk.market_code.lower() for stk in sm if stk.valid and stk.type in + (constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_GEM, + constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ) + ] _ = get_nng_senders() + if source == 'qmt': + get_spot = qmt_get_spot + elif source == 'qq': + get_spot = qq_get_spot + start_time = Datetime.now() delta = next_delta(start_time, seconds, phase1_delta, phase2_delta, ignore_weekend) next_time = start_time + delta @@ -308,7 +317,7 @@ def collect(server, use_proxy, source, seconds, phase1, phase2, ignore_weekend): @click.command() @click.option('-server', '--server', default='tcp://*:9200') @click.option('-use_proxy', '--use_proxy', is_flag=True, help='是否使用代理,须自行申请芝麻http代理并加入ip白名单') -@click.option('-source', '--source', default='qq', type=click.Choice(['sina', 'qq']), help='数据来源') +@click.option('-source', '--source', default='qq', type=click.Choice(['qmt', 'qq']), help='数据来源') @click.option('-seconds', '--seconds', default=10) @click.option('-phase1', '--phase1', default='9:00-12:00') @click.option('-phase2', '--phase2', default='13:00-15:00') diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py new file mode 100644 index 00000000..3c8e5254 --- /dev/null +++ b/hikyuu/gui/start_qmt.py @@ -0,0 +1,36 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- + + +from hikyuu.fetcher.stock.zh_stock_a_qmt import parse_one_result_qmt +from hikyuu.gui.spot_server import release_nng_senders, start_send_spot, end_send_spot, send_spot + + +def callback(datas): + records = [] + for stock_code, data in datas.items(): + # print(stock_code, data) + records.append(parse_one_result_qmt(stock_code, data)) + + if records: + start_send_spot() + send_spot(records) + end_send_spot() + + +if __name__ == "__main__": + from hikyuu.interactive import * + from xtquant import xtdata + + code_list = [f'{s.code}.{s.market}' for s in sm if s.valid] + # code_list = ['000001.SZ'] + + xtdata.subscribe_whole_quote(code_list, callback) + + try: + xtdata.run() + except Exception as e: + hku_error(e) + finally: + # 退出释放资源 + release_nng_senders() diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index d27b655e..8df28558 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -466,11 +466,36 @@ def realtime_update_from_tushare(): stock.realtime_update(record) +def realtime_update_from_qmt(): + from xtquant import xtdata + code_list = [f'{s.code}.{s.market}' for s in sm if s.valid] + full_tick = xtdata.get_full_tick(code_list) + for qmt_code, data in full_tick.items(): + try: + code, market = qmt_code.split(".") + stock = sm[f'{market}{code}'] + record = KRecord() + record.datetime = Datetime(data['timetag'].split(' ')[0]) + record.open = data['open'] + record.high = data['high'] + record.low = data['low'] + record.close = data['lastPrice'] + record.volume = data['volume'] * 0.1 + record.amount = data['amount'] * 0.001 + stock.realtime_update(record) + except Exception as e: + hku_error(str(e)) + except: + pass + + def realtime_update_inner(source='sina'): if source == 'sina' or source == 'qq': realtime_update_from_sina_qq(source) elif source == 'tushare': realtime_update_from_tushare() + elif source == 'qmt': + realtime_update_from_qmt() else: print(source, ' not support!') @@ -482,13 +507,13 @@ def realtime_update_wrap(): """ 更新实时日线数据 参数: - source: 数据源('sina' | 'qq' | 'tushare') + source: 数据源('qq' | 'qmt' | 'tushare' | 'sina') delta: 更新间隔时间 """ from datetime import timedelta, datetime nonlocal pre_update_time now_update_time = datetime.now() - if (pre_update_time is None) or (now_update_time - pre_update_time) > timedelta(0, delta, 0): + if (source == 'qmt') or (pre_update_time is None) or (now_update_time - pre_update_time) > timedelta(0, delta, 0): realtime_update_inner(source) pre_update_time = datetime.now() print("更新完毕!", pre_update_time) From 50dea7ac63dd6e23cfafbdeab019e160d0773687 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 23 Aug 2024 12:47:14 +0800 Subject: [PATCH 499/601] uupdate BrokerTradeManager --- hikyuu/trade_manage/broker_easytrader.py | 15 ++- .../hikyuu/strategy/BrokerTradeManager.cpp | 99 ++++++++++++++----- .../hikyuu/strategy/BrokerTradeManager.h | 14 ++- hikyuu_pywrap/strategy/_Strategy.cpp | 4 + hikyuu_pywrap/trade_manage/_TradeManager.cpp | 7 ++ 5 files changed, 99 insertions(+), 40 deletions(-) diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index d38a87d3..81d7ded9 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -28,19 +28,18 @@ class EasyTraderOrderBroker: self.buffer[code] = (old_num - num, stoploss, goal_price) def get_asset_info(self): + '''以下只适用于华泰''' balance = self.user.balance - cash = 0.0 - for item in balance: - cash += item['可用资金'] + cash = balance['可用金额'] positions = [] for v in self.user.position: - if v["交易市场"] == "沪A": + if v["市场类别"] == "上海A": market = "SH" - elif v["交易市场"] == "深A": + elif v["市场类别"] == "深圳A": market = "SZ" else: - hku_info(f"Ignored not supported market: {v['交易市场']}") + hku_info(f"Ignored not supported market: {v['市场类别']}") continue code = v["证券代码"] @@ -49,7 +48,7 @@ class EasyTraderOrderBroker: stoploss, goal_price = self.buffer[market_code] else: stoploss, goal_price = 0.0, 0.0 - positions.append(dict(market=market, code=code, number=( - v['当前持仓'] + v['买入冻结'] - v['卖出冻结']), stoploss=stoploss, goal_price=goal_price)) + positions.append(dict(market=market, code=code, + number=v['可用余额'], stoploss=stoploss, goal_price=goal_price, cost_price=v['成本价'])) return dict(datetime=str(Datetime.now()), cash=cash, positions=positions) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index e62e2053..67a972ed 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -24,7 +24,6 @@ BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const Trade } void BrokerTradeManager::_reset() { - HKU_WARN("The subclass does not implement a reset method"); m_datetime = Datetime::max(); m_cash = 0.0; m_position.clear(); @@ -50,37 +49,39 @@ void BrokerTradeManager::fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) } try { - json asset(brk_asset); + json asset = json::parse(brk_asset); m_datetime = asset.contains("datetime") ? m_datetime = Datetime(asset["datetime"].get()) : m_datetime = Datetime::now(); - - m_cash = asset["cash"]; + m_cash = asset["cash"].get(); auto& positions = asset["positions"]; for (auto iter = positions.cbegin(); iter != positions.cend(); ++iter) { - const auto& jpos = *iter; - auto market = jpos["market"].get(); - auto code = jpos["code"].get(); - Stock stock = getStock(fmt::format("{}{}", market, code)); - if (stock.isNull()) { - HKU_WARN("Not found stock: {}{}", market, code); - continue; + try { + const auto& jpos = *iter; + auto market = jpos["market"].get(); + auto code = jpos["code"].get(); + Stock stock = getStock(fmt::format("{}{}", market, code)); + if (stock.isNull()) { + HKU_DEBUG("Not found stock: {}{}", market, code); + continue; + } + + PositionRecord pos; + pos.stock = stock; + pos.takeDatetime = m_datetime; + pos.number = jpos["number"].get(); + pos.stoploss = jpos["stoploss"].get(); + pos.goalPrice = jpos["goal_price"].get(); + pos.totalNumber = pos.number; + price_t cost_price = jpos["cost_price"].get(); + pos.buyMoney = pos.number * cost_price; + pos.totalRisk = (pos.stoploss - cost_price) * pos.number; + m_position[stock.id()] = pos; + } catch (const std::exception& e) { + HKU_ERROR(e.what()); } - - PositionRecord pos; - pos.stock = stock; - pos.takeDatetime = m_datetime; - pos.number = jpos["number"].get(); - pos.stoploss = jpos["stoploss"].get(); - pos.goalPrice = jpos["goal_price"].get(); - pos.totalNumber = pos.number; - price_t cost_price = jpos["cost_price"].get(); - pos.buyMoney = pos.number * cost_price; - pos.totalRisk = (pos.stoploss - cost_price) * pos.number; - m_position[stock.id()] = pos; } - } catch (const std::exception& e) { HKU_ERROR(e.what()); } @@ -97,6 +98,13 @@ PositionRecordList BrokerTradeManager::getPositionList() const { return result; } +PositionRecord BrokerTradeManager::getPosition(const Datetime& date, const Stock& stock) { + PositionRecord ret; + HKU_IF_RETURN(date < m_datetime || stock.isNull(), ret); + auto iter = m_position.find(stock.id()); + return iter != m_position.end() ? iter->second : ret; +} + bool BrokerTradeManager::checkin(const Datetime& datetime, price_t cash) { HKU_IF_RETURN(datetime < m_datetime, false); m_cash += cash; @@ -279,4 +287,47 @@ FundsRecord BrokerTradeManager::getFunds(const Datetime& datetime, KQuery::KType return (datetime >= m_datetime) ? getFunds(ktype) : FundsRecord(); } +string BrokerTradeManager::str() const { + std::stringstream os; + os << std::fixed; + os.precision(2); + + FundsRecord funds = getFunds(); + string strip(",\n"); + os << "BrokerTradeManager {\n" + << " name: " << name() << strip << " date: " << initDatetime() << strip + << " cash: " << initCash() << 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 << " Position: \n"; + + StockManager& sm = StockManager::instance(); + KQuery query(-1); + PositionRecordList position = getPositionList(); + PositionRecordList::const_iterator iter = position.begin(); + for (; iter != position.end(); ++iter) { + price_t invest = iter->buyMoney - iter->sellMoney + iter->totalCost; + KData k = iter->stock.getKData(query); + price_t cur_val = k[0].closePrice * iter->number; + price_t bonus = cur_val - invest; + DatetimeList date_list = + sm.getTradingCalendar(KQueryByDate(Datetime(iter->takeDatetime.date()))); + os << " " << iter->stock.market_code() << " " << iter->stock.name() << " " + << k[0].datetime << " " << date_list.size() << " " << iter->number << " " << invest + << " " << cur_val << " " << bonus << " " << 100 * bonus / invest << "%\n"; + } + + os << "}"; + + os.unsetf(std::ostream::floatfield); + os.precision(); + return os.str(); +} + +TradeManagerPtr HKU_API crtBrokerTM(const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, + const string& name) { + return std::make_shared(broker, costfunc, name); +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index a43ddd00..3a8bf64e 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -177,10 +177,7 @@ public: * @param date 指定日期 * @param stock 指定的证券 */ - virtual PositionRecord getPosition(const Datetime& date, const Stock& stock) { - HKU_WARN("The subclass does not implement this method"); - return PositionRecord(); - } + virtual PositionRecord getPosition(const Datetime& date, const Stock& stock) override; /** * 获取指定证券的空头持仓记录 @@ -408,10 +405,7 @@ public: } /** 字符串输出 */ - virtual string str() const { - HKU_WARN("The subclass does not implement this method"); - return string(); - } + virtual string str() const override; /** * 以csv格式输出交易记录、未平仓记录、已平仓记录、资产净值曲线 @@ -430,4 +424,8 @@ private: position_map_type m_position; // 当前持仓交易对象的持仓记录 }; +TradeManagerPtr HKU_API crtBrokerTM(const OrderBrokerPtr& broker, + const TradeCostPtr& costfunc = TC_Zero(), + const string& name = "SYS"); + } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index f2ea4bd9..55b8625b 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -6,6 +6,7 @@ */ #include +#include #include #include @@ -89,4 +90,7 @@ void export_Strategy(py::module& m) { self.runDailyAt(new_func, time, ignore_holiday); }, py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true); + + m.def("crtBrokerTM", crtBrokerTM, py::arg("broker"), py::arg("cost_func") = TC_Zero(), + py::arg("name") = "SYS"); } \ No newline at end of file diff --git a/hikyuu_pywrap/trade_manage/_TradeManager.cpp b/hikyuu_pywrap/trade_manage/_TradeManager.cpp index 5b449a6f..ee214ccf 100644 --- a/hikyuu_pywrap/trade_manage/_TradeManager.cpp +++ b/hikyuu_pywrap/trade_manage/_TradeManager.cpp @@ -227,6 +227,11 @@ public: void tocsv(const string& path) override { PYBIND11_OVERLOAD(void, TradeManagerBase, tocsv, path); } + + void fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) override { + PYBIND11_OVERRIDE_NAME(void, TradeManagerBase, "fetch_asset_info_from_broker", + fetchAssetInfoFromBroker, broker); + } }; FundsRecord (TradeManagerBase::*getFunds_1)(KQuery::KType) const = &TradeManagerBase::getFunds; @@ -560,5 +565,7 @@ void export_TradeManager(py::module& m) { :param Datetime date: 当前时刻)") + .def("fetch_asset_info_from_broker", &TradeManagerBase::fetchAssetInfoFromBroker) + DEF_PICKLE(TradeManagerPtr); } From bddfd5ce91ceea208006f289c6acb99678970cb2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 23 Aug 2024 15:33:05 +0800 Subject: [PATCH 500/601] update BrokerTradeManager --- .../hikyuu/strategy/BrokerTradeManager.cpp | 46 ++++++------------- .../hikyuu/strategy/BrokerTradeManager.h | 2 +- 2 files changed, 16 insertions(+), 32 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp index 67a972ed..c39d764e 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.cpp @@ -23,11 +23,7 @@ BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const Trade m_broker_last_datetime = m_datetime; } -void BrokerTradeManager::_reset() { - m_datetime = Datetime::max(); - m_cash = 0.0; - m_position.clear(); -} +void BrokerTradeManager::_reset() {} shared_ptr BrokerTradeManager::_clone() { BrokerTradeManager* p = new BrokerTradeManager(); @@ -100,13 +96,11 @@ PositionRecordList BrokerTradeManager::getPositionList() const { PositionRecord BrokerTradeManager::getPosition(const Datetime& date, const Stock& stock) { PositionRecord ret; - HKU_IF_RETURN(date < m_datetime || stock.isNull(), ret); auto iter = m_position.find(stock.id()); return iter != m_position.end() ? iter->second : ret; } bool BrokerTradeManager::checkin(const Datetime& datetime, price_t cash) { - HKU_IF_RETURN(datetime < m_datetime, false); m_cash += cash; return true; } @@ -118,9 +112,6 @@ TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock result.business = BUSINESS_INVALID; HKU_ERROR_IF_RETURN(stock.isNull(), result, "{} Stock is Null!", datetime); - HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result, - "{} {} datetime must be >= lastDatetime({})!", datetime, - stock.market_code(), lastDatetime()); HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} numer is zero!", datetime, stock.market_code()); HKU_ERROR_IF_RETURN(number < stock.minTradeNumber(), result, @@ -166,15 +157,13 @@ TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock roundEx(position.totalRisk + (realPrice - stoploss) * number * stock.unit(), precision); } - if (datetime > m_broker_last_datetime) { - list::const_iterator broker_iter = m_broker_list.begin(); - for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter) - ->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice, - from); - if (datetime > m_broker_last_datetime) { - m_broker_last_datetime = datetime; - } + list::const_iterator broker_iter = m_broker_list.begin(); + for (; broker_iter != m_broker_list.end(); ++broker_iter) { + (*broker_iter) + ->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice, + from); + if (datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } @@ -188,9 +177,6 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc TradeRecord result; HKU_ERROR_IF_RETURN(stock.isNull(), result, "{} Stock is Null!", datetime); - HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result, - "{} {} datetime must be >= lastDatetime({})!", datetime, - stock.market_code(), lastDatetime()); HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} number is zero!", datetime, stock.market_code()); @@ -243,15 +229,13 @@ TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stoc m_position.erase(stock.id()); } - if (datetime > m_broker_last_datetime) { - list::const_iterator broker_iter = m_broker_list.begin(); - for (; broker_iter != m_broker_list.end(); ++broker_iter) { - (*broker_iter) - ->sell(datetime, stock.market(), stock.code(), realPrice, real_number, stoploss, - goalPrice, from); - if (datetime > m_broker_last_datetime) { - m_broker_last_datetime = datetime; - } + list::const_iterator broker_iter = m_broker_list.begin(); + for (; broker_iter != m_broker_list.end(); ++broker_iter) { + (*broker_iter) + ->sell(datetime, stock.market(), stock.code(), realPrice, real_number, stoploss, + goalPrice, from); + if (datetime > m_broker_last_datetime) { + m_broker_last_datetime = datetime; } } diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index 3a8bf64e..0e3811a3 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -75,7 +75,7 @@ public: * @note 如果不带日期参数,无法根据权息信息调整持仓 */ virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override { - return (datetime >= m_datetime) ? currentCash() : 0.0; + return m_cash; } /** From e9f005453f5bb9a7a1e3126f42820e6f7b7bcee5 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 24 Aug 2024 14:53:17 +0800 Subject: [PATCH 501/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20realtime=5Fupdate?= =?UTF-8?q?=EF=BC=8C=E7=A7=BB=E9=99=A4sina|tushare=E6=BA=90=EF=BC=8C?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=8C=87=E5=AE=9Astock=E5=88=97=E8=A1=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 164 ++++++------------------------------------ 1 file changed, 22 insertions(+), 142 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 8df28558..5932c2b3 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -255,44 +255,6 @@ def select(cond, start=Datetime(201801010000), end=Datetime.now(), print_out=Tru # ============================================================================== -def UpdateOneRealtimeRecord_from_sina(tmpstr): - try: - if len(tmpstr) > 3 and tmpstr[:3] == 'var': - a = tmpstr.split(',') - if len(a) < 9: - return - - open, close, high, low = float(a[1]), float(a[3]), float(a[4]), float(a[5]) - transamount = float(a[9]) - transcount = float(a[8]) - - try: - d = Datetime(a[-3]) - except: - d = Datetime(a[-4]) - temp = (open, high, low, close) - if 0 in temp: - return - - stockstr = a[0].split('=') - stock = sm[stockstr[0][-8:]] - - record = KRecord() - record.datetime = d - record.open = open - record.high = high - record.low = low - record.close = close - record.amount = transamount - record.volume = transcount / 100 - - stock.realtime_update(record) - - except Exception as e: - print(tmpstr) - print(e) - - def UpdateOneRealtimeRecord_from_qq(tmpstr): try: if len(tmpstr) > 3 and tmpstr[:2] == 'v_': @@ -328,20 +290,6 @@ def UpdateOneRealtimeRecord_from_qq(tmpstr): print(e) -def realtimePartUpdate_from_sina(queryStr): - result = urllib.request.urlopen(queryStr).read() - try: - result = result.decode('gbk') - except Exception as e: - print(result) - print(e) - return - - result = result.split('\n') - for tmpstr in result: - UpdateOneRealtimeRecord_from_sina(tmpstr) - - def realtimePartUpdate_from_qq(queryStr): result = urllib.request.urlopen(queryStr).read() try: @@ -356,14 +304,8 @@ def realtimePartUpdate_from_qq(queryStr): UpdateOneRealtimeRecord_from_qq(tmpstr) -def realtime_update_from_sina_qq(source): - if source == 'sina': - hku_error("sina已不支持获取实时数据") - return - # queryStr = "http://hq.sinajs.cn/list=" - # update_func = realtimePartUpdate_from_sina - # max_size = 140 - elif source == 'qq': +def realtime_update_from_website(source, stk_list=None): + if source == 'qq': queryStr = "http://qt.gtimg.cn/q=" update_func = realtimePartUpdate_from_qq max_size = 60 @@ -374,7 +316,9 @@ def realtime_update_from_sina_qq(source): count = 0 # urls = [] tmpstr = queryStr - for stock in sm: + if stk_list is None: + stk_list = sm + for stock in stk_list: if stock.valid and stock.type in ( constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, constant.STOCKTYPE_GEM, constant.STOCKTYPE_A_BJ, ): @@ -402,73 +346,11 @@ def realtime_update_from_sina_qq(source): # pool.join() -def realtime_update_from_tushare(): - import tushare as ts - - # 更新股票行情 - df = ts.get_today_all() - for i in range(len(df)): - if df.ix[i, 'open'] == 0: - continue # 停牌 - - code = df.ix[i][0] - stock = get_stock('sh' + code) - - if stock.isNull() == True or (stock.type != constant.STOCKTYPE_A and stock.type != constant.STOCKTYPE_A_BJ): - stock = get_stock('sz' + code) - if stock.isNull() == True: - continue - - record = KRecord() - record.open = df.ix[i, 'open'] - record.high = df.ix[i, 'high'] - record.lowe = df.ix[i, 'low'] - record.close = df.ix[i, 'trade'] - record.amount = float(df.ix[i, 'amount']) - record.volume = float(df.ix[i, 'volume']) - - from datetime import date - d = date.today() - record.datetime = Datetime(d) - stock.realtime_update(record) - - # 更新指数行情 - df = ts.get_index() - for i in range(len(df)): - code = df.ix[i][0] - stock = get_stock('sh' + code) - if stock.isNull() == True or stock.type != constant.STOCKTYPE_INDEX: - stock = get_stock('sz' + code) - if stock.isNull() == True: - continue - - total = stock.getCount(Query.DAY) - if total == 0: - continue - - last_record = stock.getKRecord(total - 1) - - record = KRecord() - record.open = df.ix[i, 'open'] - record.high = df.ix[i, 'high'] - record.low = df.ix[i, 'low'] - record.close = df.ix[i, 'close'] - record.volume = float(df.ix[i, 'volume']) - record.amount = float(df.ix[i, 'amount']) - - if ( - last_record.close != record.close or last_record.high != record.high or last_record.low != record.low - or last_record.open != record.open - ): - from datetime import date - d = date.today() - record.datetime = Datetime(d) - stock.realtime_update(record) - - -def realtime_update_from_qmt(): +def realtime_update_from_qmt(stk_list=None): from xtquant import xtdata - code_list = [f'{s.code}.{s.market}' for s in sm if s.valid] + if stk_list is None: + stk_list = sm + code_list = [f'{s.code}.{s.market}' for s in stk_list if s.valid] full_tick = xtdata.get_full_tick(code_list) for qmt_code, data in full_tick.items(): try: @@ -489,37 +371,35 @@ def realtime_update_from_qmt(): pass -def realtime_update_inner(source='sina'): - if source == 'sina' or source == 'qq': - realtime_update_from_sina_qq(source) - elif source == 'tushare': - realtime_update_from_tushare() +def realtime_update_inner(source='qq', stk_list=None): + if source == 'qq': + realtime_update_from_website(source, stk_list) elif source == 'qmt': - realtime_update_from_qmt() + realtime_update_from_qmt(stk_list) else: - print(source, ' not support!') + hku_error(f'Not support website source: {source}!') def realtime_update_wrap(): pre_update_time = None - def realtime_update_closure(source='qq', delta=60): + def realtime_update_closure(source='qq', delta=60, stk_list=None): """ 更新实时日线数据 - 参数: - source: 数据源('qq' | 'qmt' | 'tushare' | 'sina') - delta: 更新间隔时间 + :param str source: 数据源 ('qq' | 'qmt') + :param int delta: 最小更新间隔时间, 防止更新过于频繁 + :param sequence stk_list: 待更新的stock列表, 如为 None 则更新全部 """ from datetime import timedelta, datetime nonlocal pre_update_time now_update_time = datetime.now() if (source == 'qmt') or (pre_update_time is None) or (now_update_time - pre_update_time) > timedelta(0, delta, 0): - realtime_update_inner(source) + realtime_update_inner(source, stk_list) pre_update_time = datetime.now() - print("更新完毕!", pre_update_time) + print(f"更新完毕!更新时间: {pre_update_time}") else: - print("更新间隔小于" + str(delta) + "秒,未更新") - print("上次更新时间: ", pre_update_time) + print(f"更新间隔小于 {str(delta)} 秒,未更新") + print(f"上次更新时间: {pre_update_time}") return realtime_update_closure From 1962b56bfb16a620d9a1b5495baeb4277c13c0e0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 24 Aug 2024 18:08:38 +0800 Subject: [PATCH 502/601] add runInStrategy --- hikyuu_cpp/demo/demo2.cpp | 2 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 28 +++++++++++++++++++++++++ hikyuu_cpp/hikyuu/strategy/Strategy.h | 19 +++++++++++------ 3 files changed, 42 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp index ea76e49e..a6aacf88 100644 --- a/hikyuu_cpp/demo/demo2.cpp +++ b/hikyuu_cpp/demo/demo2.cpp @@ -50,7 +50,7 @@ int main(int argc, char* argv[]) { auto t = std::thread([]() { // 以线程的方式执行另一个策略 // 注意:同一进程内的所有 strategy 共享的是同一个上下文, - // 即使后续创建的 strategy 指定了新的上下文,但不会生效!!! + // 即使后续创建的 strategy 指定了新的 stock 列表,但不会生效!!! Strategy stg2("stratege2"); stg2.onChange(changed2); stg2.start(); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index d5e5d460..cc637ed3 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -261,4 +261,32 @@ void Strategy::_startEventLoop() { } } +void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, + const OrderBrokerPtr& broker, const TradeCostPtr& costFunc) { + HKU_ASSERT(sys && broker && costFunc); + HKU_ASSERT(!stk.isNull()); + HKU_ASSERT(query != Null()); + auto tm = crtBrokerTM(broker, costFunc, sys->name()); + sys->setTM(tm); + sys->setSP(SlippagePtr()); // 清除移滑价差算法 + sys->run(stk, query); +} + +void HKU_API runInstrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costFunc) { + HKU_ASSERT(pf && broker && costFunc); + HKU_ASSERT(query != Null()); + + auto se = pf->getSE(); + HKU_ASSERT(se); + const auto& sys_list = se->getProtoSystemList(); + for (const auto& sys : sys_list) { + HKU_CHECK(!sys->getSP(), "Exist Slippage part in sys, You must clear it! {}", sys->name()); + } + + auto tm = crtBrokerTM(broker, costFunc, pf->name()); + pf->setTM(tm); + pf->run(query, adjust_cycle, true); +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 2f0d13c3..5540d8f4 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -8,12 +8,13 @@ #pragma once #include -#include "../DataType.h" -#include "../StrategyContext.h" -#include "../global/SpotRecord.h" -#include "../utilities/thread/FuncWrapper.h" -#include "../utilities/thread/ThreadSafeQueue.h" -#include "../trade_sys/portfolio/Portfolio.h" +#include "hikyuu/DataType.h" +#include "hikyuu/StrategyContext.h" +#include "hikyuu/global/SpotRecord.h" +#include "hikyuu/utilities/thread/FuncWrapper.h" +#include "hikyuu/utilities/thread/ThreadSafeQueue.h" +#include "hikyuu/trade_sys/portfolio/Portfolio.h" +#include "BrokerTradeManager.h" namespace hku { @@ -129,4 +130,10 @@ private: typedef shared_ptr StrategyPtr; +void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, + const OrderBrokerPtr& broker, const TradeCostPtr& costFunc); + +void HKU_API runInstrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costFunc); + } // namespace hku \ No newline at end of file From b0b79c06b189bb5fd031b60345c42e713c8f905e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 01:17:32 +0800 Subject: [PATCH 503/601] add runInStrategy --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 21 ++++++++++++++------- hikyuu_cpp/hikyuu/strategy/Strategy.h | 6 +++--- hikyuu_pywrap/strategy/_Strategy.cpp | 11 +++++++++++ 3 files changed, 28 insertions(+), 10 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index cc637ed3..9307f250 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -262,19 +262,23 @@ void Strategy::_startEventLoop() { } void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, - const OrderBrokerPtr& broker, const TradeCostPtr& costFunc) { - HKU_ASSERT(sys && broker && costFunc); + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc) { + HKU_ASSERT(sys && broker && sys->getTM()); HKU_ASSERT(!stk.isNull()); HKU_ASSERT(query != Null()); - auto tm = crtBrokerTM(broker, costFunc, sys->name()); + HKU_CHECK(!sys->getParam("buy_delay") && !sys->getParam("sell_delay"), + "Thie method only support buy|sell on close!"); + + auto tm = crtBrokerTM(broker, costfunc, sys->name()); + tm->fetchAssetInfoFromBroker(broker); sys->setTM(tm); sys->setSP(SlippagePtr()); // 清除移滑价差算法 sys->run(stk, query); } -void HKU_API runInstrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, - const OrderBrokerPtr& broker, const TradeCostPtr& costFunc) { - HKU_ASSERT(pf && broker && costFunc); +void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc) { + HKU_ASSERT(pf && broker && pf->getTM()); HKU_ASSERT(query != Null()); auto se = pf->getSE(); @@ -282,9 +286,12 @@ void HKU_API runInstrategy(const PFPtr& pf, const KQuery& query, int adjust_cycl const auto& sys_list = se->getProtoSystemList(); for (const auto& sys : sys_list) { HKU_CHECK(!sys->getSP(), "Exist Slippage part in sys, You must clear it! {}", sys->name()); + HKU_CHECK(!sys->getParam("buy_delay") && !sys->getParam("sell_delay"), + "Thie method only support buy|sell on close!"); } - auto tm = crtBrokerTM(broker, costFunc, pf->name()); + auto tm = crtBrokerTM(broker, costfunc, pf->name()); + tm->fetchAssetInfoFromBroker(broker); pf->setTM(tm); pf->run(query, adjust_cycle, true); } diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 5540d8f4..94a144b7 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -131,9 +131,9 @@ private: typedef shared_ptr StrategyPtr; void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, - const OrderBrokerPtr& broker, const TradeCostPtr& costFunc); + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); -void HKU_API runInstrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, - const OrderBrokerPtr& broker, const TradeCostPtr& costFunc); +void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 55b8625b..8280ed70 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -93,4 +93,15 @@ void export_Strategy(py::module& m) { m.def("crtBrokerTM", crtBrokerTM, py::arg("broker"), py::arg("cost_func") = TC_Zero(), py::arg("name") = "SYS"); + + m.def("run_in_strategy", + py::overload_cast(runInStrategy), + py::arg("sys"), py::arg("stock"), py::arg("query"), py::arg("broker"), + py::arg("cost_func")); + m.def("run_in_strategy", + py::overload_cast(runInStrategy), + py::arg("pf"), py::arg("query"), py::arg("adjust_cycle"), py::arg("broker"), + py::arg("cost_func")); } \ No newline at end of file From 56f6dcdf073d96b4396b5739fc853d000713b6de Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 03:01:55 +0800 Subject: [PATCH 504/601] update --- .../strategy/RunPortfolioInStrategy.cpp | 78 +++++++++++++++++ .../hikyuu/strategy/RunPortfolioInStrategy.h | 36 ++++++++ .../hikyuu/strategy/RunSystemInStrategy.cpp | 84 +++++++++++++++++++ .../hikyuu/strategy/RunSystemInStrategy.h | 37 ++++++++ 4 files changed, 235 insertions(+) create mode 100644 hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp create mode 100644 hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h create mode 100644 hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp create mode 100644 hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h diff --git a/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp new file mode 100644 index 00000000..b184e7b2 --- /dev/null +++ b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-25 + * Author: fasiondog + */ + +#include "BrokerTradeManager.h" +#include "RunPortfolioInStrategy.h" + +namespace hku { + +RunPortfolioInStrategy::RunPortfolioInStrategy(const PFPtr& pf, const KQuery& query, + int adjust_cycle, const OrderBrokerPtr& broker, + const TradeCostPtr& costfunc) +: m_pf(pf), m_adjust_cycle(adjust_cycle), m_broker(broker) { + HKU_ASSERT(pf && broker); + + if (query.queryType() == KQuery::INDEX) { + m_query = KQueryByIndex(query.start(), Null(), query.kType(), query.recoverType()); + } else if (query.queryType() == KQuery::DATE) { + m_query = + KQueryByDate(query.startDatetime(), Null(), query.kType(), query.recoverType()); + } else { + HKU_THROW("Invalid query: {}", query); + } + + auto se = pf->getSE(); + HKU_ASSERT(se); + const auto& sys_list = se->getProtoSystemList(); + for (const auto& sys : sys_list) { + HKU_CHECK(!sys->getSP(), "Exist Slippage part in sys, You must clear it! {}", sys->name()); + HKU_CHECK(!sys->getParam("buy_delay") && !sys->getParam("sell_delay"), + "Thie method only support buy|sell on close!"); + } + + auto tm = crtBrokerTM(broker, costfunc, pf->name()); + m_pf->setTM(tm); +} + +void RunPortfolioInStrategy::run() { + m_pf->getTM()->fetchAssetInfoFromBroker(m_broker); + m_pf->run(m_query, m_adjust_cycle, true); +} + +StrategyPtr HKU_API crtPFStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, + const string& name, const string& config_file) { + std::shared_ptr runner = + std::make_shared(pf, query, adjust_cycle, broker, costfunc); + + std::function func = [=]() { runner->run(); }; + + vector code_list; + std::set stk_set; + auto sys_list = pf->getSE()->getProtoSystemList(); + for (const auto& sys : sys_list) { + auto stock = sys->getStock(); + if (stk_set.count(stock.id()) == 0) { + stk_set.insert(stock.id()); + code_list.emplace_back(stock.market_code()); + } + } + + KQuery::KType ktype = query.kType(); + StrategyPtr stg = + std::make_shared(code_list, vector{ktype}, name, config_file); + + int32_t m = KQuery::getKTypeInMin(ktype); + if (m < KQuery::getKTypeInMin(KQuery::DAY)) { + stg->runDaily(std::move(func), Minutes(m)); + } else { + stg->runDailyAt(std::move(func), TimeDelta(0, 14, 50)); + } + return stg; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h new file mode 100644 index 00000000..ea776b37 --- /dev/null +++ b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-25 + * Author: fasiondog + */ + +#pragma once + +#include "hikyuu/trade_sys/portfolio/Portfolio.h" +#include "Strategy.h" + +namespace hku { + +class HKU_API RunPortfolioInStrategy { +public: + RunPortfolioInStrategy() = default; + RunPortfolioInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); + virtual ~RunPortfolioInStrategy() = default; + + void run(); + +private: + PFPtr m_pf; + OrderBrokerPtr m_broker; + KQuery m_query; + int m_adjust_cycle; +}; + +StrategyPtr HKU_API crtPFStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, + const string& name = "PFStrategy", + const string& config_file = ""); + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp new file mode 100644 index 00000000..dd19d154 --- /dev/null +++ b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-24 + * Author: fasiondog + */ + +#include "BrokerTradeManager.h" +#include "RunSystemInStrategy.h" + +namespace hku { + +RunSystemInStrategy::RunSystemInStrategy(const SYSPtr& sys, const OrderBrokerPtr& broker, + const KQuery& query, const TradeCostPtr& costfunc) +: m_sys(sys), m_broker(broker) { + HKU_ASSERT(sys && broker); + + if (query.queryType() == KQuery::INDEX) { + m_query = KQueryByIndex(query.start(), Null(), query.kType(), query.recoverType()); + } else if (query.queryType() == KQuery::DATE) { + m_query = + KQueryByDate(query.startDatetime(), Null(), query.kType(), query.recoverType()); + } else { + HKU_THROW("Invalid query: {}", query); + } + + auto tm = crtBrokerTM(broker, costfunc, sys->name()); + m_sys->setTM(tm); + m_sys->setSP(SlippagePtr()); // 清除移滑价差算法 +} + +void RunSystemInStrategy::run(const Stock& stock) { + if (m_sys->getParam("buy_delay") && m_buyRequest.valid) { + KData k = stock.getKData( + KQueryByIndex(-1, Null(), m_query.kType(), m_query.recoverType())); + const auto& stock = m_sys->getStock(); + m_broker->buy(m_buyRequest.datetime, stock.market(), stock.code(), 10.0, + m_buyRequest.number, m_buyRequest.stoploss, m_buyRequest.goal, + m_buyRequest.from); + } + + if (m_sys->getParam("sell_delay") && m_sellRequest.valid) { + KData k = stock.getKData( + KQueryByIndex(-1, Null(), m_query.kType(), m_query.recoverType())); + const auto& stock = m_sys->getStock(); + m_broker->sell(m_sellRequest.datetime, stock.market(), stock.code(), 10.0, + m_sellRequest.number, m_sellRequest.stoploss, m_sellRequest.goal, + m_sellRequest.from); + } + + m_sys->getTM()->fetchAssetInfoFromBroker(m_broker); + m_sys->run(stock, m_query); + + if (m_sys->getParam("buy_delay")) { + m_buyRequest = m_sys->getBuyTradeRequest(); + } + + if (m_sys->getParam("sell_delay")) { + m_sellRequest = m_sys->getSellTradeRequest(); + } +} + +StrategyPtr HKU_API crtSysStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, + const string& name, const string& config_file) { + std::shared_ptr runner = + std::make_shared(sys, broker, query, costfunc); + + std::function func = [=]() { runner->run(stk); }; + + KQuery::KType ktype = query.kType(); + StrategyPtr stg = std::make_shared(vector{stk.market_code()}, + vector{ktype}, name, config_file); + + int32_t m = KQuery::getKTypeInMin(ktype); + if (m < KQuery::getKTypeInMin(KQuery::DAY)) { + stg->runDaily(std::move(func), Minutes(m), stk.market()); + } else { + stg->runDailyAt(std::move(func), TimeDelta(0, 14, 50)); + } + return stg; +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h new file mode 100644 index 00000000..1c52d5ec --- /dev/null +++ b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-08-24 + * Author: fasiondog + */ + +#pragma once +#include "hikyuu/trade_sys/system/System.h" +#include "Strategy.h" + +namespace hku { + +class HKU_API RunSystemInStrategy { +public: + RunSystemInStrategy() = default; + RunSystemInStrategy(const SYSPtr& sys, const OrderBrokerPtr& broker, const KQuery& query, + const TradeCostPtr& costfunc); + virtual ~RunSystemInStrategy() = default; + + void run(const Stock& stock); + +private: + SYSPtr m_sys; + OrderBrokerPtr m_broker; + KQuery m_query; + + TradeRequest m_buyRequest; + TradeRequest m_sellRequest; +}; + +StrategyPtr HKU_API crtSysStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, + const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, + const string& name = "SYSStrategy", + const string& config_file = ""); + +} // namespace hku \ No newline at end of file From 02a8c00f2ae3a3aff40b6f1fe6149cafa5bb975e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 14:08:54 +0800 Subject: [PATCH 505/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 8 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 135 ++++++++++++++---------- hikyuu_cpp/hikyuu/strategy/Strategy.h | 18 +++- hikyuu_pywrap/strategy/_Strategy.cpp | 34 +++--- 4 files changed, 116 insertions(+), 79 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 4cd8d76e..575f2909 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -7,11 +7,11 @@ from hikyuu import sm def on_change(stk, spot): - print(stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1) + print("[on_change]:", stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1) def on_spot(rev_time): - print("rev_time:", rev_time) + print("[on_received_spot] rev_time:", rev_time) def my_func(): @@ -24,8 +24,8 @@ def my_func(): # 以 Strategy 方式运行示例 if __name__ == '__main__': s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) - # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) + # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(10), False) s.on_change(on_change) s.on_received_spot(on_spot) - s.run_daily(my_func, Minutes(1)) + s.run_daily(my_func, Minutes(1)) # , ignore_market=True) s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 9307f250..58803124 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -62,9 +62,7 @@ Strategy::~Strategy() { CLS_INFO("Quit Strategy {}!", m_name); } -void Strategy::run() { - CLS_IF_RETURN(m_running, void()); - +void Strategy::_init() { StockManager& sm = StockManager::instance(); // sm 尚未初始化,则初始化 @@ -85,9 +83,15 @@ void Strategy::run() { // 先将行情接收代理停止,以便后面加入处理函数 stopSpotAgent(); +} + +void Strategy::start() { + _init(); + + _runDailyAt(); auto& agent = *getGlobalSpotAgent(); - agent.addProcess([this](const SpotRecord& spot) { receivedSpot(spot); }); + agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { if (m_on_recieved_spot) { EVENT([=]() { m_on_recieved_spot(revTime); }); @@ -95,30 +99,23 @@ void Strategy::run() { }); startSpotAgent(true); - m_running = true; -} + _runDaily(); -void Strategy::start() { - CLS_CHECK(m_running, "No handler functions are registered!"); CLS_INFO("start even loop ..."); _startEventLoop(); } void Strategy::onChange(std::function&& changeFunc) { - if (!m_running) { - run(); - } + HKU_CHECK(changeFunc, "Invalid changeFunc!"); m_on_change = changeFunc; } void Strategy::onReceivedSpot(std::function&& recievedFucn) { - if (!m_running) { - run(); - } + HKU_CHECK(recievedFucn, "Invalid recievedFucn!"); m_on_recieved_spot = recievedFucn; } -void Strategy::receivedSpot(const SpotRecord& spot) { +void Strategy::_receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { if (m_on_change) { @@ -128,14 +125,17 @@ void Strategy::receivedSpot(const SpotRecord& spot) { } void Strategy::runDaily(std::function&& func, const TimeDelta& delta, - const std::string& market) { - if (!m_running) { - run(); - } + const std::string& market, bool ignoreMarket) { + HKU_CHECK(func, "Invalid func!"); + m_run_daily_delta = delta; + m_run_daily_market = market; + m_ignoreMarket = ignoreMarket; - try { - auto* scheduler = getScheduler(); - auto new_func = [=]() { + if (ignoreMarket) { + m_run_daily_func = [=]() { EVENT(func); }; + + } else { + m_run_daily_func = [=]() { const auto& sm = StockManager::instance(); auto today = Datetime::today(); int day = today.dayOfWeek(); @@ -143,7 +143,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, return; } - auto market_info = sm.getMarketInfo(market); + auto market_info = sm.getMarketInfo(m_run_daily_market); Datetime open1 = today + market_info.openTime1(); Datetime close1 = today + market_info.closeTime1(); Datetime open2 = today + market_info.openTime2(); @@ -153,59 +153,79 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, EVENT(func); } }; + } +} +void Strategy::_runDaily() { + HKU_IF_RETURN(!m_run_daily_func, void()); + + auto* scheduler = getScheduler(); + if (m_ignoreMarket) { + scheduler->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); + return; + } + + try { const auto& sm = StockManager::instance(); - auto market_info = sm.getMarketInfo(market); + auto market_info = sm.getMarketInfo(m_run_daily_market); auto today = Datetime::today(); auto now = Datetime::now(); TimeDelta now_time = now - today; if (now_time >= market_info.closeTime2()) { scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } else if (now_time >= market_info.openTime2()) { int64_t ticks = now_time.ticks() - market_info.openTime2().ticks(); - int64_t delta_ticks = delta.ticks(); + int64_t delta_ticks = m_run_daily_delta.ticks(); if (ticks % delta_ticks == 0) { - scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + scheduler->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); } else { auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks); scheduler->addFuncAtTime(now + delay, [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } } else if (now_time >= market_info.closeTime1()) { scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } else if (now_time < market_info.closeTime1() && now_time >= market_info.openTime1()) { int64_t ticks = now_time.ticks() - market_info.openTime1().ticks(); - int64_t delta_ticks = delta.ticks(); + int64_t delta_ticks = m_run_daily_delta.ticks(); if (ticks % delta_ticks == 0) { - scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + scheduler->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); } else { auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks); scheduler->addFuncAtTime(now + delay, [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } } else if (now_time < market_info.openTime1()) { scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } else { @@ -218,26 +238,29 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, void Strategy::runDailyAt(std::function&& func, const TimeDelta& delta, bool ignoreHoliday) { - if (!m_running) { - run(); + HKU_CHECK(func, "Invalid func!"); + m_run_daily_at_delta = delta; + + if (ignoreHoliday) { + m_run_daily_at_func = [=]() { + const auto& sm = StockManager::instance(); + auto today = Datetime::today(); + int day = today.dayOfWeek(); + if (day != 0 && day != 6 && !sm.isHoliday(today)) { + EVENT(func); + } + }; + + } else { + m_run_daily_at_func = [=]() { EVENT(func); }; } +} - auto new_func = [=]() { - if (!ignoreHoliday) { - EVENT(func); - return; - } - - const auto& sm = StockManager::instance(); - auto today = Datetime::today(); - int day = today.dayOfWeek(); - if (day != 0 && day != 6 && !sm.isHoliday(today)) { - EVENT(func); - } - }; - - auto* scheduler = getScheduler(); - scheduler->addFuncAtTimeEveryDay(delta, new_func); +void Strategy::_runDailyAt() { + if (m_run_daily_at_func) { + auto* scheduler = getScheduler(); + scheduler->addFuncAtTimeEveryDay(m_run_daily_at_delta, m_run_daily_at_func); + } } /* diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 94a144b7..1143277d 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -50,9 +50,10 @@ public: * @param func 待执行的任务 * @param delta 间隔时间 * @param market 指定的市场 + * @param ignoreMarket 是否忽略市场时间限制,如为 true,则为定时循环不受开闭市时间限制 */ void runDaily(std::function&& func, const TimeDelta& delta, - const std::string& market = "SH"); + const std::string& market = "SH", bool ignoreMarket = false); /** * 每日在指定时刻执行任务 @@ -89,11 +90,20 @@ private: StrategyContext m_context; std::function m_on_recieved_spot; std::function m_on_change; - bool m_running{false}; + + std::function m_run_daily_func; + TimeDelta m_run_daily_delta; + string m_run_daily_market; + bool m_ignoreMarket{false}; + + std::function m_run_daily_at_func; + TimeDelta m_run_daily_at_delta; private: - void run(); - void receivedSpot(const SpotRecord& spot); + void _init(); + void _receivedSpot(const SpotRecord& spot); + void _runDaily(); + void _runDailyAt(); private: static std::atomic_bool ms_keep_running; diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 8280ed70..18ccb657 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -58,21 +58,25 @@ void export_Strategy(py::module& m) { }; self.onReceivedSpot(new_func); }) - .def("run_daily", - [](Strategy& self, py::object func, const TimeDelta& time) { - HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); - py::object c_func = func.attr("__call__"); - auto new_func = [=]() { - try { - c_func(); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR("Unknown error!"); - } - }; - self.runDaily(new_func, time); - }) + .def( + "run_daily", + [](Strategy& self, py::object func, const TimeDelta& time, std::string market, + bool ignore_market) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + auto new_func = [=]() { + try { + c_func(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.runDaily(new_func, time, market, ignore_market); + }, + py::arg("func"), py::arg("time"), py::arg("market") = "SH", + py::arg("ignore_market") = false) .def( "run_daily_at", [](Strategy& self, py::object func, const TimeDelta& time, bool ignore_holiday) { From ee8e7b0fb803a552c9e4b142ef795dd8632f8d03 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 15:47:39 +0800 Subject: [PATCH 506/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20python=20borker=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_manage/broker.py | 12 ++++++------ hikyuu/trade_manage/broker_easytrader.py | 24 +++++++++++++----------- hikyuu/trade_manage/broker_mail.py | 8 ++++---- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index a55f8770..1b855ed5 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -48,11 +48,11 @@ class OrderBrokerWrap(OrderBrokerBase): def _buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from): """实现 OrderBrokerBase 的 _buy 接口""" - self._broker.buy('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) + self._broker.buy(market, code, price, num, stoploss, goal_price, part_from) def _sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from): """实现 OrderBrokerBase 的 _sell 接口""" - self._broker.sell('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) + self._broker.sell(market, code, price, num, stoploss, goal_price, part_from) def _get_asset_info(self): try: @@ -71,11 +71,11 @@ class TestOrderBroker: def __init__(self): pass - def buy(self, code, price, num, stoploss, goal_price, part_from): - print(f"买入:{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}") + def buy(self, market, code, price, num, stoploss, goal_price, part_from): + print(f"买入:{market}{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}") - def sell(self, code, price, num, stoploss, goal_price, part_from): - print(f"卖出:{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}") + def sell(self, market, code, price, num, stoploss, goal_price, part_from): + print(f"卖出:{market}{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}") def crtOB(broker, name="NO_NAME"): diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index 81d7ded9..2f9f9ba4 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -12,20 +12,22 @@ class EasyTraderOrderBroker: self.user = user self.buffer = {} - def buy(self, code, price, num, stoploss, goal_price, part_from): - self.user.buy(code[2:], price=price, amount=num) - print("买入:%s %.3f %i" % (code, price, num)) - self.buffer[code] = (num, stoploss, goal_price) + def buy(self, market, code, price, num, stoploss, goal_price, part_from): + self.user.buy(code, price=price, amount=num) + market_code = f"{market}{code}" + print(f"买入:{market_code} {price} {num}") + self.buffer[market_code] = (num, stoploss, goal_price) - def sell(self, code, price, num, stoploss, goal_price, part_from): - self.user.sell(code[2:], price=price, amount=num) - print("卖出:%s %.3f %i" % (code, price, num)) - if code in self.buffer: - old_num = self.buffer[code][0] + def sell(self, market, code, price, num, stoploss, goal_price, part_from): + self.user.sell(code, price=price, amount=num) + market_code = f"{market}{code}" + print(f"卖出:{market_code} {price} {num}") + if market_code in self.buffer: + old_num = self.buffer[market_code][0] if old_num == num: - self.buffer.pop(code) + self.buffer.pop(market_code) else: - self.buffer[code] = (old_num - num, stoploss, goal_price) + self.buffer[market_code] = (old_num - num, stoploss, goal_price) def get_asset_info(self): '''以下只适用于华泰''' diff --git a/hikyuu/trade_manage/broker_mail.py b/hikyuu/trade_manage/broker_mail.py index 83889bf8..f6437f88 100644 --- a/hikyuu/trade_manage/broker_mail.py +++ b/hikyuu/trade_manage/broker_mail.py @@ -72,7 +72,7 @@ class MailOrderBroker: smtpObj.login(self._sender, self._pwd) smtpObj.sendmail(self._sender, self._receivers, message.as_string()) - def buy(self, code, price, num, stoploss, goal_price, part_from): + def buy(self, market, code, price, num, stoploss, goal_price, part_from): """执行买入操作,向指定的邮箱发送邮件,格式如下:: 邮件标题:【Hkyuu提醒】买入 证券代码 @@ -82,11 +82,11 @@ class MailOrderBroker: :param float price: 买入价格 :param int num: 买入数量 """ - action = "买入:{},价格:{},数量:{} ".format(code, price, num) + action = "买入:{}{},价格:{},数量:{} ".format(market, code, price, num) title = "【Hkyuu提醒】买入 {}".format(code) self._sendmail(title, action) - def sell(self, code, price, num, stoploss, goal_price, part_from): + def sell(self, market, code, price, num, stoploss, goal_price, part_from): """执行卖出操作,向指定的邮箱发送邮件,格式如下:: 邮件标题:【Hkyuu提醒】卖出 证券代码 @@ -96,6 +96,6 @@ class MailOrderBroker: :param float price: 卖出价格 :param int num: 卖出数量 """ - title = "【Hkyuu提醒】卖出 {}".format(code) + title = "【Hkyuu提醒】卖出 {}{}".format(market, code) action = "卖出:{},价格:{},数量:{} ".format(code, price, num) self._sendmail(title, action) From 2c85d256c983adf6b69cc3e31256ff57897c852e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 17:04:58 +0800 Subject: [PATCH 507/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20broker=20=E5=B8=AE?= =?UTF-8?q?=E5=8A=A9=E4=B8=8E=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/trade_manage/OrderBroker.rst | 93 ++++++++++++++++----- hikyuu/trade_manage/broker.py | 8 +- hikyuu/trade_manage/broker_easytrader.py | 8 +- hikyuu_pywrap/trade_manage/_OrderBroker.cpp | 56 +++++++------ 4 files changed, 115 insertions(+), 50 deletions(-) diff --git a/docs/source/trade_manage/OrderBroker.rst b/docs/source/trade_manage/OrderBroker.rst index e7e1b29b..4af76441 100644 --- a/docs/source/trade_manage/OrderBroker.rst +++ b/docs/source/trade_manage/OrderBroker.rst @@ -38,28 +38,40 @@ Python中的订单代理包装 .. py:class:: OrderBrokerWrap - 订单代理包装类,用户可以参考自定义自己的订单代理,加入额外的处理 + 用于包装 python 中订单代理包装类,这样 python 中的代理类无需从 OrderBrokerBase 继承,只需包含 buy, sell, get_asset_info 方法的实现即可。此类 python 代理类需要使用 crtOB 进行包装后, 才可供 c++ 调用。 .. py:method:: __init__(self, broker, name) :param broker: python broker 实例 :param str name: 名称 - .. py:method:: _buy(self, code, price, num) + .. py:method:: _buy(self, market, code, price, num, stoploss, goal_price, part_from) 包装 Python 变量的 buy 方法 + :param str market: 证券市场 "SH" | "SZ" :param str code: 证券代码 :param float price: 买入价格 - :param int num: 买入数量 + :param float num: 买入数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 - .. py:method:: _sell(self, code, price, num) + .. py:method:: _sell(self, market, code, price, num, stoploss, goal_price, part_from) 包装 Python 变量的 sell 方法 + :param str market: 证券市场 :param str code: 证券代码 :param float price: 卖出价格 - :param int num: 卖出数量 + :param float num: 卖出数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 + + .. py:method:: _get_asset_info(self) + + 详情参见 :py:meth:`OrderBrokerBase._get_asset_info` .. py:function:: crtOB(broker[, name="NO_NAME"]) @@ -90,29 +102,36 @@ Python中的订单代理包装 :param str pwd: 密码 :param list receivers: 接受者邮箱列表 - .. py:method:: buy(self, code, price, num) + .. py:method:: buy(self, market, code, price, num) 执行买入操作,向指定的邮箱发送邮件,格式如下: 邮件标题:【Hkyuu提醒】买入 证券代码 邮件内容:买入:证券代码,价格:买入的价格,数量:买入数量 + :param str market: 证券市场 :param str code: 证券代码 :param float price: 买入价格 - :param int num: 买入数量 + :param float num: 买入数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 - .. py:method:: sell(self, code, price, num) + .. py:method:: sell(self, market, code, price, num) 执行卖出操作,向指定的邮箱发送邮件,格式如下: 邮件标题:【Hkyuu提醒】卖出 证券代码 邮件内容:卖出:证券代码,价格:卖出的价格,数量:卖出数量 + :param str market: 证券市场 :param str code: 证券代码 :param float price: 卖出价格 - :param int num: 卖出数量 - + :param float num: 卖出数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 订单代理基类 @@ -124,6 +143,7 @@ Python中非必须使用 OrderBrokerBase 来实现自定义的订单代理。只 * :py:meth:`OrderBrokerBase._buy` - 【必须】执行实际买入操作 * :py:meth:`OrderBrokerBase._sell` - 【必须】执行实际卖出操作 +* :py:meth:`OrderBrokerBase._get_asset_info` - 【可选】返回当前资产信息,如需在 Strategy 中使用 sys/pf,需要实现该接口 .. py:class:: OrderBrokerBase @@ -138,38 +158,71 @@ Python中非必须使用 OrderBrokerBase 来实现自定义的订单代理。只 :param str name: 代理名称 - .. py:method:: buy(self, code, price, num) + .. py:method:: buy(self, market, code, price, num, stoploss, goal_price, part_from) 执行买入操作 + :param str market: 证券市场 :param str code: 证券代码 :param float price: 买入价格 - :param int num: 买入数量 + :param float num: 买入数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 :return: 买入操作的执行时刻 :rtype: Datetime - .. py:method:: sell(self, code, price, num) + .. py:method:: sell(self, market, code, price, num, stoploss, goal_price, part_from) 执行买入操作 + :param str market: 证券市场 :param str code: 证券代码 - :param float price: 买入价格 - :param int num: 买入数量 + :param float price: 卖出价格 + :param float num: 卖出数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 :return: 卖出操作的执行时刻 :rtype: Datetime - .. py:method:: _buy(self, code, price, num) + .. py:method:: _buy(self, market, code, price, num, stoploss, goal_price, part_from) 【重载接口】执行实际买入操作 :param str code: 证券代码 :param float price: 买入价格 - :param int num: 买入数量 + :param float num: 买入数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 - .. py:method:: _sell(self, code, price, num) + .. py:method:: _sell(self, market, code, price, num, stoploss, goal_price, part_from) 【重载接口】执行实际买入操作 + :param str market: 证券市场 :param str code: 证券代码 - :param float price: 买入价格 - :param int num: 买入数量 + :param float price: 卖出价格 + :param float num: 卖出数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源 + + .. py:method:: _get_asset_info(self) + + 【子类接口】获取当前资产信息,子类需返回符合如下规范的 json 字符串:: + + { + "datetime": "2001-01-01 18:00:00.12345", + "cash": 0.0, + "positions": [ + {"market": "SZ", "code": "000001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0, + "cost_price": 0.0}, + {"market": "SH", "code": "600001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0, + "cost_price": 0.0}, + ] + } + + :return: 以字符串(json格式)方式返回当前资产信息 + :rtype: str diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index 1b855ed5..bd6a1538 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -47,7 +47,13 @@ class OrderBrokerWrap(OrderBrokerBase): self._broker = broker def _buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from): - """实现 OrderBrokerBase 的 _buy 接口""" + """ + 实现 OrderBrokerBase 的 _buy 接口 + :param str market: 证券市场 + :param str code: 证券代码 + :param float price: 买入价格 + :param int num: 买入数量 + """ self._broker.buy(market, code, price, num, stoploss, goal_price, part_from) def _sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from): diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index 2f9f9ba4..04ad118c 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -15,13 +15,13 @@ class EasyTraderOrderBroker: def buy(self, market, code, price, num, stoploss, goal_price, part_from): self.user.buy(code, price=price, amount=num) market_code = f"{market}{code}" - print(f"买入:{market_code} {price} {num}") + print(f"计划买入:{market_code} {price} {num}") self.buffer[market_code] = (num, stoploss, goal_price) def sell(self, market, code, price, num, stoploss, goal_price, part_from): self.user.sell(code, price=price, amount=num) market_code = f"{market}{code}" - print(f"卖出:{market_code} {price} {num}") + print(f"计划卖出:{market_code} {price} {num}") if market_code in self.buffer: old_num = self.buffer[market_code][0] if old_num == num: @@ -53,4 +53,6 @@ class EasyTraderOrderBroker: positions.append(dict(market=market, code=code, number=v['可用余额'], stoploss=stoploss, goal_price=goal_price, cost_price=v['成本价'])) - return dict(datetime=str(Datetime.now()), cash=cash, positions=positions) + ret = dict(datetime=str(Datetime.now()), cash=cash, positions=positions) + print(ret) + return ret diff --git a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp index 25560843..dfd3f41d 100644 --- a/hikyuu_pywrap/trade_manage/_OrderBroker.cpp +++ b/hikyuu_pywrap/trade_manage/_OrderBroker.cpp @@ -60,30 +60,12 @@ void export_OrderBroker(py::module& m) { py::overload_cast(&OrderBrokerBase::name), py::return_value_policy::copy, "名称(可读写)") - .def("buy", &OrderBrokerBase::buy, R"(buy(self, datetime, market, code, price, num) - - 执行买入操作 - - :param Datetime datetime: 策略指示时间 - :param str market: 市场标识 - :param str code: 证券代码 - :param float price: 买入价格 - :param float num: 买入数量)") - - .def("sell", &OrderBrokerBase::sell, R"(sell(self, datetime, market, code, price, num) - - 执行卖出操作 - - :param Datetime datetime: 策略指示时间 - :param str market: 市场标识 - :param str code: 证券代码 - :param float price: 卖出价格 - :param float num: 卖出数量)") - - .def("get_asset_info", &OrderBrokerBase::getAssetInfo) + .def("buy", &OrderBrokerBase::buy, "详情见子类实现接口: _buy") + .def("sell", &OrderBrokerBase::sell, "详情见子类实现接口: _sell") + .def("get_asset_info", &OrderBrokerBase::getAssetInfo, "详情见子类实现接口: _get_asset_info") .def("_buy", &OrderBrokerBase::_buy, - R"(_buy(self, datetime, market, code, price, num) + R"(_buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from) 【子类接口】执行买入操作 @@ -91,10 +73,13 @@ void export_OrderBroker(py::module& m) { :param str market: 市场标识 :param str code: 证券代码 :param float price: 买入价格 - :param float num: 买入数量)") + :param float num: 买入数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源)") .def("_sell", &OrderBrokerBase::_sell, - R"(_sell(self, datetime, market, code, price, num) + R"(_sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from) 【子类接口】执行卖出操作 @@ -102,7 +87,26 @@ void export_OrderBroker(py::module& m) { :param str market: 市场标识 :param str code: 证券代码 :param float price: 卖出价格 - :param float num: 卖出数量)") + :param float num: 卖出数量 + :param float stoploss: 计划止损价 + :param float goal_price: 计划盈利目标价 + :param SystemPart part_from: 信号来源)") - .def("_get_asset_info", &OrderBrokerBase::_getAssetInfo); + .def("_get_asset_info", &OrderBrokerBase::_getAssetInfo, R"(_get_asset_info(self) + + 【子类接口】获取当前资产信息,子类需返回符合如下规范的 json 字符串: + + { + "datetime": "2001-01-01 18:00:00.12345", + "cash": 0.0, + "positions": [ + {"market": "SZ", "code": "000001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0, + "cost_price": 0.0}, + {"market": "SH", "code": "600001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0, + "cost_price": 0.0}, + ] + } + + :return: 以字符串(json格式)方式返回当前资产信息 + :rtype: str)"); } From 14a263f9297581c20f21a24557c60458c509cdd6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 17:24:12 +0800 Subject: [PATCH 508/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=20python=20strategy?= =?UTF-8?q?=20=E8=BF=90=E8=A1=8C=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/__init__.py | 1 - .../{strategy.py => strategy_demo1.py} | 21 ++++++++--- hikyuu/strategy/strategy_demo2.py | 35 +++++++++++++++++++ hikyuu/trade_manage/broker_easytrader.py | 9 +++-- 4 files changed, 59 insertions(+), 7 deletions(-) rename hikyuu/strategy/{strategy.py => strategy_demo1.py} (55%) create mode 100644 hikyuu/strategy/strategy_demo2.py diff --git a/hikyuu/strategy/__init__.py b/hikyuu/strategy/__init__.py index 24133630..6287b1fe 100644 --- a/hikyuu/strategy/__init__.py +++ b/hikyuu/strategy/__init__.py @@ -1,3 +1,2 @@ #!/usr/bin/python # -*- coding: utf8 -*- -# cp936 diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy_demo1.py similarity index 55% rename from hikyuu/strategy/strategy.py rename to hikyuu/strategy/strategy_demo1.py index 575f2909..0d283d7d 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy_demo1.py @@ -2,6 +2,7 @@ # -*- coding: utf8 -*- # cp936 +import easytrader from hikyuu import Strategy, Query, Datetime, TimeDelta, Seconds, Minutes from hikyuu import sm @@ -14,8 +15,12 @@ def on_spot(rev_time): print("[on_received_spot] rev_time:", rev_time) -def my_func(): - print("calculate:", Datetime.now()) +def my_func1(): + print("[my_func1]", str(Datetime.now())) + + +def my_func2(): + print("[my_func2] calculate:", Datetime.now()) for s in sm: print(s) @@ -24,8 +29,16 @@ def my_func(): # 以 Strategy 方式运行示例 if __name__ == '__main__': s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) - # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(10), False) + + # 当前自动10秒后执行,忽略节假日限制 + s.run_daily_at(my_func1, Datetime.now() - Datetime.today() + Seconds(10), False) + + # 收到指定 stock 的行情更新 s.on_change(on_change) + + # 收到行情更新 s.on_received_spot(on_spot) - s.run_daily(my_func, Minutes(1)) # , ignore_market=True) + + # 每隔 1 分钟循环一次 (ignore_market 忽略开闭市时间限制, 否则仅在开盘期间执行) + s.run_daily(my_func2, Minutes(1)) # , ignore_market=True) s.start() diff --git a/hikyuu/strategy/strategy_demo2.py b/hikyuu/strategy/strategy_demo2.py new file mode 100644 index 00000000..7d916c01 --- /dev/null +++ b/hikyuu/strategy/strategy_demo2.py @@ -0,0 +1,35 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- +# cp936 + +import easytrader +from hikyuu import * + + +# 创建 easytrade 订单代理(示例仅支持华泰)可自行参照 EasyTraderOrderBroker 修改 +# buy|sell 中已屏蔽实际通过easytrade下单,防止调试误操作,请自行根据需要打开 +user = easytrader.use('ht_client') +user.connect(r'D:\htwt\xiadan.exe') +easy_ob = EasyTraderOrderBroker(user) +broker = crtOB(easy_ob) + +# 获取交易系统策略 +# 目前 run_in_strategy 只支持非延迟交易,即收盘时立刻买入/卖出 +sys = get_part("default.sys.趋势双均线") +sys.set_param("buy_delay", False) +sys.set_param("sell_delay", False) + + +# 执行策略主体 +def my_func(): + run_in_strategy(sys, sm['sz000001'], Query(Datetime(20240101)), broker, TC_Zero()) + + +# 注意:每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! +# 以 Strategy 方式运行示例 +if __name__ == '__main__': + s = Strategy(['sh000001', 'sz000001'], [Query.MIN, Query.DAY]) + + # 每交易日 14点55分 执行 + s.run_daily_at(my_func, TimeDelta(0, 14, 55)) + s.start() diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index 04ad118c..f638e209 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -8,18 +8,23 @@ from hikyuu import Datetime, hku_info class EasyTraderOrderBroker: + ''' + 使用华泰客户端实例 + 注意:buy|sell 中已屏蔽实际通过easytrade下单,防止调试误操作,请自行根据需要打开 + ''' + def __init__(self, user): self.user = user self.buffer = {} def buy(self, market, code, price, num, stoploss, goal_price, part_from): - self.user.buy(code, price=price, amount=num) + # self.user.buy(code, price=price, amount=num) market_code = f"{market}{code}" print(f"计划买入:{market_code} {price} {num}") self.buffer[market_code] = (num, stoploss, goal_price) def sell(self, market, code, price, num, stoploss, goal_price, part_from): - self.user.sell(code, price=price, amount=num) + # self.user.sell(code, price=price, amount=num) market_code = f"{market}{code}" print(f"计划卖出:{market_code} {price} {num}") if market_code in self.buffer: From f009b507284cfcdb7cf5355c06deeb8999bc2161 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 17:46:42 +0800 Subject: [PATCH 509/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/strategy/Strategy.h | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 1143277d..c2f48ded 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -19,8 +19,12 @@ namespace hku { /** - * @brief 策略运行时 * @ingroup Stratgy + * @{ + */ + +/** + * @brief 策略运行时 */ class HKU_API Strategy { CLASS_LOGGER_IMP(Strategy) @@ -140,10 +144,29 @@ private: typedef shared_ptr StrategyPtr; +/** + * @brief 在策略运行时中执行系统交易 SYS + * @note 目前仅支持 buy_delay| sell_delay 均为 false 的系统,即 close 时执行交易 + * @param sys 交易系统 + * @param stk 交易对象 + * @param query 查询条件 + * @param broker 订单代理 + * @param costfunc 成本函数 + */ void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); +/** + * @brief 在策略运行时中执行组合策略 PF + * @note 目前仅支持 buy_delay| sell_delay 均为 false 的系统,即 close 时执行交易 + * @param pf 资产组合 + * @param query 查询条件 + * @param adjust_cycle 调仓周期 + * @param broker 订单代理 + * @param costfunc 成本函数 + */ void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); +/** @} */ } // namespace hku \ No newline at end of file From 940c1d89b432807bcbde0d443dcd7a3b8c48c0cc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 17:58:04 +0800 Subject: [PATCH 510/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy_demo1.py | 9 +++++++-- hikyuu/strategy/strategy_demo2.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/hikyuu/strategy/strategy_demo1.py b/hikyuu/strategy/strategy_demo1.py index 0d283d7d..4966c6c0 100644 --- a/hikyuu/strategy/strategy_demo1.py +++ b/hikyuu/strategy/strategy_demo1.py @@ -25,9 +25,14 @@ def my_func2(): print(s) -# 注意:每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! -# 以 Strategy 方式运行示例 +# 注意: +# 1.每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! +# 2.请开启 HikyuuTdx 行情采集,否则接收不到数据 +# Strategy 方式运行示例 if __name__ == '__main__': + # 创建策略运行时,必须指定 stock 和 ktype 列表 + # strategy 只会加载指定的 stock, ktype 的数据,行情接收也只会更新这些数据 + # 如需使用交易日历,请记得同时指定 sh000001 s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) # 当前自动10秒后执行,忽略节假日限制 diff --git a/hikyuu/strategy/strategy_demo2.py b/hikyuu/strategy/strategy_demo2.py index 7d916c01..ad3e4710 100644 --- a/hikyuu/strategy/strategy_demo2.py +++ b/hikyuu/strategy/strategy_demo2.py @@ -25,10 +25,15 @@ def my_func(): run_in_strategy(sys, sm['sz000001'], Query(Datetime(20240101)), broker, TC_Zero()) -# 注意:每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! -# 以 Strategy 方式运行示例 +# 注意: +# 1.每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! +# 2.请开启 HikyuuTdx 行情采集,否则接收不到数据 +# Strategy 方式运行示例 if __name__ == '__main__': - s = Strategy(['sh000001', 'sz000001'], [Query.MIN, Query.DAY]) + # 创建策略运行时,必须指定 stock 和 ktype 列表 + # strategy 只会加载指定的 stock, ktype 的数据,行情接收也只会更新这些数据 + # 如需使用交易日历,请记得同时指定 sh000001 + s = Strategy(['sh000001', 'sz000001'], [Query.DAY]) # 每交易日 14点55分 执行 s.run_daily_at(my_func, TimeDelta(0, 14, 55)) From 59316d806eeda41babfad18c0a1038ba519119c2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 18:00:40 +0800 Subject: [PATCH 511/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy_demo1.py | 9 +++++++-- hikyuu/strategy/strategy_demo2.py | 11 ++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/hikyuu/strategy/strategy_demo1.py b/hikyuu/strategy/strategy_demo1.py index 0d283d7d..4966c6c0 100644 --- a/hikyuu/strategy/strategy_demo1.py +++ b/hikyuu/strategy/strategy_demo1.py @@ -25,9 +25,14 @@ def my_func2(): print(s) -# 注意:每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! -# 以 Strategy 方式运行示例 +# 注意: +# 1.每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! +# 2.请开启 HikyuuTdx 行情采集,否则接收不到数据 +# Strategy 方式运行示例 if __name__ == '__main__': + # 创建策略运行时,必须指定 stock 和 ktype 列表 + # strategy 只会加载指定的 stock, ktype 的数据,行情接收也只会更新这些数据 + # 如需使用交易日历,请记得同时指定 sh000001 s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) # 当前自动10秒后执行,忽略节假日限制 diff --git a/hikyuu/strategy/strategy_demo2.py b/hikyuu/strategy/strategy_demo2.py index 7d916c01..ad3e4710 100644 --- a/hikyuu/strategy/strategy_demo2.py +++ b/hikyuu/strategy/strategy_demo2.py @@ -25,10 +25,15 @@ def my_func(): run_in_strategy(sys, sm['sz000001'], Query(Datetime(20240101)), broker, TC_Zero()) -# 注意:每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! -# 以 Strategy 方式运行示例 +# 注意: +# 1.每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行! +# 2.请开启 HikyuuTdx 行情采集,否则接收不到数据 +# Strategy 方式运行示例 if __name__ == '__main__': - s = Strategy(['sh000001', 'sz000001'], [Query.MIN, Query.DAY]) + # 创建策略运行时,必须指定 stock 和 ktype 列表 + # strategy 只会加载指定的 stock, ktype 的数据,行情接收也只会更新这些数据 + # 如需使用交易日历,请记得同时指定 sh000001 + s = Strategy(['sh000001', 'sz000001'], [Query.DAY]) # 每交易日 14点55分 执行 s.run_daily_at(my_func, TimeDelta(0, 14, 55)) From e49d51441ae6e2897ec268d0e14aed3bf503d6c1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 18:02:07 +0800 Subject: [PATCH 512/601] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=A4=BA=E4=BE=8B?= =?UTF-8?q?=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy_demo1.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/hikyuu/strategy/strategy_demo1.py b/hikyuu/strategy/strategy_demo1.py index 4966c6c0..ca3ec0f2 100644 --- a/hikyuu/strategy/strategy_demo1.py +++ b/hikyuu/strategy/strategy_demo1.py @@ -2,8 +2,7 @@ # -*- coding: utf8 -*- # cp936 -import easytrader -from hikyuu import Strategy, Query, Datetime, TimeDelta, Seconds, Minutes +from hikyuu import Strategy, Query, Datetime, Seconds, Minutes from hikyuu import sm From ed7f4d2b969c73ab2268be1dfc6e46312b74b4ac Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 21:58:20 +0800 Subject: [PATCH 513/601] =?UTF-8?q?=E5=AF=B9=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E4=B8=AD=E7=9A=84K=E7=BA=BF=E7=B1=BB=E5=9E=8B=E5=92=8C?= =?UTF-8?q?=E5=85=B6=E9=A2=84=E5=8A=A0=E8=BD=BD=E5=8F=82=E6=95=B0=E8=BF=9B?= =?UTF-8?q?=E8=A1=8C=E6=A3=80=E6=9F=A5=EF=BC=8C=E5=B9=B6=E7=BB=99=E5=87=BA?= =?UTF-8?q?=E8=AD=A6=E5=91=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 58803124..4a9e6a90 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -74,6 +74,17 @@ void Strategy::_init() { // 初始化 hikyuu_init(m_config_file, false, m_context); + + // 对上下文中的K线类型和其预加载参数进行检查,并给出警告 + const auto& ktypes = m_context.getKTypeList(); + const auto& preload_params = sm.getPreloadParameter(); + for (const auto& ktype : ktypes) { + std::string low_ktype = ktype; + to_lower(low_ktype); + HKU_ERROR_IF(!preload_params.tryGet(low_ktype, false), + "The K-line type in the context is not configured to be preloaded!"); + } + } else { m_context = sm.getStrategyContext(); } From 2df783b3e34858f1e1c9d558ec71c65f285d6b46 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 22:11:25 +0800 Subject: [PATCH 514/601] =?UTF-8?q?Strategy=E6=B7=BB=E5=8A=A0Context?= =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E6=9E=84=E9=80=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StrategyContext.h | 3 +++ hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 5 +++++ hikyuu_cpp/hikyuu/strategy/Strategy.h | 2 ++ hikyuu_pywrap/_StrategyContext.cpp | 5 +++-- hikyuu_pywrap/strategy/_Strategy.cpp | 2 ++ 5 files changed, 15 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index 8eb91a5f..133f43b2 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -22,6 +22,9 @@ public: explicit StrategyContext(vector&& stockCodeList) : m_stockCodeList(std::move(stockCodeList)) {} + StrategyContext(const vector& stockCodeList, const vector& ktypeList) + : m_stockCodeList(stockCodeList), m_ktypeList(ktypeList) {} + virtual ~StrategyContext() = default; bool isAll() const noexcept; diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 4a9e6a90..a5ad5989 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -57,6 +57,11 @@ Strategy::Strategy(const vector& codeList, const vector& m_context.setKTypeList(ktypeList); } +Strategy::Strategy(const StrategyContext& context, const string& name, const string& config_file) +: Strategy(name, config_file) { + m_context = m_context; +} + Strategy::~Strategy() { ms_keep_running = false; CLS_INFO("Quit Strategy {}!", m_name); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index c2f48ded..e3b39030 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -34,6 +34,8 @@ public: explicit Strategy(const string& name, const string& config_file = ""); Strategy(const vector& codeList, const vector& ktypeList, const string& name = "Strategy", const string& config_file = ""); + explicit Strategy(const StrategyContext& context, const string& name = "Strategy", + const string& config_file = ""); virtual ~Strategy(); diff --git a/hikyuu_pywrap/_StrategyContext.cpp b/hikyuu_pywrap/_StrategyContext.cpp index 519ef002..4f93c4ae 100644 --- a/hikyuu_pywrap/_StrategyContext.cpp +++ b/hikyuu_pywrap/_StrategyContext.cpp @@ -29,11 +29,12 @@ void setKTypeList(StrategyContext* self, const py::sequence& seq) { void export_StrategeContext(py::module& m) { py::class_(m, "StrategyContext", "策略上下文") .def(py::init<>()) + .def(py::init&, const vector&>(), py::arg("stock_list"), + py::arg("ktype_list")) .def_property("start_datetime", get_start_datetime, set_start_datetime, "起始日期") .def_property("stock_list", py::overload_cast<>(&StrategyContext::getStockCodeList, py::const_), setStockList, "股票代码列表") - .def_property("ktype_list", - py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), + .def_property("ktype_list", py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), setKTypeList, "需要的K线类型"); } diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 18ccb657..b0155b51 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -20,6 +20,8 @@ void export_Strategy(py::module& m) { const std::string&>(), py::arg("code_list"), py::arg("ktype_list"), py::arg("name") = "Strategy", py::arg("config") = "") + .def(py::init(), py::arg("context"), + py::arg("name") = "Strategy", py::arg("config") = "") .def_property("name", py::overload_cast<>(&Strategy::name, py::const_), py::overload_cast(&Strategy::name), py::return_value_policy::copy, "策略名称") From 0288e77644cd5fd31d9a307ee4992d4225ac3200 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 22:58:17 +0800 Subject: [PATCH 515/601] =?UTF-8?q?add=20crt=5Fsys=5Fstrategy=EF=BC=8C?= =?UTF-8?q?=E8=A1=A5=E5=85=85=E7=A4=BA=E4=BE=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy_demo1.py | 5 ++++ hikyuu/strategy/strategy_demo2.py | 11 +++++++-- hikyuu/strategy/strategy_demo3.py | 24 +++++++++++++++++++ .../hikyuu/strategy/RunSystemInStrategy.cpp | 13 +++++----- .../hikyuu/strategy/RunSystemInStrategy.h | 6 ++--- hikyuu_pywrap/strategy/_Strategy.cpp | 5 ++++ 6 files changed, 53 insertions(+), 11 deletions(-) create mode 100644 hikyuu/strategy/strategy_demo3.py diff --git a/hikyuu/strategy/strategy_demo1.py b/hikyuu/strategy/strategy_demo1.py index ca3ec0f2..0be90e9a 100644 --- a/hikyuu/strategy/strategy_demo1.py +++ b/hikyuu/strategy/strategy_demo1.py @@ -2,6 +2,11 @@ # -*- coding: utf8 -*- # cp936 +# +# 警告:Hikyuu 为量化研究工具,本身不包含程序化交易接口。此部分仅为策略调度运行时示例, +# 供自行实现程序化交易时参考,请自行负责程序化交易可能造成的损失。 +# + from hikyuu import Strategy, Query, Datetime, Seconds, Minutes from hikyuu import sm diff --git a/hikyuu/strategy/strategy_demo2.py b/hikyuu/strategy/strategy_demo2.py index ad3e4710..b60440a1 100644 --- a/hikyuu/strategy/strategy_demo2.py +++ b/hikyuu/strategy/strategy_demo2.py @@ -1,13 +1,16 @@ #!/usr/bin/python # -*- coding: utf8 -*- -# cp936 import easytrader from hikyuu import * +# +# 警告:Hikyuu 为量化研究工具,本身不包含程序化交易接口。此部分仅为策略调度运行时示例, +# 供自行实现程序化交易时参考,请自行负责程序化交易可能造成的损失。 +# # 创建 easytrade 订单代理(示例仅支持华泰)可自行参照 EasyTraderOrderBroker 修改 -# buy|sell 中已屏蔽实际通过easytrade下单,防止调试误操作,请自行根据需要打开 +# buy|sell 中已屏蔽实际通过easytrade下单,防止调试误操作 user = easytrader.use('ht_client') user.connect(r'D:\htwt\xiadan.exe') easy_ob = EasyTraderOrderBroker(user) @@ -22,6 +25,8 @@ sys.set_param("sell_delay", False) # 执行策略主体 def my_func(): + # 这里示例使用的是 TC_Zero() 零成本算法,但实际建议使用接近实际的成本算法 + # 因为 sys 不依赖于成交单中的实际成本,而是使用成本算法预算需要买入的数量 run_in_strategy(sys, sm['sz000001'], Query(Datetime(20240101)), broker, TC_Zero()) @@ -38,3 +43,5 @@ if __name__ == '__main__': # 每交易日 14点55分 执行 s.run_daily_at(my_func, TimeDelta(0, 14, 55)) s.start() + + # 上述,也可以参见 strategy_demo3.py 中使用 crt_sys_strategy 快捷创建 strategy diff --git a/hikyuu/strategy/strategy_demo3.py b/hikyuu/strategy/strategy_demo3.py new file mode 100644 index 00000000..448f0c8a --- /dev/null +++ b/hikyuu/strategy/strategy_demo3.py @@ -0,0 +1,24 @@ +#!/usr/bin/python +# -*- coding: utf8 -*- + +# +# 警告:Hikyuu 为量化研究工具,本身不包含程序化交易接口。此部分仅为策略调度运行时示例, +# 供自行实现程序化交易时参考,请自行负责程序化交易可能造成的损失。 +# + +if __name__ == '__main__': + import easytrader + from hikyuu import * + + # 创建 easytrade 订单代理(示例仅支持华泰)可自行参照 EasyTraderOrderBroker 修改 + user = easytrader.use('ht_client') + user.connect(r'D:\htwt\xiadan.exe') + easy_ob = EasyTraderOrderBroker(user) + broker = crtOB(easy_ob) + + sys = get_part("default.sys.趋势双均线") + + # 直接使用 sys 创建 strategy 示例,如果为日线,则自动每日 14点55分 执行 + # 如果 query 为日线以下(分钟线、5分钟线)则自动按对应间隔(分钟、5分钟)循环执行 + stg = crt_sys_strategy(sys, "sz000001", Query(Datetime(20240101), ktype=Query.DAY), broker, TC_Zero(), "demo3") + stg.start() diff --git a/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp index dd19d154..e3792007 100644 --- a/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.cpp @@ -60,21 +60,22 @@ void RunSystemInStrategy::run(const Stock& stock) { } } -StrategyPtr HKU_API crtSysStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, - const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, - const string& name, const string& config_file) { +StrategyPtr HKU_API crtSysStrategy(const SYSPtr& sys, const string& stk_market_code, + const KQuery& query, const OrderBrokerPtr& broker, + const TradeCostPtr& costfunc, const string& name, + const string& config_file) { std::shared_ptr runner = std::make_shared(sys, broker, query, costfunc); - std::function func = [=]() { runner->run(stk); }; + std::function func = [=]() { runner->run(getStock(stk_market_code)); }; KQuery::KType ktype = query.kType(); - StrategyPtr stg = std::make_shared(vector{stk.market_code()}, + StrategyPtr stg = std::make_shared(vector{stk_market_code, "SH000001"}, vector{ktype}, name, config_file); int32_t m = KQuery::getKTypeInMin(ktype); if (m < KQuery::getKTypeInMin(KQuery::DAY)) { - stg->runDaily(std::move(func), Minutes(m), stk.market()); + stg->runDaily(std::move(func), Minutes(m), "SH"); } else { stg->runDailyAt(std::move(func), TimeDelta(0, 14, 50)); } diff --git a/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h index 1c52d5ec..8f6514f9 100644 --- a/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h +++ b/hikyuu_cpp/hikyuu/strategy/RunSystemInStrategy.h @@ -29,9 +29,9 @@ private: TradeRequest m_sellRequest; }; -StrategyPtr HKU_API crtSysStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& query, - const OrderBrokerPtr& broker, const TradeCostPtr& costfunc, - const string& name = "SYSStrategy", +StrategyPtr HKU_API crtSysStrategy(const SYSPtr& sys, const string& stk_market_code, + const KQuery& query, const OrderBrokerPtr& broker, + const TradeCostPtr& costfunc, const string& name = "SYSStrategy", const string& config_file = ""); } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index b0155b51..d34db3d9 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -110,4 +111,8 @@ void export_Strategy(py::module& m) { const TradeCostPtr&>(runInStrategy), py::arg("pf"), py::arg("query"), py::arg("adjust_cycle"), py::arg("broker"), py::arg("cost_func")); + + m.def("crt_sys_strategy", crtSysStrategy, py::arg("sys"), py::arg("stk_market_code"), + py::arg("query"), py::arg("broker"), py::arg("cost_func"), + py::arg("name") = "SYSStrategy", py::arg("config") = ""); } \ No newline at end of file From 6384c5773b89a275ea12907fec7192de3e42ad76 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 26 Aug 2024 01:53:34 +0800 Subject: [PATCH 516/601] Release 2.1.2 --- docs/source/release.rst | 10 ++++++++++ xmake.lua | 2 +- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 029a3200..fcd35943 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,16 @@ 版本发布说明 ======================= +2.1.2 - 2024年8月26日 +------------------------- + +1. 优化 Strategy,调整 OrderBroker 接口,增加 strategy 示例 +2. 增加 miniqmt 行情采集 +3. 优化 realtime_update,移除sina|tushare源,增加指定stock列表 +4. 优化内部调度使用内部公共任务组 +5. fixed reload 时重新加载历史财务信息 + + 2.1.1 - 2024年8月9日 ------------------------- diff --git a/xmake.lua b/xmake.lua index 4c51ea60..3adda477 100644 --- a/xmake.lua +++ b/xmake.lua @@ -6,7 +6,7 @@ set_project("hikyuu") add_rules("mode.debug", "mode.release") -- version -set_version("2.1.1", {build = "%Y%m%d%H%M"}) +set_version("2.1.2", {build = "%Y%m%d%H%M"}) set_warnings("all") From bf9ececb0acfcd36b9512d766215f2d907893dfe Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 26 Aug 2024 02:40:49 +0800 Subject: [PATCH 517/601] =?UTF-8?q?=E6=B8=85=E9=99=A4=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KDataImp.cpp | 1 - hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp | 2 +- hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/KDataImp.cpp b/hikyuu_cpp/hikyuu/KDataImp.cpp index bcca0799..06daed1e 100644 --- a/hikyuu_cpp/hikyuu/KDataImp.cpp +++ b/hikyuu_cpp/hikyuu/KDataImp.cpp @@ -298,7 +298,6 @@ void KDataImp::_recoverEqualForward() { KRecordList kdata = m_buffer; // 防止同一天两条权息记录 StockWeightList::const_iterator weightIter = weightList.begin(); - StockWeightList::const_iterator pre_weightIter; size_t pre_pos = 0; for (; weightIter != weightList.end(); ++weightIter) { // 计算流通股份变动比例,但不处理仅仅只有流通股本改变的情况 diff --git a/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp index b184e7b2..bd53dda6 100644 --- a/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.cpp @@ -13,7 +13,7 @@ namespace hku { RunPortfolioInStrategy::RunPortfolioInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, const OrderBrokerPtr& broker, const TradeCostPtr& costfunc) -: m_pf(pf), m_adjust_cycle(adjust_cycle), m_broker(broker) { +: m_pf(pf), m_broker(broker), m_adjust_cycle(adjust_cycle) { HKU_ASSERT(pf && broker); if (query.queryType() == KQuery::INDEX) { diff --git a/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h index ea776b37..034c757e 100644 --- a/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h +++ b/hikyuu_cpp/hikyuu/strategy/RunPortfolioInStrategy.h @@ -25,7 +25,7 @@ private: PFPtr m_pf; OrderBrokerPtr m_broker; KQuery m_query; - int m_adjust_cycle; + int m_adjust_cycle = 1; }; StrategyPtr HKU_API crtPFStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, From 8a517d681ee2c117c1f34918c38aa0569ca7a9c9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 27 Aug 2024 00:37:36 +0800 Subject: [PATCH 518/601] add getDataFromBufferServer --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 44 +++++++++++++++++++++++++ hikyuu_cpp/hikyuu/strategy/Strategy.h | 3 ++ hikyuu_pywrap/strategy/_Strategy.cpp | 2 ++ xmake.lua | 1 + 4 files changed, 50 insertions(+) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index a5ad5989..39898fad 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -9,6 +9,7 @@ #include #include "hikyuu/utilities/os.h" #include "hikyuu/utilities/ini_parser/IniParser.h" +#include "hikyuu/utilities/node/NodeClient.h" #include "hikyuu/global/GlobalSpotAgent.h" #include "hikyuu/global/schedule/scheduler.h" #include "hikyuu/global/GlobalTaskGroup.h" @@ -335,4 +336,47 @@ void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycl pf->run(query, adjust_cycle, true); } +void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& stklist, + const KQuery::KType& ktype) { + // SPEND_TIME(getDataFromBufferServer); + NodeClient client(addr); + try { + HKU_CHECK(client.dial(), "Failed dial server!"); + json req; + req["cmd"] = "market"; + req["ktype"] = ktype; + json code_list; + for (const auto& stk : stklist) { + code_list.emplace_back(stk.market_code()); + } + req["codes"] = std::move(code_list); + + json res; + client.post(req, res); + HKU_ERROR_IF_RETURN(res["ret"] != NodeErrorCode::SUCCESS, void(), + "Recieved error: {}, msg: {}", res["ret"].get(), + res["msg"].get()); + + const auto& jdata = res["data"]; + for (auto iter = jdata.cbegin(); iter != jdata.cend(); ++iter) { + const auto& r = *iter; + try { + string market_code = r[0].get(); + Stock stk = getStock(market_code); + if (!stk.isNull()) { + KRecord k(Datetime(r[1].get()), r[2], r[3], r[4], r[5], r[6], r[7]); + stk.realtimeUpdate(k, ktype); + } + } catch (const std::exception& e) { + HKU_ERROR("Failed decode json: {}! {}", to_string(r), e.what()); + } + } + + } catch (const std::exception& e) { + HKU_ERROR("Failed get data from buffer server! {}", e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } +} + } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index e3b39030..756d491a 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -170,5 +170,8 @@ void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& qu void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); +void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& stklist, + const KQuery::KType& ktype); + /** @} */ } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index d34db3d9..4309b1bd 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -115,4 +115,6 @@ void export_Strategy(py::module& m) { m.def("crt_sys_strategy", crtSysStrategy, py::arg("sys"), py::arg("stk_market_code"), py::arg("query"), py::arg("broker"), py::arg("cost_func"), py::arg("name") = "SYSStrategy", py::arg("config") = ""); + + m.def("get_data_from_buffer_server", getDataFromBufferServer); } \ No newline at end of file diff --git a/xmake.lua b/xmake.lua index 3adda477..cda59619 100644 --- a/xmake.lua +++ b/xmake.lua @@ -112,6 +112,7 @@ set_configvar("HKU_ENABLE_MO", 0) set_configvar("HKU_ENABLE_HTTP_CLIENT", 1) set_configvar("HKU_ENABLE_HTTP_CLIENT_SSL", 0) set_configvar("HKU_ENABLE_HTTP_CLIENT_ZIP", 0) +set_configvar("HKU_ENABLE_NODE", 1) local boost_version = "1.85.0" local hdf5_version = "1.12.2" From bd830985a6951f22620dbf024b6db4c43071183a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 27 Aug 2024 16:02:26 +0800 Subject: [PATCH 519/601] =?UTF-8?q?fixed=20xtquant=20=E5=8F=AA=E6=94=AF?= =?UTF-8?q?=E6=8C=81=20windows?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 77 ++++++++++++++------------ hikyuu/interactive.py | 6 +- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index 01d21575..53d25b4a 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -4,46 +4,55 @@ # Create on: 2024-08-22 # Author: fasiondog -from xtquant import xtdata from hikyuu import Datetime from hikyuu.util import * +try: + from xtquant import xtdata -@hku_catch(trace=True, callback=lambda stk: hku_warn("Failed parse stk: {}", stk)) -def parse_one_result_qmt(stk_code: str, data: dict): - '''将 qmt tick 数据转为 spot 数据 + @hku_catch(trace=True, callback=lambda stk: hku_warn("Failed parse stk: {}", stk)) + def parse_one_result_qmt(stk_code: str, data: dict): + '''将 qmt tick 数据转为 spot 数据 - :param str stk_code: qmt 风格的证券编码,如 000001.SZ - :param dict data: 对应的 qmt 全推 tick 数据 - ''' - result = {} - code, market = stk_code.split('.') - result['market'] = market - result['code'] = code - result['name'] = '' - result['datetime'] = Datetime(data['timetag']) if 'timetag' in data else xtdata.timetag_to_datetime( - data['time'], "%Y-%m-%d %H:%M:%S") + :param str stk_code: qmt 风格的证券编码,如 000001.SZ + :param dict data: 对应的 qmt 全推 tick 数据 + ''' + result = {} + code, market = stk_code.split('.') + result['market'] = market + result['code'] = code + result['name'] = '' + result['datetime'] = Datetime(data['timetag']) if 'timetag' in data else xtdata.timetag_to_datetime( + data['time'], "%Y-%m-%d %H:%M:%S") - result['yesterday_close'] = data['lastClose'] - result['open'] = data['open'] - result['high'] = data['high'] - result['low'] = data['low'] - result['close'] = data['lastPrice'] - result['amount'] = data['amount'] * 0.001 # 转千元 - result['volume'] = data['pvolume'] * 0.01 # 转手数 + result['yesterday_close'] = data['lastClose'] + result['open'] = data['open'] + result['high'] = data['high'] + result['low'] = data['low'] + result['close'] = data['lastPrice'] + result['amount'] = data['amount'] * 0.001 # 转千元 + result['volume'] = data['pvolume'] * 0.01 # 转手数 - for i in range(5): - result[f'bid{i+1}'] = data['bidPrice'][i] - result[f'bid{i+1}_amount'] = data['bidVol'][i] - result[f'ask{i+1}'] = data['askPrice'][i] - result[f'ask{i+1}_amount'] = data['askVol'][i] - return result + for i in range(5): + result[f'bid{i+1}'] = data['bidPrice'][i] + result[f'bid{i+1}_amount'] = data['bidVol'][i] + result[f'ask{i+1}'] = data['askPrice'][i] + result[f'ask{i+1}_amount'] = data['askVol'][i] + return result + def get_spot(stocklist, unused1, unused2, batch_func=None): + code_list = [f'{s.code}.{s.market}' for s in stocklist] + full_tick = xtdata.get_full_tick(code_list) + records = [parse_one_result_qmt(code, data) for code, data in full_tick.items()] + if batch_func is not None: + batch_func(records) + return records -def get_spot(stocklist, unused1, unused2, batch_func=None): - code_list = [f'{s.code}.{s.market}' for s in stocklist] - full_tick = xtdata.get_full_tick(code_list) - records = [parse_one_result_qmt(code, data) for code, data in full_tick.items()] - if batch_func is not None: - batch_func(records) - return records +except: + def parse_one_result_qmt(stk_code: str, data: dict): + hku_error("Not fount xtquant") + return dict() + + def get_spot(stocklist, unused1, unused2, batch_func=None): + hku_error("Not fount xtquant") + return list() diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 5932c2b3..b7918cb7 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -347,7 +347,11 @@ def realtime_update_from_website(source, stk_list=None): def realtime_update_from_qmt(stk_list=None): - from xtquant import xtdata + try: + from xtquant import xtdata + except: + # xtquant 不支持 linux,需自行下载安装 + return if stk_list is None: stk_list = sm code_list = [f'{s.code}.{s.market}' for s in stk_list if s.valid] From c291b9a1f082a245aa34d11398622d576bc52648 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 27 Aug 2024 18:56:32 +0800 Subject: [PATCH 520/601] =?UTF-8?q?=E8=B0=83=E6=95=B4mysql/sqlite=20codepr?= =?UTF-8?q?e=20=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0019.sql | 6 ++++++ hikyuu/data/sqlite_upgrade/0020.sql | 4 ++++ 2 files changed, 10 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0019.sql create mode 100644 hikyuu/data/sqlite_upgrade/0020.sql diff --git a/hikyuu/data/mysql_upgrade/0019.sql b/hikyuu/data/mysql_upgrade/0019.sql new file mode 100644 index 00000000..f63cc12b --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0019.sql @@ -0,0 +1,6 @@ +UPDATE `hku_base`.`coderuletype` SET `codepre`=510 WHERE `codepre`=51 AND `marketid`=1; +UPDATE `hku_base`.`coderuletype` SET `codepre`=500 WHERE `codepre`=50 AND `marketid`=1; +UPDATE `hku_base`.`coderuletype` SET `codepre`=200 WHERE `codepre`=20 AND `marketid`=2; +INSERT INTO `hku_base`.`coderuletype` (`marketid`, `codepre`, `type`, `description`) VALUES (1, "56", 5, "上证ETF"); +INSERT INTO `hku_base`.`coderuletype` (`marketid`, `codepre`, `type`, `description`) VALUES (1, "58", 5, "上证ETF"); +UPDATE `hku_base`.`version` set `version` = 19; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0020.sql b/hikyuu/data/sqlite_upgrade/0020.sql new file mode 100644 index 00000000..8b744115 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0020.sql @@ -0,0 +1,4 @@ +BEGIN TRANSACTION; +UPDATE `coderuletype` SET `codepre`=150 WHERE `codepre`=15 AND `marketid`=2; +UPDATE `version` set `version` = 20; +COMMIT; \ No newline at end of file From 5af90429e17e4029e995531dd173f842e66ea507 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 27 Aug 2024 22:21:52 +0800 Subject: [PATCH 521/601] update --- hikyuu_cpp/demo/demo3.cpp | 114 ++++++++++++++++++++++++++++++++++++++ hikyuu_cpp/demo/xmake.lua | 24 ++++++++ 2 files changed, 138 insertions(+) create mode 100644 hikyuu_cpp/demo/demo3.cpp diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp new file mode 100644 index 00000000..9f5b3327 --- /dev/null +++ b/hikyuu_cpp/demo/demo3.cpp @@ -0,0 +1,114 @@ +// demo.cpp : 定义控制台应用程序的入口点。 +// + +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +using namespace hku; + +NodeServer server; + +void signal_handle(int signal) { + if (signal == SIGINT || signal == SIGTERM) { + HKU_INFO("Shutdown now ..."); + server.stop(); + exit(0); + } +} + +int main(int argc, char* argv[]) { + std::signal(SIGINT, signal_handle); + std::signal(SIGTERM, signal_handle); + + initLogger(false, "./demo.log"); + +#if defined(_WIN32) + // Windows 下设置控制台程序输出代码页为 UTF8 + auto old_cp = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); +#endif + + try { + // 配置文件的位置自行修改 + hikyuu_init("C:\\Users\\admin\\.hikyuu\\hikyuu.ini"); + + // 启动行情接收(只是计算回测可以不需要) + startSpotAgent(true); + + server.setAddr("tcp://0.0.0.0:9201"); + + server.regHandle("market", [](json&& req) { + HKU_INFO("--> req from {}:{}", req["remote_host"].get(), + req["remote_port"].get()); + HKU_ASSERT(req.contains("ktype")); + HKU_ASSERT(req.contains("codes")); + + string ktype = req["ktype"].get(); + auto& sm = StockManager::instance(); + const auto& param = sm.getPreloadParameter(); + string low_ktype = ktype; + to_lower(low_ktype); + HKU_CHECK(param.tryGet(low_ktype, false), "The ktype: {} is not be preloaded!", + ktype); + + json data; + const auto& jcodes = req["codes"]; + for (auto iter = jcodes.cbegin(); iter != jcodes.cend(); ++iter) { + string market_code = to_string(*iter); + market_code = market_code.substr(1, market_code.size() - 2); + Stock stk = getStock(market_code); + if (stk.isNull()) { + HKU_WARN("Not found stock: {}", market_code); + continue; + } + + KRecordList krecords = + stk.getKRecordList(KQueryByIndex(-1, Null(), ktype)); + if (!krecords.empty()) { + const auto& k = krecords.back(); + json jr; + jr.emplace_back(market_code); + jr.emplace_back(k.datetime.str()); + jr.emplace_back(k.openPrice); + jr.emplace_back(k.highPrice); + jr.emplace_back(k.lowPrice); + jr.emplace_back(k.closePrice); + jr.emplace_back(k.transAmount); + jr.emplace_back(k.transCount); + data.emplace_back(std::move(jr)); + } + } + + json res; + res["data"] = data; + // HKU_INFO("<-- res: {}", to_string(res)); + return res; + }); + + server.start(); + // server.loop(); + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(10)); + } + + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + + server.stop(); + +#if defined(_WIN32) + SetConsoleOutputCP(old_cp); +#endif + return 0; +} diff --git a/hikyuu_cpp/demo/xmake.lua b/hikyuu_cpp/demo/xmake.lua index 59fb328a..1953b42e 100644 --- a/hikyuu_cpp/demo/xmake.lua +++ b/hikyuu_cpp/demo/xmake.lua @@ -44,3 +44,27 @@ target("demo2") add_files("./demo2.cpp") target_end() + + +target("demo3") + set_kind("binary") + set_default(false) + + add_packages("boost", "spdlog", "fmt", "nng", "nlohmann_json") + add_includedirs("..") + + if is_plat("windows") then + add_cxflags("-wd4267") + add_cxflags("-wd4251") + end + + if is_plat("windows") and get_config("kind") == "shared" then + add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") + add_defines("SQLITE_API=__declspec(dllimport)") + end + + add_deps("hikyuu") + + add_files("./demo3.cpp") +target_end() From d51a6491d5d4b095ec2a7730fa8e400e494c972a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 27 Aug 2024 22:44:39 +0800 Subject: [PATCH 522/601] Release 2.1.3 --- docs/source/release.rst | 7 +++++++ xmake.lua | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index fcd35943..1359c17f 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,6 +1,13 @@ 版本发布说明 ======================= +2.1.2 - 2024年8月27日 +------------------------- + +1. fixed 未安装 xtquant 时无法使用 HikyuuTdx +2. 调整 codepre 配置,补充上证ETF基金 + + 2.1.2 - 2024年8月26日 ------------------------- diff --git a/xmake.lua b/xmake.lua index cda59619..7cdd5f58 100644 --- a/xmake.lua +++ b/xmake.lua @@ -6,7 +6,7 @@ set_project("hikyuu") add_rules("mode.debug", "mode.release") -- version -set_version("2.1.2", {build = "%Y%m%d%H%M"}) +set_version("2.1.3", {build = "%Y%m%d%H%M"}) set_warnings("all") From 3008b2dfbe270a6f2e4609e52e4b4343f0ca6e58 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 01:53:53 +0800 Subject: [PATCH 523/601] update --- hikyuu_cpp/demo/demo3.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp index 9f5b3327..dd270185 100644 --- a/hikyuu_cpp/demo/demo3.cpp +++ b/hikyuu_cpp/demo/demo3.cpp @@ -7,6 +7,7 @@ #include #include #include +#include #if defined(_WIN32) #include @@ -38,7 +39,7 @@ int main(int argc, char* argv[]) { try { // 配置文件的位置自行修改 - hikyuu_init("C:\\Users\\admin\\.hikyuu\\hikyuu.ini"); + hikyuu_init(fmt::format("{}/.hikyuu/hikyuu.ini", getUserDir())); // 启动行情接收(只是计算回测可以不需要) startSpotAgent(true); @@ -61,6 +62,7 @@ int main(int argc, char* argv[]) { json data; const auto& jcodes = req["codes"]; + // HKU_INFO("codes size: {}", jcodes.size()); for (auto iter = jcodes.cbegin(); iter != jcodes.cend(); ++iter) { string market_code = to_string(*iter); market_code = market_code.substr(1, market_code.size() - 2); From ac67dd3b03f01ca0c4e8478b213afe442980e947 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 01:57:48 +0800 Subject: [PATCH 524/601] update demo1 --- hikyuu_cpp/demo/demo1.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/demo/demo1.cpp b/hikyuu_cpp/demo/demo1.cpp index 83bd837e..1c377d22 100644 --- a/hikyuu_cpp/demo/demo1.cpp +++ b/hikyuu_cpp/demo/demo1.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #if defined(_WIN32) #include @@ -20,8 +21,7 @@ int main(int argc, char* argv[]) { #endif // 配置文件的位置自行修改 - hikyuu_init("C:\\Users\\admin\\.hikyuu\\hikyuu.ini"); - // hikyuu_init("/home/fsd/.hikyuu/hikyuu.ini"); + hikyuu_init(fmt::format("{}/.hikyuu/hikyuu.ini", getUserDir())); StockManager& sm = StockManager::instance(); From 3097c2b454c30cc253be9980c931113b2a12c295 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 12:55:45 +0800 Subject: [PATCH 525/601] =?UTF-8?q?=E5=8F=96=E6=B6=88=E6=8E=A5=E6=94=B6?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE=E6=97=B6=E7=9A=84=E5=BC=80?= =?UTF-8?q?=E9=97=AD=E5=B8=82=E6=97=B6=E9=97=B4=E9=99=90=E5=88=B6=EF=BC=8C?= =?UTF-8?q?=E6=9C=89=E8=A1=8C=E6=83=85=E6=8E=A8=E9=80=81=E7=AB=AF=E8=87=AA?= =?UTF-8?q?=E8=A1=8C=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 2a6637ff..1ebca6fd 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -38,7 +38,7 @@ static string getSpotMarketCode(const SpotRecord& spot) { static void updateStockDayData(const SpotRecord& spot) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull(), void()); - HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); + // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); KRecord krecord(Datetime(spot.datetime.year(), spot.datetime.month(), spot.datetime.day()), spot.open, spot.high, spot.low, spot.close, spot.amount, spot.volume); stk.realtimeUpdate(krecord, KQuery::DAY); @@ -47,7 +47,7 @@ static void updateStockDayData(const SpotRecord& spot) { static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull(), void()); - HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); + // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); std::function endOfPhase; std::function startOfPhase; @@ -87,6 +87,8 @@ static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { } KRecord last_record = stk.getKRecord(total - 1, ktype); + HKU_IF_RETURN(!last_record.isValid(), void()); + if (spot_end_of_phase > last_record.datetime) { // 如果当前的日期大于最后记录的日期,则为新增数据,直接更新并返回 stk.realtimeUpdate(KRecord(spot_end_of_phase, spot.open, spot.high, spot.low, spot.close, @@ -119,7 +121,7 @@ static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull(), void()); - HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); + // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); TimeDelta gap; if (KQuery::MIN == ktype) { From b63cf630fba454cad956b82abc14c9001e68afd1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 12:58:48 +0800 Subject: [PATCH 526/601] =?UTF-8?q?=E5=A2=9E=E5=BC=BA=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=96=B9=E5=BC=8F=E4=B8=8B=E7=A8=B3=E5=AE=9A?= =?UTF-8?q?=E6=80=A7=EF=BC=8C=E9=98=B2=E6=AD=A2=E6=80=A7=E8=83=BD=E8=BE=83?= =?UTF-8?q?=E5=B7=AE=E7=9A=84=E6=9C=BA=E5=99=A8=E5=8F=AF=E8=83=BD=E5=87=BA?= =?UTF-8?q?=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 19 ++++++++++++++++--- hikyuu_cpp/hikyuu/Stock.h | 2 +- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 8 +++++++- 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index da851d09..fb1e8662 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -402,6 +402,7 @@ bool Stock::isBuffer(KQuery::KType ktype) const { HKU_IF_RETURN(!m_data, false); string nktype(ktype); to_upper(nktype); + std::unique_lock lock(*(m_data->pMutex[ktype])); return m_data->pKData.find(nktype) != m_data->pKData.end() && m_data->pKData[nktype]; } @@ -497,11 +498,22 @@ size_t Stock::getCount(KQuery::KType kType) const { HKU_IF_RETURN(!m_data, 0); string nktype(kType); to_upper(nktype); - if (m_data->pKData.find(nktype) != m_data->pKData.end() && m_data->pKData[nktype]) { + if (isBuffer(nktype)) { return _getCountFromBuffer(nktype); } - return m_kdataDriver ? m_kdataDriver->getConnect()->getCount(market(), code(), nktype) : 0; + size_t ret = + m_kdataDriver ? m_kdataDriver->getConnect()->getCount(market(), code(), nktype) : 0; + + // 异步加载时如果数据库数据量大于预加载数量强制返回预加载最大数量 + const auto& preload_params = StockManager::instance().getPreloadParameter(); + if (preload_params.tryGet(nktype, false)) { + int num = preload_params.tryGet(fmt::format("{}_max", nktype), 4096); + if (ret > num) { + ret = num; + } + } + return ret; } price_t Stock::getMarketValue(const Datetime& datetime, KQuery::KType inktype) const { @@ -702,7 +714,8 @@ bool Stock::_getIndexRangeByDateFromBuffer(const KQuery& query, size_t& out_star KRecord Stock::_getKRecordFromBuffer(size_t pos, const KQuery::KType& ktype) const { std::shared_lock lock(*(m_data->pMutex[ktype])); - return m_data->pKData[ktype]->at(pos); + const auto& buf = *(m_data->pKData[ktype]); + return pos >= buf.size() ? KRecord() : buf[pos]; } KRecord Stock::getKRecord(size_t pos, const KQuery::KType& kType) const { diff --git a/hikyuu_cpp/hikyuu/Stock.h b/hikyuu_cpp/hikyuu/Stock.h index e8ee4445..e0fbbd18 100644 --- a/hikyuu_cpp/hikyuu/Stock.h +++ b/hikyuu_cpp/hikyuu/Stock.h @@ -162,7 +162,7 @@ public: */ bool getIndexRange(const KQuery& query, size_t& out_start, size_t& out_end) const; - /** 获取指定索引的K线数据记录,未作越界检查 */ + /** 获取指定索引的K线数据记录,pos 无效时返回 Null */ KRecord getKRecord(size_t pos, const KQuery::KType& dataType = KQuery::DAY) const; /** 根据数据类型(日线/周线等),获取指定日期的KRecord */ diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 5de1f3f6..24552478 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -54,7 +54,13 @@ public: : m_func(func), m_spot(spot) {} void operator()() { - m_func(m_spot); + try { + m_func(m_spot); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } } private: From cb2a69b73a879790f90a4f321c402a1836ed6546 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 21:44:52 +0800 Subject: [PATCH 527/601] =?UTF-8?q?=E5=B0=9A=E6=9C=AA=E8=A2=AB=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD=E8=87=B3=E5=86=85=E5=AD=98=E7=9A=84=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=BF=BD=E7=95=A5=E8=A1=8C=E6=83=85=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 1ebca6fd..72fd4b90 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -37,7 +37,7 @@ static string getSpotMarketCode(const SpotRecord& spot) { static void updateStockDayData(const SpotRecord& spot) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); - HKU_IF_RETURN(stk.isNull(), void()); + HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(KQuery::DAY), void()); // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); KRecord krecord(Datetime(spot.datetime.year(), spot.datetime.month(), spot.datetime.day()), spot.open, spot.high, spot.low, spot.close, spot.amount, spot.volume); @@ -46,7 +46,7 @@ static void updateStockDayData(const SpotRecord& spot) { static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); - HKU_IF_RETURN(stk.isNull(), void()); + HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(ktype), void()); // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); std::function endOfPhase; @@ -120,7 +120,7 @@ static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); - HKU_IF_RETURN(stk.isNull(), void()); + HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(ktype), void()); // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); TimeDelta gap; From 1fb030bf8b0cf0113a5725df89cb173427c40dbd Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 22:12:59 +0800 Subject: [PATCH 528/601] =?UTF-8?q?getDataFromBufferServer=20=E5=A2=9E?= =?UTF-8?q?=E5=8A=A0K=E7=BA=BF=E6=98=AF=E5=90=A6=E8=A2=AB=E9=A2=84?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 39898fad..e6967c79 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -339,6 +339,12 @@ void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycl void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& stklist, const KQuery::KType& ktype) { // SPEND_TIME(getDataFromBufferServer); + const auto& preload = StockManager::instance().getPreloadParameter(); + string low_ktype = ktype; + to_lower(low_ktype); + HKU_ERROR_IF_RETURN(!preload.tryGet(low_ktype, false), void(), + "The {} kdata is not preload! Can't update!", low_ktype); + NodeClient client(addr); try { HKU_CHECK(client.dial(), "Failed dial server!"); @@ -358,6 +364,7 @@ void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& s res["msg"].get()); const auto& jdata = res["data"]; + // HKU_INFO("{}", to_string(jdata)); for (auto iter = jdata.cbegin(); iter != jdata.cend(); ++iter) { const auto& r = *iter; try { From 18fe64e2e3903e2bfb8472a14e78fd9ad22b1f0f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 28 Aug 2024 23:56:57 +0800 Subject: [PATCH 529/601] =?UTF-8?q?=E5=88=86=E9=92=9F=E7=BA=A7=E5=88=AB?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=96=B0=E6=97=B6?= =?UTF-8?q?=EF=BC=8C=E8=BF=87=E6=BB=A4=E6=9C=AC=E8=BA=AB=E6=B2=A1=E6=9C=89?= =?UTF-8?q?=E5=88=86=E9=92=9F=E7=BA=A7=E5=88=AB=E6=95=B0=E6=8D=AE=E7=9A=84?= =?UTF-8?q?=E8=AF=81=E5=88=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 3 ++- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 11 ++++++++--- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index fb1e8662..100b32c0 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -901,7 +901,8 @@ void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { } else if (tmp.datetime < record.datetime) { m_data->pKData[ktype]->push_back(record); } else { - HKU_INFO("Ignore record, datetime < last record.datetime!"); + HKU_INFO("Ignore record, datetime({}) < last record.datetime({})! {} {}", record.datetime, + tmp.datetime, market_code(), inktype); } } diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 72fd4b90..6501973f 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -149,8 +149,13 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { } Datetime minute = spot.datetime; - minute = minute - (minute - minute.startOfDay()) % gap; - KRecordList klist = stk.getKRecordList(KQuery(minute, minute + gap, ktype)); + Datetime today = minute.startOfDay(); + // 非24小时交易品种,且时间和当天零时相同认为无分钟线级别数据 + HKU_IF_RETURN(stk.type() != STOCKTYPE_CRYPTO && minute == today, void()); + + Datetime start_minute = minute - (minute - today) % gap; + Datetime end_minute = start_minute + gap; + KRecordList klist = stk.getKRecordList(KQuery(start_minute, end_minute, ktype)); price_t sum_amount = 0.0, sum_volume = 0.0; for (const auto& k : klist) { sum_amount += k.transAmount; @@ -160,7 +165,7 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { price_t amount = spot.amount > sum_amount ? spot.amount - sum_amount : spot.amount; price_t spot_volume = spot.volume * 100; // spot 传过来的是手数 price_t volume = spot_volume > sum_volume ? spot_volume - sum_volume : spot_volume; - KRecord krecord(minute, spot.open, spot.high, spot.low, spot.close, amount, volume); + KRecord krecord(end_minute, spot.open, spot.high, spot.low, spot.close, amount, volume); stk.realtimeUpdate(krecord, ktype); } From 0fc80b8d691f0a9f341dbbf0e29c202dad69d947 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 29 Aug 2024 00:10:53 +0800 Subject: [PATCH 530/601] update spot_server.py --- hikyuu/gui/spot_server.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hikyuu/gui/spot_server.py b/hikyuu/gui/spot_server.py index 744445ca..cb70977b 100644 --- a/hikyuu/gui/spot_server.py +++ b/hikyuu/gui/spot_server.py @@ -271,7 +271,9 @@ def collect(server, use_proxy, source, seconds, phase1, phase2, ignore_weekend): sm = StockManager.instance() if source == 'qmt': - stk_list = [s for s in sm if s.valid] + stk_list = [s for s in sm if s.valid and s.type in ( + constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, + constant.STOCKTYPE_GEM, constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ)] else: stk_list = [ stk.market_code.lower() for stk in sm if stk.valid and stk.type in From 1331954b06582728a601dfe8a8ccc8a19ab1e44a Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 29 Aug 2024 01:00:50 +0800 Subject: [PATCH 531/601] =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E6=97=B6=E5=8C=85=E6=8B=AC=E9=97=AD=E5=B8=82?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=82=B9=E6=95=B0=E6=8D=AE=EF=BC=8C=E6=81=A2?= =?UTF-8?q?=E5=A4=8D=E6=9B=B4=E6=96=B0=E6=97=B6=E5=AF=B9=E5=BC=80=E9=97=AD?= =?UTF-8?q?=E5=B8=82=E7=9A=84=E6=97=B6=E9=97=B4=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 4 ++-- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 100b32c0..468b04b1 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -865,11 +865,11 @@ bool Stock::isTransactionTime(Datetime time) { Datetime today = Datetime::today(); Datetime openTime1 = today + market_info.openTime1(); Datetime closeTime1 = today + market_info.closeTime1(); - HKU_IF_RETURN(time >= openTime1 && time < closeTime1, true); // close判断不包括等于 + HKU_IF_RETURN(time >= openTime1 && time <= closeTime1, true); // close判断包括等于 Datetime openTime2 = today + market_info.openTime2(); Datetime closeTime2 = today + market_info.closeTime2(); - return time >= openTime2 && time < closeTime2; + return time >= openTime2 && time <= closeTime2; } void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 6501973f..80b37c0f 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -38,7 +38,7 @@ static string getSpotMarketCode(const SpotRecord& spot) { static void updateStockDayData(const SpotRecord& spot) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(KQuery::DAY), void()); - // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); + HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); KRecord krecord(Datetime(spot.datetime.year(), spot.datetime.month(), spot.datetime.day()), spot.open, spot.high, spot.low, spot.close, spot.amount, spot.volume); stk.realtimeUpdate(krecord, KQuery::DAY); @@ -47,7 +47,7 @@ static void updateStockDayData(const SpotRecord& spot) { static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(ktype), void()); - // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); + HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); std::function endOfPhase; std::function startOfPhase; @@ -121,7 +121,7 @@ static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(ktype), void()); - // HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); + HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); TimeDelta gap; if (KQuery::MIN == ktype) { From baa9c4b67c676f0fa10d088d81962ae302a3ba13 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 29 Aug 2024 15:18:18 +0800 Subject: [PATCH 532/601] =?UTF-8?q?=E4=BC=98=E5=8C=96init=E8=AF=BB?= =?UTF-8?q?=E5=8F=96=E9=85=8D=E7=BD=AE=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 2 - hikyuu_cpp/hikyuu/KQuery.cpp | 2 +- hikyuu_cpp/hikyuu/KQuery.h | 4 +- hikyuu_cpp/hikyuu/StockManager.cpp | 40 +------------------ hikyuu_cpp/hikyuu/StockManager.h | 7 +--- hikyuu_cpp/hikyuu/hikyuu.cpp | 63 ++++++++++++------------------ hikyuu_cpp/hikyuu/hikyuu.h | 4 ++ 7 files changed, 35 insertions(+), 87 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index b7918cb7..8bc0ee6b 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -83,8 +83,6 @@ ini.read(config_file, encoding='utf-8') hku_param = Parameter() hku_param["tmpdir"] = ini.get('hikyuu', 'tmpdir') hku_param["datadir"] = ini.get('hikyuu', 'datadir') -if ini.has_option('hikyuu', 'logger'): - hku_param["logger"] = ini['hikyuu']['logger'] if ini.has_option('hikyuu', 'quotation_server'): hku_param["quotation_server"] = ini['hikyuu']['quotation_server'] diff --git a/hikyuu_cpp/hikyuu/KQuery.cpp b/hikyuu_cpp/hikyuu/KQuery.cpp index 1aaa1bdf..9d4ff62a 100644 --- a/hikyuu_cpp/hikyuu/KQuery.cpp +++ b/hikyuu_cpp/hikyuu/KQuery.cpp @@ -55,7 +55,7 @@ static const unordered_map g_ktype2min{ }; // 获取所有的 KType -vector& KQuery::getAllKType() { +const vector& KQuery::getAllKType() { return g_all_ktype; } diff --git a/hikyuu_cpp/hikyuu/KQuery.h b/hikyuu_cpp/hikyuu/KQuery.h index 5b63dcc7..7a9885cf 100644 --- a/hikyuu_cpp/hikyuu/KQuery.h +++ b/hikyuu_cpp/hikyuu/KQuery.h @@ -70,7 +70,7 @@ public: // static const string INVALID_KTYPE; /** 获取所有的 KType */ - static vector& getAllKType(); + static const vector& getAllKType(); static int32_t getKTypeInMin(KType); @@ -93,7 +93,7 @@ public: m_end(Null()), m_queryType(INDEX), m_dataType(DAY), - m_recoverType(NO_RECOVER){}; + m_recoverType(NO_RECOVER) {}; /** * K线查询,范围[start, end) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index a7db183f..1d25b72b 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -55,44 +55,6 @@ StockManager& StockManager::instance() { return (*m_sm); } -Parameter default_preload_param() { - Parameter param; - param.set("day", true); - param.set("week", false); - param.set("month", false); - param.set("quarter", false); - param.set("halfyear", false); - param.set("year", false); - param.set("min", false); - param.set("min5", false); - param.set("min15", false); - param.set("min30", false); - param.set("min60", false); - param.set("hour2", false); - param.set("ticks", false); - param.set("day_max", 100000); - param.set("week_max", 100000); - param.set("month_max", 100000); - param.set("quarter_max", 100000); - param.set("halfyear_max", 100000); - param.set("year_max", 100000); - param.set("min_max", 5120); - param.set("min5_max", 5120); - param.set("min15_max", 5120); - param.set("min30_max", 5120); - param.set("min60_max", 5120); - param.set("hour2_max", 5120); - param.set("ticks_max", 5120); - return param; -} - -Parameter default_other_param() { - Parameter param; - param.set("tmpdir", "."); - param.set("logger", ""); - return param; -} - void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockParam, const Parameter& kdataParam, const Parameter& preloadParam, const Parameter& hikyuuParam, const StrategyContext& context) { @@ -239,7 +201,7 @@ void StockManager::reload() { continue; } - auto& ktype_list = KQuery::getAllKType(); + const auto& ktype_list = KQuery::getAllKType(); for (auto& ktype : ktype_list) { if (iter->second.isBuffer(ktype)) { tg->submit([=]() mutable { diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index c6acd0e8..dccee812 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -21,9 +21,6 @@ namespace hku { typedef vector MarketList; -Parameter default_preload_param(); -Parameter default_other_param(); - /** * 证券信息统一管理类 * @ingroup StockManage @@ -47,8 +44,8 @@ public: * @param context 策略上下文 */ void init(const Parameter& baseInfoParam, const Parameter& blockParam, - const Parameter& kdataParam, const Parameter& preloadParam = default_preload_param(), - const Parameter& hikyuuParam = default_other_param(), + const Parameter& kdataParam, const Parameter& preloadParam, + const Parameter& hikyuuParam, const StrategyContext& context = StrategyContext({"all"})); /** 重新加载 */ diff --git a/hikyuu_cpp/hikyuu/hikyuu.cpp b/hikyuu_cpp/hikyuu/hikyuu.cpp index a37c9d5d..e8cbb723 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.cpp +++ b/hikyuu_cpp/hikyuu/hikyuu.cpp @@ -21,33 +21,32 @@ static Parameter g_hikyuu_context; void hikyuu_init(const string& config_file_name, bool ignore_preload, const StrategyContext& context) { - IniParser config; - try { - config.read(config_file_name); - - } catch (std::invalid_argument& e) { - HKU_FATAL("Reading configure error! {}", e.what()); - exit(1); - } catch (std::logic_error& e) { - HKU_FATAL("Reading configure error! {}", e.what()); - exit(1); - } catch (...) { - HKU_WARN("Reading configure error! Don't know error!"); - exit(1); + Parameter baseParam, blockParam, kdataParam, preloadParam, hkuParam; + getConfigFromIni(config_file_name, baseParam, blockParam, kdataParam, preloadParam, hkuParam); + if (ignore_preload) { + const auto& ktypes = KQuery::getAllKType(); + for (const auto& ktype : ktypes) { + string low_ktype = ktype; + to_lower(low_ktype); + preloadParam.set(low_ktype, false); + } } - Parameter baseParam, blockParam, kdataParam, preloadParam, hkuParam; + StockManager& sm = StockManager::instance(); + sm.init(baseParam, blockParam, kdataParam, preloadParam, hkuParam, context); +} + +void HKU_API getConfigFromIni(const string& config_file_name, Parameter& baseParam, + Parameter& blockParam, Parameter& kdataParam, Parameter& preloadParam, + Parameter& hkuParam) { + IniParser config; + config.read(config_file_name); hkuParam.set("tmpdir", config.get("hikyuu", "tmpdir", ".")); hkuParam.set("datadir", config.get("hikyuu", "datadir", ".")); hkuParam.set("quotation_server", config.get("hikyuu", "quotation_server", "ipc:///tmp/hikyuu_real.ipc")); - if (!config.hasSection("baseinfo")) { - HKU_FATAL("Missing configure of baseinfo!"); - exit(1); - } - IniParser::StringListPtr option = config.getOptionList("baseinfo"); for (auto iter = option->begin(); iter != option->end(); ++iter) { string value = config.get("baseinfo", *iter); @@ -69,26 +68,14 @@ void hikyuu_init(const string& config_file_name, bool ignore_preload, kdataParam.set(*iter, config.get("kdata", *iter)); } - option = config.getOptionList("preload"); - - for (auto iter = option->begin(); iter != option->end(); ++iter) { - try { - auto pos = (*iter).find("max"); - if (pos == std::string::npos) { - preloadParam.set(*iter, - ignore_preload ? false : config.getBool("preload", *iter)); - } else if (!ignore_preload) { - preloadParam.set(*iter, config.getInt("preload", *iter)); - } - } catch (const std::exception& e) { - HKU_ERROR("proload param ({}) error! {}!", *iter, e.what()); - } catch (...) { - HKU_ERROR("proload param ({})! Unknown error!", *iter); - } + const auto& ktypes = KQuery::getAllKType(); + for (const auto& ktype : ktypes) { + string low_ktype = ktype; + to_lower(low_ktype); + preloadParam.set(low_ktype, config.getBool("preload", low_ktype, "False")); + string num_preload = fmt::format("{}_max", low_ktype); + preloadParam.set(num_preload, config.getInt("preload", num_preload, "4096")); } - - StockManager& sm = StockManager::instance(); - sm.init(baseParam, blockParam, kdataParam, preloadParam, hkuParam, context); } } // namespace hku diff --git a/hikyuu_cpp/hikyuu/hikyuu.h b/hikyuu_cpp/hikyuu/hikyuu.h index 600e0baa..f08808c5 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.h +++ b/hikyuu_cpp/hikyuu/hikyuu.h @@ -35,6 +35,10 @@ namespace hku { void HKU_API hikyuu_init(const string& config_file_name, bool ignore_preload = false, const StrategyContext& context = StrategyContext({"all"})); +void HKU_API getConfigFromIni(const string& config_file_name, Parameter& baseParam, + Parameter& blockParam, Parameter& kdataParam, Parameter& preloadParam, + Parameter& hkuParam); + /** @} */ } // namespace hku From cae2add39e03cf85100b8243022e081818f57ece Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 29 Aug 2024 22:11:18 +0800 Subject: [PATCH 533/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=E9=BB=98=E8=AE=A4?= =?UTF-8?q?=E5=B8=82=E5=9C=BA=E9=97=AD=E5=B8=82=E6=97=B6=E9=97=B4=EF=BC=8C?= =?UTF-8?q?=E5=BB=B6=E9=95=BF5=E5=88=86=E9=92=9F=EF=BC=8C=E6=94=BE?= =?UTF-8?q?=E5=9C=A8qmt=E7=AD=89=E6=9C=80=E5=90=8E=E8=A1=8C=E6=83=85?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=9C=A815=E7=82=B9=E4=B9=8B=E5=90=8E?= =?UTF-8?q?=E5=8F=AF=E8=83=BD=E5=BB=B6=E8=BF=9F=E6=95=B0=E7=A7=92=E6=89=8D?= =?UTF-8?q?=E6=9C=89=E6=9C=80=E7=BB=88=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0020.sql | 4 ++++ hikyuu/data/sqlite_upgrade/0021.sql | 6 ++++++ 2 files changed, 10 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0020.sql create mode 100644 hikyuu/data/sqlite_upgrade/0021.sql diff --git a/hikyuu/data/mysql_upgrade/0020.sql b/hikyuu/data/mysql_upgrade/0020.sql new file mode 100644 index 00000000..8504a27a --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0020.sql @@ -0,0 +1,4 @@ +UPDATE `hku_base`.`market` SET `closeTime1`=1135, `closeTime2`=1505 WHERE `marketid`=1; +UPDATE `hku_base`.`market` SET `closeTime1`=1135, `closeTime2`=1505 WHERE `marketid`=2; +UPDATE `hku_base`.`market` SET `closeTime1`=1135, `closeTime2`=1505 WHERE `marketid`=3; +UPDATE `hku_base`.`version` set `version` = 20; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0021.sql b/hikyuu/data/sqlite_upgrade/0021.sql new file mode 100644 index 00000000..0af1b2a6 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0021.sql @@ -0,0 +1,6 @@ +BEGIN TRANSACTION; +UPDATE `market` SET `closeTime1`=1135, `closeTime2`=1505 WHERE `marketid`=1; +UPDATE `market` SET `closeTime1`=1135, `closeTime2`=1505 WHERE `marketid`=2; +UPDATE `market` SET `closeTime1`=1135, `closeTime2`=1505 WHERE `marketid`=3; +UPDATE `version` set `version` = 21; +COMMIT; \ No newline at end of file From 6c3fd9f0d1761a967865d30bb33fedf818e40005 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 30 Aug 2024 01:27:51 +0800 Subject: [PATCH 534/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E7=BA=BF=E7=A8=8B=E6=B1=A0=E7=BA=BF=E7=A8=8B=E6=95=B0=E5=8F=AF?= =?UTF-8?q?=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 21 ++++++++++++-------- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 19 +++++++++++------- hikyuu_cpp/hikyuu/hikyuu.cpp | 14 +++++++++++++ hikyuu_cpp/hikyuu/hikyuu.h | 9 +++++++++ hikyuu_cpp/hikyuu/strategy/Strategy.h | 1 + 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 1d25b72b..88d1ded9 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -480,6 +480,7 @@ void StockManager::loadAllHolidays() { } void StockManager::loadAllStockWeights() { + HKU_IF_RETURN(!m_hikyuuParam.tryGet("load_stock_weight", true), void()); HKU_INFO("Loading stock weight..."); if (m_context.isAll()) { auto all_stkweight_dict = m_baseInfoDriver->getAllStockWeightList(); @@ -511,10 +512,12 @@ void StockManager::loadAllZhBond10() { } void StockManager::loadHistoryFinanceField() { - auto fields = m_baseInfoDriver->getHistoryFinanceField(); - for (const auto& field : fields) { - m_field_ix_to_name[field.first - 1] = field.second; - m_field_name_to_ix[field.second] = field.first - 1; + if (m_hikyuuParam.tryGet("load_history_finance", true)) { + auto fields = m_baseInfoDriver->getHistoryFinanceField(); + for (const auto& field : fields) { + m_field_ix_to_name[field.first - 1] = field.second; + m_field_name_to_ix[field.second] = field.first - 1; + } } } @@ -531,10 +534,12 @@ vector> StockManager::getHistoryFinanceAllFields() con } void StockManager::loadHistoryFinance() { - auto* tg = getGlobalTaskGroup(); - std::lock_guard lock1(*m_stockDict_mutex); - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - tg->submit([=]() { iter->second.getHistoryFinance(); }); + if (m_hikyuuParam.tryGet("load_history_finance", true)) { + auto* tg = getGlobalTaskGroup(); + std::lock_guard lock1(*m_stockDict_mutex); + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { + tg->submit([=]() { iter->second.getHistoryFinance(); }); + } } } diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp index d7b7a1f3..746452d5 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp @@ -10,6 +10,7 @@ #include "hikyuu/GlobalInitializer.h" #include "hikyuu/utilities/Log.h" #include "GlobalTaskGroup.h" +#include "hikyuu/StockManager.h" namespace hku { @@ -18,13 +19,17 @@ static TaskGroup* g_threadPool; TaskGroup* getGlobalTaskGroup() { static std::once_flag oc; std::call_once(oc, [&]() { - auto cpu_num = std::thread::hardware_concurrency(); - if (cpu_num >= 4) { - cpu_num -= 2; - } else if (cpu_num > 1) { - cpu_num--; - } - g_threadPool = new TaskGroup(cpu_num); + // auto cpu_num = std::thread::hardware_concurrency(); + // if (cpu_num >= 4) { + // cpu_num -= 2; + // } else if (cpu_num > 1) { + // cpu_num--; + // } + const auto& param = StockManager::instance().getHikyuuParameter(); + size_t worker_num = + param.tryGet("commont_thread_pool_num", std::thread::hardware_concurrency()); + HKU_INFO("work_num: {}", worker_num); + g_threadPool = new TaskGroup(worker_num); }); return g_threadPool; } diff --git a/hikyuu_cpp/hikyuu/hikyuu.cpp b/hikyuu_cpp/hikyuu/hikyuu.cpp index e8cbb723..478c9776 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.cpp +++ b/hikyuu_cpp/hikyuu/hikyuu.cpp @@ -46,6 +46,20 @@ void HKU_API getConfigFromIni(const string& config_file_name, Parameter& basePar hkuParam.set("datadir", config.get("hikyuu", "datadir", ".")); hkuParam.set("quotation_server", config.get("hikyuu", "quotation_server", "ipc:///tmp/hikyuu_real.ipc")); + // 加载权息数据 + hkuParam.set("load_stock_weight", config.getBool("hikyuu", "load_stock_weight", "True")); + + // 加载历史财务数据 + hkuParam.set("load_history_finance", + config.getBool("hikyuu", "load_history_finance", "True")); + + // 公共线程池线程数量 + size_t cpu_num = 2 * std::thread::hardware_concurrency(); + if (cpu_num > 30) { + cpu_num = 30; + } + hkuParam.set("commont_thread_pool_num", config.getInt("hikyuu", "commont_thread_pool_num", + fmt::format("{}", cpu_num))); IniParser::StringListPtr option = config.getOptionList("baseinfo"); for (auto iter = option->begin(); iter != option->end(); ++iter) { diff --git a/hikyuu_cpp/hikyuu/hikyuu.h b/hikyuu_cpp/hikyuu/hikyuu.h index f08808c5..27cb874a 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.h +++ b/hikyuu_cpp/hikyuu/hikyuu.h @@ -35,6 +35,15 @@ namespace hku { void HKU_API hikyuu_init(const string& config_file_name, bool ignore_preload = false, const StrategyContext& context = StrategyContext({"all"})); +/** + * @brief 尝试从 ini 文件获取配置参数 + * @param config_file_name ini 文件名 + * @param baseParam [out] + * @param blockParam [out] + * @param kdataParam [out] + * @param preloadParam [out] + * @param hkuParam [out] + */ void HKU_API getConfigFromIni(const string& config_file_name, Parameter& baseParam, Parameter& blockParam, Parameter& kdataParam, Parameter& preloadParam, Parameter& hkuParam); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 756d491a..e59fa451 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -28,6 +28,7 @@ namespace hku { */ class HKU_API Strategy { CLASS_LOGGER_IMP(Strategy) + PARAMETER_SUPPORT public: Strategy(); From 08526c2f180b4b04f9772dc144ade18b4e77326b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 30 Aug 2024 12:48:41 +0800 Subject: [PATCH 535/601] =?UTF-8?q?=E8=B0=83=E6=95=B4K=E7=BA=BF=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=8A=A0=E8=BD=BD=E7=AD=96=E7=95=A5=EF=BC=8C=E5=85=88?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E5=90=8C=E4=B8=80=E7=B1=BB=E5=9E=8B=E7=9A=84?= =?UTF-8?q?K=E7=BA=BF=EF=BC=8C=E4=BD=BF=E7=94=A8=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E7=B1=BB=E5=9E=8B=E6=95=B0=E6=8D=AE=E6=B5=8B=E8=AF=95=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E7=94=A8=E7=AD=89=E5=BE=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KQuery.cpp | 2 +- hikyuu_cpp/hikyuu/KQuery.h | 2 +- hikyuu_cpp/hikyuu/StockManager.cpp | 56 +++++++++--------------------- 3 files changed, 18 insertions(+), 42 deletions(-) diff --git a/hikyuu_cpp/hikyuu/KQuery.cpp b/hikyuu_cpp/hikyuu/KQuery.cpp index 9d4ff62a..72c683e7 100644 --- a/hikyuu_cpp/hikyuu/KQuery.cpp +++ b/hikyuu_cpp/hikyuu/KQuery.cpp @@ -55,7 +55,7 @@ static const unordered_map g_ktype2min{ }; // 获取所有的 KType -const vector& KQuery::getAllKType() { +const vector& KQuery::getAllKType() { return g_all_ktype; } diff --git a/hikyuu_cpp/hikyuu/KQuery.h b/hikyuu_cpp/hikyuu/KQuery.h index 7a9885cf..3bc301b2 100644 --- a/hikyuu_cpp/hikyuu/KQuery.h +++ b/hikyuu_cpp/hikyuu/KQuery.h @@ -70,7 +70,7 @@ public: // static const string INVALID_KTYPE; /** 获取所有的 KType */ - static const vector& getAllKType(); + static const vector& getAllKType(); static int32_t getKTypeInMin(KType); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 88d1ded9..1a490ca7 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -139,20 +139,28 @@ void StockManager::setKDataDriver(const KDataDriverConnectPoolPtr& driver) { } void StockManager::loadAllKData() { - const auto& ktypes = KQuery::getAllKType(); + // 按 K 线类型控制加载顺序 + // const auto& ktypes = KQuery::getAllKType(); + vector ktypes{KQuery::DAY, KQuery::MIN, KQuery::WEEK, KQuery::MONTH, + KQuery::QUARTER, KQuery::HALFYEAR, KQuery::YEAR, KQuery::MIN5, + KQuery::MIN15, KQuery::MIN30, KQuery::MIN60, KQuery::MIN3, + KQuery::HOUR2, KQuery::HOUR4, KQuery::HOUR6, KQuery::HOUR12}; + HKU_ASSERT(ktypes.size() == KQuery::getAllKType().size()); + vector low_ktypes; low_ktypes.reserve(ktypes.size()); for (const auto& ktype : ktypes) { auto& back = low_ktypes.emplace_back(ktype); to_lower(back); - HKU_INFO_IF(m_preloadParam.tryGet(back, false), "Preloading all {} kdata to buffer!", + HKU_INFO_IF(m_preloadParam.tryGet(back, false), "Preloading all {} kdata to buffer !", back); } + // 先加载同类K线 auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); if (!driver->getPrototype()->canParallelLoad()) { - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - for (size_t i = 0, len = ktypes.size(); i < len; i++) { + for (size_t i = 0, len = ktypes.size(); i < len; i++) { + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { const auto& low_ktype = low_ktypes[i]; if (m_preloadParam.tryGet(low_ktype, false)) { iter->second.loadKDataToBuffer(ktypes[i]); @@ -163,8 +171,8 @@ void StockManager::loadAllKData() { } else { // 异步并行加载 auto* tg = getGlobalTaskGroup(); - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - for (size_t i = 0, len = ktypes.size(); i < len; i++) { + for (size_t i = 0, len = ktypes.size(); i < len; i++) { + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { const auto& low_ktype = low_ktypes[i]; if (m_preloadParam.tryGet(low_ktype, false)) { tg->submit( @@ -179,6 +187,7 @@ void StockManager::reload() { HKU_IF_RETURN(m_initializing, void()); m_initializing = true; + HKU_INFO("start reload ..."); loadAllHolidays(); loadAllMarketInfos(); loadAllStockTypeInfo(); @@ -188,40 +197,7 @@ void StockManager::reload() { loadHistoryFinanceField(); m_blockDriver->load(); - - HKU_INFO("start reload kdata to buffer"); - std::vector can_not_parallel_stk_list; // 记录不支持并行加载的Stock - { - auto* tg = getGlobalTaskGroup(); - std::lock_guard lock(*m_stockDict_mutex); - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - auto driver = iter->second.getKDataDirver(); - if (!driver->getPrototype()->canParallelLoad()) { - can_not_parallel_stk_list.push_back(iter->second); - continue; - } - - const auto& ktype_list = KQuery::getAllKType(); - for (auto& ktype : ktype_list) { - if (iter->second.isBuffer(ktype)) { - tg->submit([=]() mutable { - Stock& stk = iter->second; - stk.loadKDataToBuffer(ktype); - }); - } - } - } - } - - for (auto& stk : can_not_parallel_stk_list) { - const auto& ktype_list = KQuery::getAllKType(); - for (const auto& ktype : ktype_list) { - if (stk.isBuffer(ktype)) { - stk.loadKDataToBuffer(ktype); - } - } - } - + loadAllKData(); loadHistoryFinance(); m_initializing = false; } From 1305c73512f7a0b2b400fadfbecae2a813e731c6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 30 Aug 2024 12:49:32 +0800 Subject: [PATCH 536/601] =?UTF-8?q?fixed=20reload=E5=90=8E=20TMP=20market?= =?UTF-8?q?=20=E4=B8=A2=E5=A4=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 1a490ca7..510e1f3e 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -120,11 +120,6 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa initInnerTask(); - // add special Market, for temp csv file - m_marketInfoDict["TMP"] = - MarketInfo("TMP", "Temp Csv file", "temp load from csv file", "000001", Null(), - TimeDelta(0), TimeDelta(0), TimeDelta(0), TimeDelta(0)); - std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); m_initializing = false; @@ -436,6 +431,11 @@ void StockManager::loadAllMarketInfos() { to_upper(market); m_marketInfoDict[market] = marketInfo; } + + // add special Market, for temp csv file + m_marketInfoDict["TMP"] = + MarketInfo("TMP", "Temp Csv file", "temp load from csv file", "000001", Null(), + TimeDelta(0), TimeDelta(0), TimeDelta(0), TimeDelta(0)); } void StockManager::loadAllStockTypeInfo() { From 0d5babe00c28afb773140dc14b18de11f3f82b40 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 30 Aug 2024 14:31:08 +0800 Subject: [PATCH 537/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20StockManager=20?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=E9=83=A8=E5=88=86=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 73 ++++++++++++------------------ hikyuu_cpp/hikyuu/StockManager.h | 4 +- 2 files changed, 32 insertions(+), 45 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 510e1f3e..52f1fdf2 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -87,6 +87,22 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa m_baseInfoDriver = DataDriverFactory::getBaseInfoDriver(baseInfoParam); HKU_CHECK(m_baseInfoDriver, "Failed get base info driver!"); + // 获取板块驱动 + m_blockDriver = DataDriverFactory::getBlockDriver(blockParam); + + auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); + HKU_CHECK(driver, "driver is null!"); + if (m_kdataDriverParam != driver->getPrototype()->getParameter()) { + m_kdataDriverParam = driver->getPrototype()->getParameter(); + } + + loadData(); + initInnerTask(); + + m_initializing = false; +} + +void StockManager::loadData() { loadAllHolidays(); loadAllMarketInfos(); loadAllStockTypeInfo(); @@ -95,42 +111,16 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa loadAllZhBond10(); loadHistoryFinanceField(); - // 获取板块驱动 - m_blockDriver = DataDriverFactory::getBlockDriver(blockParam); - // 获取K线数据驱动并预加载指定的数据 HKU_INFO("Loading KData..."); std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); - auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); - HKU_CHECK(driver, "driver is null!"); - if (m_kdataDriverParam != driver->getPrototype()->getParameter()) { - m_kdataDriverParam = driver->getPrototype()->getParameter(); - } - setKDataDriver(driver); - - // 加载 block,须在 stock 的 kdatadriver 被设置之后调用 m_blockDriver->load(); - - // 加载 K 线至缓存 loadAllKData(); - - // 加载历史财务信息 loadHistoryFinance(); - initInnerTask(); - std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); - m_initializing = false; -} - -void StockManager::setKDataDriver(const KDataDriverConnectPoolPtr& driver) { - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - if (iter->second.market() == "TMP") - continue; - iter->second.setKDataDriver(driver); - } } void StockManager::loadAllKData() { @@ -183,17 +173,7 @@ void StockManager::reload() { m_initializing = true; HKU_INFO("start reload ..."); - loadAllHolidays(); - loadAllMarketInfos(); - loadAllStockTypeInfo(); - loadAllStocks(); - loadAllStockWeights(); - loadAllZhBond10(); - loadHistoryFinanceField(); - - m_blockDriver->load(); - loadAllKData(); - loadHistoryFinance(); + loadData(); m_initializing = false; } @@ -372,6 +352,8 @@ void StockManager::loadAllStocks() { } } + auto kdriver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); + std::lock_guard lock(*m_stockDict_mutex); for (auto& info : stockInfos) { Datetime startDate, endDate; @@ -385,15 +367,17 @@ void StockManager::loadAllStocks() { } catch (...) { endDate = Null(); } - Stock _stock(info.market, info.code, info.name, info.type, info.valid, startDate, endDate, - info.tick, info.tickValue, info.precision, info.minTradeNumber, - info.maxTradeNumber); - string market_code = _stock.market_code(); - ; + + string market_code = fmt::format("{}{}", info.market, info.code); to_upper(market_code); + auto iter = m_stockDict.find(market_code); if (iter == m_stockDict.end()) { - m_stockDict[market_code] = _stock; + Stock _stock(info.market, info.code, info.name, info.type, info.valid, startDate, + endDate, info.tick, info.tickValue, info.precision, info.minTradeNumber, + info.maxTradeNumber); + _stock.setKDataDriver(kdriver); + m_stockDict[market_code] = std::move(_stock); } else { Stock& stock = iter->second; if (!stock.m_data) { @@ -416,6 +400,9 @@ void StockManager::loadAllStocks() { stock.m_data->m_maxTradeNumber = info.maxTradeNumber; stock.m_data->m_history_finance_ready = false; } + if (!stock.getKDataDirver()) { + stock.setKDataDriver(kdriver); + } } } } diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index dccee812..f05f2321 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -225,8 +225,8 @@ public: } private: - /* 设置K线驱动 */ - void setKDataDriver(const KDataDriverConnectPoolPtr&); + /* 加载全部数据 */ + void loadData(); /* 加载 K线数据至缓存 */ void loadAllKData(); From 7654ec246c6aebc46a059976abece421cfc8ec7e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 30 Aug 2024 18:54:00 +0800 Subject: [PATCH 538/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=8A=A0=E8=BD=BD=EF=BC=9B=E5=8E=BB=E9=99=A4=E5=85=AC=E5=85=B1?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E7=BB=84=E5=87=8F=E5=B0=91=E7=BA=BF=E7=A8=8B?= =?UTF-8?q?=EF=BC=9B=E4=BC=98=E5=8C=96Strategy=EF=BC=8C=E5=A4=9A=E7=BA=BF?= =?UTF-8?q?=E7=A8=8B=E6=97=B6=E7=9B=B4=E6=8E=A5=E4=BD=BF=E7=94=A8=E8=87=AA?= =?UTF-8?q?=E5=B7=B1=E6=89=80=E5=9C=A8=E7=BA=BF=E7=A8=8B=E8=BF=9B=E8=A1=8C?= =?UTF-8?q?=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 3 - hikyuu_cpp/hikyuu/StockManager.cpp | 65 ++++++++++++------- hikyuu_cpp/hikyuu/StockManager.h | 11 +++- .../hikyuu/data_driver/BaseInfoDriver.cpp | 1 - hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp | 46 ------------- hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h | 44 ------------- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 4 +- .../hikyuu/global/schedule/inner_tasks.cpp | 1 - .../hikyuu/global/schedule/scheduler.cpp | 4 +- hikyuu_cpp/hikyuu/hikyuu.cpp | 8 --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 21 ++---- 11 files changed, 59 insertions(+), 149 deletions(-) delete mode 100644 hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp delete mode 100644 hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 0f6e0fcb..5da311f9 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -25,7 +25,6 @@ #include "hikyuu.h" #include "GlobalInitializer.h" #include "StockManager.h" -#include "global/GlobalTaskGroup.h" #include "global/GlobalSpotAgent.h" #include "global/schedule/scheduler.h" #include "indicator/IndicatorImp.h" @@ -70,7 +69,6 @@ void GlobalInitializer::init() { DataDriverFactory::init(); StockManager::instance(); IndicatorImp::initDynEngine(); - getGlobalSpotAgent(); } void GlobalInitializer::clean() { @@ -87,7 +85,6 @@ void GlobalInitializer::clean() { #endif releaseScheduler(); - releaseGlobalTaskGroup(); releaseGlobalSpotAgent(); IndicatorImp::releaseDynEngine(); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 52f1fdf2..822e5513 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -18,7 +18,6 @@ #include "hikyuu/utilities/ini_parser/IniParser.h" #include "hikyuu/utilities/thread/ThreadPool.h" #include "StockManager.h" -#include "global/GlobalTaskGroup.h" #include "global/schedule/inner_tasks.h" #include "data_driver/kdata/cvs/KDataTempCsvDriver.h" @@ -33,7 +32,7 @@ void StockManager::quit() { } } -StockManager::StockManager() : m_initializing(false) { +StockManager::StockManager() : m_initializing(false), m_data_ready(false) { m_stockDict_mutex = new std::mutex; m_marketInfoDict_mutex = new std::mutex; m_stockTypeInfo_mutex = new std::mutex; @@ -103,6 +102,9 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa } void StockManager::loadData() { + std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); + m_data_ready = false; + loadAllHolidays(); loadAllMarketInfos(); loadAllStockTypeInfo(); @@ -111,13 +113,16 @@ void StockManager::loadData() { loadAllZhBond10(); loadHistoryFinanceField(); + HKU_INFO("Loading block..."); + m_blockDriver->load(); + // 获取K线数据驱动并预加载指定的数据 HKU_INFO("Loading KData..."); - std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); - m_blockDriver->load(); + auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); + + // 加载K线及历史财务信息 loadAllKData(); - loadHistoryFinance(); std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); @@ -153,18 +158,42 @@ void StockManager::loadAllKData() { } } + if (m_hikyuuParam.tryGet("load_history_finance", true)) { + ThreadPool tg; + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { + tg.submit([stk = iter->second]() { stk.getHistoryFinance(); }); + } + tg.join(); + } + + m_data_ready = true; + } else { // 异步并行加载 - auto* tg = getGlobalTaskGroup(); - for (size_t i = 0, len = ktypes.size(); i < len; i++) { - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - const auto& low_ktype = low_ktypes[i]; - if (m_preloadParam.tryGet(low_ktype, false)) { - tg->submit( - [=, ktype = ktypes[i]]() mutable { iter->second.loadKDataToBuffer(ktype); }); + std::thread t = std::thread([this, ktypes, low_ktypes]() { + ThreadPool tg(std::thread::hardware_concurrency()); + for (size_t i = 0, len = ktypes.size(); i < len; i++) { + std::lock_guard lock(*m_stockDict_mutex); + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { + if (m_preloadParam.tryGet(low_ktypes[i], false)) { + tg.submit([stk = iter->second, ktype = std::move(ktypes[i])]() mutable { + stk.loadKDataToBuffer(ktype); + }); + } } } - } + + if (m_hikyuuParam.tryGet("load_history_finance", true)) { + std::lock_guard lock(*m_stockDict_mutex); + for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { + tg.submit([stk = iter->second]() { stk.getHistoryFinance(); }); + } + } + + tg.join(); + m_data_ready = true; + }); + t.detach(); } } @@ -496,14 +525,4 @@ vector> StockManager::getHistoryFinanceAllFields() con return ret; } -void StockManager::loadHistoryFinance() { - if (m_hikyuuParam.tryGet("load_history_finance", true)) { - auto* tg = getGlobalTaskGroup(); - std::lock_guard lock1(*m_stockDict_mutex); - for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - tg->submit([=]() { iter->second.getHistoryFinance(); }); - } - } -} - } // namespace hku diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index f05f2321..33a78103 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -87,6 +87,9 @@ public: /** 获取证券数量 */ size_t size() const; + /** 是否所有数据准备完毕 */ + bool dataReady() const; + /** * 根据"市场简称证券代码"获取对应的证券实例 * @param querystr 格式:“市场简称证券代码”,如"sh000001" @@ -252,15 +255,13 @@ private: /** 加载历史财经字段索引 */ void loadHistoryFinanceField(); - /** 加载历史财务数据 */ - void loadHistoryFinance(); - private: StockManager(); private: static StockManager* m_sm; std::atomic_bool m_initializing; + std::atomic_bool m_data_ready; // 用于指示是否所有数据准备完毕 std::thread::id m_thread_id; // 记录线程id,用于判断Stratege是以独立进程方式还是线程方式运行 string m_tmpdir; string m_datadir; @@ -298,6 +299,10 @@ inline size_t StockManager::size() const { return m_stockDict.size(); } +inline bool StockManager::dataReady() const { + return m_data_ready; +} + inline Stock StockManager::operator[](const string& query) const { return getStock(query); } diff --git a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.cpp index d69a122c..cf08be7c 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.cpp @@ -7,7 +7,6 @@ #include #include "../StockManager.h" -#include "../global/GlobalTaskGroup.h" #include "BaseInfoDriver.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp deleted file mode 100644 index 746452d5..00000000 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.cpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * GlobalTaskGroup.cpp - * - * Copyright (c) 2019 hikyuu.org - * - * Created on: 2020-4-20 - * Author: fasiondog - */ - -#include "hikyuu/GlobalInitializer.h" -#include "hikyuu/utilities/Log.h" -#include "GlobalTaskGroup.h" -#include "hikyuu/StockManager.h" - -namespace hku { - -static TaskGroup* g_threadPool; - -TaskGroup* getGlobalTaskGroup() { - static std::once_flag oc; - std::call_once(oc, [&]() { - // auto cpu_num = std::thread::hardware_concurrency(); - // if (cpu_num >= 4) { - // cpu_num -= 2; - // } else if (cpu_num > 1) { - // cpu_num--; - // } - const auto& param = StockManager::instance().getHikyuuParameter(); - size_t worker_num = - param.tryGet("commont_thread_pool_num", std::thread::hardware_concurrency()); - HKU_INFO("work_num: {}", worker_num); - g_threadPool = new TaskGroup(worker_num); - }); - return g_threadPool; -} - -void releaseGlobalTaskGroup() { - HKU_TRACE("releaseGlobalTaskGroup"); - if (g_threadPool) { - g_threadPool->stop(); - delete g_threadPool; - g_threadPool = nullptr; - } -} - -} /* namespace hku */ \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h b/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h deleted file mode 100644 index 72ab4bad..00000000 --- a/hikyuu_cpp/hikyuu/global/GlobalTaskGroup.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * GlobalTaskGroup.h - * - * Copyright (c) 2019 hikyuu.org - * - * Created on: 2020-4-20 - * Author: fasiondog - */ - -#pragma once -#ifndef HKU_GLOBAL_TASK_GROUP -#define HKU_GLOBAL_TASK_GROUP - -#include "../utilities/thread/thread.h" - -namespace hku { - -using TaskGroup = ThreadPool; - -/** - * 获取全局线程池任务组 - * @note 请使用 future 获取任务返回 - */ -TaskGroup* getGlobalTaskGroup(); - -template -using task_handle = std::future; - -/** - * 向全局任务池中增加任务 - */ -template -task_handle::type> addTask(FunctionType f) { - return getGlobalTaskGroup()->submit(f); -} - -/** - * 程序退出时释放全局任务组实例,仅内部调用 - */ -void releaseGlobalTaskGroup(); - -} /* namespace hku */ - -#endif /* HKU_GLOBAL_TASK_GROUP */ diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 24552478..7c2eb084 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -128,7 +128,7 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { #pragma warning(disable : 4267) #endif - // 更新日线数据 + // 更新K线数据 auto* spot_list = GetSpotList(spot_list_buf); auto* spots = spot_list->spot(); size_t total = spots->size(); @@ -138,7 +138,7 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { auto spot_record = parseFlatSpot(spot); if (spot_record) { for (const auto& process : m_processList) { - m_process_task_list.push_back(m_tg.submit(ProcessTask(process, *spot_record))); + m_process_task_list.emplace_back(m_tg.submit(ProcessTask(process, *spot_record))); } } } diff --git a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp index a9622c1e..433bcbd0 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp @@ -7,7 +7,6 @@ #include "inner_tasks.h" #include "scheduler.h" -#include "../GlobalTaskGroup.h" #include "../../StockManager.h" namespace hku { diff --git a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp index 90d58f4e..c2deaa72 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/scheduler.cpp @@ -8,7 +8,6 @@ #include "hikyuu/GlobalInitializer.h" #include #include "hikyuu/utilities/Log.h" -#include "hikyuu/global/GlobalTaskGroup.h" #include "scheduler.h" namespace hku { @@ -17,8 +16,7 @@ static TimerManager *g_scheduler; TimerManager *getScheduler() { static std::once_flag oc; - // 使用内部公共任务组,减少内部线程 - std::call_once(oc, [&]() { g_scheduler = new TimerManager(getGlobalTaskGroup()); }); + std::call_once(oc, [&]() { g_scheduler = new TimerManager(1); }); return g_scheduler; } diff --git a/hikyuu_cpp/hikyuu/hikyuu.cpp b/hikyuu_cpp/hikyuu/hikyuu.cpp index 478c9776..1654777f 100644 --- a/hikyuu_cpp/hikyuu/hikyuu.cpp +++ b/hikyuu_cpp/hikyuu/hikyuu.cpp @@ -53,14 +53,6 @@ void HKU_API getConfigFromIni(const string& config_file_name, Parameter& basePar hkuParam.set("load_history_finance", config.getBool("hikyuu", "load_history_finance", "True")); - // 公共线程池线程数量 - size_t cpu_num = 2 * std::thread::hardware_concurrency(); - if (cpu_num > 30) { - cpu_num = 30; - } - hkuParam.set("commont_thread_pool_num", config.getInt("hikyuu", "commont_thread_pool_num", - fmt::format("{}", cpu_num))); - IniParser::StringListPtr option = config.getOptionList("baseinfo"); for (auto iter = option->begin(); iter != option->end(); ++iter) { string value = config.get("baseinfo", *iter); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index e6967c79..b937ca4f 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -12,19 +12,10 @@ #include "hikyuu/utilities/node/NodeClient.h" #include "hikyuu/global/GlobalSpotAgent.h" #include "hikyuu/global/schedule/scheduler.h" -#include "hikyuu/global/GlobalTaskGroup.h" #include "hikyuu/global/sysinfo.h" #include "hikyuu/hikyuu.h" #include "Strategy.h" -// python 中运行拉回主线程循环,非 python 环境则直接执行 -#define EVENT(func) \ - if (runningInPython()) { \ - event(func); \ - } else { \ - func(); \ - } - namespace hku { std::atomic_bool Strategy::ms_keep_running = true; @@ -111,7 +102,7 @@ void Strategy::start() { agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { if (m_on_recieved_spot) { - EVENT([=]() { m_on_recieved_spot(revTime); }); + event([=]() { m_on_recieved_spot(revTime); }); } }); startSpotAgent(true); @@ -136,7 +127,7 @@ void Strategy::_receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { if (m_on_change) { - EVENT([=]() { m_on_change(stk, spot); }); + event([=]() { m_on_change(stk, spot); }); } } } @@ -149,7 +140,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, m_ignoreMarket = ignoreMarket; if (ignoreMarket) { - m_run_daily_func = [=]() { EVENT(func); }; + m_run_daily_func = [=]() { event(func); }; } else { m_run_daily_func = [=]() { @@ -167,7 +158,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, Datetime close2 = today + market_info.closeTime2(); Datetime now = Datetime::now(); if ((now >= open1 && now <= close1) || (now >= open2 && now <= close2)) { - EVENT(func); + event(func); } }; } @@ -264,12 +255,12 @@ void Strategy::runDailyAt(std::function&& func, const TimeDelta& delta, auto today = Datetime::today(); int day = today.dayOfWeek(); if (day != 0 && day != 6 && !sm.isHoliday(today)) { - EVENT(func); + event(func); } }; } else { - m_run_daily_at_func = [=]() { EVENT(func); }; + m_run_daily_at_func = [=]() { event(func); }; } } From d360deb079ffb7edb7c850b6744d67a282255df9 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 30 Aug 2024 23:09:07 +0800 Subject: [PATCH 539/601] fixed Operator SE serialization leak --- .../selector/imp/OperatorSelector.cpp | 20 +++++++++++-------- .../trade_sys/selector/imp/OperatorSelector.h | 11 +++++++++- .../selector/imp/OperatorValueSelector.h | 19 +++++++++++++++++- 3 files changed, 40 insertions(+), 10 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index ed87da2c..1cbdf948 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -50,10 +50,16 @@ OperatorSelector::OperatorSelector(const string& name) : SelectorBase(name) {} OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, const SelectorPtr& se2) : SelectorBase(name), m_se1(se1), m_se2(se2) { - auto inter = findIntersection(se1, se2); - if (se1 && se2) { + build(); +} + +OperatorSelector::~OperatorSelector() {} + +void OperatorSelector::build() { + auto inter = findIntersection(m_se1, m_se2); + if (m_se1 && m_se2) { std::map tmpdict; - const auto& raw_sys_list1 = se1->getProtoSystemList(); + const auto& raw_sys_list1 = m_se1->getProtoSystemList(); for (const auto& sys : raw_sys_list1) { m_pro_sys_list.emplace_back(sys); m_se1_set.insert(sys); @@ -62,7 +68,7 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, } } - const auto& raw_sys_list2 = se2->getProtoSystemList(); + const auto& raw_sys_list2 = m_se2->getProtoSystemList(); for (size_t i = 0, total = raw_sys_list2.size(); i < total; i++) { const auto& sys = raw_sys_list2[i]; auto iter = inter.find(sys.get()); @@ -74,7 +80,7 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, } } - } else if (se1) { + } else if (m_se1) { // m_se1 = se1->clone(); auto sys_list = m_se1->getProtoSystemList(); for (auto& sys : sys_list) { @@ -82,7 +88,7 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, } m_pro_sys_list = std::move(sys_list); - } else if (se2) { + } else if (m_se2) { // m_se2 = se2->clone(); auto sys_list = m_se2->getProtoSystemList(); for (auto& sys : sys_list) { @@ -92,8 +98,6 @@ OperatorSelector::OperatorSelector(const string& name, const SelectorPtr& se1, } } -OperatorSelector::~OperatorSelector() {} - void OperatorSelector::_reset() { if (m_se1) { m_se1->reset(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index 2a52aab0..471a6e95 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -43,6 +43,7 @@ protected: protected: static void sortSystemWeightList(SystemWeightList& swlist); + void build(); void cloneRebuild(const SelectorPtr& se1, const SelectorPtr& se2); protected: @@ -63,11 +64,19 @@ private: #if HKU_SUPPORT_SERIALIZATION friend class boost::serialization::access; template - void serialize(Archive& ar, const unsigned int version) { + void save(Archive& ar, const unsigned int version) const { ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); ar& BOOST_SERIALIZATION_NVP(m_se1); ar& BOOST_SERIALIZATION_NVP(m_se2); } + + template + void load(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_se1); + ar& BOOST_SERIALIZATION_NVP(m_se2); + build(); + } #endif }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h index 004213cb..f85d171a 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h @@ -36,12 +36,29 @@ private: //============================================ #if HKU_SUPPORT_SERIALIZATION friend class boost::serialization::access; + // template + // void serialize(Archive& ar, const unsigned int version) { + // ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + // ar& BOOST_SERIALIZATION_NVP(m_se); + // ar& BOOST_SERIALIZATION_NVP(m_value); + // } template - void serialize(Archive& ar, const unsigned int version) { + void save(Archive& ar, const unsigned int version) const { ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); ar& BOOST_SERIALIZATION_NVP(m_se); ar& BOOST_SERIALIZATION_NVP(m_value); } + + template + void load(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(SelectorBase); + ar& BOOST_SERIALIZATION_NVP(m_se); + ar& BOOST_SERIALIZATION_NVP(m_value); + if (m_se) { + m_pro_sys_list = m_se->getProtoSystemList(); + } + } + #endif }; From 9038473eb8dba8d99440b1c4f5778eab9c1b5d0f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 01:31:25 +0800 Subject: [PATCH 540/601] =?UTF-8?q?fixed=20=E6=96=B0=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E5=8A=A0=E8=BD=BDlinux=E4=B8=8B=E9=80=80=E5=87=BA=E6=97=B6hdf5?= =?UTF-8?q?=E5=85=B3=E9=97=AD=E5=BC=82=E5=B8=B8=EF=BC=9B=E5=85=B6=E4=BB=96?= =?UTF-8?q?=E5=B0=8F=E8=B0=83=E6=95=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 8 +++++ hikyuu_cpp/hikyuu/StockManager.cpp | 34 +++++++++++++------- hikyuu_cpp/hikyuu/StockManager.h | 12 +++++-- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 10 ++++-- hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 5 +-- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 5da311f9..a01a3905 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -89,6 +89,14 @@ void GlobalInitializer::clean() { IndicatorImp::releaseDynEngine(); +#if !HKU_OS_WINDOWS + // 主动停止异步数据加载任务组,否则 hdf5 在 linux 下会报关闭异常 + auto *tg = StockManager::instance().getLoadTaskGroup(); + if (tg) { + tg->stop(); + } +#endif + #if HKU_ENABLE_LEAK_DETECT || defined(MSVC_LEAKER_DETECT) // 非内存泄漏检测时,内存让系统自动释放,避免某些场景下 windows 下退出速度过慢 StockManager::quit(); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 822e5513..d97f0b63 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -105,16 +105,23 @@ void StockManager::loadData() { std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); m_data_ready = false; - loadAllHolidays(); + ThreadPool tg(2); + tg.submit([this]() { this->loadAllHolidays(); }); + tg.submit([this]() { this->loadHistoryFinanceField(); }); + tg.submit([this]() { this->loadAllStockTypeInfo(); }); + tg.submit([this]() { this->loadAllZhBond10(); }); + + // loadAllHolidays(); loadAllMarketInfos(); - loadAllStockTypeInfo(); + // loadAllStockTypeInfo(); loadAllStocks(); loadAllStockWeights(); - loadAllZhBond10(); - loadHistoryFinanceField(); + // loadAllZhBond10(); + // loadHistoryFinanceField(); HKU_INFO("Loading block..."); - m_blockDriver->load(); + // m_blockDriver->load(); + tg.submit([this]() { this->m_blockDriver->load(); }); // 获取K线数据驱动并预加载指定的数据 HKU_INFO("Loading KData..."); @@ -124,6 +131,8 @@ void StockManager::loadData() { // 加载K线及历史财务信息 loadAllKData(); + tg.join(); + std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); } @@ -171,14 +180,16 @@ void StockManager::loadAllKData() { } else { // 异步并行加载 std::thread t = std::thread([this, ktypes, low_ktypes]() { - ThreadPool tg(std::thread::hardware_concurrency()); + this->m_load_tg = std::make_unique(); + // ThreadPool tg(std::thread::hardware_concurrency()); for (size_t i = 0, len = ktypes.size(); i < len; i++) { std::lock_guard lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { if (m_preloadParam.tryGet(low_ktypes[i], false)) { - tg.submit([stk = iter->second, ktype = std::move(ktypes[i])]() mutable { - stk.loadKDataToBuffer(ktype); - }); + m_load_tg->submit( + [stk = iter->second, ktype = std::move(ktypes[i])]() mutable { + stk.loadKDataToBuffer(ktype); + }); } } } @@ -186,11 +197,12 @@ void StockManager::loadAllKData() { if (m_hikyuuParam.tryGet("load_history_finance", true)) { std::lock_guard lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { - tg.submit([stk = iter->second]() { stk.getHistoryFinance(); }); + m_load_tg->submit([stk = iter->second]() { stk.getHistoryFinance(); }); } } - tg.join(); + m_load_tg->join(); + m_load_tg.reset(); m_data_ready = true; }); t.detach(); diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 33a78103..38b7136b 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -10,8 +10,9 @@ #include #include -#include "utilities/Parameter.h" -#include "data_driver/DataDriverFactory.h" +#include "hikyuu/utilities/Parameter.h" +#include "hikyuu/utilities/thread/thread.h" +#include "hikyuu/data_driver/DataDriverFactory.h" #include "Block.h" #include "MarketInfo.h" #include "StockTypeInfo.h" @@ -218,6 +219,11 @@ public: return m_thread_id; } + /** 仅由程序退出使使用!!! */ + ThreadPool* getLoadTaskGroup() { + return m_load_tg.get(); + } + public: typedef StockMapIterator const_iterator; const_iterator begin() const { @@ -293,6 +299,8 @@ private: Parameter m_preloadParam; Parameter m_hikyuuParam; StrategyContext m_context; + + std::unique_ptr m_load_tg; // 异步数据加载辅助线程组 }; inline size_t StockManager::size() const { diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 7c2eb084..818a757a 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -25,14 +25,18 @@ const size_t SpotAgent::ms_endTagLength = strlen(SpotAgent::ms_endTag); Datetime SpotAgent::ms_start_rev_time; -void SpotAgent::setQuotationServer(const string& server) { - ms_pubUrl = server; +SpotAgent::SpotAgent() { + m_tg = std::make_unique(); } SpotAgent::~SpotAgent() { stop(); } +void SpotAgent::setQuotationServer(const string& server) { + ms_pubUrl = server; +} + void SpotAgent::start() { stop(); if (m_stop) { @@ -138,7 +142,7 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { auto spot_record = parseFlatSpot(spot); if (spot_record) { for (const auto& process : m_processList) { - m_process_task_list.emplace_back(m_tg.submit(ProcessTask(process, *spot_record))); + m_process_task_list.emplace_back(m_tg->submit(ProcessTask(process, *spot_record))); } } } diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index cafc4b4e..aaa703b3 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -27,7 +27,7 @@ namespace hku { */ class HKU_API SpotAgent { public: - SpotAgent() = default; + SpotAgent(); /** 析构函数 */ virtual ~SpotAgent(); @@ -109,7 +109,8 @@ private: int m_revTimeout = 100; // 连接数据服务超时时长(毫秒) size_t m_batch_count = 0; // 记录本次批次接收的数据数量 std::thread m_receiveThread; // 数据接收线程 - ThreadPool m_tg; // 数据处理任务线程池 + // ThreadPool m_tg; // 数据处理任务线程池 + std::unique_ptr m_tg; vector> m_process_task_list; // 下面属性被修改时需要加锁,以便可以使用多线程方式运行 strategy From d8d82008c3b0d48e3a3de2556dcbac32040cc337 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 02:26:27 +0800 Subject: [PATCH 541/601] update --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index a01a3905..632a31bc 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -89,13 +89,11 @@ void GlobalInitializer::clean() { IndicatorImp::releaseDynEngine(); -#if !HKU_OS_WINDOWS // 主动停止异步数据加载任务组,否则 hdf5 在 linux 下会报关闭异常 auto *tg = StockManager::instance().getLoadTaskGroup(); if (tg) { tg->stop(); } -#endif #if HKU_ENABLE_LEAK_DETECT || defined(MSVC_LEAKER_DETECT) // 非内存泄漏检测时,内存让系统自动释放,避免某些场景下 windows 下退出速度过慢 From 8fe41fca91c6095040cf3edd50fec510b0447f31 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 03:00:06 +0800 Subject: [PATCH 542/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy=20?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE=E5=A4=84=E4=BB=BB=E5=8A=A1?= =?UTF-8?q?=E7=BB=84=E6=95=B0=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 1 - hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 8 ++++---- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 5 +++-- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 4 ++-- hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 2 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 9 ++++++++- hikyuu_pywrap/global/_SpotAgent.cpp | 3 ++- 7 files changed, 20 insertions(+), 12 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index d97f0b63..7729f25b 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -181,7 +181,6 @@ void StockManager::loadAllKData() { // 异步并行加载 std::thread t = std::thread([this, ktypes, low_ktypes]() { this->m_load_tg = std::make_unique(); - // ThreadPool tg(std::thread::hardware_concurrency()); for (size_t i = 0, len = ktypes.size(); i < len; i++) { std::lock_guard lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 80b37c0f..23d3e619 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -14,9 +14,9 @@ namespace hku { SpotAgent* g_spot_agent = nullptr; -SpotAgent* getGlobalSpotAgent() { +SpotAgent* getGlobalSpotAgent(size_t worker_num) { if (!g_spot_agent) { - g_spot_agent = new SpotAgent(); + g_spot_agent = new SpotAgent(worker_num); } return g_spot_agent; } @@ -169,11 +169,11 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { stk.realtimeUpdate(krecord, ktype); } -void HKU_API startSpotAgent(bool print) { +void HKU_API startSpotAgent(bool print, size_t worker_num) { StockManager& sm = StockManager::instance(); SpotAgent::setQuotationServer( sm.getHikyuuParameter().tryGet("quotation_server", "ipc:///tmp/hikyuu_real.ipc")); - auto& agent = *getGlobalSpotAgent(); + auto& agent = *getGlobalSpotAgent(worker_num); HKU_CHECK(!agent.isRunning(), "The agent is running, please stop first!"); agent.setPrintFlag(print); diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index 5df25ed9..077822b6 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -15,7 +15,8 @@ namespace hku { * @param print 打印接收数据进展 * @ingroup Agent */ -void HKU_API startSpotAgent(bool print = true); +void HKU_API startSpotAgent(bool print = true, + size_t worker_num = std::thread::hardware_concurrency()); /** * 终止 Spot 数据接收代理 @@ -23,7 +24,7 @@ void HKU_API startSpotAgent(bool print = true); */ void HKU_API stopSpotAgent(); -SpotAgent* getGlobalSpotAgent(); +SpotAgent* getGlobalSpotAgent(size_t worker_num = std::thread::hardware_concurrency()); void releaseGlobalSpotAgent(); diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 818a757a..37edc342 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -25,8 +25,8 @@ const size_t SpotAgent::ms_endTagLength = strlen(SpotAgent::ms_endTag); Datetime SpotAgent::ms_start_rev_time; -SpotAgent::SpotAgent() { - m_tg = std::make_unique(); +SpotAgent::SpotAgent(size_t worker_num) { + m_tg = std::make_unique(worker_num); } SpotAgent::~SpotAgent() { diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index aaa703b3..f232172e 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -27,7 +27,7 @@ namespace hku { */ class HKU_API SpotAgent { public: - SpotAgent(); + SpotAgent(size_t worker_num); /** 析构函数 */ virtual ~SpotAgent(); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index b937ca4f..775c06d1 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -98,7 +98,14 @@ void Strategy::start() { _runDailyAt(); - auto& agent = *getGlobalSpotAgent(); + size_t stock_num = StockManager::instance().size(); + size_t spot_worker_num = stock_num / 300; + size_t cpu_num = std::thread::hardware_concurrency(); + if (spot_worker_num > cpu_num) { + spot_worker_num = cpu_num; + } + + auto& agent = *getGlobalSpotAgent(spot_worker_num); agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { if (m_on_recieved_spot) { diff --git a/hikyuu_pywrap/global/_SpotAgent.cpp b/hikyuu_pywrap/global/_SpotAgent.cpp index 71ecfd1d..f69ae2bc 100644 --- a/hikyuu_pywrap/global/_SpotAgent.cpp +++ b/hikyuu_pywrap/global/_SpotAgent.cpp @@ -12,6 +12,7 @@ using namespace hku; namespace py = pybind11; void export_SpotAgent(py::module& m) { - m.def("start_spot_agent", startSpotAgent, py::arg("print") = false); + m.def("start_spot_agent", startSpotAgent, py::arg("print") = false, + py::arg("worker_num") = std::thread::hardware_concurrency()); m.def("stop_spot_agent", stopSpotAgent); } From 6995629d409b619fa8420af43addb8e621562b75 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 03:14:46 +0800 Subject: [PATCH 543/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy=20start?= =?UTF-8?q?=20=E8=87=AA=E8=A1=8C=E6=8E=A7=E5=88=B6=E6=98=AF=E5=90=A6?= =?UTF-8?q?=E5=90=AF=E5=8A=A8=E8=A1=8C=E6=83=85=E6=8E=A5=E6=94=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 32 +++++++++++++------------ hikyuu_cpp/hikyuu/strategy/Strategy.h | 2 +- hikyuu_pywrap/strategy/_Strategy.cpp | 2 +- 3 files changed, 19 insertions(+), 17 deletions(-) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 775c06d1..f2824b73 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -93,26 +93,28 @@ void Strategy::_init() { stopSpotAgent(); } -void Strategy::start() { +void Strategy::start(bool autoRecieveSpot) { _init(); _runDailyAt(); - size_t stock_num = StockManager::instance().size(); - size_t spot_worker_num = stock_num / 300; - size_t cpu_num = std::thread::hardware_concurrency(); - if (spot_worker_num > cpu_num) { - spot_worker_num = cpu_num; - } - - auto& agent = *getGlobalSpotAgent(spot_worker_num); - agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); - agent.addPostProcess([this](Datetime revTime) { - if (m_on_recieved_spot) { - event([=]() { m_on_recieved_spot(revTime); }); + if (autoRecieveSpot) { + size_t stock_num = StockManager::instance().size(); + size_t spot_worker_num = stock_num / 300; + size_t cpu_num = std::thread::hardware_concurrency(); + if (spot_worker_num > cpu_num) { + spot_worker_num = cpu_num; } - }); - startSpotAgent(true); + + auto& agent = *getGlobalSpotAgent(spot_worker_num); + agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); + agent.addPostProcess([this](Datetime revTime) { + if (m_on_recieved_spot) { + event([=]() { m_on_recieved_spot(revTime); }); + } + }); + startSpotAgent(true); + } _runDaily(); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index e59fa451..f461a11a 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -89,7 +89,7 @@ public: /** * 启动策略执行,必须在已注册相关处理函数后执行 */ - void start(); + void start(bool autoRecieveSpot = true); private: string m_name; diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 4309b1bd..8b4f1c8f 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -30,7 +30,7 @@ void export_Strategy(py::module& m) { .def_property_readonly("context", &Strategy::context, py::return_value_policy::copy, "策略上下文") - .def("start", &Strategy::start) + .def("start", &Strategy::start, py::arg("auto_recieve_spot") = true) .def("on_change", [](Strategy& self, py::object func) { HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); From b8333c7e59076a17eb8f11594630e589e4ca6c25 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 10:12:42 +0800 Subject: [PATCH 544/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=8A=A0=E8=BD=BD=20?= =?UTF-8?q?hikyuu.interactive=20=E5=8F=AF=E9=80=9A=E8=BF=87=E7=8E=AF?= =?UTF-8?q?=E5=A2=83=E5=8F=98=E9=87=8F=E4=B8=8E=E4=B8=8A=E4=B8=8B=E6=96=87?= =?UTF-8?q?=E6=8E=A7=E5=88=B6=E7=9B=B8=E5=85=B3=E6=95=B0=E6=8D=AE=E5=8A=A0?= =?UTF-8?q?=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 16 +++++++++++- hikyuu_cpp/hikyuu/StockManager.cpp | 26 +++++++++++++------ hikyuu_cpp/hikyuu/StrategyContext.cpp | 14 +++++----- hikyuu_cpp/hikyuu/StrategyContext.h | 9 +++++-- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 2 +- .../hikyuu/hikyuu/test_StrategyContext.cpp | 4 +-- hikyuu_pywrap/_StockManager.cpp | 3 +++ hikyuu_pywrap/_StrategyContext.cpp | 17 ++++++++---- 8 files changed, 64 insertions(+), 27 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 8bc0ee6b..26633c45 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -112,7 +112,21 @@ for p in kdata_config: continue kdata_param[p] = ini.get('kdata', p) -sm.init(base_param, block_param, kdata_param, preload_param, hku_param) +context = StrategyContext() +if 'HKU_STOCK_LIST' in os.environ: + context.stock_list = os.environ['HKU_STOCK_LIST'].split(";") +if 'HKU_KTYPE_LIST' in os.environ: + context.ktype_list = os.environ['HKU_KTYPE_LIST'].split(";") +if 'HKU_LOAD_HISTORY_FINANCE' in os.environ: + load_str = os.environ['HKU_LOAD_HISTORY_FINANCE'].upper() + load_finance = os.environ['HKU_LOAD_HISTORY_FINANCE'] in ("1", "TRUE") + hku_param.set("load_history_finance", load_finance) +if 'HKU_LOAD_STOCK_WEIGHT' in os.environ: + load_str = os.environ['HKU_LOAD_STOCK_WEIGHT'].upper() + load_stk_weight = os.environ['HKU_LOAD_STOCK_WEIGHT'] in ("1", "TRUE") + hku_param.set("load_stock_weight", load_stk_weight) + +sm.init(base_param, block_param, kdata_param, preload_param, hku_param, context) # set_log_level(LOG_LEVEL.INFO) # 启动行情接收代理 diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 7729f25b..8a96acd7 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -139,14 +139,24 @@ void StockManager::loadData() { void StockManager::loadAllKData() { // 按 K 线类型控制加载顺序 - // const auto& ktypes = KQuery::getAllKType(); - vector ktypes{KQuery::DAY, KQuery::MIN, KQuery::WEEK, KQuery::MONTH, - KQuery::QUARTER, KQuery::HALFYEAR, KQuery::YEAR, KQuery::MIN5, - KQuery::MIN15, KQuery::MIN30, KQuery::MIN60, KQuery::MIN3, - KQuery::HOUR2, KQuery::HOUR4, KQuery::HOUR6, KQuery::HOUR12}; - HKU_ASSERT(ktypes.size() == KQuery::getAllKType().size()); + vector default_ktypes{ + KQuery::DAY, KQuery::MIN, KQuery::WEEK, KQuery::MONTH, KQuery::QUARTER, KQuery::HALFYEAR, + KQuery::YEAR, KQuery::MIN5, KQuery::MIN15, KQuery::MIN30, KQuery::MIN60, KQuery::MIN3, + KQuery::HOUR2, KQuery::HOUR4, KQuery::HOUR6, KQuery::HOUR12}; + vector ktypes; vector low_ktypes; + + // 如果上下文指定了 ktype list,则按上下文指定的 ktype 顺序加载,否则按默认顺序加载 + const auto& context_ktypes = m_context.getKTypeList(); + if (context_ktypes.empty()) { + ktypes = std::move(default_ktypes); + HKU_ASSERT(ktypes.size() == KQuery::getAllKType().size()); + + } else { + ktypes = context_ktypes; + } + low_ktypes.reserve(ktypes.size()); for (const auto& ktype : ktypes) { auto& back = low_ktypes.emplace_back(ktype); @@ -373,9 +383,9 @@ void StockManager::loadAllStocks() { if (m_context.isAll()) { stockInfos = m_baseInfoDriver->getAllStockInfo(); } else { - const vector& context_stock_code_list = m_context.getStockCodeList(); + auto load_stock_code_list = m_context.getAllNeedLoadStockCodeList(); auto all_market = getAllMarket(); - for (auto stkcode : context_stock_code_list) { + for (auto stkcode : load_stock_code_list) { to_upper(stkcode); bool find = false; for (auto& market : all_market) { diff --git a/hikyuu_cpp/hikyuu/StrategyContext.cpp b/hikyuu_cpp/hikyuu/StrategyContext.cpp index 6a730f8e..52eb60fe 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.cpp +++ b/hikyuu_cpp/hikyuu/StrategyContext.cpp @@ -21,12 +21,6 @@ void StrategyContext::setKTypeList(const vector& ktypeList) { to_upper(ktype); return ktype; }); - - // 对 ktype 按时间长度进行升序排序 - std::sort(m_ktypeList.begin(), m_ktypeList.end(), - [](const KQuery::KType& a, const KQuery::KType& b) { - return KQuery::getKTypeInMin(a) < KQuery::getKTypeInMin(b); - }); } bool StrategyContext::isAll() const noexcept { @@ -36,8 +30,12 @@ bool StrategyContext::isAll() const noexcept { }) != m_stockCodeList.end(); } -bool StrategyContext::isValid() const noexcept { - return m_stockCodeList.empty() || m_ktypeList.empty(); +vector StrategyContext::getAllNeedLoadStockCodeList() const { + vector ret{m_stockCodeList}; + for (const auto& code : m_mustLoad) { + ret.push_back(code); + } + return ret; } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index 133f43b2..70f48aec 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -29,8 +29,6 @@ public: bool isAll() const noexcept; - bool isValid() const noexcept; - Datetime startDatetime() const noexcept { return m_startDatetime; } @@ -57,8 +55,15 @@ public: return m_ktypeList; } + const vector& getMustLoadStockCodeList() const { + return m_mustLoad; + } + + vector getAllNeedLoadStockCodeList() const; + private: Datetime m_startDatetime{19901219}; + vector m_mustLoad{"sh000001", "sh000300"}; // 默认必须加载的 stock vector m_stockCodeList; vector m_ktypeList; }; diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index 077822b6..33b52c66 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -26,6 +26,6 @@ void HKU_API stopSpotAgent(); SpotAgent* getGlobalSpotAgent(size_t worker_num = std::thread::hardware_concurrency()); -void releaseGlobalSpotAgent(); +void HKU_API releaseGlobalSpotAgent(); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp index b36da36c..bd9f83c9 100644 --- a/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp +++ b/hikyuu_cpp/unit_test/hikyuu/hikyuu/test_StrategyContext.cpp @@ -20,8 +20,8 @@ TEST_CASE("test_StrategyContext") { sc.setKTypeList( {KQuery::MONTH, KQuery::MIN5, KQuery::DAY, KQuery::MIN, KQuery::WEEK, KQuery::MIN60}); - vector expect{KQuery::MIN, KQuery::MIN5, KQuery::MIN60, - KQuery::DAY, KQuery::WEEK, KQuery::MONTH}; + vector expect{KQuery::MONTH, KQuery::MIN5, KQuery::DAY, + KQuery::MIN, KQuery::WEEK, KQuery::MIN60}; const auto ktypes = sc.getKTypeList(); for (size_t i = 0, len = ktypes.size(); i < len; i++) { CHECK_EQ(ktypes[i], expect[i]); diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 9d2f05e6..65b88e22 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -31,6 +31,9 @@ void export_StockManager(py::module& m) { param hikyuu_param 其他参数 param StrategyContext context 策略上下文, 默认加载全部证券)") + .def_property_readonly("data_ready", &StockManager::dataReady, + "是否所有数据已准备就绪(加载完毕)") + .def("reload", &StockManager::reload, "重新加载所有证券数据") .def("tmpdir", &StockManager::tmpdir, R"(tmpdir(self) -> str diff --git a/hikyuu_pywrap/_StrategyContext.cpp b/hikyuu_pywrap/_StrategyContext.cpp index 4f93c4ae..29dfa482 100644 --- a/hikyuu_pywrap/_StrategyContext.cpp +++ b/hikyuu_pywrap/_StrategyContext.cpp @@ -32,9 +32,16 @@ void export_StrategeContext(py::module& m) { .def(py::init&, const vector&>(), py::arg("stock_list"), py::arg("ktype_list")) .def_property("start_datetime", get_start_datetime, set_start_datetime, "起始日期") - .def_property("stock_list", - py::overload_cast<>(&StrategyContext::getStockCodeList, py::const_), - setStockList, "股票代码列表") - .def_property("ktype_list", py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), - setKTypeList, "需要的K线类型"); + .def_property( + "stock_list", py::overload_cast<>(&StrategyContext::getStockCodeList, py::const_), + [](StrategyContext& self, const py::sequence& stk_list) { + self.setStockCodeList(python_list_to_vector(stk_list)); + }, + "股票代码列表") + .def_property( + "ktype_list", py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), + [](StrategyContext& self, const py::sequence& ktype_list) { + self.setKTypeList(python_list_to_vector(ktype_list)); + }, + "需要的K线类型"); } From 0a0fe2ed9aeb0f042a23e9e7711b45741b9b6e4c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 10:15:00 +0800 Subject: [PATCH 545/601] update --- hikyuu_cpp/demo/demo3.cpp | 116 -------------------------------------- hikyuu_cpp/demo/xmake.lua | 24 -------- 2 files changed, 140 deletions(-) delete mode 100644 hikyuu_cpp/demo/demo3.cpp diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp deleted file mode 100644 index dd270185..00000000 --- a/hikyuu_cpp/demo/demo3.cpp +++ /dev/null @@ -1,116 +0,0 @@ -// demo.cpp : 定义控制台应用程序的入口点。 -// - -#include -#include -#include -#include -#include -#include -#include - -#if defined(_WIN32) -#include -#endif - -using namespace hku; - -NodeServer server; - -void signal_handle(int signal) { - if (signal == SIGINT || signal == SIGTERM) { - HKU_INFO("Shutdown now ..."); - server.stop(); - exit(0); - } -} - -int main(int argc, char* argv[]) { - std::signal(SIGINT, signal_handle); - std::signal(SIGTERM, signal_handle); - - initLogger(false, "./demo.log"); - -#if defined(_WIN32) - // Windows 下设置控制台程序输出代码页为 UTF8 - auto old_cp = GetConsoleOutputCP(); - SetConsoleOutputCP(CP_UTF8); -#endif - - try { - // 配置文件的位置自行修改 - hikyuu_init(fmt::format("{}/.hikyuu/hikyuu.ini", getUserDir())); - - // 启动行情接收(只是计算回测可以不需要) - startSpotAgent(true); - - server.setAddr("tcp://0.0.0.0:9201"); - - server.regHandle("market", [](json&& req) { - HKU_INFO("--> req from {}:{}", req["remote_host"].get(), - req["remote_port"].get()); - HKU_ASSERT(req.contains("ktype")); - HKU_ASSERT(req.contains("codes")); - - string ktype = req["ktype"].get(); - auto& sm = StockManager::instance(); - const auto& param = sm.getPreloadParameter(); - string low_ktype = ktype; - to_lower(low_ktype); - HKU_CHECK(param.tryGet(low_ktype, false), "The ktype: {} is not be preloaded!", - ktype); - - json data; - const auto& jcodes = req["codes"]; - // HKU_INFO("codes size: {}", jcodes.size()); - for (auto iter = jcodes.cbegin(); iter != jcodes.cend(); ++iter) { - string market_code = to_string(*iter); - market_code = market_code.substr(1, market_code.size() - 2); - Stock stk = getStock(market_code); - if (stk.isNull()) { - HKU_WARN("Not found stock: {}", market_code); - continue; - } - - KRecordList krecords = - stk.getKRecordList(KQueryByIndex(-1, Null(), ktype)); - if (!krecords.empty()) { - const auto& k = krecords.back(); - json jr; - jr.emplace_back(market_code); - jr.emplace_back(k.datetime.str()); - jr.emplace_back(k.openPrice); - jr.emplace_back(k.highPrice); - jr.emplace_back(k.lowPrice); - jr.emplace_back(k.closePrice); - jr.emplace_back(k.transAmount); - jr.emplace_back(k.transCount); - data.emplace_back(std::move(jr)); - } - } - - json res; - res["data"] = data; - // HKU_INFO("<-- res: {}", to_string(res)); - return res; - }); - - server.start(); - // server.loop(); - while (true) { - std::this_thread::sleep_for(std::chrono::seconds(10)); - } - - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR("Unknown error!"); - } - - server.stop(); - -#if defined(_WIN32) - SetConsoleOutputCP(old_cp); -#endif - return 0; -} diff --git a/hikyuu_cpp/demo/xmake.lua b/hikyuu_cpp/demo/xmake.lua index 1953b42e..59fb328a 100644 --- a/hikyuu_cpp/demo/xmake.lua +++ b/hikyuu_cpp/demo/xmake.lua @@ -44,27 +44,3 @@ target("demo2") add_files("./demo2.cpp") target_end() - - -target("demo3") - set_kind("binary") - set_default(false) - - add_packages("boost", "spdlog", "fmt", "nng", "nlohmann_json") - add_includedirs("..") - - if is_plat("windows") then - add_cxflags("-wd4267") - add_cxflags("-wd4251") - end - - if is_plat("windows") and get_config("kind") == "shared" then - add_defines("HKU_API=__declspec(dllimport)") - add_defines("HKU_UTILS_API=__declspec(dllimport)") - add_defines("SQLITE_API=__declspec(dllimport)") - end - - add_deps("hikyuu") - - add_files("./demo3.cpp") -target_end() From 05977f87584601022ffe2f14d8cd2c37da60b5d0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 10:54:58 +0800 Subject: [PATCH 546/601] =?UTF-8?q?interactive=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=8E=A5=E6=94=B6=E4=BB=A3=E7=90=86=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E7=8E=AF=E5=A2=83=E5=8F=98=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 23 ++-- hikyuu/puppet.py | 297 ------------------------------------------ 2 files changed, 11 insertions(+), 309 deletions(-) delete mode 100644 hikyuu/puppet.py diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 26633c45..84eb0b2b 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -51,21 +51,12 @@ SOFTWARE. """ import urllib -import sys import os import configparser from hikyuu.data.hku_config_template import generate_default_config from hikyuu import * -# ============================================================================== -# 引入扯线木偶 -# ============================================================================== -# Puppet是一套以同花顺交易客户端为核心的完整的闭环实盘交易系统框架。 -# 来自:"睿瞳深邃(https://github.com/Raytone-D" 感谢睿瞳深邃的大度共享 :-) -# 可以用:tm.regBroker(crtRB(Puppet())) 的方式注册进tm实例,实现实盘下单 -if sys.platform == 'win32': - from .puppet import * # ============================================================================== # @@ -119,18 +110,26 @@ if 'HKU_KTYPE_LIST' in os.environ: context.ktype_list = os.environ['HKU_KTYPE_LIST'].split(";") if 'HKU_LOAD_HISTORY_FINANCE' in os.environ: load_str = os.environ['HKU_LOAD_HISTORY_FINANCE'].upper() - load_finance = os.environ['HKU_LOAD_HISTORY_FINANCE'] in ("1", "TRUE") + load_finance = load_str in ("1", "TRUE") hku_param.set("load_history_finance", load_finance) if 'HKU_LOAD_STOCK_WEIGHT' in os.environ: load_str = os.environ['HKU_LOAD_STOCK_WEIGHT'].upper() - load_stk_weight = os.environ['HKU_LOAD_STOCK_WEIGHT'] in ("1", "TRUE") + load_stk_weight = load_str in ("1", "TRUE") hku_param.set("load_stock_weight", load_stk_weight) sm.init(base_param, block_param, kdata_param, preload_param, hku_param, context) # set_log_level(LOG_LEVEL.INFO) +start_spot = False +if 'HKU_START_SPOT' in os.environ: + spot_str = os.environ['HKU_START_SPOT'].upper() + start_spot = spot_str in ('1', 'TRUE') +spot_worker_num = os.cpu_count() +if 'HKU_SPOT_WORKER_NUM' in os.environ: + spot_worker_num = int(os.environ['HKU_SPOT_WORKER_NUM']) + # 启动行情接收代理 -start_spot_agent(False) +start_spot_agent(False, spot_worker_num) # ============================================================================== # diff --git a/hikyuu/puppet.py b/hikyuu/puppet.py deleted file mode 100644 index 7a4d9efd..00000000 --- a/hikyuu/puppet.py +++ /dev/null @@ -1,297 +0,0 @@ -""" -扯线木偶界面自动化应用编程接口(Puppet UIAutomation API),是扯线木偶量化框架(Puppet Quant Framework)的一个组件。 -技术群:624585416 -""" -__author__ = "睿瞳深邃(https://github.com/Raytone-D)" -__project__ = 'Puppet' -__version__ = "0.4.12" -__license__ = 'MIT' - -# coding: utf-8 -import ctypes -from functools import reduce -import time -import pyperclip - -MSG = {'WM_SETTEXT': 12, - 'WM_GETTEXT': 13, - 'WM_KEYDOWN': 256, - 'WM_KEYUP': 257, - 'WM_COMMAND': 273, - 'BM_CLICK': 245, - 'CB_GETCOUNT': 326, - 'CB_SETCURSEL': 334, - 'CBN_SELCHANGE': 1, - 'COPY_DATA': 57634} - -NODE = {'FRAME': (59648, 59649), - 'FORM': (59648, 59649, 1047, 200, 1047), - 'ACCOUNT': (59392, 0, 1711), - 'COMBO': (59392, 0, 2322), - 'BUY': (161, (1032, 1033, 1034), 1006), - 'SELL':(162, (1032, 1033, 1034), 1006), - '撤单': 163, - '双向委托': 512, - '新股申购': 554, - '中签查询': 1070} - -TWO_WAY = {'买入代码': 1032, - '买入价格': 1033, - '买入数量': 1034, - '买入': 1006, - '卖出代码': 1035, - '卖出价格': 1058, - '卖出数量': 1039, - '卖出': 1008, - '可用余额': 1038, - '刷新': 32790, - '全撤': 30001, - '撤买': 30002, - '撤卖': 30003, - '报表': 1047} - -TAB = {'持仓': ord('W'), - '成交': ord('E'), - '委托': ord('R')} - -SCHEDULE = {'证券代码': '', - '证券名称': '', - '实际数量': '', - '市值': ''} - -CANCEL = {'全选': 1098, - '撤单': 1099, - '全撤': 30001, - '撤买': 30002, - '撤卖': 30003, - '填单': 3348, - '查单': 3349} - -NEW = {'新股代码': 1032, - '新股名称': 1036, - '申购价格': 1033, - '可申购数量': 1018, - '申购数量': 1034, - '申购': 1006} - -RAFFLE = ['新股代码', '证券代码', '申购价格', '申购上限'] -#CMD = {'COPY': 57634} -VKCODE = {'F1': 112, - 'F2': 113, - 'F3': 114, - 'F4': 115, - 'F5': 116, - 'F6': 117} - -MKT = {'CYB': '3', - 'SH': '7', - 'SZ': '0', - '创业板': '3', - '沪市': '7', - '深市': '0'} - -op = ctypes.windll.user32 - -def switch_combo(index, idCombo, hCombo): - op.SendMessageW(hCombo, MSG['CB_SETCURSEL'], index, 0) - op.SendMessageW(op.GetParent(hCombo), MSG['WM_COMMAND'], MSG['CBN_SELCHANGE']<<16|idCombo, hCombo) - -class Puppet: - """ - 界面自动化操控包装类 - # 方法 # '委买': buy(), '委卖': sell(), '撤单': cancel(), '打新': raffle(), - # 属性 # '帐号': account, '可用余额': balance, '持仓': position, '成交': deals, '可撤委托': cancelable, - # # '新股': new, '中签': bingo, - """ - def __init__(self, main=None, title='网上股票交易系统5.0'): - - print('我正在热身,稍等一下...') - self.main = main or op.FindWindowW(0, title) - self.switch = lambda node: op.SendMessageW(self.main, MSG['WM_COMMAND'], node, 0) - self._order = [] - for i in (NODE['BUY'],NODE['SELL']): - node, parts, button = i - self.switch(node) - time.sleep(0.3) - x = reduce(op.GetDlgItem, NODE['FRAME'], self.main) - self._order.append((tuple(op.GetDlgItem(x, v) for v in parts), button, x)) - - op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['双向委托'], 0) # 切换到交易操作台 - self.wait_a_second = lambda sec=0.2: time.sleep(sec) - self.wait_a_second() # 可调整区间值(0.01~0.5) - self.buff = ctypes.create_unicode_buffer(32) - self.two_way = reduce(op.GetDlgItem, NODE['FRAME'], self.main) - self.members = {k: op.GetDlgItem(self.two_way, v) for k, v in TWO_WAY.items()} - print('我准备好了,开干吧!人生巅峰在前面!') if self.main else print("没找到已登录的客户交易端,我先撤了!") - # 获取登录账号 - self.account = reduce(op.GetDlgItem, NODE['ACCOUNT'], self.main) - op.SendMessageW(self.account, MSG['WM_GETTEXT'], 32, self.buff) - self.account = self.buff.value - self.combo = reduce(op.GetDlgItem, NODE['COMBO'], self.main) - self.count = op.SendMessageW(self.combo, MSG['CB_GETCOUNT']) - - def switch_tab(self, hCtrl, keyCode, param=0): # 单击 - op.PostMessageW(hCtrl, MSG['WM_KEYDOWN'], keyCode, param) - self.wait_a_second(0.5) - op.PostMessageW(hCtrl, MSG['WM_KEYUP'], keyCode, param) - - def copy_data(self, key=0): # background mode - "将CVirtualGridCtrl|Custom的数据复制到剪贴板,默认取当前的表格" - if key: - self.switch_tab(self.two_way, key) # 切换到持仓('W')、成交('E')、委托('R') - print("正在等待实时数据返回,请稍候...") - pyperclip.copy('') - # 查到只有列表头的空白数据等3秒...orz - for i in range(10): - time.sleep(0.3) - op.SendMessageW(reduce(op.GetDlgItem, NODE['FORM'], self.main), - MSG['WM_COMMAND'], MSG['COPY_DATA'], NODE['FORM'][-1]) - if len(pyperclip.paste().splitlines()) > 1: - break - return pyperclip.paste() - - def buy(self, symbol, price, qty): - self.switch(NODE['BUY'][0]) - tuple(map(lambda hCtrl, arg: op.SendMessageW( - hCtrl, MSG['WM_SETTEXT'], 0, str(arg)), self._order[0][0], (symbol, price, qty))) - op.PostMessageW(self._order[0][-1], MSG['WM_COMMAND'], self._order[0][1], 0) - - def sell(self, symbol, price, qty): - self.switch(NODE['SELL'][0]) - tuple(map(lambda hCtrl, arg: op.SendMessageW( - hCtrl, MSG['WM_SETTEXT'], 0, str(arg)), self._order[1][0], (symbol, price, qty))) - op.PostMessageW(self._order[1][-1], MSG['WM_COMMAND'], self._order[1][1], 0) - - def buy2(self, symbol, price, qty): # 买入(B) - self.switch(NODE['双向委托']) - op.SendMessageW(self.members['买入代码'], MSG['WM_SETTEXT'], 0, str(symbol)) - op.SendMessageW(self.members['买入价格'], MSG['WM_SETTEXT'], 0, str(price)) - op.SendMessageW(self.members['买入数量'], MSG['WM_SETTEXT'], 0, str(qty)) - #op.SendMessageW(self.members['买入'], MSG['BM_CLICK'], 0, 0) - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], TWO_WAY['买入'], self.members['买入']) - - def sell2(self, symbol, price, qty): # 卖出(S) - self.switch(NODE['双向委托']) - op.SendMessageW(self.members['卖出代码'], MSG['WM_SETTEXT'], 0, str(symbol)) - op.SendMessageW(self.members['卖出价格'], MSG['WM_SETTEXT'], 0, str(price)) - op.SendMessageW(self.members['卖出数量'], MSG['WM_SETTEXT'], 0, str(qty)) - #op.SendMessageW(self.members['卖出'], MSG['BM_CLICK'], 0, 0) - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], TWO_WAY['卖出'], self.members['卖出']) - - def refresh(self): # 刷新(F5) - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], TWO_WAY['刷新'], self.members['刷新']) - - def cancel(self, symbol=None, way='撤买'): - - op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['撤单'], 0) # 切换到撤单操作台 - if way and str(symbol).isdecimal(): - #print(self.copy_data()) - self.cancel_c = reduce(op.GetDlgItem, NODE['FRAME'], self.main) - self.cancel_ctrl = {k: op.GetDlgItem(self.cancel_c, v) for k, v in CANCEL.items()} - op.SendMessageW(self.cancel_ctrl['填单'], MSG['WM_SETTEXT'], 0, symbol) - self.wait_a_second() - op.PostMessageW(self.cancel_c, MSG['WM_COMMAND'], CANCEL['查单'], self.cancel_ctrl['查单']) - op.PostMessageW(self.cancel_c, MSG['WM_COMMAND'], CANCEL[way], self.cancel_ctrl[way]) - schedule = self.copy_data() - op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['双向委托'], 0) # 必须返回交易操作台 - return schedule - - @property - def balance(self): - print('可用余额: %s' % ('$'*68)) - op.SendMessageW(self.members['可用余额'], MSG['WM_GETTEXT'], 32, self.buff) - return self.buff.value - - @property - def position(self): - print('实时持仓: %s' % ('$'*68)) - return self.copy_data(TAB['持仓']) - - @property - def market_value(self): - form = (x.split() for x in self.position.splitlines()) - index = next(form).index('市值') - return sum((float(row[index]) for row in form)) - - @property - def deals(self): - print('当天成交: %s' % ('$'*68)) - return self.copy_data(TAB['成交']) - - @property - def cancelable(self): - print('可撤委托: %s' % ('$'*68)) - return self.cancel(way=False) - - @property - def new(self): - print('新股名单: %s' % ('$'*68)) - return self.raffle(way=False) - - @property - def bingo(self): - print('新股中签: {0}'.format('$'*68)) - op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['中签查询'], 0) - return self.copy_data() - - def cancel_all(self): # 全撤(Z) - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 30001, self.members[30001]) - - def cancel_buy(self): # 撤买(X) - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 30002, self.members[30002]) - - def cancel_sell(self): # 撤卖(C) - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 30003, self.members[30003]) - - def cancel_last(self): # 撤最后一笔,仅限华泰定制版有效 - op.PostMessageW(self.two_way, MSG['WM_COMMAND'], 2053, self.members[2053]) - - def cancel_same(self): # 撤相同代码,仅限华泰定制版 - #op.PostMessageW(self.two_way, WM_COMMAND, 30022, self.members[30022]) - pass - - def raffle(self, skip=None, way=True): # 打新股。 - op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['新股申购'], 0) - #close_pop() # 弹窗无需关闭,不影响交易。 - schedule = self.copy_data() - if way: - print("开始打新股%s" % ('>'*68)) - print(schedule) - self.raffle_c = reduce(op.GetDlgItem, NODE['FRAME'], self.main) - self.raffle_ctrl = {k: op.GetDlgItem(self.raffle_c, v) for k, v in NEW.items()} - new = [x.split() for x in schedule.splitlines()] - index = [new[0].index(x) for x in RAFFLE if x in new[0]] # 索引映射:代码0, 价格1, 数量2 - new = map(lambda x: [x[y] for y in index], new[1:]) - for symbol, price, qty in new: - if symbol[0] == skip: - print({symbol: (qty, "跳过<%s>开头的新股!" % skip)}) - continue - if qty == '0': - print({symbol: (qty, "数量为零")}) - continue - op.SendMessageW(self.raffle_ctrl['新股代码'], MSG['WM_SETTEXT'], 0, symbol) - self.wait_a_second(1) - #op.SendMessageW(self.raffle_ctrl['可申购数量'], MSG['WM_GETTEXT'], 32, self.buff) - #qty = self.buff.value - op.SendMessageW(self.raffle_ctrl['申购数量'], MSG['WM_SETTEXT'], 0, qty) - self.wait_a_second() - op.PostMessageW(self.raffle_c, MSG['WM_COMMAND'], NEW['申购'], self.raffle_ctrl['申购']) - print({symbol: (qty, "已申购")}) - print(self.cancelable) - op.SendMessageW(self.main, MSG['WM_COMMAND'], NODE['双向委托'], 0) # 切换到交易操作台 - return schedule - -if __name__ == '__main__': - - trader = Puppet() - #trader = Puppet(title='广发证券核新网上交易系统7.60') - if trader.account: - print(trader.account) # 帐号 - #print(trader.new) # 查当天新股名单 - #trader.raffle(MKT['创业板']) # 确定打新股,跳过创业板不打。 - #print(trader.balance) # 可用余额 - #print(trader.position) # 实时持仓 - #print(trader.deals) # 当天成交 - #print(trader.cancelable) # 可撤委托 - print(trader.market_value) - From 8582433e5270708b65352de15a092d66665acf36 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 31 Aug 2024 17:59:11 +0800 Subject: [PATCH 547/601] =?UTF-8?q?=E6=B8=85=E7=90=86cppcheck=E9=A3=8E?= =?UTF-8?q?=E6=A0=BC=E5=91=8A=E8=AD=A6=EF=BC=8Cdoxygen=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E5=91=8A=E8=AD=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h | 1 + hikyuu_cpp/hikyuu/doc.h | 2 +- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 1 + hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 2 +- hikyuu_cpp/hikyuu/indicator/crt/AMA.h | 2 +- hikyuu_cpp/hikyuu/indicator/crt/CORR.h | 2 +- hikyuu_cpp/hikyuu/indicator/crt/ICIR.h | 1 + hikyuu_cpp/hikyuu/indicator/crt/SMA.h | 2 +- hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h | 2 +- hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp | 5 ----- hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h | 3 +-- hikyuu_cpp/hikyuu/strategy/Strategy.h | 4 ++-- hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h | 3 +++ hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h | 3 +-- .../trade_sys/allocatefunds/AllocateFundsBase.h | 1 - .../hikyuu/trade_sys/condition/ConditionBase.h | 1 + hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h | 1 + .../hikyuu/trade_sys/selector/imp/FixedSelector.h | 2 +- .../trade_sys/selector/imp/MultiFactorSelector.cpp | 4 ++-- .../trade_sys/selector/imp/OperatorSelector.cpp | 9 ++------- .../trade_sys/selector/imp/OperatorSelector.h | 2 +- .../trade_sys/selector/imp/OperatorValueSelector.h | 2 +- hikyuu_cpp/hikyuu/utilities/TimerManager.h | 4 ++-- hikyuu_cpp/hikyuu/utilities/arithmetic.h | 8 ++++---- hikyuu_cpp/hikyuu/utilities/base64.h | 3 +-- hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h | 4 ++-- hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h | 4 +--- .../hikyuu/utilities/thread/MQStealThreadPool.h | 13 ++++++------- 28 files changed, 41 insertions(+), 50 deletions(-) diff --git a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h index eaf5cc53..8ccafbdd 100644 --- a/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/BaseInfoDriver.h @@ -123,6 +123,7 @@ public: * @param market 市场简称 * @param code 证券代码 * @param start 财务报告发布起始日期 + * @param end 查询结束日期 * @return vector [[财务报告发布日期(ymd), 字段1, 字段2, ...], ...] */ virtual vector getHistoryFinance(const string& market, const string& code, diff --git a/hikyuu_cpp/hikyuu/doc.h b/hikyuu_cpp/hikyuu/doc.h index b61d9b2b..f435eec4 100644 --- a/hikyuu_cpp/hikyuu/doc.h +++ b/hikyuu_cpp/hikyuu/doc.h @@ -137,7 +137,7 @@ * @details 合成多因子 * @ingroup TradeSystem * - * @defgroup Stratgy Strategy 策略运行时 + * @defgroup Strategy Strategy 策略运行时 * @details 策略运行时 * @ingroup Hikyuu * diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index 33b52c66..ab74c7d5 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -13,6 +13,7 @@ namespace hku { /** * 启动 Spot 数据接收代理,如果之前已经处于运行状态,将抛出异常 * @param print 打印接收数据进展 + * @param worker_num 接收数据后处理时的工作任务组线程数 * @ingroup Agent */ void HKU_API startSpotAgent(bool print = true, diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index f232172e..3ae1db8d 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -27,7 +27,7 @@ namespace hku { */ class HKU_API SpotAgent { public: - SpotAgent(size_t worker_num); + explicit SpotAgent(size_t worker_num); /** 析构函数 */ virtual ~SpotAgent(); diff --git a/hikyuu_cpp/hikyuu/indicator/crt/AMA.h b/hikyuu_cpp/hikyuu/indicator/crt/AMA.h index 94f7173e..44374294 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/AMA.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/AMA.h @@ -32,7 +32,7 @@ Indicator HKU_API AMA(const IndParam& n, const IndParam& fast_n, const IndParam& /** * 佩里.J 考夫曼(Perry J.Kaufman)自适应移动平均,参见《精明交易者》(2006年 广东经济出版社) - * @param indicator 待计算的数据 + * @param ind 待计算的数据 * @param n 计算均值的周期窗口,必须为大于2的整数,默认为10天 * @param fast_n 对应快速周期N,默认为2 * @param slow_n 对应慢速EMA线的N值,默认为30,不过当超过60左右该指标会收敛不会有太大的影响 diff --git a/hikyuu_cpp/hikyuu/indicator/crt/CORR.h b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h index bfe7ec51..ba36a71e 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/CORR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/CORR.h @@ -18,7 +18,7 @@ namespace hku { * @param n 滚动窗口 (大于2或等于0),等于0时使用输入的ind实际长度。 * @ingroup Indicator */ -Indicator HKU_API CORR(int n = 10); Indicator HKU_API CORR(const Indicator& ind1, const Indicator& ind2, int n = 10); +Indicator HKU_API CORR(int n = 10); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h index 5055ff26..5bae471b 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/ICIR.h @@ -16,6 +16,7 @@ namespace hku { * @brief 计算指定的因子相对于参考证券的 ICIR (实际为 RankIC) * @details IR:信息比率(Information Ratio,简称IR)= * IC的多周期均值/IC的标准方差,代表因子获取稳定Alpha的能力。 + * @param ind 因子公式 * @param stks 证券组合 * @param query 查询条件 * @param ref_stk 参照证券,默认 sh000300 沪深300 diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SMA.h b/hikyuu_cpp/hikyuu/indicator/crt/SMA.h index fa482a14..9eab16b9 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SMA.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SMA.h @@ -34,7 +34,7 @@ Indicator HKU_API SMA(const IndParam& n, const IndParam& m); *
  * 用法:若Y=SMA(X,N,M) 则 Y=[M*X+(N-M)*Y')/N,其中Y'表示上一周期Y值
  * 
- * @param data 待计算的数据 + * @param ind 待计算的数据 * @param n 计算均值的周期窗口,必须为大于0的整数 * @param m 系数 * @ingroup Indicator diff --git a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h index b12f6c15..7bac4937 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/SPEARMAN.h @@ -17,7 +17,7 @@ namespace hku { * @param n 滚动窗口(大于2 或 等于0),等于0时,代表 n 实际使用 ind 的长度 * @ingroup Indicator */ -Indicator HKU_API SPEARMAN(int n = 0); Indicator HKU_API SPEARMAN(const Indicator& ind1, const Indicator& ind2, int n = 0); +Indicator HKU_API SPEARMAN(int n = 0); } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp index 6019e88a..8ff27106 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDropna.cpp @@ -40,11 +40,6 @@ void IDropna::_calculate(const Indicator& ind) { std::unique_ptr buf(new price_t[m_result_num * row_len]); #endif - if (!buf) { - HKU_ERROR("Memory allocation failed!"); - return; - } - DatetimeList dates; size_t pos = 0; for (size_t i = ind.discard(); i < total; i++) { diff --git a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h index 0e3811a3..0ff9e7c6 100644 --- a/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h +++ b/hikyuu_cpp/hikyuu/strategy/BrokerTradeManager.h @@ -181,10 +181,9 @@ public: /** * 获取指定证券的空头持仓记录 - * @param date 指定日期 * @param stock 指定的证券 */ - virtual PositionRecord getShortPosition(const Stock&) const { + virtual PositionRecord getShortPosition(const Stock& stock) const { HKU_WARN("The subclass does not implement this method"); return PositionRecord(); } diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index f461a11a..9963408f 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -74,8 +74,7 @@ public: /** * 正确数据发生变化调用,即接收到相应行情数据变更 * @note 通常用于调试。且只要收到行情采集消息就会触发,不受开、闭市时间限制 - * @param stk 数据发生变化的 stock - * @param spot 接收到的具体数据 + * @param changeFunc 回调函数 */ void onChange(std::function&& changeFunc); @@ -83,6 +82,7 @@ public: * 一批行情数据接受完毕后通知 * @note 通常仅用于调试打印,该批行情数据中不一定含有上下文中包含的 stock * 且只要收到行情采集消息就会触发,不受开、闭市时间限制。 + * @param recievedFucn 回调函数 */ void onReceivedSpot(std::function&& recievedFucn); diff --git a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h index 0597eed3..1bc3e97f 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/OrderBrokerBase.h @@ -134,6 +134,9 @@ public: * @param code 证券代码 * @param price 卖出价格 * @param num 卖出数量 + * @param stoploss 新预期的止损价 + * @param goalPrice 新预期的目标价位 + * @param from 系统部件来源 */ virtual void _sell(Datetime datetime, const string& market, const string& code, price_t price, double num, price_t stoploss, price_t goalPrice, SystemPart from) = 0; diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h index 0ab8d5a1..96cf9db8 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManagerBase.h @@ -442,10 +442,9 @@ public: /** * 获取指定证券的空头持仓记录 - * @param date 指定日期 * @param stock 指定的证券 */ - virtual PositionRecord getShortPosition(const Stock&) const { + virtual PositionRecord getShortPosition(const Stock& stock) const { HKU_WARN("The subclass does not implement this method"); return PositionRecord(); } diff --git a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h index fcaf48dc..79cf05cd 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/allocatefunds/AllocateFundsBase.h @@ -47,7 +47,6 @@ public: * @param date 指定日期 * @param se_list 系统实例选择器选出的系统实例 * @param running_list 当前运行中的系统实例 - * @param ignore_list 忽略不进行调仓的运行中系统 * @return 需延迟执行卖出操作的系统列表,其中权重为相应需卖出的数量 */ SystemWeightList adjustFunds(const Datetime& date, const SystemWeightList& se_list, diff --git a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h index ef0849a4..a7d6c095 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h +++ b/hikyuu_cpp/hikyuu/trade_sys/condition/ConditionBase.h @@ -72,6 +72,7 @@ public: /** * 加入有效时间,在_calculate中调用 * @param datetime 系统有效日期 + * @param value 值 */ void _addValid(const Datetime& datetime, price_t value = 1.0); diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h index 50ce9a24..d51ca33f 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.h @@ -53,6 +53,7 @@ public: * @note * 由于各个组件可能存在参数变化的情况,无法自动感知判断是否需要重新计算,此时需要手工指定强制计算 * @param query 查询条件 + * @param adjust_cycle 调仓周期 * @param force 是否强制重计算 */ void run(const KQuery& query, int adjust_cycle = 1, bool force = false); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h index f04332ae..28141fc1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/FixedSelector.h @@ -19,7 +19,7 @@ class FixedSelector : public SelectorBase { public: FixedSelector(); - FixedSelector(double weight); + explicit FixedSelector(double weight); virtual ~FixedSelector(); }; diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp index 59710ccb..d46b6aa9 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/MultiFactorSelector.cpp @@ -85,10 +85,10 @@ SystemWeightList MultiFactorSelector::getSelected(Datetime date) { ScoreRecordList scores; if (getParam("ignore_null")) { - scores = m_mf->getScores(date, 0, getParam("topn"), + scores = m_mf->getScores(date, 0, topn, [](const ScoreRecord& sc) { return !std::isnan(sc.value); }); } else { - scores = m_mf->getScores(date, 0, getParam("topn")); + scores = m_mf->getScores(date, 0, topn); } if (getParam("only_should_buy")) { diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp index 1cbdf948..1a8ad6f8 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.cpp @@ -281,13 +281,8 @@ SystemWeightList OperatorSelector::getIntersectionSelected( SystemWeightList ret; HKU_IF_RETURN(!m_se1 || !m_se2, ret); - SystemWeightList sws1, sws2; - if (m_se1) { - sws1 = m_se1->getSelected(date); - } - if (m_se2) { - sws2 = m_se2->getSelected(date); - } + SystemWeightList sws1 = m_se1->getSelected(date); + SystemWeightList sws2 = m_se2->getSelected(date); HKU_IF_RETURN(sws1.empty() || sws2.empty(), ret); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h index 471a6e95..e491f419 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorSelector.h @@ -14,7 +14,7 @@ namespace hku { class HKU_API OperatorSelector : public SelectorBase { public: OperatorSelector(); - OperatorSelector(const string& name); + explicit OperatorSelector(const string& name); OperatorSelector(const string& name, const SelectorPtr& se1, const SelectorPtr& se2); virtual ~OperatorSelector(); diff --git a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h index f85d171a..464214ee 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h +++ b/hikyuu_cpp/hikyuu/trade_sys/selector/imp/OperatorValueSelector.h @@ -14,7 +14,7 @@ namespace hku { class HKU_API OperatorValueSelector : public SelectorBase { public: OperatorValueSelector(); - OperatorValueSelector(const string& name); + explicit OperatorValueSelector(const string& name); OperatorValueSelector(const string& name, const SelectorPtr& se, double value); virtual ~OperatorValueSelector(); diff --git a/hikyuu_cpp/hikyuu/utilities/TimerManager.h b/hikyuu_cpp/hikyuu/utilities/TimerManager.h index 522caf56..3c78fe94 100644 --- a/hikyuu_cpp/hikyuu/utilities/TimerManager.h +++ b/hikyuu_cpp/hikyuu/utilities/TimerManager.h @@ -196,7 +196,7 @@ public: * @param start_time 允许运行的起始时间 * @param end_time 允许运行的结束时间 * @param repeat_num 重复次数,必须大于0,等于std::numeric_limits::max()时表示无限循环 - * @param delay 间隔时间,需大于 TimeDelta(0) + * @param duration 间隔时间,需大于 TimeDelta(0) * @param f 待执行的延迟任务 * @param args 任务具体参数 * @return timer id @@ -226,7 +226,7 @@ public: * @tparam F 任务类型 * @tparam Args 任务参数 * @param repeat_num 重复次数,必须大于0,等于std::numeric_limits::max()时表示无限循环 - * @param delay 间隔时间,需大于 TimeDelta(0) + * @param duration 间隔时间,需大于 TimeDelta(0) * @param f 待执行的延迟任务 * @param args 任务具体参数 * @return timer id diff --git a/hikyuu_cpp/hikyuu/utilities/arithmetic.h b/hikyuu_cpp/hikyuu/utilities/arithmetic.h index 906191ff..807369de 100644 --- a/hikyuu_cpp/hikyuu/utilities/arithmetic.h +++ b/hikyuu_cpp/hikyuu/utilities/arithmetic.h @@ -316,7 +316,7 @@ inline std::vector split(const std::string &str, const std::string /** * byte 转 16 进制字符串, 如 "abcd" 转换为 "61626364" - * @param in_byte 输入的 byte 数组 + * @param bytes 输入的 byte 数组 * @param in_len byte 数组长度 */ inline std::string byteToHexStr(const char *bytes, size_t in_len) { @@ -345,7 +345,7 @@ inline std::string byteToHexStr(const char *bytes, size_t in_len) { /** * byte 转 16 进制字符串, 如 "abcd" 转换为 "61626364" - * @param in_byte std::string 格式的输入 + * @param bytes std::string 格式的输入 */ inline std::string byteToHexStr(const std::string &bytes) { return byteToHexStr(bytes.c_str(), bytes.size()); @@ -353,7 +353,7 @@ inline std::string byteToHexStr(const std::string &bytes) { /** * byte 转 16 进制字符串, 如 "abcd" 转换为 "0x61 0x62 0x63 0x64" - * @param in_byte 输入的 byte 数组 + * @param bytes 输入的 byte 数组 * @param in_len byte 数组长度 */ inline std::string byteToHexStrForPrint(const char *bytes, size_t in_len) { @@ -389,7 +389,7 @@ inline std::string byteToHexStrForPrint(const char *bytes, size_t in_len) { /** * byte 转 16 进制字符串, 如 "abcd" 转换为 "61626364" - * @param in_byte 输入的 byte 数组 + * @param bytes 输入的 byte 数组 */ inline std::string byteToHexStrForPrint(const std::string &bytes) { return byteToHexStrForPrint(bytes.c_str(), bytes.size()); diff --git a/hikyuu_cpp/hikyuu/utilities/base64.h b/hikyuu_cpp/hikyuu/utilities/base64.h index 54ea3d99..0d5d28f7 100644 --- a/hikyuu_cpp/hikyuu/utilities/base64.h +++ b/hikyuu_cpp/hikyuu/utilities/base64.h @@ -27,8 +27,7 @@ std::string HKU_UTILS_API base64_encode(unsigned char const* bytes_to_encode, si /** * 字符串编码为 base64 - * @param bytes_to_encode 内存起始地址 - * @param in_len 待计算的字节长度 + * @param src 输入字符串 * @note 通过 func(unsigned char *, unsigned int) 函数实现,而不是直接只提供 string_view * 版本的原因是:c++17 string_view 处理 nullptr 时,程序会直接挂掉,无异常 */ diff --git a/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h b/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h index b40be05e..45d629e9 100644 --- a/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h +++ b/hikyuu_cpp/hikyuu/utilities/mo/moFileReader.h @@ -342,7 +342,7 @@ public: }; /** \brief Reads a .mo-file - * \param[in] _filename The path to the file to load. + * \param[in] data The path to the file to load. * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. * * This is the core-feature. This method loads the .mo-file and stores @@ -357,7 +357,7 @@ public: } /** \brief Reads a .mo-file - * \param[in] _filename The path to the file to load. + * \param[in] filename The path to the file to load. * \return SUCCESS on success or one of the other error-codes in eErrorCode on error. * * This is the core-feature. This method loads the .mo-file and stores diff --git a/hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h b/hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h index 68e727fc..a082d552 100644 --- a/hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h +++ b/hikyuu_cpp/hikyuu/utilities/node/NodeMessage.h @@ -37,8 +37,6 @@ namespace hku { * 对消息进行解码,消息类型和消息体必须匹配 * @tparam T 消息体类型 * @param msg 消息 - * @param out 解码输出的消息 - * @exception yas::io_exception 消息解码失败 * @exception NodeErrorCode 消息类型不匹配 */ inline json decodeMsg(nng_msg *msg) { @@ -68,7 +66,7 @@ inline void encodeMsg(nng_msg *msg, const json &in) { /** * 构造错误消息响应 - * @param msg[out] 消息 + * @param msg [out] 消息 * @param errcode 错误码 * @param errmsg 错误消息 */ diff --git a/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h b/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h index 3c50803b..d763d200 100644 --- a/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h +++ b/hikyuu_cpp/hikyuu/utilities/thread/MQStealThreadPool.h @@ -47,10 +47,9 @@ public: * 构造函数,创建指定数量的线程 * @param n 指定的线程数 * @param until_empty 任务队列为空时,自动停止运行 - * @param exit_thread_callback 工作线程结束时回调函数 */ - explicit MQStealThreadPool(size_t n, bool util_empty = true) - : m_done(false), m_worker_num(n), m_runnging_util_empty(util_empty) { + explicit MQStealThreadPool(size_t n, bool until_empty = true) + : m_done(false), m_worker_num(n), m_runnging_until_empty(until_empty) { try { m_interrupt_flags.resize(m_worker_num, nullptr); for (size_t i = 0; i < m_worker_num; i++) { @@ -189,7 +188,7 @@ public: } // 指示各工作线程在未获取到工作任务时,停止运行 - if (m_runnging_util_empty) { + if (m_runnging_until_empty) { while (true) { bool can_quit = true; for (size_t i = 0; i < m_worker_num; i++) { @@ -234,9 +233,9 @@ public: private: typedef FuncWrapper task_type; - std::atomic_bool m_done; // 线程池全局需终止指示 - size_t m_worker_num; // 工作线程数量 - bool m_runnging_util_empty; // 运行直到队列空时停止 + std::atomic_bool m_done; // 线程池全局需终止指示 + size_t m_worker_num; // 工作线程数量 + bool m_runnging_until_empty; // 运行直到队列空时停止 std::vector>> m_queues; // 线程任务队列 std::vector m_interrupt_flags; // 线程终止标志 From 5df15e1261a8d4e18aa7d3d971d7c6b4380792ca Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 1 Sep 2024 01:38:11 +0800 Subject: [PATCH 548/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= =?UTF-8?q?=E5=8F=8A=E5=AE=8C=E5=96=84StrategyContext(continue)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/KQuery.cpp | 11 ++++ hikyuu_cpp/hikyuu/KQuery.h | 3 + hikyuu_cpp/hikyuu/StrategyContext.cpp | 72 +++++++++++++++++---- hikyuu_cpp/hikyuu/StrategyContext.h | 73 +++++++++++++++------- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 2 +- hikyuu_cpp/hikyuu/strategy/Strategy.h | 5 +- hikyuu_pywrap/_StrategyContext.cpp | 19 +----- 7 files changed, 133 insertions(+), 52 deletions(-) diff --git a/hikyuu_cpp/hikyuu/KQuery.cpp b/hikyuu_cpp/hikyuu/KQuery.cpp index 72c683e7..b2a042c4 100644 --- a/hikyuu_cpp/hikyuu/KQuery.cpp +++ b/hikyuu_cpp/hikyuu/KQuery.cpp @@ -63,6 +63,17 @@ int32_t KQuery::getKTypeInMin(KType ktype) { return g_ktype2min.at(ktype); } +bool KQuery::isKType(const string& ktype) { + string nktype(ktype); + to_upper(nktype); + for (const auto& v : g_all_ktype) { + if (nktype == v) { + return true; + } + } + return false; +} + KQuery::KQuery(Datetime start, Datetime end, const KType& ktype, RecoverType recoverType) : m_start(start == Null() ? (int64_t)start.number() : (int64_t)(start.number() * 100 + start.second())), diff --git a/hikyuu_cpp/hikyuu/KQuery.h b/hikyuu_cpp/hikyuu/KQuery.h index 3bc301b2..143d857e 100644 --- a/hikyuu_cpp/hikyuu/KQuery.h +++ b/hikyuu_cpp/hikyuu/KQuery.h @@ -74,6 +74,9 @@ public: static int32_t getKTypeInMin(KType); + /** 判断是否为有效 ktype */ + static bool isKType(const string& ktype); + /** * 复权类型 * @note 日线以上,如周线/月线不支持复权 diff --git a/hikyuu_cpp/hikyuu/StrategyContext.cpp b/hikyuu_cpp/hikyuu/StrategyContext.cpp index 52eb60fe..cd6a749f 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.cpp +++ b/hikyuu_cpp/hikyuu/StrategyContext.cpp @@ -9,18 +9,68 @@ namespace hku { -void StrategyContext::setStockCodeList(const vector& stockList) { - m_stockCodeList.resize(stockList.size()); - std::copy(stockList.begin(), stockList.end(), m_stockCodeList.begin()); +StrategyContext::StrategyContext(const vector& stockCodeList) { + _removeDuplicateCode(stockCodeList); } -void StrategyContext::setKTypeList(const vector& ktypeList) { - m_ktypeList.resize(ktypeList.size()); - std::transform(ktypeList.begin(), ktypeList.end(), m_ktypeList.begin(), - [](KQuery::KType ktype) { - to_upper(ktype); - return ktype; - }); +StrategyContext::StrategyContext(const vector& stockCodeList, + const vector& ktypeList) { + _removeDuplicateCode(stockCodeList); + _checkAndRemoveDuplicateKType(ktypeList); +} + +StrategyContext::StrategyContext(StrategyContext&& rv) +: m_startDatetime(std::move(rv.m_startDatetime)), + m_mustLoad(std::move(rv.m_mustLoad)), + m_stockCodeList(std::move(rv.m_stockCodeList)), + m_ktypeList(std::move(m_ktypeList)) {} + +StrategyContext& StrategyContext::operator=(const StrategyContext& rv) { + if (this != &rv) { + m_startDatetime = rv.m_startDatetime; + m_mustLoad = rv.m_mustLoad; + m_stockCodeList = rv.m_stockCodeList; + m_ktypeList = rv.m_ktypeList; + } + return *this; +} + +StrategyContext& StrategyContext::operator=(StrategyContext&& rv) { + if (this != &rv) { + m_startDatetime = std::move(rv.m_startDatetime); + m_mustLoad = std::move(rv.m_mustLoad); + m_stockCodeList = std::move(rv.m_stockCodeList); + m_ktypeList = std::move(rv.m_ktypeList); + } + return *this; +} + +void StrategyContext::_removeDuplicateCode(const vector& stockCodeList) { + m_stockCodeList.reserve(stockCodeList.size()); + std::set code_set; + for (const auto& code : m_mustLoad) { + code_set.insert(code); + } + for (const auto& code : stockCodeList) { + if (code_set.find(code) == code_set.end()) { + m_stockCodeList.push_back(code); + } else { + code_set.insert(code); + } + } +} + +void StrategyContext::_checkAndRemoveDuplicateKType(const vector& ktypeList) { + m_ktypeList.reserve(ktypeList.size()); + std::set ktype_set; + for (const auto& ktype : ktypeList) { + HKU_CHECK(KQuery::isKType(ktype), "Invalid ktype: {}", ktype); + if (ktype_set.find(ktype) == ktype_set.end()) { + m_ktypeList.push_back(ktype); + } else { + ktype_set.insert(ktype); + } + } } bool StrategyContext::isAll() const noexcept { @@ -30,7 +80,7 @@ bool StrategyContext::isAll() const noexcept { }) != m_stockCodeList.end(); } -vector StrategyContext::getAllNeedLoadStockCodeList() const { +vector StrategyContext::getAllNeedLoadStockCodeList() const noexcept { vector ret{m_stockCodeList}; for (const auto& code : m_mustLoad) { ret.push_back(code); diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index 70f48aec..749a019a 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -12,54 +12,81 @@ namespace hku { -class HKU_API StrategyContext { +/** + * 策略上下文,定义策略执行时包含的证券/K线级别信息 + * @ingroup Strategy + * + */ +class HKU_API StrategyContext final { public: - StrategyContext() {} + StrategyContext() = default; + ~StrategyContext() = default; + + explicit StrategyContext(const vector& stockCodeList); + + /** + * 构造函数 + * @note 证券列表中如果包含 ("ALL") 则表示全部证券; + * 指定K线类型列表同时影响着K线数据的优先加载顺序,靠前的将优先加载 + * @param stockCodeList 指定的证券代码列表,如:{"sh000001", "sz000001"} + * @param ktypeList 指定的 K线数据列表,如:{"day", "min"} + */ + StrategyContext(const vector& stockCodeList, const vector& ktypeList); + StrategyContext(const StrategyContext&) = default; + StrategyContext(StrategyContext&& rv); - explicit StrategyContext(const vector& stockCodeList) - : m_stockCodeList(stockCodeList) {} - explicit StrategyContext(vector&& stockCodeList) - : m_stockCodeList(std::move(stockCodeList)) {} - - StrategyContext(const vector& stockCodeList, const vector& ktypeList) - : m_stockCodeList(stockCodeList), m_ktypeList(ktypeList) {} - - virtual ~StrategyContext() = default; + StrategyContext& operator=(const StrategyContext&); + StrategyContext& operator=(StrategyContext&&); + /** + * 是否为加载全部证券,只要 stockCodeList 包含 "ALL"(不区分大小写),即认为加载全部 + * @return true + * @return false + */ bool isAll() const noexcept; Datetime startDatetime() const noexcept { return m_startDatetime; } - void startDatetime(const Datetime& d) { - HKU_CHECK(!d.isNull(), "Don't use null datetime!"); - m_startDatetime = d; - } - void setStockCodeList(vector&& stockList) { m_stockCodeList = std::move(stockList); } - void setStockCodeList(const vector& stockList); + void setStockCodeList(const vector& stockList) { + _removeDuplicateCode(stockList); + } - const vector& getStockCodeList() const { + const vector& getStockCodeList() const noexcept { return m_stockCodeList; } - void setKTypeList(const vector& ktypeList); + void setKTypeList(const vector& ktypeList) { + _checkAndRemoveDuplicateKType(ktypeList); + } - /** 该返回的 ktype 列表,已经按从小到大进行排序 */ - const vector& getKTypeList() const { + const vector& getKTypeList() const noexcept { return m_ktypeList; } - const vector& getMustLoadStockCodeList() const { + /** + * 隐含的默认必须被加载的证券列表 + * @note 影响交易日历判断、和某些常被作为默认比较基准的证券,通常被作为某些函数的默认值 + */ + const vector& getMustLoadStockCodeList() const noexcept { return m_mustLoad; } - vector getAllNeedLoadStockCodeList() const; + /** + * 返回所有需要加载的证券列表(含指定的证券列表和默认包含必须加载的证券列表) + * @return vector + */ + vector getAllNeedLoadStockCodeList() const noexcept; + +private: + void _removeDuplicateCode(const vector& stockCodeList); + void _checkAndRemoveDuplicateKType(const vector& ktypeList); private: Datetime m_startDatetime{19901219}; diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index ab74c7d5..6653c8d8 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -13,7 +13,7 @@ namespace hku { /** * 启动 Spot 数据接收代理,如果之前已经处于运行状态,将抛出异常 * @param print 打印接收数据进展 - * @param worker_num 接收数据后处理时的工作任务组线程数 + * @param worker_num 接收数据后处理时的工作任务组线程数(该参数仅在第一次调用时生效) * @ingroup Agent */ void HKU_API startSpotAgent(bool print = true, diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 9963408f..ede8b346 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -19,7 +19,7 @@ namespace hku { /** - * @ingroup Stratgy + * @ingroup Strategy * @{ */ @@ -38,6 +38,9 @@ public: explicit Strategy(const StrategyContext& context, const string& name = "Strategy", const string& config_file = ""); + Strategy(const Strategy&) = delete; + Strategy& operator=(const Strategy&) = delete; + virtual ~Strategy(); const string& name() const { diff --git a/hikyuu_pywrap/_StrategyContext.cpp b/hikyuu_pywrap/_StrategyContext.cpp index 29dfa482..a599f3a3 100644 --- a/hikyuu_pywrap/_StrategyContext.cpp +++ b/hikyuu_pywrap/_StrategyContext.cpp @@ -11,27 +11,14 @@ namespace py = pybind11; using namespace hku; -Datetime (StrategyContext::*get_start_datetime)() const = &StrategyContext::startDatetime; -void (StrategyContext::*set_start_datetime)(const Datetime&) = &StrategyContext::startDatetime; - -void (StrategyContext::*set_stock_list)(const vector&) = &StrategyContext::setStockCodeList; - -void setStockList(StrategyContext* self, const py::sequence& seq) { - vector stk_list = python_list_to_vector(seq); - self->setStockCodeList(std::move(stk_list)); -} - -void setKTypeList(StrategyContext* self, const py::sequence& seq) { - vector stk_list = python_list_to_vector(seq); - self->setKTypeList(stk_list); -} - void export_StrategeContext(py::module& m) { py::class_(m, "StrategyContext", "策略上下文") .def(py::init<>()) .def(py::init&, const vector&>(), py::arg("stock_list"), py::arg("ktype_list")) - .def_property("start_datetime", get_start_datetime, set_start_datetime, "起始日期") + .def_property_readonly("start_datetime", + py::overload_cast<>(&StrategyContext::startDatetime, py::const_), + "起始日期") .def_property( "stock_list", py::overload_cast<>(&StrategyContext::getStockCodeList, py::const_), [](StrategyContext& self, const py::sequence& stk_list) { From 668def29cd5f77734bdf678cd1281292a85610b3 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 1 Sep 2024 04:58:54 +0800 Subject: [PATCH 549/601] =?UTF-8?q?=E5=AE=8C=E5=96=84StrategyContext?= =?UTF-8?q?=EF=BC=9Bfixed=20interactive=20=E6=9C=AA=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E8=AF=81=E5=88=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/GlobalInitializer.cpp | 1 + hikyuu_cpp/hikyuu/StrategyContext.cpp | 29 ++------------------ hikyuu_cpp/hikyuu/StrategyContext.h | 20 ++++++-------- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 7 +++-- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 4 +-- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 14 +++++++--- hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 17 ++++++++---- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 4 +-- hikyuu_pywrap/_StrategyContext.cpp | 16 ++++------- 9 files changed, 45 insertions(+), 67 deletions(-) diff --git a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp index 632a31bc..2e419635 100644 --- a/hikyuu_cpp/hikyuu/GlobalInitializer.cpp +++ b/hikyuu_cpp/hikyuu/GlobalInitializer.cpp @@ -69,6 +69,7 @@ void GlobalInitializer::init() { DataDriverFactory::init(); StockManager::instance(); IndicatorImp::initDynEngine(); + getGlobalSpotAgent(); } void GlobalInitializer::clean() { diff --git a/hikyuu_cpp/hikyuu/StrategyContext.cpp b/hikyuu_cpp/hikyuu/StrategyContext.cpp index cd6a749f..5a9bd50f 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.cpp +++ b/hikyuu_cpp/hikyuu/StrategyContext.cpp @@ -19,32 +19,6 @@ StrategyContext::StrategyContext(const vector& stockCodeList, _checkAndRemoveDuplicateKType(ktypeList); } -StrategyContext::StrategyContext(StrategyContext&& rv) -: m_startDatetime(std::move(rv.m_startDatetime)), - m_mustLoad(std::move(rv.m_mustLoad)), - m_stockCodeList(std::move(rv.m_stockCodeList)), - m_ktypeList(std::move(m_ktypeList)) {} - -StrategyContext& StrategyContext::operator=(const StrategyContext& rv) { - if (this != &rv) { - m_startDatetime = rv.m_startDatetime; - m_mustLoad = rv.m_mustLoad; - m_stockCodeList = rv.m_stockCodeList; - m_ktypeList = rv.m_ktypeList; - } - return *this; -} - -StrategyContext& StrategyContext::operator=(StrategyContext&& rv) { - if (this != &rv) { - m_startDatetime = std::move(rv.m_startDatetime); - m_mustLoad = std::move(rv.m_mustLoad); - m_stockCodeList = std::move(rv.m_stockCodeList); - m_ktypeList = std::move(rv.m_ktypeList); - } - return *this; -} - void StrategyContext::_removeDuplicateCode(const vector& stockCodeList) { m_stockCodeList.reserve(stockCodeList.size()); std::set code_set; @@ -74,7 +48,8 @@ void StrategyContext::_checkAndRemoveDuplicateKType(const vector& } bool StrategyContext::isAll() const noexcept { - return std::find_if(m_stockCodeList.begin(), m_stockCodeList.end(), [](string val) { + return m_stockCodeList.empty() || + std::find_if(m_stockCodeList.begin(), m_stockCodeList.end(), [](string val) { to_upper(val); return val == "ALL"; }) != m_stockCodeList.end(); diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index 749a019a..6499b262 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -17,10 +17,10 @@ namespace hku { * @ingroup Strategy * */ -class HKU_API StrategyContext final { +class HKU_API StrategyContext { public: StrategyContext() = default; - ~StrategyContext() = default; + virtual ~StrategyContext() = default; explicit StrategyContext(const vector& stockCodeList); @@ -33,14 +33,14 @@ public: */ StrategyContext(const vector& stockCodeList, const vector& ktypeList); - StrategyContext(const StrategyContext&) = default; - StrategyContext(StrategyContext&& rv); - - StrategyContext& operator=(const StrategyContext&); - StrategyContext& operator=(StrategyContext&&); + // 自定义移动构造与赋值会引起 python 中无法正常退出 + // StrategyContext(const StrategyContext&) = default; + // StrategyContext(StrategyContext&& rv) = delete; + // StrategyContext& operator=(const StrategyContext&) = default; + // StrategyContext& operator=(StrategyContext&&) = delete; /** - * 是否为加载全部证券,只要 stockCodeList 包含 "ALL"(不区分大小写),即认为加载全部 + * 是否为加载全部证券,只要 stockCodeList 包含 "ALL"(不区分大小写) 或者为空,即认为加载全部 * @return true * @return false */ @@ -50,10 +50,6 @@ public: return m_startDatetime; } - void setStockCodeList(vector&& stockList) { - m_stockCodeList = std::move(stockList); - } - void setStockCodeList(const vector& stockList) { _removeDuplicateCode(stockList); } diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 23d3e619..1b7aa9a2 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -14,9 +14,9 @@ namespace hku { SpotAgent* g_spot_agent = nullptr; -SpotAgent* getGlobalSpotAgent(size_t worker_num) { +SpotAgent* getGlobalSpotAgent() { if (!g_spot_agent) { - g_spot_agent = new SpotAgent(worker_num); + g_spot_agent = new SpotAgent(); } return g_spot_agent; } @@ -173,9 +173,10 @@ void HKU_API startSpotAgent(bool print, size_t worker_num) { StockManager& sm = StockManager::instance(); SpotAgent::setQuotationServer( sm.getHikyuuParameter().tryGet("quotation_server", "ipc:///tmp/hikyuu_real.ipc")); - auto& agent = *getGlobalSpotAgent(worker_num); + auto& agent = *getGlobalSpotAgent(); HKU_CHECK(!agent.isRunning(), "The agent is running, please stop first!"); + agent.setWorkerNum(worker_num); agent.setPrintFlag(print); // 防止调用 stopSpotAgent 后重新 startSpotAgent diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index 6653c8d8..a6248a07 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -13,7 +13,7 @@ namespace hku { /** * 启动 Spot 数据接收代理,如果之前已经处于运行状态,将抛出异常 * @param print 打印接收数据进展 - * @param worker_num 接收数据后处理时的工作任务组线程数(该参数仅在第一次调用时生效) + * @param worker_num 接收数据后处理时的工作任务组线程数 * @ingroup Agent */ void HKU_API startSpotAgent(bool print = true, @@ -25,7 +25,7 @@ void HKU_API startSpotAgent(bool print = true, */ void HKU_API stopSpotAgent(); -SpotAgent* getGlobalSpotAgent(size_t worker_num = std::thread::hardware_concurrency()); +SpotAgent* getGlobalSpotAgent(); void HKU_API releaseGlobalSpotAgent(); diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 37edc342..e20a6429 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -25,10 +25,6 @@ const size_t SpotAgent::ms_endTagLength = strlen(SpotAgent::ms_endTag); Datetime SpotAgent::ms_start_rev_time; -SpotAgent::SpotAgent(size_t worker_num) { - m_tg = std::make_unique(worker_num); -} - SpotAgent::~SpotAgent() { stop(); } @@ -41,15 +37,25 @@ void SpotAgent::start() { stop(); if (m_stop) { m_stop = false; + if (m_tg) { + m_tg.reset(); + m_tg = std::make_unique(m_work_num); + } m_receiveThread = std::thread([this]() { work_thread(); }); } } void SpotAgent::stop() { m_stop = true; + if (m_tg) { + m_tg->stop(); + } if (m_receiveThread.joinable()) { m_receiveThread.join(); } + if (m_tg) { + m_tg.reset(); + } } class ProcessTask { diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index 3ae1db8d..e75712db 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -27,7 +27,7 @@ namespace hku { */ class HKU_API SpotAgent { public: - explicit SpotAgent(size_t worker_num); + SpotAgent() = default; /** 析构函数 */ virtual ~SpotAgent(); @@ -43,6 +43,11 @@ public: return !m_stop; } + void setWorkerNum(size_t worker_num) { + std::lock_guard lock(m_mutex); + m_work_num = worker_num; + } + /** 设置是否打印数据接收进展情况,主要用于在交互环境下关闭打印 */ void setPrintFlag(bool print) { std::lock_guard lock(m_mutex); @@ -106,11 +111,11 @@ private: enum STATUS m_status = WAITING; // 当前内部状态 std::atomic_bool m_stop = true; // 结束代理工作标识 - int m_revTimeout = 100; // 连接数据服务超时时长(毫秒) - size_t m_batch_count = 0; // 记录本次批次接收的数据数量 - std::thread m_receiveThread; // 数据接收线程 - // ThreadPool m_tg; // 数据处理任务线程池 - std::unique_ptr m_tg; + int m_revTimeout = 100; // 连接数据服务超时时长(毫秒) + size_t m_batch_count = 0; // 记录本次批次接收的数据数量 + std::thread m_receiveThread; // 数据接收线程 + std::unique_ptr m_tg; // 数据处理任务线程池 + size_t m_work_num = 1; // 数据处理任务线程池线程数 vector> m_process_task_list; // 下面属性被修改时需要加锁,以便可以使用多线程方式运行 strategy diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index f2824b73..9f998329 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -106,14 +106,14 @@ void Strategy::start(bool autoRecieveSpot) { spot_worker_num = cpu_num; } - auto& agent = *getGlobalSpotAgent(spot_worker_num); + auto& agent = *getGlobalSpotAgent(); agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { if (m_on_recieved_spot) { event([=]() { m_on_recieved_spot(revTime); }); } }); - startSpotAgent(true); + startSpotAgent(true, spot_worker_num); } _runDaily(); diff --git a/hikyuu_pywrap/_StrategyContext.cpp b/hikyuu_pywrap/_StrategyContext.cpp index a599f3a3..e826630c 100644 --- a/hikyuu_pywrap/_StrategyContext.cpp +++ b/hikyuu_pywrap/_StrategyContext.cpp @@ -14,21 +14,15 @@ using namespace hku; void export_StrategeContext(py::module& m) { py::class_(m, "StrategyContext", "策略上下文") .def(py::init<>()) + .def(py::init&>()) .def(py::init&, const vector&>(), py::arg("stock_list"), py::arg("ktype_list")) .def_property_readonly("start_datetime", py::overload_cast<>(&StrategyContext::startDatetime, py::const_), - "起始日期") + py::return_value_policy::copy, "起始日期") .def_property( "stock_list", py::overload_cast<>(&StrategyContext::getStockCodeList, py::const_), - [](StrategyContext& self, const py::sequence& stk_list) { - self.setStockCodeList(python_list_to_vector(stk_list)); - }, - "股票代码列表") - .def_property( - "ktype_list", py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), - [](StrategyContext& self, const py::sequence& ktype_list) { - self.setKTypeList(python_list_to_vector(ktype_list)); - }, - "需要的K线类型"); + &StrategyContext::setStockCodeList, py::return_value_policy::copy, "股票代码列表") + .def_property("ktype_list", py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), + &StrategyContext::setKTypeList, py::return_value_policy::copy, "需要的K线类型"); } From e887383dbf1d5650b01ad31ac90faada57071ec6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 1 Sep 2024 16:11:27 +0800 Subject: [PATCH 550/601] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=8A=E4=B8=8B?= =?UTF-8?q?=E6=96=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/stock_manager.rst | 5 +++ hikyuu/interactive.py | 2 +- hikyuu_cpp/hikyuu/StockManager.cpp | 1 + hikyuu_cpp/hikyuu/StrategyContext.cpp | 52 +++++++++++++++++++++++---- hikyuu_cpp/hikyuu/StrategyContext.h | 17 +++++++-- hikyuu_pywrap/_StockManager.cpp | 2 ++ hikyuu_pywrap/_StrategyContext.cpp | 8 ++++- 7 files changed, 77 insertions(+), 10 deletions(-) diff --git a/docs/source/stock_manager.rst b/docs/source/stock_manager.rst index b8ee171a..bb78b890 100644 --- a/docs/source/stock_manager.rst +++ b/docs/source/stock_manager.rst @@ -141,6 +141,11 @@ StockManager/Block/Stock :return: 其他hikyuu参数 :rtype: Parameter + + .. py:method:: get_context(self) + + :return: 获取当前上下文 + :rtype: StrategyContext .. py:method:: tmpdir(self) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 84eb0b2b..ab44695d 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -103,7 +103,7 @@ for p in kdata_config: continue kdata_param[p] = ini.get('kdata', p) -context = StrategyContext() +context = StrategyContext(["all"]) if 'HKU_STOCK_LIST' in os.environ: context.stock_list = os.environ['HKU_STOCK_LIST'].split(";") if 'HKU_KTYPE_LIST' in os.environ: diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 8a96acd7..a52cdfca 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -57,6 +57,7 @@ StockManager& StockManager::instance() { void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockParam, const Parameter& kdataParam, const Parameter& preloadParam, const Parameter& hikyuuParam, const StrategyContext& context) { + HKU_CHECK(!context.empty(), "No stock code list is included in the context!"); HKU_WARN_IF_RETURN(m_initializing, void(), "The last initialization has not finished. Please try again later!"); diff --git a/hikyuu_cpp/hikyuu/StrategyContext.cpp b/hikyuu_cpp/hikyuu/StrategyContext.cpp index 5a9bd50f..1e1dd581 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.cpp +++ b/hikyuu_cpp/hikyuu/StrategyContext.cpp @@ -9,6 +9,41 @@ namespace hku { +HKU_API std::ostream& operator<<(std::ostream& os, const StrategyContext& context) { + os << context.str(); + return os; +} + +string StrategyContext::str() const { + std::stringstream os; + os << "StrategyContext{\n" + << " start datetime: " << m_startDatetime << ",\n" + << " stock code list: ["; + size_t len = m_stockCodeList.size(); + if (len > 5) { + len = 5; + } + for (size_t i = 0; i < len; i++) { + os << "\"" << m_stockCodeList[i] << "\", "; + } + if (m_stockCodeList.size() >= 5) { + os << "..."; + } + os << "],\n" + << " ktype list: ["; + for (size_t i = 0, total = m_ktypeList.size(); i < total; i++) { + os << "\"" << m_ktypeList[i] << "\", "; + } + os << "],\n" + << " default load: ["; + for (size_t i = 0, total = m_mustLoad.size(); i < total; i++) { + os << "\"" << m_mustLoad[i] << "\", "; + } + os << "],\n" + << "}"; + return os.str(); +} + StrategyContext::StrategyContext(const vector& stockCodeList) { _removeDuplicateCode(stockCodeList); } @@ -20,11 +55,9 @@ StrategyContext::StrategyContext(const vector& stockCodeList, } void StrategyContext::_removeDuplicateCode(const vector& stockCodeList) { + m_stockCodeList.clear(); m_stockCodeList.reserve(stockCodeList.size()); std::set code_set; - for (const auto& code : m_mustLoad) { - code_set.insert(code); - } for (const auto& code : stockCodeList) { if (code_set.find(code) == code_set.end()) { m_stockCodeList.push_back(code); @@ -35,6 +68,7 @@ void StrategyContext::_removeDuplicateCode(const vector& stockCodeList) } void StrategyContext::_checkAndRemoveDuplicateKType(const vector& ktypeList) { + m_ktypeList.clear(); m_ktypeList.reserve(ktypeList.size()); std::set ktype_set; for (const auto& ktype : ktypeList) { @@ -48,17 +82,23 @@ void StrategyContext::_checkAndRemoveDuplicateKType(const vector& } bool StrategyContext::isAll() const noexcept { - return m_stockCodeList.empty() || - std::find_if(m_stockCodeList.begin(), m_stockCodeList.end(), [](string val) { + return std::find_if(m_stockCodeList.begin(), m_stockCodeList.end(), [](string val) { to_upper(val); return val == "ALL"; }) != m_stockCodeList.end(); } vector StrategyContext::getAllNeedLoadStockCodeList() const noexcept { - vector ret{m_stockCodeList}; + vector ret; + std::set code_set; for (const auto& code : m_mustLoad) { ret.push_back(code); + code_set.insert(code); + } + for (const auto& code : m_stockCodeList) { + if (code_set.find(code) == code_set.end()) { + ret.push_back(code); + } } return ret; } diff --git a/hikyuu_cpp/hikyuu/StrategyContext.h b/hikyuu_cpp/hikyuu/StrategyContext.h index 6499b262..15e7111b 100644 --- a/hikyuu_cpp/hikyuu/StrategyContext.h +++ b/hikyuu_cpp/hikyuu/StrategyContext.h @@ -22,12 +22,17 @@ public: StrategyContext() = default; virtual ~StrategyContext() = default; + /** + * 构造函数 + * @note 未指定 ktypelist 时,默认按预加载参数加载全部 + * @param stockCodeList 指定的证券代码列表,如:如:{"sz000001", "sz000002"} + */ explicit StrategyContext(const vector& stockCodeList); /** * 构造函数 * @note 证券列表中如果包含 ("ALL") 则表示全部证券; - * 指定K线类型列表同时影响着K线数据的优先加载顺序,靠前的将优先加载 + * 指定K线类型列表同时影响着K线数据的优先加载顺序,靠前的将优先加载。同时受系统配置中预加载参数影响! * @param stockCodeList 指定的证券代码列表,如:{"sh000001", "sz000001"} * @param ktypeList 指定的 K线数据列表,如:{"day", "min"} */ @@ -40,12 +45,16 @@ public: // StrategyContext& operator=(StrategyContext&&) = delete; /** - * 是否为加载全部证券,只要 stockCodeList 包含 "ALL"(不区分大小写) 或者为空,即认为加载全部 + * 是否为加载全部证券,只要 stockCodeList 包含 "ALL"(不区分大小写) ,即认为加载全部 * @return true * @return false */ bool isAll() const noexcept; + bool empty() const noexcept { + return m_stockCodeList.empty(); + } + Datetime startDatetime() const noexcept { return m_startDatetime; } @@ -80,6 +89,8 @@ public: */ vector getAllNeedLoadStockCodeList() const noexcept; + string str() const; + private: void _removeDuplicateCode(const vector& stockCodeList); void _checkAndRemoveDuplicateKType(const vector& ktypeList); @@ -91,4 +102,6 @@ private: vector m_ktypeList; }; +HKU_API std::ostream& operator<<(std::ostream& os, const StrategyContext& context); + } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 65b88e22..2389a8ce 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -54,6 +54,8 @@ void export_StockManager(py::module& m) { py::return_value_policy::copy, "获取当前预加载参数") .def("get_hikyuu_parameter", &StockManager::getHikyuuParameter, py::return_value_policy::copy, "获取当前其他参数") + .def("get_context", &StockManager::getStrategyContext, py::return_value_policy::copy, + "获取当前上下文") .def("get_market_list", &StockManager::getAllMarket, R"(get_market_list(self) diff --git a/hikyuu_pywrap/_StrategyContext.cpp b/hikyuu_pywrap/_StrategyContext.cpp index e826630c..58e26077 100644 --- a/hikyuu_pywrap/_StrategyContext.cpp +++ b/hikyuu_pywrap/_StrategyContext.cpp @@ -17,6 +17,10 @@ void export_StrategeContext(py::module& m) { .def(py::init&>()) .def(py::init&, const vector&>(), py::arg("stock_list"), py::arg("ktype_list")) + + .def("__str__", &StrategyContext::str) + .def("__repr__", &StrategyContext::str) + .def_property_readonly("start_datetime", py::overload_cast<>(&StrategyContext::startDatetime, py::const_), py::return_value_policy::copy, "起始日期") @@ -24,5 +28,7 @@ void export_StrategeContext(py::module& m) { "stock_list", py::overload_cast<>(&StrategyContext::getStockCodeList, py::const_), &StrategyContext::setStockCodeList, py::return_value_policy::copy, "股票代码列表") .def_property("ktype_list", py::overload_cast<>(&StrategyContext::getKTypeList, py::const_), - &StrategyContext::setKTypeList, py::return_value_policy::copy, "需要的K线类型"); + &StrategyContext::setKTypeList, py::return_value_policy::copy, "需要的K线类型") + + .def("empty", &StrategyContext::empty, "上下文证券代码列表是否为空"); } From 8e119984eded9b66b4c7535401405c564789c9c4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 1 Sep 2024 22:29:14 +0800 Subject: [PATCH 551/601] =?UTF-8?q?=E5=88=86=E9=92=9F=E7=BA=A7=E5=88=AB?= =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=9B=B4=E6=96=B0=E6=97=B6=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=AF=AF=E5=B7=AE=E4=BF=9D=E6=8A=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 1b7aa9a2..e92eab91 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -162,9 +162,11 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { sum_volume += k.transCount; } - price_t amount = spot.amount > sum_amount ? spot.amount - sum_amount : spot.amount; - price_t spot_volume = spot.volume * 100; // spot 传过来的是手数 - price_t volume = spot_volume > sum_volume ? spot_volume - sum_volume : spot_volume; + price_t amount = + spot.amount > sum_amount ? spot.amount - sum_amount : (sum_amount == 0.0 ? spot.amount : 0.0); + price_t spot_volume = spot.volume * 100.; // spot 传过来的是手数 + price_t volume = + spot_volume > sum_volume ? spot_volume - sum_volume : (sum_volume == 0.0 ? spot_volume : 0.0); KRecord krecord(end_minute, spot.open, spot.high, spot.low, spot.close, amount, volume); stk.realtimeUpdate(krecord, ktype); } From 45782ec073bff6820e80e9495669d985bb048bfd Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 1 Sep 2024 23:25:10 +0800 Subject: [PATCH 552/601] =?UTF-8?q?fixed=20=E5=88=86=E9=92=9F=E7=BA=BF?= =?UTF-8?q?=E6=9B=B4=E6=96=B0,=20=E6=9B=B4=E6=96=B0=E6=97=A5=E7=BA=BF?= =?UTF-8?q?=E4=BB=A5=E4=B8=8A=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/Stock.cpp | 11 ++++- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 52 ++++++-------------- 2 files changed, 25 insertions(+), 38 deletions(-) diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 468b04b1..53a9fc7b 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -897,7 +897,16 @@ void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { // 如果传入的记录日期等于最后一条记录日期,则更新最后一条记录;否则,追加入缓存 if (tmp.datetime == record.datetime) { - tmp = record; + if (tmp.highPrice < record.highPrice) { + tmp.highPrice = record.highPrice; + } + if (tmp.lowPrice > record.lowPrice) { + tmp.lowPrice = record.lowPrice; + } + tmp.closePrice = record.closePrice; + tmp.transAmount = record.transAmount; + tmp.transCount = record.transCount; + } else if (tmp.datetime < record.datetime) { m_data->pKData[ktype]->push_back(record); } else { diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index e92eab91..094b4685 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -76,46 +76,24 @@ static void updateStockDayUpData(const SpotRecord& spot, KQuery::KType ktype) { if (KQuery::WEEK == ktype) { spot_end_of_phase = spot_end_of_phase - TimeDelta(2); // 周五日期 } - size_t total = stk.getCount(ktype); - // 没有历史数据,则直接更新并返回 - if (total == 0) { - stk.realtimeUpdate(KRecord(spot_end_of_phase, spot.open, spot.high, spot.low, spot.close, - spot.amount, spot.volume), - ktype); - return; + // 重新计算交易金额、交易量 + Datetime spot_start_of_phase = startOfPhase(&spot_day); + KRecordList klist = + stk.getKRecordList(KQuery(spot_start_of_phase, spot_end_of_phase + TimeDelta(1), ktype)); + price_t sum_amount = 0.0, sum_volume = 0.0; + for (const auto& k : klist) { + sum_amount += k.transAmount; + sum_volume += k.transCount; } - KRecord last_record = stk.getKRecord(total - 1, ktype); - HKU_IF_RETURN(!last_record.isValid(), void()); - - if (spot_end_of_phase > last_record.datetime) { - // 如果当前的日期大于最后记录的日期,则为新增数据,直接更新并返回 - stk.realtimeUpdate(KRecord(spot_end_of_phase, spot.open, spot.high, spot.low, spot.close, - spot.amount, spot.volume), - ktype); - - } else if (spot_end_of_phase == last_record.datetime) { - // 如果当前日期等于最后记录的日期,则需要重新计算最高价、最低价、交易金额、交易量 - Datetime spot_start_of_phase = startOfPhase(&spot_day); - KRecordList klist = - stk.getKRecordList(KQuery(spot_start_of_phase, spot_end_of_phase + TimeDelta(1), ktype)); - price_t amount = 0.0, volume = 0.0; - for (const auto& k : klist) { - amount += k.transAmount; - volume += k.transCount; - } - stk.realtimeUpdate( - KRecord(spot_end_of_phase, last_record.openPrice, - spot.high > last_record.highPrice ? spot.high : last_record.highPrice, - spot.low < last_record.lowPrice ? spot.low : last_record.lowPrice, spot.close, - amount, volume), - ktype); - } else { - // 不应该出现的情况:当前日期小于最后记录的日期 - HKU_WARN( - "Ignore, What should not happen, the current date is less than the last recorded date!"); - } + price_t amount = + spot.amount > sum_amount ? spot.amount - sum_amount : (sum_amount == 0.0 ? spot.amount : 0.0); + price_t spot_volume = spot.volume; + price_t volume = + spot_volume > sum_volume ? spot_volume - sum_volume : (sum_volume == 0.0 ? spot_volume : 0.0); + KRecord krecord(spot_end_of_phase, spot.open, spot.high, spot.low, spot.close, amount, volume); + stk.realtimeUpdate(krecord, ktype); } static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { From 3be20d710f62bfcfcc64dbe61511a8b629203018 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 01:04:08 +0800 Subject: [PATCH 553/601] update docs --- docs/source/conf.py | 98 ++++++++++++------------ docs/source/index.rst | 2 +- docs/source/kdriver/data_driver.rst | 68 ----------------- docs/source/release.rst | 112 ++++++++++++++++------------ docs/source/strategy.rst | 55 ++++++++++++++ 5 files changed, 168 insertions(+), 167 deletions(-) delete mode 100644 docs/source/kdriver/data_driver.rst create mode 100644 docs/source/strategy.rst diff --git a/docs/source/conf.py b/docs/source/conf.py index 51b0b489..1e600f77 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -20,12 +20,12 @@ import shlex # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -#sys.path.insert(0, os.path.abspath('.')) +# sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. -#needs_sphinx = '1.0' +# needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom @@ -41,7 +41,7 @@ templates_path = ['_templates'] source_suffix = '.rst' # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' @@ -56,9 +56,9 @@ author = 'fasiondog' # built documents. # # The short X.Y version. -version = '1.0.3' +version = '2.1.4' # The full version, including alpha/beta/rc tags. -release = '1.0.3' +release = '2.1.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. @@ -69,9 +69,9 @@ language = 'zh_CN' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. @@ -79,27 +79,27 @@ exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. -#keep_warnings = False +# keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False @@ -109,27 +109,27 @@ todo_include_todos = False # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. html_theme = 'sphinxdoc' -#可选:alabaster, sphinx_rtd_theme, classic, sphinxdoc, scrolls, agogo, traditional, nature, haiku, pyramid, bizstyle -#http://www.sphinx-doc.org/en/stable/theming.html#using-a-theme +# 可选:alabaster, sphinx_rtd_theme, classic, sphinxdoc, scrolls, agogo, traditional, nature, haiku, pyramid, bizstyle +# http://www.sphinx-doc.org/en/stable/theming.html#using-a-theme # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = "./_static/hikyuu_logo.png" +# html_logo = "./_static/hikyuu_logo.png" # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 @@ -144,48 +144,48 @@ html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. -#html_extra_path = [] +# html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = None +# html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: @@ -195,11 +195,11 @@ html_search_language = 'zh' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value -#html_search_options = {'type': 'default'} +# html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. -#html_search_scorer = 'scorer.js' +# html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'Hikyuudoc' @@ -208,16 +208,16 @@ htmlhelp_basename = 'Hikyuudoc' latex_elements = { # The paper size ('letterpaper' or 'a4paper'). - #'papersize': 'letterpaper', + # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). - #'pointsize': '10pt', + # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. - #'preamble': '', + # 'preamble': '', # Latex figure (float) alignment - #'figure_align': 'htbp', + # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples @@ -229,23 +229,23 @@ latex_documents = [ # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # If true, show page references after internal links. -#latex_show_pagerefs = False +# latex_show_pagerefs = False # If true, show URL addresses after external links. -#latex_show_urls = False +# latex_show_urls = False # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output --------------------------------------- @@ -254,7 +254,7 @@ latex_documents = [ man_pages = [(master_doc, 'hikyuu', 'Hikyuu Documentation', [author], 1)] # If true, show URL addresses after external links. -#man_show_urls = False +# man_show_urls = False # -- Options for Texinfo output ------------------------------------------- @@ -267,13 +267,13 @@ texinfo_documents = [ ] # Documents to append as an appendix to all manuals. -#texinfo_appendices = [] +# texinfo_appendices = [] # If false, no module index is generated. -#texinfo_domain_indices = True +# texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. -#texinfo_show_urls = 'footnote' +# texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. -#texinfo_no_detailmenu = False +# texinfo_no_detailmenu = False diff --git a/docs/source/index.rst b/docs/source/index.rst index dc0826d3..34c8987e 100644 --- a/docs/source/index.rst +++ b/docs/source/index.rst @@ -19,7 +19,7 @@ Welcome to Hikyuu's documentation! trade_manage/trade_manage trade_sys/trade_sys trade_portfolio/trade_portfolio - kdriver/data_driver.rst + strategy reference developer.rst release diff --git a/docs/source/kdriver/data_driver.rst b/docs/source/kdriver/data_driver.rst deleted file mode 100644 index 0b4dfa34..00000000 --- a/docs/source/kdriver/data_driver.rst +++ /dev/null @@ -1,68 +0,0 @@ -.. py:currentmodule:: hikyuu.data_driver -.. highlight:: python - -自定义K线驱动 -============== - -可参见详见安装目录或源码目录下“data_driverpytdx_data_driver.py”示例,该示例使用pytdx作为数据源(不建议直接使用,仅供参考)。如有需要使用MySQL、CSV等存储K线数据的,可参考该示例自行实现。 - - - -K线数据驱动基类 ----------------- - -.. py:class:: KDataDriver - - K线数据驱动基类 - - 自定义K线数据驱动接口: - - * :py:meth:`KDataDriver._init` - 【可选】初始化子类私有变量 - * :py:meth:`KDataDriver.isIndexFirst` - 【必须】指示该引擎是按位置索引查询方式更快还是按日期 - * :py:meth:`KDataDriver.getKRecordList` - 【必须】初始化子类私有变量 - * :py:meth:`KDataDriver.getCount` - 【必须】初始化子类私有变量 - * :py:meth:`KDataDriver._getIndexRangeByDate` - 【必须】初始化子类私有变量 - - .. py:attribute:: name 名称 - - .. py:method:: getParam(self, name) - - 获取指定的参数 - - :param str name: 参数名称 - :return: 参数值 - :raises out_of_range: 无此参数 - - .. py:method:: _init(self) - - 【重载接口】(可选)初始化子类私有变量 - - .. py:method:: isIndexFirst(self) - - 【重载接口】(必须)指示该引擎是按位置索引查询方式更快还是按日期 - - .. py:method:: getKRecordList(self, market, code, query) - - 【重载接口】(必须)按指定的位置[start_ix, end_ix)读取K线数据至out_buffer - - :param str market: 市场标识 - :param str code: 证券代码 - :param Query query: 查询条件 - :rtype: getKRecordList - - .. py:method:: getCount(self, market, code, ktype) - - 【重载接口】(必须)获取K线数量 - - :param str market: 市场标识 - :param str code: 证券代码 - :param Query.KType ktype: K线类型 - - .. py:method:: _getIndexRangeByDate(self, market, code, query) - - 【重载接口】(必须)按日期获取指定的K线数据 - - :param str market: 市场标识 - :param str code: 证券代码 - :param Query query: 日期查询条件(QueryByDate) - \ No newline at end of file diff --git a/docs/source/release.rst b/docs/source/release.rst index 1359c17f..e64fcc2e 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -1,15 +1,29 @@ 版本发布说明 ======================= -2.1.2 - 2024年8月27日 -------------------------- +版本发布说明 +-------------- + +2.1.4 - +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1. fixed 分钟基本行情数据更新错误 +2. 优化数据加载策略,先加载同一K线类型数据,在加载下一预加载的K线 +3. 优化内部使用线程数量,以便 Strategy 运行时节省系统资源 +4. interactive 工具可以使用环境变量控制部分数据加载策略,方便使用常规 .py 文件进行策略分析时,无需等待所有数据加载完毕,节省首次执行时间。 +5. 完善 Strategy 和 StrategyContext +6. fixed OperatorSelector 系列序列化时内存泄漏 + + +2.1.3 - 2024年8月27日 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. fixed 未安装 xtquant 时无法使用 HikyuuTdx 2. 调整 codepre 配置,补充上证ETF基金 2.1.2 - 2024年8月26日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 优化 Strategy,调整 OrderBroker 接口,增加 strategy 示例 2. 增加 miniqmt 行情采集 @@ -19,7 +33,7 @@ 2.1.1 - 2024年8月9日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 预加载历史财务信息 2. fixed windows下 MySQL blob 数据读取错误导致读取历史财务信息时消耗巨大内存 @@ -27,7 +41,7 @@ 2.1.0 - 2024年6月18日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 新增特性 - Selector 支持 +-×÷、AND、OR 操作,方便验证共振 @@ -52,14 +66,14 @@ 2.0.9 - 2024年5月27日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. fixed Parameter 中对 Block 的支持,造成 INSUM 无法参与其他指标的计算 2. Porfolio 添加对延迟系统可能出现的未来信号保护 2.0.8 - 2024年5月22日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. fixed 权息数据中转送股导入错误 2. 增加 BLOCKSETNUM、INSUM 横向统计指标 @@ -69,7 +83,7 @@ 2.0.7 - 2024年5月18日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. sys.performance 绘图时标题中加上对应的证券名称及标识,以及修正统计范围为回测截止时间 2. 优化内建信号指示器 SG_Band, 支持使用 3 个指标分别作为参考、下轨、上轨 @@ -80,7 +94,7 @@ 2.0.6 - 2024年5月13日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 安装包HikyuuTDX不采用gui模式,win11下GUI模式会造成直接timeout 2. 策略部件 python 导出时,支持 python 的动态属性,在 hub 中支持 @@ -88,7 +102,7 @@ 2.0.5 - 2024年5月8日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 主要修复 1. fixed 接收spot时,分钟级别的成交量为股数 @@ -101,7 +115,7 @@ 2.0.4 - 2024年5月6日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 缺陷修复 - fixed ETF 权息缺少扩缩股 @@ -119,7 +133,7 @@ 2.0.3 - 2024年4月25日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 增强 FINANCE,增加 only_year_report 和 dynamic 参数,以便进行市盈率等计算 2. Indicaotr.plot 绘制时,将 x 轴设置为日期 @@ -130,7 +144,7 @@ 2.0.2 - 2024年4月19日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 新增特性 - 历史财务信息入库,并增加指标 FINANCE 获取相应历史财务数据 @@ -144,7 +158,7 @@ 2.0.1 - 2024年4月7日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 新增 TURNOVER (换手率指标) 2. 新增股票类型 STOCKTYPE_A_BJ (北交所), 修复科创板和北交所股票最小交易量为1 @@ -161,7 +175,7 @@ 2.0.0 - 2024年4月3日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 新增特性 - 新增 MF 多因子组件,用于时间截面对各标的排序评分,重新整理 PF(投资组合)、SE(选股算法)。从投资组合(PF)--截面评分(MF)--选股过滤(SE)--系统策略(SYS)--择时(SG)--资金管理(MM)--止损(ST)/止盈(TP)--盈利目标(PG) 全链条的交易组件化。 @@ -197,7 +211,7 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.3.5 - 2024年2月29日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 整体性能优化 - 整体性能优化,Indicator 计算速度再次提升 10% ~ 20% @@ -219,14 +233,14 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.3.4 - 2024年2月1日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. fixed windows 下第三方依赖 hikyuu 的 C++ 代码中无法使用 KData 2. 调整 matplotlib font manager 日志级别 1.3.3 - 2024年1月31日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 配合 hub (策略组件仓库) 使用 C++ 部件更新,参见 ``_ 2. 尝试获取用户目录下的 hosts.py,方便修改相关 pytdx 服务器设置 @@ -235,7 +249,7 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.3.2 - 2024年1月6日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 整体调整与优化 - 整体从 boost.python 切换至 pybind11,以便在 C++ 部分中可以方便的进行 GIL 解锁,并行调用 python 代码 @@ -267,7 +281,7 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.3.1 - 2023年12月6日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 增加通达信时间指标(DATE/TIME/YEAR/MONTH/WEEK/DAY/HOUR/MINUTE) 2. 增加 SLOPE 计算线性回归斜率指标 @@ -279,7 +293,7 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.3.0 - 2023年11月5日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 性能优化 @@ -308,7 +322,7 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.2.9 - 2023年10月9日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 稳定性与兼容性 - 修复了 setup.py 更新编译模式时的问题,确保并行编译参数能够正常生效 @@ -340,7 +354,7 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 1.2.8 - 2023年8月16日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. fixed 多broker时m_broker_last_datetime更新 2. support Query.HOUR2 @@ -357,13 +371,13 @@ SpendTimer 改输出到 std::cout ,以便 jupyter 可以捕获输出 13. 优化编译工程 1.2.7 - 2022年11月21日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ fixed MySQL引擎只能导入数据,但实际无法使用 1.2.6 - 2022年11月18日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 新增发布 linux 下 pypi 包,linux 下也可以通过 pip install hikyuu 进行安装 2. 获取股票代码表失败时增加保护 @@ -374,7 +388,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.2.5 - 2022年9月3日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 增加北京交易所数据 2. 改进数据下载,修复 pytdx 数据下载缺失部分数据 @@ -386,7 +400,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.2.4 - 2022年6月30日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 修复 trade_manage持久化,费率设置为TC_FixedA2017会造成持久化中断 2. 修改 TradeManager::getFunds 中的截止时间 23:59 分被误写为 11:59 分 @@ -394,7 +408,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.2.3 - 2022年3月6日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 指标支持动态参数 @@ -462,7 +476,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.2.1 - 2022年2月2日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 修复 importdata 无法导入的问题 2. 交易系统 System 支持使用复权数据 @@ -474,7 +488,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.2.0 - 2022年1月11日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. HikyuuTdx 执行导入时自动保存配置,避免第一次使用 hikyuu 必须退出先退出 Hikyuutdx 的问题 2. 增加创业板 301 开头股票代码 @@ -489,7 +503,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.9 - 2021年11月11日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 补充科创板 2. 完善基础设施,增加MQThreadPool、MQStealThreadPool,优化StealThreadPool @@ -503,20 +517,20 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.8 - 2021年2月27日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. HikyuuTDX 切换mysql导入时错误提示目录不存在 2. tdx本地导入修复,并支持导入MySQL 1.1.7 - 2021年2月13日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 更新examples/notebook相关示例 2. fixed bugs 1.1.6 - 2020年2月5日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 优化 hikyuu.interactive 启动加载速度 2. 完善 HikyuuTDX 预加载设置参数,可根据机器内存大小自行设置需加载至内存的K线数据,加快 hikyuu 运行速度 @@ -528,7 +542,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.5 - 2020年11月9日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 导入工具修复权息信息导入 2. 支持 MySQL 作为存储引擎(通过导入工具配置) @@ -546,7 +560,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.3 - 2019年6月11日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 原表示浮点数的 Null 值更改为和 numpy 一致,在c++中为 std::nan, python中 为numpy::nan 2. Indicator 支持按日期获取数据,如:c['2019-6-11'] 或 c[Datetime(201906110000)] (注:由于 indicator的四则运算无法判定绑定的上下文,所以四则运算产生的结果无法获取对应日期,此时需要先执行 setContext 对结果指定上下文) @@ -556,7 +570,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.2 - 2019年4月18日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 修复 Indicator 无法作为原型使用,导致部分预定义的 SG 等无法正在运行的BUG。如:: @@ -580,7 +594,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.1 - 2019年4月8日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. HikyuuTDX 新增当前财务信息及历史财务信息下载 2. Stock 新增 getFinanceInfo、getHistoryFinanceInfo 支持当前及历史财务信息 @@ -630,7 +644,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.1.0 - 2019年2月28日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 复权增加周线及其以上支持 2. 支持历史分笔、分时数据 @@ -647,7 +661,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.9 - 2018年10月23日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 更新周线、月线等周线及其之上的K线BAR记录,从以开始时间为准,改为以结束时间为准。(如从老版本升级,需手工删除sh_day.h5、sz_day.h5文件中的week、month等目录,只保留data目录。可运行 tools/delelte_index.py 完成删除,运行前请自行修改相关文件路径等信息)。 2. 实现将C++中的日志输出重定向至Python,使Jupyter notebook可以看到C++部分的打印信息提示。注意:部分情景可能导致notebook因打印信息过多失去响应,此时可在产生较多打印信息的命令之前运行“iodog.close()”关闭重定向,后续可以再使用“iodog.open()”重新打开重定向信息输出。 @@ -661,7 +675,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.8 - 2018年1月22日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 实现一个简单资产组合回测框架 PF_Simple(多标的、相同策略),因目标是多标的、多策略的资产组合框架,所以后续接口可能变化! 2. 新增固定列表选择器 SE_Fixed 配合 PF_Simple 使用。 @@ -680,7 +694,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.7 - 2017年12月15日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1、合入网友哥本哈根达斯提供的修改,复权时不处理只有股本变化的权息记录,和通达信等软件处理保持一致。 @@ -693,7 +707,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.6 - 2017年11月20日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 完善Python帮助,以便在Shell中直接使用 help(cmd) 查询 2. 修改数据驱动,支持直接使用Python编写数据驱动。实现使用 pytdx 作为K线数据驱动的示例,详见安装目录下“data_driver\pytdx_data_driver.py”。如有需要使用MySQL、CSV等存储K线数据的,可参考该示例自行实现。 @@ -704,7 +718,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.5 - 2017年9月25日 -------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 增加载入临时的CSV K线数据功能,可用于期货或A股之外的数据测试。详情参见 StockManager 的 addTempCsvStock、removeTempCsvStock 方法帮助。 2. CVAL指标支持创建指定长度的固定数值指标 @@ -714,7 +728,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.4 - 2017年7月5日 ----------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1、Indicator、Operand 支持直接AND和OR操作,如: @@ -757,7 +771,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.3 - 2017年7月3日 ------------------------- +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1、Indicator、Operand 支持直接和数字进行四则运算及比较运算,如: @@ -800,7 +814,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.2 - 2017年6月19日 ------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 修复延迟操作情况下止损未按预期卖出的BUG(建议升级) @@ -812,7 +826,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.1 - 2017年5月30日 ------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. 改变安装方式,支持 pip install hikyuu 2. 完善快速配置脚本 hku_config.py @@ -822,7 +836,7 @@ fixed MySQL引擎只能导入数据,但实际无法使用 1.0.0 - 2017年4月28日 ------------------------ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 2017年4月28日发布初始版本 2017年5月12日发布32位安装包 \ No newline at end of file diff --git a/docs/source/strategy.rst b/docs/source/strategy.rst new file mode 100644 index 00000000..0d636097 --- /dev/null +++ b/docs/source/strategy.rst @@ -0,0 +1,55 @@ +.. currentmodule:: hikyuu +.. highlight:: python + +程序化交易 +======================= + +程序化交易也就是自动化交易,也就是大家常见的各种量化框架,本质就是任务的定时调度 + 通知回调。 + +Hikyuu 主要聚焦于快速策略分析,本身不提供实盘交易,Strategy 运行时仅供大家学习参考如何和实盘进行对接,造成盈亏请自行负责。 + +具体可参见安装目录下的 strategy 子目录下的相关 demo。 + + +.. py:class:: Strategy + + 策略运行时 + + .. py:attribute:: name 名称 + .. py:attribute:: context 策略上下文 + + .. py:method:: start(self) + + 启动策略执行,请在完成相关回调设置后执行。 + + :param bool auto_recieve_spot: 是否自动接收行情数据 + + .. py:method:: on_change(self, func) + + 设置证券数据更新回调通知 + + :param func: 一个可调用的对象如普通函数,需接收 stock 和 ktype 参数 + + .. py:method:: on_received_spot(self, func) + + 设置证券数据更新通知回调 + + :param func: 可调用对象如普通函数,没有参数 + + .. py:method:: run_daily(self, func) + + 设置日内循环执行回调。如果忽略市场开闭市,则自启动时刻开始按间隔时间循环, + 否则第一次执行时将开盘时间对齐时间间隔,且在非开市时间停止执行。 + + :param func: 可调用对象如普通函数,没有参数 + :param TimeDelta time: 间隔时间,如间隔3秒:TimeDelta(0, 0, 0, 3) 或 Seconds(3) + :param str market: 使用哪个市场的开闭市时间 + :param ignore_market: 忽略市场开闭市时间 + + .. py:method:: run_daily_at(self, func) + + 设置每日定点执行回调 + + :param func: 可调用对象如普通函数,没有参数 + :param TimeDelta time: 执行时刻,如每日15点:TimeDelta(0, 15) + :param ignore_holiday: 节假日不执行 \ No newline at end of file From b8813ca2f09e392d4930da0ca0e1a47477e72b60 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 01:15:24 +0800 Subject: [PATCH 554/601] =?UTF-8?q?=E8=A1=A5=E5=85=85=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 4 +- hikyuu_cpp/hikyuu/strategy/Strategy.h | 6 + hikyuu_pywrap/strategy/_Strategy.cpp | 119 +++++++++++++------ 3 files changed, 91 insertions(+), 38 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 094b4685..ac7069ae 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -39,8 +39,8 @@ static void updateStockDayData(const SpotRecord& spot) { Stock stk = StockManager::instance().getStock(getSpotMarketCode(spot)); HKU_IF_RETURN(stk.isNull() || !stk.isBuffer(KQuery::DAY), void()); HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void()); - KRecord krecord(Datetime(spot.datetime.year(), spot.datetime.month(), spot.datetime.day()), - spot.open, spot.high, spot.low, spot.close, spot.amount, spot.volume); + KRecord krecord(spot.datetime.startOfDay(), spot.open, spot.high, spot.low, spot.close, + spot.amount, spot.volume); stk.realtimeUpdate(krecord, KQuery::DAY); } diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index ede8b346..07ff75e3 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -174,6 +174,12 @@ void HKU_API runInStrategy(const SYSPtr& sys, const Stock& stk, const KQuery& qu void HKU_API runInStrategy(const PFPtr& pf, const KQuery& query, int adjust_cycle, const OrderBrokerPtr& broker, const TradeCostPtr& costfunc); +/** + * 从 hikyuu 数据缓存服务器拉取更新最新的缓存数据 + * @param addr 缓存服务地址,如: tcp://192.168.1.1:9201 + * @param stklist 待更新的股票列表 + * @param ktype 指定更新的K线类型 + */ void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& stklist, const KQuery::KType& ktype); diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 8b4f1c8f..5f60ad16 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -20,47 +20,78 @@ void export_Strategy(py::module& m) { .def(py::init&, const vector&, const std::string&, const std::string&>(), py::arg("code_list"), py::arg("ktype_list"), py::arg("name") = "Strategy", - py::arg("config") = "") + py::arg("config") = "", R"(创建策略运行时 + + :param list code_list: 证券代码列表,如:["sz000001", "sz000002"], "all" 代表全部证券 + :param list ktype_list: K线类型列表, 如: ["day", "min"] + :param str name: 策略名称 + :param str config: 配置文件名称(如需要使用独立的配置文件,否则为空时使用默认的hikyuu配置文件))") + .def(py::init(), py::arg("context"), - py::arg("name") = "Strategy", py::arg("config") = "") + py::arg("name") = "Strategy", py::arg("config") = "", + R"(使用上下文创建策略运行时 + + :param StrategyContext context: 上下文实例 + :param str name: 策略名称 + :param str config: 配置文件名称(如需要使用独立的配置文件,否则为空时使用默认的hikyuu配置文件))") + .def_property("name", py::overload_cast<>(&Strategy::name, py::const_), py::overload_cast(&Strategy::name), py::return_value_policy::copy, "策略名称") .def_property_readonly("context", &Strategy::context, py::return_value_policy::copy, - "策略上下文") + "获取策略上下文") + + .def("start", &Strategy::start, py::arg("auto_recieve_spot") = true, R"(start(self) + + 启动策略执行,请在完成相关回调设置后执行。 + + :param bool auto_recieve_spot: 是否自动接收行情数据)") + + .def( + "on_change", + [](Strategy& self, py::object func) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + auto new_func = [=](const Stock& stk, const SpotRecord& spot) { + try { + c_func(stk, spot); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.onChange(new_func); + }, + R"(onchang(self, func) + + 设置证券数据更新回调通知 + + :param func: 一个可调用的对象如普通函数,需接收 stock 和 ktype 参数)") + + .def( + "on_received_spot", + [](Strategy& self, py::object func) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + auto new_func = [=](Datetime revTime) { + try { + c_func(revTime); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.onReceivedSpot(new_func); + }, + R"(on_received_spot(self, func) + + 设置证券数据更新通知回调 + + :param func: 可调用对象如普通函数,没有参数)") - .def("start", &Strategy::start, py::arg("auto_recieve_spot") = true) - .def("on_change", - [](Strategy& self, py::object func) { - HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); - py::object c_func = func.attr("__call__"); - auto new_func = [=](const Stock& stk, const SpotRecord& spot) { - try { - c_func(stk, spot); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR("Unknown error!"); - } - }; - self.onChange(new_func); - }) - .def("on_received_spot", - [](Strategy& self, py::object func) { - HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); - py::object c_func = func.attr("__call__"); - auto new_func = [=](Datetime revTime) { - try { - c_func(revTime); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR("Unknown error!"); - } - }; - self.onReceivedSpot(new_func); - }) .def( "run_daily", [](Strategy& self, py::object func, const TimeDelta& time, std::string market, @@ -79,7 +110,16 @@ void export_Strategy(py::module& m) { self.runDaily(new_func, time, market, ignore_market); }, py::arg("func"), py::arg("time"), py::arg("market") = "SH", - py::arg("ignore_market") = false) + py::arg("ignore_market") = false, R"(run_daily(self, func) + + 设置日内循环执行回调。如果忽略市场开闭市,则自启动时刻开始按间隔时间循环, + 否则第一次执行时将开盘时间对齐时间间隔,且在非开市时间停止执行。 + + :param func: 可调用对象如普通函数,没有参数 + :param TimeDelta time: 间隔时间,如间隔3秒:TimeDelta(0, 0, 0, 3) 或 Seconds(3) + :param str market: 使用哪个市场的开闭市时间 + :param ignore_market: 忽略市场开闭市时间)") + .def( "run_daily_at", [](Strategy& self, py::object func, const TimeDelta& time, bool ignore_holiday) { @@ -96,7 +136,14 @@ void export_Strategy(py::module& m) { }; self.runDailyAt(new_func, time, ignore_holiday); }, - py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true); + py::arg("func"), py::arg("time"), py::arg("ignore_holiday") = true, + R"(run_daily_at(self, func) + + 设置每日定点执行回调 + + :param func: 可调用对象如普通函数,没有参数 + :param TimeDelta time: 执行时刻,如每日15点:TimeDelta(0, 15) + :param ignore_holiday: 节假日不执行)"); m.def("crtBrokerTM", crtBrokerTM, py::arg("broker"), py::arg("cost_func") = TC_Zero(), py::arg("name") = "SYS"); From 8ab3c94c5830693057ce1f81c422d6d4e8da76b1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 01:49:15 +0800 Subject: [PATCH 555/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=20c++=20demo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/demo1.cpp | 26 +++++-- hikyuu_cpp/demo/demo2.cpp | 16 +++- hikyuu_cpp/demo/demo3.cpp | 158 ++++++++++++++++++++++++++++++++++++++ hikyuu_cpp/demo/xmake.lua | 24 ++++++ 4 files changed, 215 insertions(+), 9 deletions(-) create mode 100644 hikyuu_cpp/demo/demo3.cpp diff --git a/hikyuu_cpp/demo/demo1.cpp b/hikyuu_cpp/demo/demo1.cpp index 1c377d22..07a8ffd1 100644 --- a/hikyuu_cpp/demo/demo1.cpp +++ b/hikyuu_cpp/demo/demo1.cpp @@ -1,5 +1,18 @@ -// demo.cpp : 定义控制台应用程序的入口点。 -// +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-09-02 + * Author: fasiondog + */ + +/************************************************************* + * + * 该示例,为使用 C++ 方式使用 hikyuu + * 1. 初始化 hikyuu + * 2. 打印 K 线数据 + * 更多使用可以参考 python,基本一致,仅函数命名风格不一样 + * + *************************************************************/ #include #include @@ -36,11 +49,10 @@ int main(int argc, char* argv[]) { } // 启动行情接收(只是计算回测可以不需要) - startSpotAgent(true); - - while (true) { - std::this_thread::sleep_for(std::chrono::seconds(1)); - } + // startSpotAgent(true); + // while (true) { + // std::this_thread::sleep_for(std::chrono::seconds(1)); + // } #if defined(_WIN32) SetConsoleOutputCP(old_cp); diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp index a6aacf88..718dc713 100644 --- a/hikyuu_cpp/demo/demo2.cpp +++ b/hikyuu_cpp/demo/demo2.cpp @@ -1,5 +1,17 @@ -// demo.cpp : 定义控制台应用程序的入口点。 -// +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-09-02 + * Author: fasiondog + */ + +/************************************************************* + * + * 该示例使用 C++ 方式执行 Strategy 运行时 + * 也就是最常见的量化框架:定时调度 + 数据推送通知回调 + * 更多信息可参考 python 版 strategy 子目录中的示例 + * + *************************************************************/ #include #include diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp new file mode 100644 index 00000000..84582ac1 --- /dev/null +++ b/hikyuu_cpp/demo/demo3.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-09-02 + * Author: fasiondog + */ + +/************************************************************* + * + * 这是利用 hikyuu 本身来实现一个数据缓存服务 + * 其接收 HikyuuTDX 行情采集发来的行情数据,并提供服务接口供其他程序来获取最新数据 + * + * 用途: + * 在程序化交易里,经常在实际下单时,希望获取一下最新数据, + * 或者是日频交易时,不开启 hikyuu 本身的行情自动接收,而是在收盘前定时执行时,手工获取下最新数据 + * + * hikyuu 中提供了对应的函数 get_data_from_buffer_server(python), getDataFromBufferServer(C++) + * 用来从该服务获取最新数据, 如: + * get_data_from_buffer_server("tcp://192.168.1.1:9201", Query.DAY) + * + * 目的: + * 如何使用更多的初始化方式,控制 hikyuu 数据加载 + * + *************************************************************/ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +using namespace hku; + +NodeServer server; + +void signal_handle(int signal) { + if (signal == SIGINT || signal == SIGTERM) { + HKU_INFO("Shutdown now ..."); + server.stop(); + releaseGlobalSpotAgent(); + StockManager::quit(); + exit(0); + } +} + +int main(int argc, char* argv[]) { + std::signal(SIGINT, signal_handle); + std::signal(SIGTERM, signal_handle); + + initLogger(false, "./demo.log"); + +#if defined(_WIN32) + // Windows 下设置控制台程序输出代码页为 UTF8 + auto old_cp = GetConsoleOutputCP(); + SetConsoleOutputCP(CP_UTF8); +#endif + + try { + // 获取基础配置参数 + Parameter baseParam, blockParam, kdataParam, preloadParam, hkuParam; + getConfigFromIni(fmt::format("{}/.hikyuu/hikyuu.ini", getUserDir()), baseParam, blockParam, + kdataParam, preloadParam, hkuParam); + + // 调整所有类型K线为预加载且预加载数量为1 + Parameter new_preloadParam; + auto ktypes = KQuery::getAllKType(); + for (auto& ktype : ktypes) { + to_lower(ktype); + new_preloadParam.set(ktype, true); + new_preloadParam.set(fmt::format("{}_max", ktype), 1); + } + // 不加载历史财务信息,不加载权息数据 + hkuParam.set("load_history_finance", false); + hkuParam.set("load_stock_weight", false); + StockManager::instance().init(baseParam, blockParam, kdataParam, new_preloadParam, + hkuParam); + + // 启动行情接收(只是计算回测可以不需要) + startSpotAgent(true); + + server.setAddr("tcp://0.0.0.0:9201"); + + server.regHandle("market", [](json&& req) { + HKU_INFO("--> req from {}:{}", req["remote_host"].get(), + req["remote_port"].get()); + HKU_ASSERT(req.contains("ktype")); + HKU_ASSERT(req.contains("codes")); + + string ktype = req["ktype"].get(); + auto& sm = StockManager::instance(); + const auto& param = sm.getPreloadParameter(); + string low_ktype = ktype; + to_lower(low_ktype); + HKU_CHECK(param.tryGet(low_ktype, false), "The ktype: {} is not be preloaded!", + ktype); + + json data; + const auto& jcodes = req["codes"]; + // HKU_INFO("codes size: {}", jcodes.size()); + for (auto iter = jcodes.cbegin(); iter != jcodes.cend(); ++iter) { + string market_code = to_string(*iter); + market_code = market_code.substr(1, market_code.size() - 2); + Stock stk = getStock(market_code); + if (stk.isNull()) { + HKU_WARN("Not found stock: {}", market_code); + continue; + } + + KRecordList krecords = + stk.getKRecordList(KQueryByIndex(-1, Null(), ktype)); + if (!krecords.empty()) { + const auto& k = krecords.back(); + json jr; + jr.emplace_back(market_code); + jr.emplace_back(k.datetime.str()); + jr.emplace_back(k.openPrice); + jr.emplace_back(k.highPrice); + jr.emplace_back(k.lowPrice); + jr.emplace_back(k.closePrice); + jr.emplace_back(k.transAmount); + jr.emplace_back(k.transCount); + data.emplace_back(std::move(jr)); + } + } + + json res; + res["data"] = data; + // HKU_INFO("<-- res: {}", to_string(res)); + return res; + }); + + server.start(); + // server.loop(); + while (true) { + std::this_thread::sleep_for(std::chrono::seconds(10)); + } + + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + + server.stop(); + releaseGlobalSpotAgent(); + StockManager::quit(); + +#if defined(_WIN32) + SetConsoleOutputCP(old_cp); +#endif + return 0; +} diff --git a/hikyuu_cpp/demo/xmake.lua b/hikyuu_cpp/demo/xmake.lua index 59fb328a..f181f759 100644 --- a/hikyuu_cpp/demo/xmake.lua +++ b/hikyuu_cpp/demo/xmake.lua @@ -44,3 +44,27 @@ target("demo2") add_files("./demo2.cpp") target_end() + +target("demo3") + set_kind("binary") + set_default(false) + + add_packages("boost", "spdlog", "fmt", "nng", "nlohmann_json") + add_includedirs("..") + + if is_plat("windows") then + add_cxflags("-wd4267") + add_cxflags("-wd4251") + end + + if is_plat("windows") and get_config("kind") == "shared" then + add_defines("HKU_API=__declspec(dllimport)") + add_defines("HKU_UTILS_API=__declspec(dllimport)") + add_defines("SQLITE_API=__declspec(dllimport)") + end + + add_deps("hikyuu") + + add_files("./demo3.cpp") +target_end() + From b6cda36dee984d34aa1c894ab755535209e16a83 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 12:48:24 +0800 Subject: [PATCH 556/601] fixed SpotAgent start --- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index e20a6429..a1afe36f 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -37,10 +37,7 @@ void SpotAgent::start() { stop(); if (m_stop) { m_stop = false; - if (m_tg) { - m_tg.reset(); - m_tg = std::make_unique(m_work_num); - } + m_tg = std::make_unique(m_work_num); m_receiveThread = std::thread([this]() { work_thread(); }); } } From c4b00893f44a36d892d89e3a0fe057c780989d1f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 14:22:24 +0800 Subject: [PATCH 557/601] =?UTF-8?q?fixed=20=E8=A1=8C=E6=83=85=E9=87=87?= =?UTF-8?q?=E9=9B=86=E4=B8=AD=E7=9A=84=E6=88=90=E4=BA=A4=E9=87=8F=E5=92=8C?= =?UTF-8?q?=E6=88=90=E4=BA=A4=E9=87=91=E9=A2=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 2 +- hikyuu/fetcher/stock/zh_stock_a_sina_qq.py | 14 ++++++++++---- hikyuu_cpp/hikyuu/KRecord.h | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index 53d25b4a..13c41cd8 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -30,7 +30,7 @@ try: result['high'] = data['high'] result['low'] = data['low'] result['close'] = data['lastPrice'] - result['amount'] = data['amount'] * 0.001 # 转千元 + result['amount'] = data['amount'] * 0.0001 # 转万元 result['volume'] = data['pvolume'] * 0.01 # 转手数 for i in range(5): diff --git a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py index 1a35f972..06597a40 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +++ b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py @@ -64,8 +64,11 @@ def parse_one_result_qq(resultstr): hku_check_ignore(len(resultstr) > 3 and resultstr[:2] == 'v_', "Invalid input param! {}", resultstr) a = resultstr.split('~') - result['market'] = a[0][2:4] - result['code'] = a[0][4:10] + market = a[0][2:4].upper() + code = a[0][4:10] + result['market'] = market + result['code'] = code + result['name'] = a[1] result['close'] = float(a[3]) # 当前价格 result['yesterday_close'] = float(a[4]) # 昨日收盘价 @@ -103,8 +106,11 @@ def parse_one_result_qq(resultstr): result['high'] = float(a[33]) # 最高价 result['low'] = float(a[34]) # 最低价 # 35: 价格/成交量(手)/成交额 - result['volume'] = float(a[36]) # 成交量(手) - result['amount'] = float(a[37]) * 10.0 # 成交额(万) + if (market == 'SZ' and code[:2] == '39') or (market == 'SH' and code[:3] == '000'): + result['volume'] = float(a[36]) * 0.01 # 成交量(手) + else: + result['volume'] = float(a[36]) # 成交量(手) + result['amount'] = float(a[37]) # 成交额(万) result['turnover_rate'] = float(a[38]) if a[38] else 0.0 # 换手率 result['pe'] = float(a[39]) if a[39] else 0.0 # 市盈率 Price Earnings Ratio,简称P/E或PER result['amplitude'] = float(a[43]) if a[43] else 0.0 # 振幅 diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index 03623065..e17ab821 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -24,7 +24,7 @@ public: price_t highPrice; ///< 最高价 price_t lowPrice; ///< 最低价 price_t closePrice; ///< 收盘价 - price_t transAmount; ///< 成交金额(千元) + price_t transAmount; ///< 成交金额(万元) price_t transCount; ///< 成交量(手),日线以下为股数 KRecord() From 5910413c7a378dea689f0aa0b265c5d59195373d Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 15:24:05 +0800 Subject: [PATCH 558/601] update for interactive realtime_update --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 4 +- hikyuu/fetcher/stock/zh_stock_a_sina_qq.py | 4 +- hikyuu/interactive.py | 144 ++++----------------- hikyuu_pywrap/_Datetime.cpp | 1 + 4 files changed, 28 insertions(+), 125 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index 13c41cd8..34a8c534 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -40,7 +40,7 @@ try: result[f'ask{i+1}_amount'] = data['askVol'][i] return result - def get_spot(stocklist, unused1, unused2, batch_func=None): + def get_spot(stocklist, unused1=None, unused2=None, batch_func=None): code_list = [f'{s.code}.{s.market}' for s in stocklist] full_tick = xtdata.get_full_tick(code_list) records = [parse_one_result_qmt(code, data) for code, data in full_tick.items()] @@ -53,6 +53,6 @@ except: hku_error("Not fount xtquant") return dict() - def get_spot(stocklist, unused1, unused2, batch_func=None): + def get_spot(stocklist, unused1=None, unused2=None, batch_func=None): hku_error("Not fount xtquant") return list() diff --git a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py index 06597a40..4412aa46 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +++ b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py @@ -61,7 +61,9 @@ def parse_one_result_sina(resultstr): def parse_one_result_qq(resultstr): result = {} hku_check_ignore(resultstr, "Invalid input param!") - hku_check_ignore(len(resultstr) > 3 and resultstr[:2] == 'v_', "Invalid input param! {}", resultstr) + # hku_check_ignore(len(resultstr) > 3 and resultstr[:2] == 'v_', "Invalid input param! {}", resultstr) + if (len(resultstr) <= 3 or resultstr[:2] != 'v_'): + return None a = resultstr.split('~') market = a[0][2:4].upper() diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index ab44695d..97cd3c54 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -266,133 +266,33 @@ def select(cond, start=Datetime(201801010000), end=Datetime.now(), print_out=Tru # ============================================================================== -def UpdateOneRealtimeRecord_from_qq(tmpstr): - try: - if len(tmpstr) > 3 and tmpstr[:2] == 'v_': - a = tmpstr.split('~') - if len(a) < 9: - return - - open, close, high, low = float(a[5]), float(a[3]), float(a[33]), float(a[34]) - transamount = float(a[36]) - transcount = float(a[37]) - - d = Datetime(int(a[30][:8] + '0000')) - temp = (open, high, low, close) - if 0 in temp: - return - - stockstr = a[0].split('=') - stock = sm[stockstr[0][-8:]] - - record = KRecord() - record.datetime = d - record.open = open - record.high = high - record.low = low - record.close = close - record.amount = transamount - record.volume = transcount / 100 - - stock.realtime_update(record) - - except Exception as e: - print(tmpstr) - print(e) - - -def realtimePartUpdate_from_qq(queryStr): - result = urllib.request.urlopen(queryStr).read() - try: - result = result.decode('gbk') - except Exception as e: - print(result) - print(e) - return - - result = result.split('\n') - for tmpstr in result: - UpdateOneRealtimeRecord_from_qq(tmpstr) - - -def realtime_update_from_website(source, stk_list=None): - if source == 'qq': - queryStr = "http://qt.gtimg.cn/q=" - update_func = realtimePartUpdate_from_qq - max_size = 60 - else: - print('Not support!') - return - - count = 0 - # urls = [] - tmpstr = queryStr - if stk_list is None: - stk_list = sm - for stock in stk_list: - if stock.valid and stock.type in ( - constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, constant.STOCKTYPE_GEM, constant.STOCKTYPE_A_BJ, - ): - tmpstr += ("%s,") % (stock.market_code.lower()) - count = count + 1 - if count >= max_size: - # urls.append(tmpstr) - update_func(tmpstr) - count = 0 - tmpstr = queryStr - - if tmpstr != queryStr: - # urls.append(tmpstr) - update_func(tmpstr) - - # 不用并行,防止过快,ip被网站屏蔽 - # from multiprocessing import Pool - # from multiprocessing.dummy import Pool as ThreadPool - # pool = ThreadPool() - # if source == 'sina': - # pool.map(realtimePartUpdate_from_sina, urls) - # else: - # pool.map(realtimePartUpdate_from_qq, urls) - # pool.close() - # pool.join() - - -def realtime_update_from_qmt(stk_list=None): - try: - from xtquant import xtdata - except: - # xtquant 不支持 linux,需自行下载安装 - return - if stk_list is None: - stk_list = sm - code_list = [f'{s.code}.{s.market}' for s in stk_list if s.valid] - full_tick = xtdata.get_full_tick(code_list) - for qmt_code, data in full_tick.items(): - try: - code, market = qmt_code.split(".") - stock = sm[f'{market}{code}'] - record = KRecord() - record.datetime = Datetime(data['timetag'].split(' ')[0]) - record.open = data['open'] - record.high = data['high'] - record.low = data['low'] - record.close = data['lastPrice'] - record.volume = data['volume'] * 0.1 - record.amount = data['amount'] * 0.001 - stock.realtime_update(record) - except Exception as e: - hku_error(str(e)) - except: - pass - - def realtime_update_inner(source='qq', stk_list=None): + if stk_list is None: + stk_list = sm if source == 'qq': - realtime_update_from_website(source, stk_list) + from hikyuu.fetcher.stock.zh_stock_a_sina_qq import get_spot + stk_list = [s.market_code.lower() for s in stk_list] + records = get_spot(stk_list, 'qq') elif source == 'qmt': - realtime_update_from_qmt(stk_list) + from hikyuu.fetcher.stock.zh_stock_a_qmt import get_spot + records = get_spot(stk_list) else: hku_error(f'Not support website source: {source}!') + return + + for r in records: + stk = sm[f'{r["market"]}{r["code"]}'] + if stk.is_null(): + continue + k = KRecord() + k.datetime = Datetime(r['datetime']) + k.open = r['open'] + k.high = r['high'] + k.low = r['low'] + k.close = r['close'] + k.volume = r['volume'] + k.amount = r['amount'] + stk.realtime_update(k) def realtime_update_wrap(): diff --git a/hikyuu_pywrap/_Datetime.cpp b/hikyuu_pywrap/_Datetime.cpp index 89c21b24..a6d14cd5 100644 --- a/hikyuu_pywrap/_Datetime.cpp +++ b/hikyuu_pywrap/_Datetime.cpp @@ -25,6 +25,7 @@ void export_Datetime(py::module& m) { .def(py::init<>()) .def(py::init()) .def(py::init()) + .def(py::init()) .def(py::init(), py::arg("year"), py::arg("month"), py::arg("day"), py::arg("hour") = 0, py::arg("minute") = 0, py::arg("second") = 0, py::arg("millisecond") = 0, py::arg("microsecond") = 0) From a1495e8aa7f3d28834a3849f495a8396c32945fa Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 15:53:07 +0800 Subject: [PATCH 559/601] =?UTF-8?q?fixed=20qmt=20=E6=88=90=E4=BA=A4?= =?UTF-8?q?=E9=87=91=E9=A2=9D=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 2 +- hikyuu_cpp/hikyuu/KRecord.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index 34a8c534..e5199330 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -30,7 +30,7 @@ try: result['high'] = data['high'] result['low'] = data['low'] result['close'] = data['lastPrice'] - result['amount'] = data['amount'] * 0.0001 # 转万元 + result['amount'] = data['amount'] * 0.001 # 转千元 result['volume'] = data['pvolume'] * 0.01 # 转手数 for i in range(5): diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index e17ab821..03623065 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -24,7 +24,7 @@ public: price_t highPrice; ///< 最高价 price_t lowPrice; ///< 最低价 price_t closePrice; ///< 收盘价 - price_t transAmount; ///< 成交金额(万元) + price_t transAmount; ///< 成交金额(千元) price_t transCount; ///< 成交量(手),日线以下为股数 KRecord() From cbf6fbb3ca191475e01af66c903bdc365f11949b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 16:29:29 +0800 Subject: [PATCH 560/601] =?UTF-8?q?fixed=20notebook=E4=B8=AD=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E5=8A=A0=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index a52cdfca..674207d3 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -106,23 +106,16 @@ void StockManager::loadData() { std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now(); m_data_ready = false; - ThreadPool tg(2); - tg.submit([this]() { this->loadAllHolidays(); }); - tg.submit([this]() { this->loadHistoryFinanceField(); }); - tg.submit([this]() { this->loadAllStockTypeInfo(); }); - tg.submit([this]() { this->loadAllZhBond10(); }); - - // loadAllHolidays(); + loadAllHolidays(); loadAllMarketInfos(); - // loadAllStockTypeInfo(); + loadAllStockTypeInfo(); loadAllStocks(); loadAllStockWeights(); - // loadAllZhBond10(); - // loadHistoryFinanceField(); + loadAllZhBond10(); + loadHistoryFinanceField(); HKU_INFO("Loading block..."); - // m_blockDriver->load(); - tg.submit([this]() { this->m_blockDriver->load(); }); + m_blockDriver->load(); // 获取K线数据驱动并预加载指定的数据 HKU_INFO("Loading KData..."); @@ -132,8 +125,6 @@ void StockManager::loadData() { // 加载K线及历史财务信息 loadAllKData(); - tg.join(); - std::chrono::duration sec = std::chrono::system_clock::now() - start_time; HKU_INFO("{:<.2f}s Loaded Data.", sec.count()); } From e9861ed6670b3adccd3d6b0be46a1ff4140de910 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 17:30:41 +0800 Subject: [PATCH 561/601] update --- hikyuu/fetcher/stock/zh_stock_a_sina_qq.py | 2 ++ hikyuu/interactive.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py index 4412aa46..b583dadb 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +++ b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py @@ -66,6 +66,8 @@ def parse_one_result_qq(resultstr): return None a = resultstr.split('~') + if len(a) <= 1: + return None market = a[0][2:4].upper() code = a[0][4:10] result['market'] = market diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 97cd3c54..6ab20873 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -285,7 +285,7 @@ def realtime_update_inner(source='qq', stk_list=None): if stk.is_null(): continue k = KRecord() - k.datetime = Datetime(r['datetime']) + k.datetime = Datetime(r['datetime']).start_of_day() k.open = r['open'] k.high = r['high'] k.low = r['low'] From e9387e0f74bf5d1450e683016ff982c905ba72a6 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 23:47:32 +0800 Subject: [PATCH 562/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20StockManager=20?= =?UTF-8?q?=E4=B8=AD=E7=9A=84=E9=94=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/StockManager.cpp | 47 ++++++++++--------- hikyuu_cpp/hikyuu/StockManager.h | 12 ++--- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 7 +++ hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 10 +++- .../hikyuu/global/schedule/inner_tasks.cpp | 13 ++++- 5 files changed, 55 insertions(+), 34 deletions(-) diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 674207d3..413f2fca 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -33,10 +33,10 @@ void StockManager::quit() { } StockManager::StockManager() : m_initializing(false), m_data_ready(false) { - m_stockDict_mutex = new std::mutex; - m_marketInfoDict_mutex = new std::mutex; - m_stockTypeInfo_mutex = new std::mutex; - m_holidays_mutex = new std::mutex; + m_stockDict_mutex = new std::shared_mutex; + m_marketInfoDict_mutex = new std::shared_mutex; + m_stockTypeInfo_mutex = new std::shared_mutex; + m_holidays_mutex = new std::shared_mutex; } StockManager::~StockManager() { @@ -79,10 +79,6 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa m_tmpdir = hikyuuParam.tryGet("tmpdir", "."); m_datadir = hikyuuParam.tryGet("datadir", "."); - m_stockDict.clear(); - m_marketInfoDict.clear(); - m_stockTypeInfo.clear(); - // 加载证券基本信息 m_baseInfoDriver = DataDriverFactory::getBaseInfoDriver(baseInfoParam); HKU_CHECK(m_baseInfoDriver, "Failed get base info driver!"); @@ -184,7 +180,7 @@ void StockManager::loadAllKData() { std::thread t = std::thread([this, ktypes, low_ktypes]() { this->m_load_tg = std::make_unique(); for (size_t i = 0, len = ktypes.size(); i < len; i++) { - std::lock_guard lock(*m_stockDict_mutex); + std::shared_lock lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { if (m_preloadParam.tryGet(low_ktypes[i], false)) { m_load_tg->submit( @@ -196,7 +192,7 @@ void StockManager::loadAllKData() { } if (m_hikyuuParam.tryGet("load_history_finance", true)) { - std::lock_guard lock(*m_stockDict_mutex); + std::shared_lock lock(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { m_load_tg->submit([stk = iter->second]() { stk.getHistoryFinance(); }); } @@ -231,15 +227,15 @@ Stock StockManager::getStock(const string& querystr) const { Stock result; string query_str = querystr; to_upper(query_str); - std::lock_guard lock(*m_stockDict_mutex); + std::shared_lock lock(*m_stockDict_mutex); auto iter = m_stockDict.find(query_str); return (iter != m_stockDict.end()) ? iter->second : result; } StockList StockManager::getStockList(std::function&& filter) const { StockList ret; + std::shared_lock lock(*m_stockDict_mutex); ret.reserve(m_stockDict.size()); - std::lock_guard lock(*m_stockDict_mutex); auto iter = m_stockDict.begin(); if (filter) { for (; iter != m_stockDict.end(); ++iter) { @@ -260,7 +256,7 @@ MarketInfo StockManager::getMarketInfo(const string& market) const { string market_tmp = market; to_upper(market_tmp); - std::lock_guard lock(*m_marketInfoDict_mutex); + std::shared_lock lock(*m_marketInfoDict_mutex); auto iter = m_marketInfoDict.find(market_tmp); if (iter != m_marketInfoDict.end()) { result = iter->second; @@ -275,7 +271,7 @@ MarketInfo StockManager::getMarketInfo(const string& market) const { StockTypeInfo StockManager::getStockTypeInfo(uint32_t type) const { StockTypeInfo result; - std::lock_guard lock(*m_stockTypeInfo_mutex); + std::shared_lock lock(*m_stockTypeInfo_mutex); auto iter = m_stockTypeInfo.find(type); if (iter != m_stockTypeInfo.end()) { result = iter->second; @@ -290,7 +286,7 @@ StockTypeInfo StockManager::getStockTypeInfo(uint32_t type) const { MarketList StockManager::getAllMarket() const { MarketList result; - std::lock_guard lock(*m_marketInfoDict_mutex); + std::shared_lock lock(*m_marketInfoDict_mutex); auto iter = m_marketInfoDict.begin(); for (; iter != m_marketInfoDict.end(); ++iter) { result.push_back(iter->first); @@ -324,6 +320,11 @@ const ZhBond10List& StockManager::getZhBond10() const { return m_zh_bond10; } +bool StockManager::isHoliday(const Datetime& d) const { + std::shared_lock lock(*m_holidays_mutex); + return m_holidays.count(d); +} + Stock StockManager::addTempCsvStock(const string& code, const string& day_filename, const string& min_filename, price_t tick, price_t tickValue, int precision, size_t minTradeNumber, size_t maxTradeNumber) { @@ -352,7 +353,7 @@ void StockManager::removeTempCsvStock(const string& code) { bool StockManager::addStock(const Stock& stock) { string market_code(stock.market_code()); to_upper(market_code); - std::lock_guard lock(*m_stockDict_mutex); + std::unique_lock lock(*m_stockDict_mutex); HKU_ERROR_IF_RETURN(m_stockDict.find(market_code) != m_stockDict.end(), false, "The stock had exist! {}", market_code); m_stockDict[market_code] = stock; @@ -362,7 +363,7 @@ bool StockManager::addStock(const Stock& stock) { void StockManager::removeStock(const string& market_code) { string n_market_code(market_code); to_upper(n_market_code); - std::lock_guard lock(*m_stockDict_mutex); + std::unique_lock lock(*m_stockDict_mutex); auto iter = m_stockDict.find(n_market_code); if (iter != m_stockDict.end()) { m_stockDict.erase(iter); @@ -396,7 +397,7 @@ void StockManager::loadAllStocks() { auto kdriver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam); - std::lock_guard lock(*m_stockDict_mutex); + std::unique_lock lock(*m_stockDict_mutex); for (auto& info : stockInfos) { Datetime startDate, endDate; try { @@ -452,7 +453,7 @@ void StockManager::loadAllStocks() { void StockManager::loadAllMarketInfos() { HKU_INFO("Loading market information..."); auto marketInfos = m_baseInfoDriver->getAllMarketInfo(); - std::lock_guard lock(*m_marketInfoDict_mutex); + std::unique_lock lock(*m_marketInfoDict_mutex); m_marketInfoDict.clear(); m_marketInfoDict.reserve(marketInfos.size()); for (auto& marketInfo : marketInfos) { @@ -470,7 +471,7 @@ void StockManager::loadAllMarketInfos() { void StockManager::loadAllStockTypeInfo() { HKU_INFO("Loading stock type information..."); auto stkTypeInfos = m_baseInfoDriver->getAllStockTypeInfo(); - std::lock_guard lock(*m_stockTypeInfo_mutex); + std::unique_lock lock(*m_stockTypeInfo_mutex); m_stockTypeInfo.clear(); m_stockTypeInfo.reserve(stkTypeInfos.size()); for (auto& stkTypeInfo : stkTypeInfos) { @@ -480,7 +481,7 @@ void StockManager::loadAllStockTypeInfo() { void StockManager::loadAllHolidays() { auto holidays = m_baseInfoDriver->getAllHolidays(); - std::lock_guard lock(*m_holidays_mutex); + std::unique_lock lock(*m_holidays_mutex); m_holidays = std::move(holidays); } @@ -489,7 +490,7 @@ void StockManager::loadAllStockWeights() { HKU_INFO("Loading stock weight..."); if (m_context.isAll()) { auto all_stkweight_dict = m_baseInfoDriver->getAllStockWeightList(); - std::lock_guard lock1(*m_stockDict_mutex); + std::shared_lock lock1(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { auto weight_iter = all_stkweight_dict.find(iter->first); if (weight_iter != all_stkweight_dict.end()) { @@ -499,7 +500,7 @@ void StockManager::loadAllStockWeights() { } } } else { - std::lock_guard lock1(*m_stockDict_mutex); + std::shared_lock lock1(*m_stockDict_mutex); for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) { Stock& stock = iter->second; auto sw_list = m_baseInfoDriver->getStockWeightList( diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 38b7136b..44beb6b3 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -275,18 +275,18 @@ private: BlockInfoDriverPtr m_blockDriver; StockMapIterator::stock_map_t m_stockDict; // SH000001 -> stock - std::mutex* m_stockDict_mutex; + std::shared_mutex* m_stockDict_mutex; typedef unordered_map MarketInfoMap; mutable MarketInfoMap m_marketInfoDict; - std::mutex* m_marketInfoDict_mutex; + std::shared_mutex* m_marketInfoDict_mutex; typedef unordered_map StockTypeInfoMap; mutable StockTypeInfoMap m_stockTypeInfo; - std::mutex* m_stockTypeInfo_mutex; + std::shared_mutex* m_stockTypeInfo_mutex; std::unordered_set m_holidays; // 节假日 - std::mutex* m_holidays_mutex; + std::shared_mutex* m_holidays_mutex; ZhBond10List m_zh_bond10; // 10年期中国国债收益率数据 @@ -315,10 +315,6 @@ inline Stock StockManager::operator[](const string& query) const { return getStock(query); } -inline bool StockManager::isHoliday(const Datetime& d) const { - return m_holidays.count(d); -} - inline const Parameter& StockManager::getBaseInfoDriverParameter() const { return m_baseInfoDriverParam; } diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index a1afe36f..99bb5380 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -8,6 +8,7 @@ #include #include #include +#include "hikyuu/StockManager.h" #include "spot_generated.h" #include "SpotAgent.h" @@ -156,6 +157,12 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { } void SpotAgent::work_thread() { + // 等待 sm 所有数据准备完毕后,再连接行情更新 + while (!StockManager::instance().dataReady() && !m_stop) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + HKU_IF_RETURN(m_stop, void()); + nng_socket sock; int rv = nng_sub0_open(&sock); diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index e75712db..4ab32590 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -44,16 +44,22 @@ public: } void setWorkerNum(size_t worker_num) { - std::lock_guard lock(m_mutex); m_work_num = worker_num; } + size_t getWorkerNum() const { + return m_work_num; + } + /** 设置是否打印数据接收进展情况,主要用于在交互环境下关闭打印 */ void setPrintFlag(bool print) { - std::lock_guard lock(m_mutex); m_print = print; } + bool getPrintFlag() const { + return m_print; + } + /** * 增加收到 Spot 数据时的处理函数 * @note 仅能在停止状态时执行此操作,否则将抛出异常 diff --git a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp index 433bcbd0..b967f74a 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp @@ -5,9 +5,10 @@ * Author: fasiondog */ +#include "hikyuu/StockManager.h" +#include "hikyuu/global/GlobalSpotAgent.h" #include "inner_tasks.h" #include "scheduler.h" -#include "../../StockManager.h" namespace hku { @@ -17,7 +18,17 @@ void initInnerTask() { } void reloadHikyuuTask() { + // 先停止行情接收 + auto* agent = getGlobalSpotAgent(); + agent->stop(); + + // 重新加载数据 StockManager::instance().reload(); + + // 重新启动行情接收 + bool print = agent->getPrintFlag(); + size_t work_num = agent->getWorkerNum(); + startSpotAgent(print, work_num); } } // namespace hku \ No newline at end of file From aeaeb6a388d2950ef0866cc6100a0c377b602273 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 23:48:30 +0800 Subject: [PATCH 563/601] =?UTF-8?q?=E6=81=A2=E5=A4=8D=E5=B8=82=E5=9C=BA?= =?UTF-8?q?=E6=97=B6=E9=97=B4=EF=BC=8C=E5=86=85=E9=83=A8=E5=BB=B6=E8=BF=9F?= =?UTF-8?q?=E5=88=A4=E5=AE=9A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0021.sql | 4 ++++ hikyuu/data/sqlite_upgrade/0022.sql | 6 ++++++ hikyuu/fetcher/stock/zh_stock_a_qmt.py | 2 +- hikyuu_cpp/hikyuu/KRecord.h | 2 +- hikyuu_cpp/hikyuu/Stock.cpp | 7 ++++--- 5 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 hikyuu/data/mysql_upgrade/0021.sql create mode 100644 hikyuu/data/sqlite_upgrade/0022.sql diff --git a/hikyuu/data/mysql_upgrade/0021.sql b/hikyuu/data/mysql_upgrade/0021.sql new file mode 100644 index 00000000..8f60f715 --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0021.sql @@ -0,0 +1,4 @@ +UPDATE `hku_base`.`market` SET `closeTime1`=1130, `closeTime2`=1500 WHERE `marketid`=1; +UPDATE `hku_base`.`market` SET `closeTime1`=1130, `closeTime2`=1500 WHERE `marketid`=2; +UPDATE `hku_base`.`market` SET `closeTime1`=1130, `closeTime2`=1500 WHERE `marketid`=3; +UPDATE `hku_base`.`version` set `version` = 21; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0022.sql b/hikyuu/data/sqlite_upgrade/0022.sql new file mode 100644 index 00000000..449cfed2 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0022.sql @@ -0,0 +1,6 @@ +BEGIN TRANSACTION; +UPDATE `market` SET `closeTime1`=1130, `closeTime2`=1500 WHERE `marketid`=1; +UPDATE `market` SET `closeTime1`=1130, `closeTime2`=1500 WHERE `marketid`=2; +UPDATE `market` SET `closeTime1`=1130, `closeTime2`=1500 WHERE `marketid`=3; +UPDATE `version` set `version` = 22; +COMMIT; \ No newline at end of file diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index e5199330..34a8c534 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -30,7 +30,7 @@ try: result['high'] = data['high'] result['low'] = data['low'] result['close'] = data['lastPrice'] - result['amount'] = data['amount'] * 0.001 # 转千元 + result['amount'] = data['amount'] * 0.0001 # 转万元 result['volume'] = data['pvolume'] * 0.01 # 转手数 for i in range(5): diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index 03623065..e17ab821 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -24,7 +24,7 @@ public: price_t highPrice; ///< 最高价 price_t lowPrice; ///< 最低价 price_t closePrice; ///< 收盘价 - price_t transAmount; ///< 成交金额(千元) + price_t transAmount; ///< 成交金额(万元) price_t transCount; ///< 成交量(手),日线以下为股数 KRecord() diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 53a9fc7b..18b304f9 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -402,7 +402,7 @@ bool Stock::isBuffer(KQuery::KType ktype) const { HKU_IF_RETURN(!m_data, false); string nktype(ktype); to_upper(nktype); - std::unique_lock lock(*(m_data->pMutex[ktype])); + std::shared_lock lock(*(m_data->pMutex[ktype])); return m_data->pKData.find(nktype) != m_data->pKData.end() && m_data->pKData[nktype]; } @@ -865,11 +865,12 @@ bool Stock::isTransactionTime(Datetime time) { Datetime today = Datetime::today(); Datetime openTime1 = today + market_info.openTime1(); Datetime closeTime1 = today + market_info.closeTime1(); - HKU_IF_RETURN(time >= openTime1 && time <= closeTime1, true); // close判断包括等于 + // 某些行情闭市最后一天tick时间可能延迟数秒,补充余量 + HKU_IF_RETURN(time >= openTime1 && time <= closeTime1 + Seconds(30), true); Datetime openTime2 = today + market_info.openTime2(); Datetime closeTime2 = today + market_info.closeTime2(); - return time >= openTime2 && time <= closeTime2; + return time >= openTime2 && time <= closeTime2 + Seconds(30); } void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { From 094f124f98782770eb1a2164f9ec72c370fd2343 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 2 Sep 2024 23:51:46 +0800 Subject: [PATCH 564/601] =?UTF-8?q?fixed=20=E5=88=86=E9=92=9F=E7=BA=A7?= =?UTF-8?q?=E5=88=ABK=E7=BA=BF=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index ac7069ae..af2b61d6 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -131,9 +131,21 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { // 非24小时交易品种,且时间和当天零时相同认为无分钟线级别数据 HKU_IF_RETURN(stk.type() != STOCKTYPE_CRYPTO && minute == today, void()); - Datetime start_minute = minute - (minute - today) % gap; - Datetime end_minute = start_minute + gap; - KRecordList klist = stk.getKRecordList(KQuery(start_minute, end_minute, ktype)); + Datetime end_minute = minute - (minute - today) % gap + gap; + + // 处理闭市时最后一条记录 + MarketInfo market_info = StockManager::instance().getMarketInfo(stk.market()); + Datetime close1 = today + market_info.closeTime1(); + Datetime close2 = today + market_info.closeTime2(); + Datetime open2 = today + market_info.openTime2(); + if (!close2.isNull() && end_minute > close2) { + end_minute = close2; + } else if (!open2.isNull() && !close1.isNull() && end_minute < open2 && end_minute > close1) { + end_minute = close1; + } + + // 计算当前之前的累积成交金额、成交量 + KRecordList klist = stk.getKRecordList(KQuery(today, end_minute, ktype)); price_t sum_amount = 0.0, sum_volume = 0.0; for (const auto& k : klist) { sum_amount += k.transAmount; From e8cc23c0f30e70ed800f2365877faabe146d4cc8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 3 Sep 2024 00:29:05 +0800 Subject: [PATCH 565/601] =?UTF-8?q?pytdx=20h5=E5=AF=BC=E5=85=A5=E6=97=B6?= =?UTF-8?q?=E4=B8=8D=E6=8E=92=E6=9F=A5=E6=88=90=E4=BA=A4=E4=B8=BA0?= =?UTF-8?q?=E7=9A=84K=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/pytdx_to_h5.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/data/pytdx_to_h5.py b/hikyuu/data/pytdx_to_h5.py index aac33e88..fdcf65cc 100644 --- a/hikyuu/data/pytdx_to_h5.py +++ b/hikyuu/data/pytdx_to_h5.py @@ -287,7 +287,7 @@ def import_one_stock_data(connect, api, h5file, market, ktype, stock_record, sta if today_datetime >= bar_datetime > last_datetime \ and bar['high'] >= bar['open'] >= bar['low'] > 0 \ and bar['high'] >= bar['close'] >= bar['low'] > 0 \ - and int(bar['vol']) != 0 and int(bar['amount']*0.001) != 0: + and int(bar['vol']) >= 0 and int(bar['amount']*0.001) >= 0: try: row['datetime'] = bar_datetime row['openPrice'] = bar['open'] * 1000 From 4ad30b8fa1538279c2d1d599b2f8b769189bb1c8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 3 Sep 2024 01:31:25 +0800 Subject: [PATCH 566/601] update --- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 6 ------ 1 file changed, 6 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 99bb5380..9737f9c2 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -157,12 +157,6 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { } void SpotAgent::work_thread() { - // 等待 sm 所有数据准备完毕后,再连接行情更新 - while (!StockManager::instance().dataReady() && !m_stop) { - std::this_thread::sleep_for(std::chrono::milliseconds(100)); - } - HKU_IF_RETURN(m_stop, void()); - nng_socket sock; int rv = nng_sub0_open(&sock); From 19bcdbd7fa7b1e7d9d0c87f4f208ba06431e6ad1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 3 Sep 2024 02:07:14 +0800 Subject: [PATCH 567/601] =?UTF-8?q?startSpotAgent=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=9C=B0=E5=9D=80=E5=8F=82=E6=95=B0?= =?UTF-8?q?=EF=BC=8C=E6=96=B9=E4=BE=BF=E4=B8=B4=E6=97=B6=E6=89=8B=E5=B7=A5?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E8=A1=8C=E6=83=85=E6=9C=8D=E5=8A=A1=E5=99=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 12 +++++++++--- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 4 +++- hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 12 +++++++++++- hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp | 3 ++- hikyuu_pywrap/global/_SpotAgent.cpp | 2 +- 5 files changed, 26 insertions(+), 7 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index af2b61d6..2307ce41 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -161,13 +161,19 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { stk.realtimeUpdate(krecord, ktype); } -void HKU_API startSpotAgent(bool print, size_t worker_num) { +void HKU_API startSpotAgent(bool print, size_t worker_num, const string& addr) { StockManager& sm = StockManager::instance(); - SpotAgent::setQuotationServer( - sm.getHikyuuParameter().tryGet("quotation_server", "ipc:///tmp/hikyuu_real.ipc")); auto& agent = *getGlobalSpotAgent(); HKU_CHECK(!agent.isRunning(), "The agent is running, please stop first!"); + if (addr.empty()) { + SpotAgent::setQuotationServer( + sm.getHikyuuParameter().tryGet("quotation_server", "ipc:///tmp/hikyuu_real.ipc")); + } else { + SpotAgent::setQuotationServer(addr); + } + + agent.setServerAddr(addr); agent.setWorkerNum(worker_num); agent.setPrintFlag(print); diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index a6248a07..5b18d4e7 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -14,10 +14,12 @@ namespace hku { * 启动 Spot 数据接收代理,如果之前已经处于运行状态,将抛出异常 * @param print 打印接收数据进展 * @param worker_num 接收数据后处理时的工作任务组线程数 + * @param addr 服务端地址,为空表示使用 hikyuu 配置文件中的行情服务器地址 * @ingroup Agent */ void HKU_API startSpotAgent(bool print = true, - size_t worker_num = std::thread::hardware_concurrency()); + size_t worker_num = std::thread::hardware_concurrency(), + const string& addr = string()); /** * 终止 Spot 数据接收代理 diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index 4ab32590..d3231168 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -60,6 +60,14 @@ public: return m_print; } + void setServerAddr(const string& addr) { + m_server_addr = addr; + } + + const string& getServerAddr() const { + return m_server_addr; + } + /** * 增加收到 Spot 数据时的处理函数 * @note 仅能在停止状态时执行此操作,否则将抛出异常 @@ -124,9 +132,11 @@ private: size_t m_work_num = 1; // 数据处理任务线程池线程数 vector> m_process_task_list; + bool m_print = true; // 是否打印连接信息 + string m_server_addr; // 服务器地址 + // 下面属性被修改时需要加锁,以便可以使用多线程方式运行 strategy std::mutex m_mutex; - bool m_print = true; // 是否打印接收进度,防止的交互模式的影响 list> m_processList; // 已注册的 spot 处理函数列表 list> m_postProcessList; // 已注册的批次后处理函数列表 }; diff --git a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp index b967f74a..3c0c48e2 100644 --- a/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp +++ b/hikyuu_cpp/hikyuu/global/schedule/inner_tasks.cpp @@ -28,7 +28,8 @@ void reloadHikyuuTask() { // 重新启动行情接收 bool print = agent->getPrintFlag(); size_t work_num = agent->getWorkerNum(); - startSpotAgent(print, work_num); + const string& addr = agent->getServerAddr(); + startSpotAgent(print, work_num, addr); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_pywrap/global/_SpotAgent.cpp b/hikyuu_pywrap/global/_SpotAgent.cpp index f69ae2bc..563c906a 100644 --- a/hikyuu_pywrap/global/_SpotAgent.cpp +++ b/hikyuu_pywrap/global/_SpotAgent.cpp @@ -13,6 +13,6 @@ namespace py = pybind11; void export_SpotAgent(py::module& m) { m.def("start_spot_agent", startSpotAgent, py::arg("print") = false, - py::arg("worker_num") = std::thread::hardware_concurrency()); + py::arg("worker_num") = std::thread::hardware_concurrency(), py::arg("addr") = string()); m.def("stop_spot_agent", stopSpotAgent); } From c08335253cf02667aca9f164f5fbacddd0b22c38 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 3 Sep 2024 13:18:25 +0800 Subject: [PATCH 568/601] =?UTF-8?q?fixed=20=E8=A1=8C=E6=83=85=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 2 +- hikyuu/fetcher/stock/zh_stock_a_sina_qq.py | 2 +- hikyuu_cpp/hikyuu/KRecord.h | 2 +- hikyuu_cpp/hikyuu/Stock.cpp | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index 34a8c534..e5199330 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -30,7 +30,7 @@ try: result['high'] = data['high'] result['low'] = data['low'] result['close'] = data['lastPrice'] - result['amount'] = data['amount'] * 0.0001 # 转万元 + result['amount'] = data['amount'] * 0.001 # 转千元 result['volume'] = data['pvolume'] * 0.01 # 转手数 for i in range(5): diff --git a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py index b583dadb..c7b90a56 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +++ b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py @@ -114,7 +114,7 @@ def parse_one_result_qq(resultstr): result['volume'] = float(a[36]) * 0.01 # 成交量(手) else: result['volume'] = float(a[36]) # 成交量(手) - result['amount'] = float(a[37]) # 成交额(万) + result['amount'] = float(a[37]) * 10.0 # 成交额(万) result['turnover_rate'] = float(a[38]) if a[38] else 0.0 # 换手率 result['pe'] = float(a[39]) if a[39] else 0.0 # 市盈率 Price Earnings Ratio,简称P/E或PER result['amplitude'] = float(a[43]) if a[43] else 0.0 # 振幅 diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index e17ab821..03623065 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -24,7 +24,7 @@ public: price_t highPrice; ///< 最高价 price_t lowPrice; ///< 最低价 price_t closePrice; ///< 收盘价 - price_t transAmount; ///< 成交金额(万元) + price_t transAmount; ///< 成交金额(千元) price_t transCount; ///< 成交量(手),日线以下为股数 KRecord() diff --git a/hikyuu_cpp/hikyuu/Stock.cpp b/hikyuu_cpp/hikyuu/Stock.cpp index 18b304f9..81d4ab61 100644 --- a/hikyuu_cpp/hikyuu/Stock.cpp +++ b/hikyuu_cpp/hikyuu/Stock.cpp @@ -911,8 +911,8 @@ void Stock::realtimeUpdate(KRecord record, KQuery::KType inktype) { } else if (tmp.datetime < record.datetime) { m_data->pKData[ktype]->push_back(record); } else { - HKU_INFO("Ignore record, datetime({}) < last record.datetime({})! {} {}", record.datetime, - tmp.datetime, market_code(), inktype); + HKU_DEBUG("Ignore record, datetime({}) < last record.datetime({})! {} {}", record.datetime, + tmp.datetime, market_code(), inktype); } } From 894e210395c7b000ba893058fd23eb93253be2f2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 3 Sep 2024 13:35:32 +0800 Subject: [PATCH 569/601] =?UTF-8?q?=E8=A1=8C=E6=83=85=E6=9B=B4=E6=96=B0?= =?UTF-8?q?=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_qmt.py | 2 +- hikyuu/fetcher/stock/zh_stock_a_sina_qq.py | 2 +- hikyuu_cpp/hikyuu/KRecord.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_qmt.py b/hikyuu/fetcher/stock/zh_stock_a_qmt.py index e5199330..dd7c3f7e 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_qmt.py +++ b/hikyuu/fetcher/stock/zh_stock_a_qmt.py @@ -30,7 +30,7 @@ try: result['high'] = data['high'] result['low'] = data['low'] result['close'] = data['lastPrice'] - result['amount'] = data['amount'] * 0.001 # 转千元 + result['amount'] = data['amount'] * 0.0001 # 转千元 result['volume'] = data['pvolume'] * 0.01 # 转手数 for i in range(5): diff --git a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py index c7b90a56..90274e99 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py +++ b/hikyuu/fetcher/stock/zh_stock_a_sina_qq.py @@ -114,7 +114,7 @@ def parse_one_result_qq(resultstr): result['volume'] = float(a[36]) * 0.01 # 成交量(手) else: result['volume'] = float(a[36]) # 成交量(手) - result['amount'] = float(a[37]) * 10.0 # 成交额(万) + result['amount'] = float(a[37]) # 成交额(万) result['turnover_rate'] = float(a[38]) if a[38] else 0.0 # 换手率 result['pe'] = float(a[39]) if a[39] else 0.0 # 市盈率 Price Earnings Ratio,简称P/E或PER result['amplitude'] = float(a[43]) if a[43] else 0.0 # 振幅 diff --git a/hikyuu_cpp/hikyuu/KRecord.h b/hikyuu_cpp/hikyuu/KRecord.h index 03623065..e17ab821 100644 --- a/hikyuu_cpp/hikyuu/KRecord.h +++ b/hikyuu_cpp/hikyuu/KRecord.h @@ -24,7 +24,7 @@ public: price_t highPrice; ///< 最高价 price_t lowPrice; ///< 最低价 price_t closePrice; ///< 收盘价 - price_t transAmount; ///< 成交金额(千元) + price_t transAmount; ///< 成交金额(万元) price_t transCount; ///< 成交量(手),日线以下为股数 KRecord() From 9f3dbb078ebcbaf707c4c167c4ff63b484b4b83b Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 02:03:28 +0800 Subject: [PATCH 570/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=AF=BC=E5=85=A5?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E6=97=B6=E5=AF=B9=E9=80=80=E5=B8=82=E8=82=A1?= =?UTF-8?q?=E7=A5=A8=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/pytdx_to_h5.py | 34 ++++++++++++++---------- hikyuu/data/pytdx_to_mysql.py | 22 +++++++++++++-- hikyuu/data/pytdx_weight_to_mysql.py | 2 +- hikyuu/data/pytdx_weight_to_sqlite.py | 2 +- hikyuu/fetcher/stock/zh_stock_a_pytdx.py | 23 +++++++++++----- hikyuu/interactive.py | 12 ++++++++- 6 files changed, 69 insertions(+), 26 deletions(-) diff --git a/hikyuu/data/pytdx_to_h5.py b/hikyuu/data/pytdx_to_h5.py index fdcf65cc..ef1cc343 100644 --- a/hikyuu/data/pytdx_to_h5.py +++ b/hikyuu/data/pytdx_to_h5.py @@ -101,6 +101,21 @@ def import_stock_name(connect, api, market, quotations=None): """ cur = connect.cursor() + deSet = set() # 记录退市证券 + if market == MARKET.SH: + df = ak.stock_info_sh_delist() + l = df[['公司代码', '公司简称']].to_dict(orient='records') if not df .empty else [] + for stock in l: + code = str(stock['公司代码']) + deSet.add(code) + elif market == MARKET.SZ: + for t in ['暂停上市公司', '终止上市公司']: + df = ak.stock_info_sz_delist(t) + l = df[['证券代码', '证券简称']].to_dict(orient='records') if not df.empty else [] + for stock in l: + code = str(stock['证券代码']) + deSet.add(code) + newStockDict = {} stk_list = get_stk_code_name_list(market) if not stk_list: @@ -110,18 +125,9 @@ def import_stock_name(connect, api, market, quotations=None): if not quotations or 'fund' in [v.lower() for v in quotations]: stk_list.extend(get_fund_code_name_list(market)) for stock in stk_list: - newStockDict[str(stock['code'])] = stock['name'] - if market == MARKET.SH: - df = ak.stock_info_sh_delist() - l = df[['公司代码', '公司简称']].to_dict(orient='records') if not df .empty else [] - for stock in l: - newStockDict[str(stock['公司代码'])] = stock['公司简称'] - elif market == MARKET.SZ: - for t in ['暂停上市公司', '终止上市公司']: - df = ak.stock_info_sz_delist(t) - l = df[['证券代码', '证券简称']].to_dict(orient='records') if not df.empty else [] - for stock in l: - newStockDict[str(stock['证券代码'])] = stock['证券简称'] + code = str(stock['code']) + if code not in deSet: + newStockDict[code] = stock['name'] marketid = get_marketid(connect, market) @@ -138,8 +144,8 @@ def import_stock_name(connect, api, market, quotations=None): oldstockid, oldcode, oldname, oldvalid = oldstock[0], oldstock[1], oldstock[2], int(oldstock[3]) oldStockDict[oldcode] = oldstockid - # 新的代码表中无此股票,则置为无效 - if (oldvalid == 1) and (oldcode not in newStockDict): + # 新的代码表中无此股票或者已退市,则置为无效 + if (oldvalid == 1) and ((oldcode not in newStockDict) or (oldcode in deSet)): cur.execute("update stock set valid=0 where stockid=%i" % oldstockid) # 股票名称发生变化,更新股票名称;如果原无效,则置为有效 diff --git a/hikyuu/data/pytdx_to_mysql.py b/hikyuu/data/pytdx_to_mysql.py index d37874f7..3c4a909e 100644 --- a/hikyuu/data/pytdx_to_mysql.py +++ b/hikyuu/data/pytdx_to_mysql.py @@ -122,6 +122,21 @@ def import_stock_name(connect, api, market, quotations=None): """ cur = connect.cursor() + deSet = set() # 记录退市证券 + if market == MARKET.SH: + df = ak.stock_info_sh_delist() + l = df[['公司代码', '公司简称']].to_dict(orient='records') if not df .empty else [] + for stock in l: + code = str(stock['公司代码']) + deSet.add(code) + elif market == MARKET.SZ: + for t in ['暂停上市公司', '终止上市公司']: + df = ak.stock_info_sz_delist(t) + l = df[['证券代码', '证券简称']].to_dict(orient='records') if not df.empty else [] + for stock in l: + code = str(stock['证券代码']) + deSet.add(code) + newStockDict = {} stk_list = get_stk_code_name_list(market) if not stk_list: @@ -131,7 +146,9 @@ def import_stock_name(connect, api, market, quotations=None): if not quotations or "fund" in [v.lower() for v in quotations]: stk_list.extend(get_fund_code_name_list(market)) for stock in stk_list: - newStockDict[str(stock["code"])] = stock["name"] + code = str(stock["code"]) + if code not in deSet: + newStockDict[code] = stock["name"] marketid = get_marketid(connect, market) @@ -156,7 +173,8 @@ def import_stock_name(connect, api, market, quotations=None): oldStockDict[oldcode] = oldstockid # 新的代码表中无此股票,则置为无效 - if (oldvalid == 1) and (oldcode not in newStockDict): + # if (oldvalid == 1) and (oldcode not in newStockDict): + if (oldvalid == 1) and ((oldcode not in newStockDict) or oldcode in deSet): cur.execute( "update `hku_base`.`stock` set valid=0 where stockid=%i" % oldstockid ) diff --git a/hikyuu/data/pytdx_weight_to_mysql.py b/hikyuu/data/pytdx_weight_to_mysql.py index 5e1e1699..2f8249af 100644 --- a/hikyuu/data/pytdx_weight_to_mysql.py +++ b/hikyuu/data/pytdx_weight_to_mysql.py @@ -38,7 +38,7 @@ def pytdx_import_weight_to_mysql(pytdx_api, connect, market): pytdx_market = to_pytdx_market(market) total_count = 0 - cur.execute("select stockid, code from `hku_base`.`stock` where marketid=%s" % (marketid)) + cur.execute("select stockid, code from `hku_base`.`stock` where marketid=%s and valid=1" % (marketid)) stockid_list = [x for x in cur.fetchall()] cur.close() diff --git a/hikyuu/data/pytdx_weight_to_sqlite.py b/hikyuu/data/pytdx_weight_to_sqlite.py index fb8fb0a8..57cb3961 100644 --- a/hikyuu/data/pytdx_weight_to_sqlite.py +++ b/hikyuu/data/pytdx_weight_to_sqlite.py @@ -35,7 +35,7 @@ def pytdx_import_weight_to_sqlite(pytdx_api, connect, market): pytdx_market = to_pytdx_market(market) total_count = 0 - stockid_list = cur.execute("select stockid, code from Stock where marketid=%s" % (marketid)) + stockid_list = cur.execute("select stockid, code from Stock where marketid=%s and valid=1" % (marketid)) stockid_list = [x for x in stockid_list] cur.close() diff --git a/hikyuu/fetcher/stock/zh_stock_a_pytdx.py b/hikyuu/fetcher/stock/zh_stock_a_pytdx.py index 3a3b3484..76fe520f 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_pytdx.py +++ b/hikyuu/fetcher/stock/zh_stock_a_pytdx.py @@ -8,7 +8,7 @@ import datetime from pytdx.hq import TdxHq_API from hikyuu.data.common_pytdx import search_best_tdx - +from hikyuu import get_stock, constant from hikyuu.util import * @@ -16,6 +16,12 @@ from hikyuu.util import * def parse_one_result(quotes): result = {} hku_check_ignore(quotes, "Invalid input param!") + try: + result['datetime'] = datetime.datetime.combine( + datetime.date.today(), datetime.time.fromisoformat(quotes['servertime']) + ) + except: + return None result['market'] = 'SH' if quotes['market'] == 1 else 'SZ' result['code'] = quotes['code'] @@ -27,8 +33,13 @@ def parse_one_result(quotes): result['low'] = quotes['low'] # 今日最低价 result['bid'] = float(quotes['bid1']) # 竞买价,即“买一”报价 result['ask'] = float(quotes['ask1']) # 竞卖价,即“卖一”报价 - result['volume'] = float(quotes['vol']) # 成交的股票手数 - result['amount'] = round(quotes['amount'] / 1000.0, 2) # 成交金额,单位为“元”,若要以“万元”为成交金额的单位,需要把该值除以一万 + # 指数 volumn 需要乘以 0.01 + stk = get_stock(f"{result['market']}{result['code']}") + if not stk.is_null() and stk.type == constant.STOCKTYPE_INDEX: + result['volume'] = float(quotes['vol']) * 0.01 + else: + result['volume'] = float(quotes['vol']) # 成交的股票手数 + result['amount'] = round(quotes['amount'] * 0.0001, 2) # 成交金额,单位为“元”,若要以“万元”为成交金额的单位,需要把该值除以一万 result['bid1_amount'] = float(quotes['bid_vol1']) # “买一”申请4695股,即47手 result['bid1'] = float(quotes['bid1']) # “买一”报价 result['bid2_amount'] = float(quotes['bid_vol2']) @@ -49,13 +60,10 @@ def parse_one_result(quotes): result['ask4'] = float(quotes['ask4']) result['ask5_amount'] = float(quotes['ask_vol5']) result['ask5'] = float(quotes['ask5']) - result['datetime'] = datetime.datetime.combine( - datetime.date.today(), datetime.time.fromisoformat(quotes['servertime']) - ) return result -@hku_catch(ret=[]) +@ hku_catch(ret=[], trace=True) def request_data(api, stklist, parse_one_result): """请求失败将抛出异常""" quotes_list = api.get_security_quotes(stklist) @@ -63,6 +71,7 @@ def request_data(api, stklist, parse_one_result): return [r for r in result if r is not None] +@spend_time def get_spot(stocklist, ip, port, batch_func=None): api = TdxHq_API() hku_check(api.connect(ip, port), 'Failed connect tdx ({}:{})!'.format(ip, port)) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index 6ab20873..af5cedb0 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -268,7 +268,17 @@ def select(cond, start=Datetime(201801010000), end=Datetime.now(), print_out=Tru def realtime_update_inner(source='qq', stk_list=None): if stk_list is None: - stk_list = sm + if source == 'qmt': + stk_list = [s for s in sm if s.valid and s.type in ( + constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, + constant.STOCKTYPE_GEM, constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ)] + else: + stk_list = [ + stk.market_code.lower() for stk in sm if stk.valid and stk.type in + (constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_GEM, + constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ) + ] + if source == 'qq': from hikyuu.fetcher.stock.zh_stock_a_sina_qq import get_spot stk_list = [s.market_code.lower() for s in stk_list] From 3cf031e8297561799dbc2b810d8ecb628cfff892 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 02:26:18 +0800 Subject: [PATCH 571/601] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=9D=83=E6=81=AF?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=8B=E8=BD=BD=E4=B8=BA=E5=A4=9A=E8=BF=9B?= =?UTF-8?q?=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/gui/HikyuuTDX.py | 2 +- hikyuu/gui/data/ImportWeightToSqliteTask.py | 43 ++++++++------------- hikyuu/gui/data/UsePytdxImportToH5Thread.py | 25 ++++++++++-- hikyuu/gui/data/UseTdxImportToH5Thread.py | 36 ++++++++++++++--- 4 files changed, 69 insertions(+), 37 deletions(-) diff --git a/hikyuu/gui/HikyuuTDX.py b/hikyuu/gui/HikyuuTDX.py index 4712b511..0fc473c1 100644 --- a/hikyuu/gui/HikyuuTDX.py +++ b/hikyuu/gui/HikyuuTDX.py @@ -631,6 +631,7 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): self.escape_time_thread = None self.start_import_pushButton.setEnabled(True) self.import_detail_textEdit.append("导入完毕!") + self.hdf5_weight_label.setText("导入完毕!") if can_upgrade(): self.import_detail_textEdit.append("========================================================") self.import_detail_textEdit.append( @@ -661,7 +662,6 @@ class MyMainWindow(QMainWindow, Ui_MainWindow): self.import_detail_textEdit.append('导入 {} 分时记录数:{}'.format(msg[3], msg[5])) elif msg_task_name == 'IMPORT_WEIGHT': - self.hdf5_weight_label.setText(msg[2]) if msg[2] == '导入权息数据完毕!': self.import_detail_textEdit.append('导入权息记录数:{}'.format(msg[3])) elif msg[2] == '导入通达信财务信息完毕!': diff --git a/hikyuu/gui/data/ImportWeightToSqliteTask.py b/hikyuu/gui/data/ImportWeightToSqliteTask.py index c82a0113..767dae8c 100644 --- a/hikyuu/gui/data/ImportWeightToSqliteTask.py +++ b/hikyuu/gui/data/ImportWeightToSqliteTask.py @@ -42,7 +42,7 @@ from hikyuu.util.check import hku_catch, hku_check class ImportWeightToSqliteTask: - def __init__(self, log_queue, queue, config, dest_dir): + def __init__(self, log_queue, queue, config, dest_dir, market, cmd, host, port): self.logger = logging.getLogger(self.__class__.__name__) self.log_queue = log_queue self.queue = queue @@ -50,6 +50,10 @@ class ImportWeightToSqliteTask: self.dest_dir = dest_dir self.msg_name = 'IMPORT_WEIGHT' self.status = "no run" + self.market = market + self.cmd = cmd # "weight" | "finance" + self.host = host + self.port = port @hku_catch(trace=True) def __call__(self): @@ -76,44 +80,31 @@ class ImportWeightToSqliteTask: self.logger.debug('use mysql import weight') except Exception as e: - #self.queue.put([self.msg_name, str(e), -1, 0, total_count]) + # self.queue.put([self.msg_name, str(e), -1, 0, total_count]) self.queue.put([self.msg_name, 'INFO', str(e), 0, 0]) self.queue.put([self.msg_name, '', 0, None, total_count]) self.status = "failure" return try: - hosts = search_best_tdx() api = TdxHq_API() - hku_check(api.connect(hosts[0][2], hosts[0][3]), "failed connect pytdx {}:{}!", hosts[0][2], hosts[0][3]) + hku_check(api.connect(self.host, self.port), "failed connect pytdx {}:{}!", self.host, self.port) - self.logger.info('正在导入权息数据') - self.queue.put([self.msg_name, '正在导入权息数据...', 0, 0, 0]) - - total_count = 0 - for market in g_market_list: - count = pytdx_import_weight(api, connect, market) - self.logger.info("导入 {} 权息记录数: {}".format(market, count)) - total_count += count - - self.queue.put([self.msg_name, '导入权息数据完毕!', 0, 0, total_count]) - self.logger.info('导入权息数据完毕') - - self.queue.put([self.msg_name, '下载通达信财务信息(上证)...', 0, 0, 0]) - x = pytdx_import_finance(connect, api, "SH") - - self.queue.put([self.msg_name, '下载通达信财务信息(深证)...', 0, 0, 0]) - x += pytdx_import_finance(connect, api, "SZ") - - self.queue.put([self.msg_name, '下载通达信财务信息(北证)...', 0, 0, 0]) - x += pytdx_import_finance(connect, api, "BJ") - self.queue.put([self.msg_name, '导入通达信财务信息完毕!', 0, 0, x]) + if self.cmd == 'weight': + count = pytdx_import_weight(api, connect, self.market) + self.logger.info("导入 {} 权息记录数: {}".format(self.market, count)) + self.queue.put([self.msg_name, '导入权息数据完毕!', 0, 0, f'{self.market} {total_count}']) + elif self.cmd == 'finance': + self.queue.put([self.msg_name, f'下载通达信当前财务信息({self.market})...', 0, 0, 0]) + x = pytdx_import_finance(connect, api, self.market) + self.logger.info(f'导入 {self.market} 通达信当前财务信息数: {x}') + self.queue.put([self.msg_name, '导入通达信财务信息完毕!', 0, 0, f'{self.market} {x}']) api.disconnect() except Exception as e: self.logger.error(e) - #self.queue.put([self.msg_name, str(e), -1, 0, total_count]) + # self.queue.put([self.msg_name, str(e), -1, 0, total_count]) self.queue.put([self.msg_name, 'INFO', str(e), 0, 0]) finally: connect.commit() diff --git a/hikyuu/gui/data/UsePytdxImportToH5Thread.py b/hikyuu/gui/data/UsePytdxImportToH5Thread.py index ee0b4f02..bb56b96c 100644 --- a/hikyuu/gui/data/UsePytdxImportToH5Thread.py +++ b/hikyuu/gui/data/UsePytdxImportToH5Thread.py @@ -87,10 +87,10 @@ class UsePytdxImportToH5Thread(QThread): sqlite_file_name = dest_dir + "/stock.db" self.tasks = [] - if self.config.getboolean('weight', 'enable', fallback=False): - self.tasks.append( - ImportWeightToSqliteTask(self.log_queue, self.queue, - self.config, dest_dir)) + # if self.config.getboolean('weight', 'enable', fallback=False): + # self.tasks.append( + # ImportWeightToSqliteTask(self.log_queue, self.queue, + # self.config, dest_dir)) if self.config.getboolean('finance', 'enable', fallback=True): self.tasks.append( @@ -112,6 +112,8 @@ class UsePytdxImportToH5Thread(QThread): task_count += market_count if self.config.getboolean('ktype', 'time', fallback=False): task_count += market_count + if self.config.getboolean('weight', 'enable', fallback=False): + task_count += (market_count*2) self.logger.info('搜索通达信服务器') self.send_message(['INFO', '搜索通达信服务器']) @@ -210,6 +212,19 @@ class UsePytdxImportToH5Thread(QThread): start_date.month * 1000000 + start_date.day * 10000)) cur_host += 1 + if self.config.getboolean('weight', 'enable', fallback=False): + for market in g_market_list: + self.tasks.append( + ImportWeightToSqliteTask(self.log_queue, self.queue, + self.config, dest_dir, market, 'weight', use_hosts[cur_host][0], + use_hosts[cur_host][1])) + cur_host += 1 + self.tasks.append( + ImportWeightToSqliteTask(self.log_queue, self.queue, + self.config, dest_dir, market, 'finance', use_hosts[cur_host][0], + use_hosts[cur_host][1])) + cur_host += 1 + def run(self): try: self.init_task() @@ -298,6 +313,8 @@ class UsePytdxImportToH5Thread(QThread): self.send_message([taskname, ktype]) elif taskname == 'IMPORT_ZH_BOND10': self.send_message([taskname, ktype]) + elif taskname == 'IMPORT_WEIGHT': + pass else: self.send_message([taskname, 'FINISHED']) continue diff --git a/hikyuu/gui/data/UseTdxImportToH5Thread.py b/hikyuu/gui/data/UseTdxImportToH5Thread.py index 3498164f..85a82bba 100644 --- a/hikyuu/gui/data/UseTdxImportToH5Thread.py +++ b/hikyuu/gui/data/UseTdxImportToH5Thread.py @@ -30,7 +30,10 @@ from hikyuu.gui.data.ImportTdxToH5Task import ImportTdxToH5Task from hikyuu.gui.data.ImportWeightToSqliteTask import ImportWeightToSqliteTask from hikyuu.gui.data.ImportHistoryFinanceTask import ImportHistoryFinanceTask +from pytdx.hq import TdxHq_API +from hikyuu.data.common import g_market_list from hikyuu.data.common_sqlite3 import create_database +from hikyuu.data.common_pytdx import search_best_tdx from hikyuu.data.tdx_to_h5 import tdx_import_stock_name_from_file from hikyuu.util import * @@ -57,18 +60,39 @@ class UseTdxImportToH5Thread(QThread): self.quotations.append('stock') if self.config['quotation']['fund']: self.quotations.append('fund') - #if self.config['quotation']['future']: + # if self.config['quotation']['future']: # self.quotations.append('future') - #通达信盘后没有债券数据。另外,如果用Pytdx下载债券数据, - #每个债券本身的数据很少但债券种类太多占用空间和时间太多,用途较少不再考虑导入 - #if self.config['quotation']['bond']: + # 通达信盘后没有债券数据。另外,如果用Pytdx下载债券数据, + # 每个债券本身的数据很少但债券种类太多占用空间和时间太多,用途较少不再考虑导入 + # if self.config['quotation']['bond']: # self.quotations.append('bond') + hosts = search_best_tdx() + api = TdxHq_API() + hku_check(api.connect(hosts[0][2], hosts[0][3]), "failed connect pytdx {}:{}!", hosts[0][2], hosts[0][3]) + self.queue = Queue() self.tasks = [] + + cur_host = 0 if self.config.getboolean('weight', 'enable', fallback=False): - self.tasks.append(ImportWeightToSqliteTask(self.log_queue, self.queue, self.config, dest_dir)) + for market in g_market_list: + self.tasks.append( + ImportWeightToSqliteTask(self.log_queue, self.queue, + self.config, dest_dir, market, 'weight', hosts[cur_host][2], + hosts[cur_host][3])) + cur_host += 1 + if cur_host >= len(hosts): + cur_host = 0 + self.tasks.append( + ImportWeightToSqliteTask(self.log_queue, self.queue, + self.config, dest_dir, market, 'finance', hosts[cur_host][2], + hosts[cur_host][3])) + cur_host += 1 + if cur_host >= len(hosts): + cur_host = 0 + if self.config.getboolean('finance', 'enable', fallback=True): self.tasks.append(ImportHistoryFinanceTask(self.log_queue, self.queue, self.config, dest_dir)) if self.config.getboolean('ktype', 'day', fallback=False): @@ -115,7 +139,7 @@ class UseTdxImportToH5Thread(QThread): dest_dir = self.config['hdf5']['dir'] hdf5_import_progress = {'SH': {'DAY': 0, '1MIN': 0, '5MIN': 0}, 'SZ': {'DAY': 0, '1MIN': 0, '5MIN': 0}} - #正在导入代码表 + # 正在导入代码表 self.send_message(['START_IMPORT_CODE']) connect = sqlite3.connect(dest_dir + "/stock.db") From 2f92c91a16b220c948404445242e5c21c3a50177 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 04:55:52 +0800 Subject: [PATCH 572/601] =?UTF-8?q?update=E8=A1=8C=E6=83=85=E8=8E=B7?= =?UTF-8?q?=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/fetcher/stock/zh_stock_a_pytdx.py | 43 ++++++++++++++++++++- hikyuu/gui/data/UsePytdxImportToH5Thread.py | 6 --- 2 files changed, 41 insertions(+), 8 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_pytdx.py b/hikyuu/fetcher/stock/zh_stock_a_pytdx.py index 76fe520f..16157bb9 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_pytdx.py +++ b/hikyuu/fetcher/stock/zh_stock_a_pytdx.py @@ -6,6 +6,7 @@ # Author: fasiondog import datetime +from concurrent import futures from pytdx.hq import TdxHq_API from hikyuu.data.common_pytdx import search_best_tdx from hikyuu import get_stock, constant @@ -71,7 +72,7 @@ def request_data(api, stklist, parse_one_result): return [r for r in result if r is not None] -@spend_time +@hku_catch(ret=([], [])) def get_spot(stocklist, ip, port, batch_func=None): api = TdxHq_API() hku_check(api.connect(ip, port), 'Failed connect tdx ({}:{})!'.format(ip, port)) @@ -80,6 +81,7 @@ def get_spot(stocklist, ip, port, batch_func=None): tmplist = [] result = [] max_size = 80 + err_list = [] for stk in stocklist: tmplist.append((1 if stk.market == 'SH' else 0, stk.code)) count += 1 @@ -89,6 +91,8 @@ def get_spot(stocklist, ip, port, batch_func=None): result.extend(phase_result) if batch_func: batch_func(phase_result) + else: + err_list.extend(tmplist) count = 0 tmplist = [] if tmplist: @@ -97,4 +101,39 @@ def get_spot(stocklist, ip, port, batch_func=None): result.extend(phase_result) if batch_func: batch_func(phase_result) - return result + else: + err_list.extend(tmplist) + return result, err_list + + +@spend_time +def get_spot2(stocklist, ip, port, batch_func=None): + hosts = search_best_tdx() + hosts_cnt = len(hosts) + num = len(stocklist) // hosts_cnt + batchslist = [] + for i in range(hosts_cnt): + batchslist.append([[stk for stk in stocklist[i*num: (i+1)*num]], hosts[i][2], hosts[i][3], batch_func]) + if len(stocklist) % hosts_cnt != 0: + pos = num * hosts_cnt + for i in range(hosts_cnt): + batchslist[i][0].append(stocklist[pos]) + pos += 1 + if pos >= len(stocklist): + break + + def do_inner(param): + ret = get_spot(param[0], param[1], param[2], param[3]) + return ret + + with futures.ThreadPoolExecutor() as executor: + res = executor.map(do_inner, batchslist, timeout=10) + + result = [] + errors = [] + for batch_result in res: + if batch_result[0]: + result.extend(batch_result[0]) + if batch_result[1]: + errors.extend(batch_result[1]) + return result, errors diff --git a/hikyuu/gui/data/UsePytdxImportToH5Thread.py b/hikyuu/gui/data/UsePytdxImportToH5Thread.py index bb56b96c..dc4c47c6 100644 --- a/hikyuu/gui/data/UsePytdxImportToH5Thread.py +++ b/hikyuu/gui/data/UsePytdxImportToH5Thread.py @@ -84,14 +84,8 @@ class UsePytdxImportToH5Thread(QThread): def init_task(self): config = self.config dest_dir = config['hdf5']['dir'] - sqlite_file_name = dest_dir + "/stock.db" self.tasks = [] - # if self.config.getboolean('weight', 'enable', fallback=False): - # self.tasks.append( - # ImportWeightToSqliteTask(self.log_queue, self.queue, - # self.config, dest_dir)) - if self.config.getboolean('finance', 'enable', fallback=True): self.tasks.append( ImportHistoryFinanceTask(self.log_queue, self.queue, self.config, dest_dir)) From 462c243daaf77325fc5c9b02664ca53a71cbdfef Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 05:29:35 +0800 Subject: [PATCH 573/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=9D=BF=E5=9D=97?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E4=B8=8B=E8=BD=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/em_block_to_mysql.py | 70 +++++++++++++++++------------ hikyuu/data/em_block_to_sqlite.py | 75 ++++++++++++++++++------------- 2 files changed, 87 insertions(+), 58 deletions(-) diff --git a/hikyuu/data/em_block_to_mysql.py b/hikyuu/data/em_block_to_mysql.py index 00d3c8c8..6cbfdeb9 100644 --- a/hikyuu/data/em_block_to_mysql.py +++ b/hikyuu/data/em_block_to_mysql.py @@ -4,6 +4,7 @@ # Create on: 20240102 # Author: fasiondog +from concurrent.futures import ThreadPoolExecutor from hikyuu.data.common import MARKET, get_stk_code_name_list from hikyuu.util import * from hikyuu.fetcher.stock.zh_block_em import * @@ -12,37 +13,50 @@ from hikyuu.fetcher.stock.zh_block_em import * def em_import_block_to_mysql(connect, code_market_dict, categorys=('行业板块', '概念板块', '地域板块', '指数板块')): all_block_info = {} - success_fetch_hy = False - if '行业板块' in categorys: - hku_info("获取行业板块信息") - x = get_all_hybk_info(code_market_dict) - if x: - all_block_info["行业板块"] = x - success_fetch_hy = True + with ThreadPoolExecutor(4) as executor: + if '行业板块' in categorys: + t1 = executor.submit(get_all_hybk_info, code_market_dict) - success_fetch_gn = False - if '概念板块' in categorys: - hku_info("获取概念板块信息") - x = get_all_gnbk_info(code_market_dict) - if x: - all_block_info["概念板块"] = x - success_fetch_gn = True + if '概念板块' in categorys: + t2 = executor.submit(get_all_gnbk_info, code_market_dict) - success_fetch_dy = False - if '地域板块' in categorys: - hku_info("获取地域板块信息") - x = get_all_dybk_info(code_market_dict) - if x: - all_block_info["地域板块"] = x - success_fetch_dy = True + if '地域板块' in categorys: + t3 = executor.submit(get_all_dybk_info, code_market_dict) - success_fetch_zs = False - if '指数板块' in categorys: - hku_info("获取指数板块信息") - x = get_all_zsbk_info(code_market_dict) - if x: - all_block_info["指数板块"] = x - success_fetch_zs = True + if '指数板块' in categorys: + t4 = executor.submit(get_all_zsbk_info, code_market_dict) + + success_fetch_hy = False + if '行业板块' in categorys: + x = t1.result() + hku_info("获取行业板块信息完毕") + if x: + all_block_info["行业板块"] = x + success_fetch_hy = True + + success_fetch_gn = False + if '概念板块' in categorys: + x = t2.result() + hku_info("获取概念板块信息完毕") + if x: + all_block_info["概念板块"] = x + success_fetch_gn = True + + success_fetch_dy = False + if '地域板块' in categorys: + x = t3.result() + hku_info("获取地域板块信息完毕") + if x: + all_block_info["地域板块"] = x + success_fetch_dy = True + + success_fetch_zs = False + if '指数板块' in categorys: + x = t4.result() + hku_info("获取指数板块信息完毕") + if x: + all_block_info["指数板块"] = x + success_fetch_zs = True blks = [] if success_fetch_hy: diff --git a/hikyuu/data/em_block_to_sqlite.py b/hikyuu/data/em_block_to_sqlite.py index ef419999..571a16ad 100644 --- a/hikyuu/data/em_block_to_sqlite.py +++ b/hikyuu/data/em_block_to_sqlite.py @@ -4,45 +4,60 @@ # Create on: 20240102 # Author: fasiondog +from concurrent.futures import ThreadPoolExecutor from hikyuu.data.common import MARKET, get_stk_code_name_list from hikyuu.util import * from hikyuu.fetcher.stock.zh_block_em import * +@spend_time def em_import_block_to_sqlite(connect, code_market_dict, categorys=('行业板块', '概念板块', '地域板块', '指数板块')): all_block_info = {} - success_fetch_hy = False - if '行业板块' in categorys: - hku_info("获取行业板块信息") - x = get_all_hybk_info(code_market_dict) - if x: - all_block_info["行业板块"] = x - success_fetch_hy = True + with ThreadPoolExecutor(4) as executor: + if '行业板块' in categorys: + t1 = executor.submit(get_all_hybk_info, code_market_dict) - success_fetch_gn = False - if '概念板块' in categorys: - hku_info("获取概念板块信息") - x = get_all_gnbk_info(code_market_dict) - if x: - all_block_info["概念板块"] = x - success_fetch_gn = True + if '概念板块' in categorys: + t2 = executor.submit(get_all_gnbk_info, code_market_dict) - success_fetch_dy = False - if '地域板块' in categorys: - hku_info("获取地域板块信息") - x = get_all_dybk_info(code_market_dict) - if x: - all_block_info["地域板块"] = x - success_fetch_dy = True + if '地域板块' in categorys: + t3 = executor.submit(get_all_dybk_info, code_market_dict) - success_fetch_zs = False - if '指数板块' in categorys: - hku_info("获取指数板块信息") - x = get_all_zsbk_info(code_market_dict) - if x: - all_block_info["指数板块"] = x - success_fetch_zs = True + if '指数板块' in categorys: + t4 = executor.submit(get_all_zsbk_info, code_market_dict) + + success_fetch_hy = False + if '行业板块' in categorys: + x = t1.result() + hku_info("获取行业板块信息完毕") + if x: + all_block_info["行业板块"] = x + success_fetch_hy = True + + success_fetch_gn = False + if '概念板块' in categorys: + x = t2.result() + hku_info("获取概念板块信息完毕") + if x: + all_block_info["概念板块"] = x + success_fetch_gn = True + + success_fetch_dy = False + if '地域板块' in categorys: + x = t3.result() + hku_info("获取地域板块信息完毕") + if x: + all_block_info["地域板块"] = x + success_fetch_dy = True + + success_fetch_zs = False + if '指数板块' in categorys: + x = t4.result() + hku_info("获取指数板块信息完毕") + if x: + all_block_info["指数板块"] = x + success_fetch_zs = True blks = [] if success_fetch_hy: @@ -85,8 +100,8 @@ if __name__ == "__main__": import sqlite3 from hikyuu.data.common_sqlite3 import create_database - dest_dir = "/home/fasiondog/stock" - # dest_dir = "d:\\stock" + # dest_dir = "/home/fasiondog/stock" + dest_dir = "d:\\stock" connect = sqlite3.connect(dest_dir + "/stock.db") create_database(connect) From cd7e15ab5df218ca69f2d01aee9068d6751ccd47 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 16:37:40 +0800 Subject: [PATCH 574/601] update demo --- hikyuu_cpp/demo/demo3.cpp | 85 +++++++++++++++---------- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 27 ++++++-- 2 files changed, 73 insertions(+), 39 deletions(-) diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp index 84582ac1..ee1c0d28 100644 --- a/hikyuu_cpp/demo/demo3.cpp +++ b/hikyuu_cpp/demo/demo3.cpp @@ -11,16 +11,13 @@ * 其接收 HikyuuTDX 行情采集发来的行情数据,并提供服务接口供其他程序来获取最新数据 * * 用途: - * 在程序化交易里,经常在实际下单时,希望获取一下最新数据, + * 在程序化交易里,经常在实际下单时,希望获取最新数据, * 或者是日频交易时,不开启 hikyuu 本身的行情自动接收,而是在收盘前定时执行时,手工获取下最新数据 * * hikyuu 中提供了对应的函数 get_data_from_buffer_server(python), getDataFromBufferServer(C++) - * 用来从该服务获取最新数据, 如: + * 用来从该服务获取最新数据(补齐当天数据), 如: * get_data_from_buffer_server("tcp://192.168.1.1:9201", Query.DAY) * - * 目的: - * 如何使用更多的初始化方式,控制 hikyuu 数据加载 - * *************************************************************/ #include @@ -67,13 +64,19 @@ int main(int argc, char* argv[]) { getConfigFromIni(fmt::format("{}/.hikyuu/hikyuu.ini", getUserDir()), baseParam, blockParam, kdataParam, preloadParam, hkuParam); - // 调整所有类型K线为预加载且预加载数量为1 + // 调整所有类型K线为预加载且预加载数量为1天的量 Parameter new_preloadParam; auto ktypes = KQuery::getAllKType(); for (auto& ktype : ktypes) { + auto minutes = KQuery::getKTypeInMin(ktype); to_lower(ktype); new_preloadParam.set(ktype, true); - new_preloadParam.set(fmt::format("{}_max", ktype), 1); + if (minutes >= 240) { + new_preloadParam.set(fmt::format("{}_max", ktype), 1); + } else { + new_preloadParam.set(fmt::format("{}_max", ktype), 240 / minutes); + HKU_INFO("{}: {}", fmt::format("{}_max", ktype), 240 / minutes); + } } // 不加载历史财务信息,不加载权息数据 hkuParam.set("load_history_finance", false); @@ -81,7 +84,7 @@ int main(int argc, char* argv[]) { StockManager::instance().init(baseParam, blockParam, kdataParam, new_preloadParam, hkuParam); - // 启动行情接收(只是计算回测可以不需要) + // 启动行情接收 startSpotAgent(true); server.setAddr("tcp://0.0.0.0:9201"); @@ -100,43 +103,57 @@ int main(int argc, char* argv[]) { HKU_CHECK(param.tryGet(low_ktype, false), "The ktype: {} is not be preloaded!", ktype); - json data; const auto& jcodes = req["codes"]; - // HKU_INFO("codes size: {}", jcodes.size()); - for (auto iter = jcodes.cbegin(); iter != jcodes.cend(); ++iter) { - string market_code = to_string(*iter); - market_code = market_code.substr(1, market_code.size() - 2); - Stock stk = getStock(market_code); - if (stk.isNull()) { - HKU_WARN("Not found stock: {}", market_code); - continue; - } + const auto& jdates = req["dates"]; + HKU_CHECK(jcodes.size() == jdates.size(), "The leght of codes and dates is not equal!"); - KRecordList krecords = - stk.getKRecordList(KQueryByIndex(-1, Null(), ktype)); - if (!krecords.empty()) { - const auto& k = krecords.back(); - json jr; - jr.emplace_back(market_code); - jr.emplace_back(k.datetime.str()); - jr.emplace_back(k.openPrice); - jr.emplace_back(k.highPrice); - jr.emplace_back(k.lowPrice); - jr.emplace_back(k.closePrice); - jr.emplace_back(k.transAmount); - jr.emplace_back(k.transCount); - data.emplace_back(std::move(jr)); + json jstklist; + for (size_t i = 0, len = jcodes.size(); i < len; i++) { + try { + string market_code = jcodes[i].get(); + Datetime start_date = Datetime(jdates[i].get()); + Stock stk = getStock(market_code); + if (stk.isNull()) { + HKU_WARN("Not found stock: {}", market_code); + continue; + } + + KData kdata = stk.getKData(KQueryByDate(start_date, Null(), ktype)); + if (kdata.empty()) { + continue; + } + + json jklist; + for (const auto& k : kdata) { + json jr; + jr.emplace_back(k.datetime.str()); + jr.emplace_back(k.openPrice); + jr.emplace_back(k.highPrice); + jr.emplace_back(k.lowPrice); + jr.emplace_back(k.closePrice); + jr.emplace_back(k.transAmount); + jr.emplace_back(k.transCount); + jklist.emplace_back(std::move(jr)); + } + + json jstk; + jstk["code"] = std::move(market_code); + jstk["data"] = std::move(jklist); + jstklist.emplace_back(std::move(jstk)); + + } catch (const std::exception& e) { + HKU_ERROR("{}! The error occurred in record: {}", e.what(), i); } } json res; - res["data"] = data; + res["data"] = std::move(jstklist); // HKU_INFO("<-- res: {}", to_string(res)); return res; }); server.start(); - // server.loop(); + while (true) { std::this_thread::sleep_for(std::chrono::seconds(10)); } diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 9f998329..fa8e8c7f 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -352,10 +352,20 @@ void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& s req["cmd"] = "market"; req["ktype"] = ktype; json code_list; + json date_list; for (const auto& stk : stklist) { - code_list.emplace_back(stk.market_code()); + if (!stk.isNull()) { + code_list.emplace_back(stk.market_code()); + auto k = stk.getKData(KQueryByIndex(-1, Null(), ktype)); + if (k.empty()) { + date_list.emplace_back(Datetime::min().str()); + } else { + date_list.emplace_back(k[k.size() - 1].datetime.str()); + } + } } req["codes"] = std::move(code_list); + req["dates"] = std::move(date_list); json res; client.post(req, res); @@ -368,12 +378,19 @@ void HKU_API getDataFromBufferServer(const std::string& addr, const StockList& s for (auto iter = jdata.cbegin(); iter != jdata.cend(); ++iter) { const auto& r = *iter; try { - string market_code = r[0].get(); + string market_code = r["code"].get(); Stock stk = getStock(market_code); - if (!stk.isNull()) { - KRecord k(Datetime(r[1].get()), r[2], r[3], r[4], r[5], r[6], r[7]); - stk.realtimeUpdate(k, ktype); + if (stk.isNull()) { + continue; } + + const auto& jklist = r["data"]; + for (auto kiter = jklist.cbegin(); kiter != jklist.cend(); ++kiter) { + const auto& k = *kiter; + KRecord kr(Datetime(k[0].get()), k[1], k[2], k[3], k[4], k[5], k[6]); + stk.realtimeUpdate(kr, ktype); + } + } catch (const std::exception& e) { HKU_ERROR("Failed decode json: {}! {}", to_string(r), e.what()); } From 08b63b5a0f61708e47dd338703f391b321cfb612 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 17:28:07 +0800 Subject: [PATCH 575/601] =?UTF-8?q?=E8=B0=83=E6=95=B4spotagent=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E5=B7=A5=E4=BD=9C=E7=BA=BF=E7=A8=8B=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/interactive.py | 3 +-- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h | 3 +-- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 28 +++++++++++++++------- hikyuu_cpp/hikyuu/strategy/Strategy.h | 3 ++- hikyuu_cpp/hikyuu/utilities/Parameter.h | 2 +- hikyuu_pywrap/global/_SpotAgent.cpp | 4 ++-- 6 files changed, 26 insertions(+), 17 deletions(-) diff --git a/hikyuu/interactive.py b/hikyuu/interactive.py index af5cedb0..741bb838 100644 --- a/hikyuu/interactive.py +++ b/hikyuu/interactive.py @@ -50,7 +50,6 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ -import urllib import os import configparser @@ -124,7 +123,7 @@ start_spot = False if 'HKU_START_SPOT' in os.environ: spot_str = os.environ['HKU_START_SPOT'].upper() start_spot = spot_str in ('1', 'TRUE') -spot_worker_num = os.cpu_count() +spot_worker_num = 1 if 'HKU_SPOT_WORKER_NUM' in os.environ: spot_worker_num = int(os.environ['HKU_SPOT_WORKER_NUM']) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h index 5b18d4e7..b8820a07 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.h @@ -17,8 +17,7 @@ namespace hku { * @param addr 服务端地址,为空表示使用 hikyuu 配置文件中的行情服务器地址 * @ingroup Agent */ -void HKU_API startSpotAgent(bool print = true, - size_t worker_num = std::thread::hardware_concurrency(), +void HKU_API startSpotAgent(bool print = true, size_t worker_num = 1, const string& addr = string()); /** diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index fa8e8c7f..fb0474d7 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -27,10 +27,13 @@ void Strategy::sig_handler(int sig) { } } -Strategy::Strategy() : Strategy("Strategy", "") {} +Strategy::Strategy() : Strategy("Strategy", "") { + _initParam(); +} Strategy::Strategy(const string& name, const string& config_file) : m_name(name), m_config_file(config_file) { + _initParam(); if (m_config_file.empty()) { string home = getUserDir(); HKU_ERROR_IF(home == "", "Failed get user home path!"); @@ -45,12 +48,14 @@ Strategy::Strategy(const string& name, const string& config_file) Strategy::Strategy(const vector& codeList, const vector& ktypeList, const string& name, const string& config_file) : Strategy(name, config_file) { + _initParam(); m_context.setStockCodeList(codeList); m_context.setKTypeList(ktypeList); } Strategy::Strategy(const StrategyContext& context, const string& name, const string& config_file) : Strategy(name, config_file) { + _initParam(); m_context = m_context; } @@ -59,6 +64,18 @@ Strategy::~Strategy() { CLS_INFO("Quit Strategy {}!", m_name); } +void Strategy::_initParam() { + setParam("spot_worker_num", 1); +} + +void Strategy::baseCheckParam(const string& name) const { + if (name == "spot_worker_num") { + HKU_ASSERT(getParam(name) > 0); + } +} + +void Strategy::paramChanged() {} + void Strategy::_init() { StockManager& sm = StockManager::instance(); @@ -99,13 +116,6 @@ void Strategy::start(bool autoRecieveSpot) { _runDailyAt(); if (autoRecieveSpot) { - size_t stock_num = StockManager::instance().size(); - size_t spot_worker_num = stock_num / 300; - size_t cpu_num = std::thread::hardware_concurrency(); - if (spot_worker_num > cpu_num) { - spot_worker_num = cpu_num; - } - auto& agent = *getGlobalSpotAgent(); agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { @@ -113,7 +123,7 @@ void Strategy::start(bool autoRecieveSpot) { event([=]() { m_on_recieved_spot(revTime); }); } }); - startSpotAgent(true, spot_worker_num); + startSpotAgent(true, getParam("spot_worker_num")); } _runDaily(); diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 07ff75e3..ab391f80 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -28,7 +28,7 @@ namespace hku { */ class HKU_API Strategy { CLASS_LOGGER_IMP(Strategy) - PARAMETER_SUPPORT + PARAMETER_SUPPORT_WITH_CHECK public: Strategy(); @@ -110,6 +110,7 @@ private: TimeDelta m_run_daily_at_delta; private: + void _initParam(); void _init(); void _receivedSpot(const SpotRecord& spot); void _runDaily(); diff --git a/hikyuu_cpp/hikyuu/utilities/Parameter.h b/hikyuu_cpp/hikyuu/utilities/Parameter.h index 6b9042e2..ee2354e2 100644 --- a/hikyuu_cpp/hikyuu/utilities/Parameter.h +++ b/hikyuu_cpp/hikyuu/utilities/Parameter.h @@ -365,7 +365,7 @@ public: \ * 子类需要实现重载以下虚函数接口: * virtual void _checkParam(const string& name) const * 基类需要实现以下接口: - * void baseCheckParam(const string& name) + * void baseCheckParam(const string& name) const * void paramChanged() * 另:python 中一般不需要引出 paramChanged/checkParam/_checkParam,python * 类继承时可以自己在初始化时进行检查 diff --git a/hikyuu_pywrap/global/_SpotAgent.cpp b/hikyuu_pywrap/global/_SpotAgent.cpp index 563c906a..cb72de9f 100644 --- a/hikyuu_pywrap/global/_SpotAgent.cpp +++ b/hikyuu_pywrap/global/_SpotAgent.cpp @@ -12,7 +12,7 @@ using namespace hku; namespace py = pybind11; void export_SpotAgent(py::module& m) { - m.def("start_spot_agent", startSpotAgent, py::arg("print") = false, - py::arg("worker_num") = std::thread::hardware_concurrency(), py::arg("addr") = string()); + m.def("start_spot_agent", startSpotAgent, py::arg("print") = false, py::arg("worker_num") = 1, + py::arg("addr") = string()); m.def("stop_spot_agent", stopSpotAgent); } From 9013b86ee0cf537121f749f5715f3973a1da43fc Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 17:42:29 +0800 Subject: [PATCH 576/601] =?UTF-8?q?Strategy=20=E8=A1=A5=E5=85=85=20quotati?= =?UTF-8?q?on=5Fserver=20=E5=8F=82=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index fb0474d7..f6dca554 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -66,6 +66,7 @@ Strategy::~Strategy() { void Strategy::_initParam() { setParam("spot_worker_num", 1); + setParam("quotation_server", string()); } void Strategy::baseCheckParam(const string& name) const { @@ -123,7 +124,8 @@ void Strategy::start(bool autoRecieveSpot) { event([=]() { m_on_recieved_spot(revTime); }); } }); - startSpotAgent(true, getParam("spot_worker_num")); + startSpotAgent(true, getParam("spot_worker_num"), + getParam("quotation_server")); } _runDaily(); From 3bf62bc623abac29ee7f9efeea6bdaec9f80af76 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 18:38:29 +0800 Subject: [PATCH 577/601] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/release.rst | 13 +++++++------ docs/source/strategy.rst | 6 ++++++ xmake.lua | 2 +- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index e64fcc2e..4e91ed67 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -7,12 +7,13 @@ 2.1.4 - ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -1. fixed 分钟基本行情数据更新错误 -2. 优化数据加载策略,先加载同一K线类型数据,在加载下一预加载的K线 -3. 优化内部使用线程数量,以便 Strategy 运行时节省系统资源 -4. interactive 工具可以使用环境变量控制部分数据加载策略,方便使用常规 .py 文件进行策略分析时,无需等待所有数据加载完毕,节省首次执行时间。 -5. 完善 Strategy 和 StrategyContext -6. fixed OperatorSelector 系列序列化时内存泄漏 +1. fixed 分钟级别行情数据更新错误 +2. 优化提速 HikyuuTdx 数据下载 +3. 优化数据加载策略,优先加载同一K线类型数据 +4. 优化内部使用线程数节省系统资源 +5. hikyuu.interactive 可以使用环境变量控制部分数据加载策略。可在使用 .py 文件进行策略分析时,节省首次执行时间。 +6. 完善 Strategy 和 StrategyContext +7. fixed OperatorSelector 序列化时内存泄漏 2.1.3 - 2024年8月27日 diff --git a/docs/source/strategy.rst b/docs/source/strategy.rst index 0d636097..2b03966c 100644 --- a/docs/source/strategy.rst +++ b/docs/source/strategy.rst @@ -11,6 +11,12 @@ Hikyuu 主要聚焦于快速策略分析,本身不提供实盘交易,Strateg 具体可参见安装目录下的 strategy 子目录下的相关 demo。 +公共参数: + + * **spot_worker_num=1** *(int)* : 接收行情数据时内部的线程数 + * **quotation_server=""** *(string)* : 指定行情服务地址,为空表示使用本机默认配置(hikyuu.ini) + + .. py:class:: Strategy 策略运行时 diff --git a/xmake.lua b/xmake.lua index 7cdd5f58..3eb05e93 100644 --- a/xmake.lua +++ b/xmake.lua @@ -6,7 +6,7 @@ set_project("hikyuu") add_rules("mode.debug", "mode.release") -- version -set_version("2.1.3", {build = "%Y%m%d%H%M"}) +set_version("2.1.4", {build = "%Y%m%d%H%M"}) set_warnings("all") From 667a76de82245771b54b11150bcad033b0ec329c Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 18:51:22 +0800 Subject: [PATCH 578/601] update release date --- docs/source/release.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index 4e91ed67..ce05b85e 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -4,7 +4,7 @@ 版本发布说明 -------------- -2.1.4 - +2.1.4 - 2024年9月4日 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 1. fixed 分钟级别行情数据更新错误 From 7b7dcb9f60ea5f1e13008f3c7f8d4db28084f278 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 4 Sep 2024 19:48:18 +0800 Subject: [PATCH 579/601] =?UTF-8?q?fixed=20demo2=20=E5=9C=A8=20linux=20?= =?UTF-8?q?=E4=B8=8B=E6=89=A7=E8=A1=8C=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/demo2.cpp | 13 +++++++------ hikyuu_cpp/hikyuu/StockManager.cpp | 2 +- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 2 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/hikyuu_cpp/demo/demo2.cpp b/hikyuu_cpp/demo/demo2.cpp index 718dc713..2eed4110 100644 --- a/hikyuu_cpp/demo/demo2.cpp +++ b/hikyuu_cpp/demo/demo2.cpp @@ -48,7 +48,11 @@ int main(int argc, char* argv[]) { SetConsoleOutputCP(CP_UTF8); #endif - Strategy stg({"sh000001", "sz000001"}, {KQuery::DAY}, "test"); + // 以多线程的方式执行多个策略 + // 注意:同一进程内的所有 strategy 共享的是同一个上下文!!! + StrategyContext context({"sh000001", "sz000001"}, {KQuery::DAY}); + + Strategy stg(context, "test"); // stock 数据变化接收,通常用于调测,直接一般不需要 stg.onChange(changed); @@ -59,11 +63,8 @@ int main(int argc, char* argv[]) { // 每日定点执行 stg.runDailyAt(my_process2, Datetime::now() - Datetime::today() + Seconds(20)); - auto t = std::thread([]() { - // 以线程的方式执行另一个策略 - // 注意:同一进程内的所有 strategy 共享的是同一个上下文, - // 即使后续创建的 strategy 指定了新的 stock 列表,但不会生效!!! - Strategy stg2("stratege2"); + auto t = std::thread([context]() { + Strategy stg2(context, "stratege2"); stg2.onChange(changed2); stg2.start(); }); diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 413f2fca..16a433f1 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -57,7 +57,6 @@ StockManager& StockManager::instance() { void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockParam, const Parameter& kdataParam, const Parameter& preloadParam, const Parameter& hikyuuParam, const StrategyContext& context) { - HKU_CHECK(!context.empty(), "No stock code list is included in the context!"); HKU_WARN_IF_RETURN(m_initializing, void(), "The last initialization has not finished. Please try again later!"); @@ -67,6 +66,7 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa } m_initializing = true; m_thread_id = std::this_thread::get_id(); + HKU_CHECK(!context.empty(), "No stock code list is included in the context!"); m_baseInfoDriverParam = baseInfoParam; m_blockDriverParam = blockParam; diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 9737f9c2..ebb39ce5 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -176,7 +176,7 @@ void SpotAgent::work_thread() { std::this_thread::sleep_for(std::chrono::seconds(5)); } - HKU_INFO_IF(m_print, "Ready to receive quotation from {} ...", ms_pubUrl); + HKU_INFO_IF(!m_stop && m_print, "Ready to receive quotation from {} ...", ms_pubUrl); while (!m_stop) { char* buf = nullptr; diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index fb0474d7..b508c037 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -21,7 +21,7 @@ namespace hku { std::atomic_bool Strategy::ms_keep_running = true; void Strategy::sig_handler(int sig) { - if (sig == SIGINT) { + if (sig == SIGINT || sig == SIGTERM) { ms_keep_running = false; exit(0); } From 13bbbcb30b30ad7ee490e477c0ec78726dc4c803 Mon Sep 17 00:00:00 2001 From: KongDong Date: Thu, 5 Sep 2024 13:27:40 +0800 Subject: [PATCH 580/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20TradeManager=20che?= =?UTF-8?q?ckout=20=E7=B2=BE=E5=BA=A6=E5=88=A4=E6=96=AD=E6=9C=89=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp index f1e1be52..1736db73 100644 --- a/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp +++ b/hikyuu_cpp/hikyuu/trade_manage/TradeManager.cpp @@ -455,11 +455,12 @@ bool TradeManager::checkout(const Datetime& datetime, price_t cash) { int precision = getParam("precision"); price_t out_cash = roundEx(cash, precision); - HKU_ERROR_IF_RETURN(out_cash > m_cash, false, - "{} cash({:<.4f}) must be <= current cash({:<.4f})!", datetime, cash, - m_cash); - m_cash = roundEx(m_cash - out_cash, precision); + price_t tmp_cash = roundEx(m_cash - out_cash, precision); + HKU_ERROR_IF_RETURN(tmp_cash < 0.0, false, "{} cash({:<.4f}) must be <= current cash({:<.4f})!", + datetime, cash, m_cash); + + m_cash = tmp_cash; m_checkout_cash = roundEx(m_checkout_cash + out_cash, precision); m_trade_list.push_back(TradeRecord(Null(), datetime, BUSINESS_CHECKOUT, out_cash, out_cash, 0.0, 0, CostRecord(), 0.0, m_cash, PART_INVALID)); From 8021a1b808ff7cc8712121ef5b5109120532bed3 Mon Sep 17 00:00:00 2001 From: KongDong Date: Fri, 6 Sep 2024 12:22:14 +0800 Subject: [PATCH 581/601] update start_qmt.py --- hikyuu/gui/start_qmt.py | 78 ++++++++++++++++++++++++++++++++++++++- hikyuu_pywrap/_KQuery.cpp | 6 ++- 2 files changed, 81 insertions(+), 3 deletions(-) diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py index 3c8e5254..b8762f9f 100644 --- a/hikyuu/gui/start_qmt.py +++ b/hikyuu/gui/start_qmt.py @@ -19,7 +19,83 @@ def callback(datas): if __name__ == "__main__": - from hikyuu.interactive import * + import os + import configparser + + from hikyuu.data.hku_config_template import generate_default_config + from hikyuu import * + + config_file = os.path.expanduser('~') + "/.hikyuu/hikyuu.ini" + if not os.path.exists(config_file): + # 创建默认配置 + hku_info("创建默认配置文件") + generate_default_config() + + ini = configparser.ConfigParser() + ini.read(config_file, encoding='utf-8') + hku_param = Parameter() + hku_param["tmpdir"] = ini.get('hikyuu', 'tmpdir') + hku_param["datadir"] = ini.get('hikyuu', 'datadir') + if ini.has_option('hikyuu', 'quotation_server'): + hku_param["quotation_server"] = ini['hikyuu']['quotation_server'] + + # 不加载历史财务信息及权息数据 + hku_param["load_history_finance"] = False + hku_param["load_stock_weight"] = False + + base_param = Parameter() + base_info_config = ini.options('baseinfo') + for p in base_info_config: + base_param[p] = ini.get('baseinfo', p) + + block_param = Parameter() + block_config = ini.options('block') + for p in block_config: + block_param[p] = ini.get('block', p) + + # 不使用配置文件中的预加载参数 + preload_param = Parameter() + if p in Query.get_all_ktype(): + preload_param[p] = True + minute = Query.get_ktype_in_min() + if minute >= 240: + preload_param[f'{p.lower()}_max'] = 1 + else: + preload_param[f'{p.lower()}_max'] = 240 // minute + + kdata_param = Parameter() + kdata_config = ini.options('kdata') + for p in kdata_config: + if p == "convert": + kdata_param[p] = ini.getboolean('kdata', p) + continue + kdata_param[p] = ini.get('kdata', p) + + context = StrategyContext(["all"]) + if 'HKU_STOCK_LIST' in os.environ: + context.stock_list = os.environ['HKU_STOCK_LIST'].split(";") + if 'HKU_KTYPE_LIST' in os.environ: + context.ktype_list = os.environ['HKU_KTYPE_LIST'].split(";") + if 'HKU_LOAD_HISTORY_FINANCE' in os.environ: + load_str = os.environ['HKU_LOAD_HISTORY_FINANCE'].upper() + load_finance = load_str in ("1", "TRUE") + hku_param.set("load_history_finance", load_finance) + if 'HKU_LOAD_STOCK_WEIGHT' in os.environ: + load_str = os.environ['HKU_LOAD_STOCK_WEIGHT'].upper() + load_stk_weight = load_str in ("1", "TRUE") + hku_param.set("load_stock_weight", load_stk_weight) + + sm.init(base_param, block_param, kdata_param, preload_param, hku_param, context) + # set_log_level(LOG_LEVEL.INFO) + + start_spot = False + if 'HKU_START_SPOT' in os.environ: + spot_str = os.environ['HKU_START_SPOT'].upper() + start_spot = spot_str in ('1', 'TRUE') + spot_worker_num = 1 + if 'HKU_SPOT_WORKER_NUM' in os.environ: + spot_worker_num = int(os.environ['HKU_SPOT_WORKER_NUM']) + from xtquant import xtdata code_list = [f'{s.code}.{s.market}' for s in sm if s.valid] diff --git a/hikyuu_pywrap/_KQuery.cpp b/hikyuu_pywrap/_KQuery.cpp index 963663f6..25cb6ca2 100644 --- a/hikyuu_pywrap/_KQuery.cpp +++ b/hikyuu_pywrap/_KQuery.cpp @@ -27,8 +27,10 @@ void export_KQuery(py::module& m) { "结束日期,当按索引查询方式创建时无效") .def_property_readonly("query_type", &KQuery::queryType, "查询方式") .def_property_readonly("ktype", &KQuery::kType, "查询的K线类型") - .def_property_readonly("recover_type", - py::overload_cast<>(&KQuery::recoverType, py::const_), "复权类别") + .def_property_readonly("recover_type", py::overload_cast<>(&KQuery::recoverType, py::const_), + "复权类别") + .def("get_all_ktype", &KQuery::getAllKType, "获取所有KType") + .def("get_ktype_in_min", &KQuery::getKTypeInMin, "获取ktype对应的分钟数") DEF_PICKLE(KQuery); From d71ca6ef61951aeb2b5a208028424097371ca048 Mon Sep 17 00:00:00 2001 From: KongDong Date: Fri, 6 Sep 2024 13:26:11 +0800 Subject: [PATCH 582/601] update --- hikyuu/fetcher/stock/zh_stock_a_pytdx.py | 6 +++--- hikyuu/gui/start_qmt.py | 13 +++++-------- 2 files changed, 8 insertions(+), 11 deletions(-) diff --git a/hikyuu/fetcher/stock/zh_stock_a_pytdx.py b/hikyuu/fetcher/stock/zh_stock_a_pytdx.py index 16157bb9..4a5fa282 100644 --- a/hikyuu/fetcher/stock/zh_stock_a_pytdx.py +++ b/hikyuu/fetcher/stock/zh_stock_a_pytdx.py @@ -73,7 +73,7 @@ def request_data(api, stklist, parse_one_result): @hku_catch(ret=([], [])) -def get_spot(stocklist, ip, port, batch_func=None): +def inner_get_spot(stocklist, ip, port, batch_func=None): api = TdxHq_API() hku_check(api.connect(ip, port), 'Failed connect tdx ({}:{})!'.format(ip, port)) @@ -107,7 +107,7 @@ def get_spot(stocklist, ip, port, batch_func=None): @spend_time -def get_spot2(stocklist, ip, port, batch_func=None): +def get_spot(stocklist, ip, port, batch_func=None): hosts = search_best_tdx() hosts_cnt = len(hosts) num = len(stocklist) // hosts_cnt @@ -123,7 +123,7 @@ def get_spot2(stocklist, ip, port, batch_func=None): break def do_inner(param): - ret = get_spot(param[0], param[1], param[2], param[3]) + ret = inner_get_spot(param[0], param[1], param[2], param[3]) return ret with futures.ThreadPoolExecutor() as executor: diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py index b8762f9f..10161815 100644 --- a/hikyuu/gui/start_qmt.py +++ b/hikyuu/gui/start_qmt.py @@ -86,15 +86,12 @@ if __name__ == "__main__": hku_param.set("load_stock_weight", load_stk_weight) sm.init(base_param, block_param, kdata_param, preload_param, hku_param, context) - # set_log_level(LOG_LEVEL.INFO) - start_spot = False - if 'HKU_START_SPOT' in os.environ: - spot_str = os.environ['HKU_START_SPOT'].upper() - start_spot = spot_str in ('1', 'TRUE') - spot_worker_num = 1 - if 'HKU_SPOT_WORKER_NUM' in os.environ: - spot_worker_num = int(os.environ['HKU_SPOT_WORKER_NUM']) + hku_info("waiting all data loaded ...") + while not sm.data_ready: + import time + time.sleep(100) + hku_info("start xtquant") from xtquant import xtdata From 70c4878f7e498a0e4a5a29bd813dd051b494e15a Mon Sep 17 00:00:00 2001 From: KongDong Date: Fri, 6 Sep 2024 15:35:15 +0800 Subject: [PATCH 583/601] update start_qmt.py --- hikyuu/gui/start_qmt.py | 36 +++++++++++++++++++++++++++++------- 1 file changed, 29 insertions(+), 7 deletions(-) diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py index 10161815..2b71d235 100644 --- a/hikyuu/gui/start_qmt.py +++ b/hikyuu/gui/start_qmt.py @@ -2,15 +2,15 @@ # -*- coding: utf-8 -*- -from hikyuu.fetcher.stock.zh_stock_a_qmt import parse_one_result_qmt +from hikyuu.fetcher.stock.zh_stock_a_qmt import parse_one_result_qmt, get_spot from hikyuu.gui.spot_server import release_nng_senders, start_send_spot, end_send_spot, send_spot def callback(datas): records = [] for stock_code, data in datas.items(): - # print(stock_code, data) records.append(parse_one_result_qmt(stock_code, data)) + # print(len(records)) if records: start_send_spot() @@ -91,13 +91,35 @@ if __name__ == "__main__": while not sm.data_ready: import time time.sleep(100) + + def get_full(): + stk_list = [s for s in sm if s.valid and s.type in ( + constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, + constant.STOCKTYPE_GEM, constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ)] + start_send_spot() + records = get_spot(stk_list, None, None, send_spot) + end_send_spot() + + def timer_func(): + import threading + today = Datetime.today() + if today.day_of_week() not in (0, 6) and not sm.is_holiday(today): + get_full() + tomorrow = Datetime.today().next_day() + delta = Datetime.today().next_day() + TimeDelta(0, 9, 30) - Datetime.now() + timer = threading.Timer(delta.total_seconds(), timer_func) + timer.start() + return timer + + # 每日9:30先获取一次当天全部数据,以便生成分钟级别数据 + # 后续订阅更新因为只更新存在变化的数据,内部分钟级别数据可能时不连续的(如果分钟内不存在变化, 不会触发hikyuu更新) + timer = timer_func() + hku_info("start xtquant") - + code_list = [f'{s.code}.{s.market}' for s in sm if s.valid and s.type in ( + constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, + constant.STOCKTYPE_GEM, constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ)] from xtquant import xtdata - - code_list = [f'{s.code}.{s.market}' for s in sm if s.valid] - # code_list = ['000001.SZ'] - xtdata.subscribe_whole_quote(code_list, callback) try: From e15d4f2cb9c2564d516d31066c130879b87cf8ca Mon Sep 17 00:00:00 2001 From: KongDong Date: Fri, 6 Sep 2024 16:45:03 +0800 Subject: [PATCH 584/601] update start_qmt.py --- hikyuu/gui/start_qmt.py | 66 +++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py index 2b71d235..32d37f8b 100644 --- a/hikyuu/gui/start_qmt.py +++ b/hikyuu/gui/start_qmt.py @@ -87,45 +87,47 @@ if __name__ == "__main__": sm.init(base_param, block_param, kdata_param, preload_param, hku_param, context) + # 后续希望每次先主动获取一次全部的tick, 这里需要等待所有数据加载完毕,以便保证全部证券收到第一次tick通知 hku_info("waiting all data loaded ...") while not sm.data_ready: import time time.sleep(100) - def get_full(): - stk_list = [s for s in sm if s.valid and s.type in ( - constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, - constant.STOCKTYPE_GEM, constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ)] - start_send_spot() - records = get_spot(stk_list, None, None, send_spot) - end_send_spot() - - def timer_func(): - import threading - today = Datetime.today() - if today.day_of_week() not in (0, 6) and not sm.is_holiday(today): - get_full() - tomorrow = Datetime.today().next_day() - delta = Datetime.today().next_day() + TimeDelta(0, 9, 30) - Datetime.now() - timer = threading.Timer(delta.total_seconds(), timer_func) - timer.start() - return timer - - # 每日9:30先获取一次当天全部数据,以便生成分钟级别数据 - # 后续订阅更新因为只更新存在变化的数据,内部分钟级别数据可能时不连续的(如果分钟内不存在变化, 不会触发hikyuu更新) - timer = timer_func() - - hku_info("start xtquant") - code_list = [f'{s.code}.{s.market}' for s in sm if s.valid and s.type in ( + stk_list = [s for s in sm if s.valid and s.type in ( constant.STOCKTYPE_A, constant.STOCKTYPE_INDEX, constant.STOCKTYPE_ETF, constant.STOCKTYPE_GEM, constant.STOCKTYPE_START, constant.STOCKTYPE_A_BJ)] + + hku_info("start xtquant") + code_list = [f'{s.code}.{s.market}' for s in stk_list] from xtquant import xtdata xtdata.subscribe_whole_quote(code_list, callback) - try: - xtdata.run() - except Exception as e: - hku_error(e) - finally: - # 退出释放资源 - release_nng_senders() + # 每日 9:30 时,主动读取行情一次,以便 hikyuu 生成当日首个分钟线 + while True: + try: + today = Datetime.today() + if today.day_of_week() not in (0, 6) and not sm.is_holiday(today): + hku_info("get full tick ...") + start_send_spot() + records = get_spot(stk_list, None, None, send_spot) + end_send_spot() + delta = Datetime.today().next_day() + TimeDelta(0, 9, 30) - Datetime.now() + hku_info(f"start timer: {delta}s") + time.sleep(delta.total_seconds()) + except KeyboardInterrupt: + print("Ctrl-C 终止") + break + except Exception as e: + hku_error(e) + time.sleep(10) + + release_nng_senders() + + # try: + # xtdata.run() + # except Exception as e: + # hku_error(e) + # finally: + # # 退出释放资源 + # release_nng_senders() + # exit(0) From f2453e81f919e53a8679c1bacd701ca9e3ec20c3 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 9 Sep 2024 08:49:43 +0800 Subject: [PATCH 585/601] update start_qmt.py --- hikyuu/gui/start_qmt.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py index 32d37f8b..5ef64989 100644 --- a/hikyuu/gui/start_qmt.py +++ b/hikyuu/gui/start_qmt.py @@ -111,7 +111,12 @@ if __name__ == "__main__": start_send_spot() records = get_spot(stk_list, None, None, send_spot) end_send_spot() - delta = Datetime.today().next_day() + TimeDelta(0, 9, 30) - Datetime.now() + now = Datetime.now() + today_open = today + TimeDelta(0, 9, 30) + if now < today_open: + delta = today_open - Datetime.now() + else: + delta = today_open + Days(1) - Datetime.now() hku_info(f"start timer: {delta}s") time.sleep(delta.total_seconds()) except KeyboardInterrupt: From 9388d8e10f5d12d52bda8cb1b53373d74b86378f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 9 Sep 2024 09:05:02 +0800 Subject: [PATCH 586/601] update demo3 spot worker num --- hikyuu_cpp/demo/demo3.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp index ee1c0d28..4428d782 100644 --- a/hikyuu_cpp/demo/demo3.cpp +++ b/hikyuu_cpp/demo/demo3.cpp @@ -85,7 +85,7 @@ int main(int argc, char* argv[]) { hkuParam); // 启动行情接收 - startSpotAgent(true); + startSpotAgent(true, 3); server.setAddr("tcp://0.0.0.0:9201"); From bd24c02020df28b4e3f58f91c52eccc76cbce102 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 9 Sep 2024 09:10:13 +0800 Subject: [PATCH 587/601] update start_qmt --- hikyuu/gui/start_qmt.py | 20 ++------------------ 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/hikyuu/gui/start_qmt.py b/hikyuu/gui/start_qmt.py index 5ef64989..6e99ae7c 100644 --- a/hikyuu/gui/start_qmt.py +++ b/hikyuu/gui/start_qmt.py @@ -56,12 +56,7 @@ if __name__ == "__main__": # 不使用配置文件中的预加载参数 preload_param = Parameter() if p in Query.get_all_ktype(): - preload_param[p] = True - minute = Query.get_ktype_in_min() - if minute >= 240: - preload_param[f'{p.lower()}_max'] = 1 - else: - preload_param[f'{p.lower()}_max'] = 240 // minute + preload_param[p] = False kdata_param = Parameter() kdata_config = ini.options('kdata') @@ -72,18 +67,7 @@ if __name__ == "__main__": kdata_param[p] = ini.get('kdata', p) context = StrategyContext(["all"]) - if 'HKU_STOCK_LIST' in os.environ: - context.stock_list = os.environ['HKU_STOCK_LIST'].split(";") - if 'HKU_KTYPE_LIST' in os.environ: - context.ktype_list = os.environ['HKU_KTYPE_LIST'].split(";") - if 'HKU_LOAD_HISTORY_FINANCE' in os.environ: - load_str = os.environ['HKU_LOAD_HISTORY_FINANCE'].upper() - load_finance = load_str in ("1", "TRUE") - hku_param.set("load_history_finance", load_finance) - if 'HKU_LOAD_STOCK_WEIGHT' in os.environ: - load_str = os.environ['HKU_LOAD_STOCK_WEIGHT'].upper() - load_stk_weight = load_str in ("1", "TRUE") - hku_param.set("load_stock_weight", load_stk_weight) + context.ktype_list = ["day"] sm.init(base_param, block_param, kdata_param, preload_param, hku_param, context) From 754a96085056616fe913eace55faa939844468df Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 9 Sep 2024 11:26:28 +0800 Subject: [PATCH 588/601] =?UTF-8?q?fixed=20=E5=88=86=E9=92=9F=E7=BA=A7?= =?UTF-8?q?=E5=88=AB=E8=A1=8C=E6=83=85=E6=95=B0=E6=8D=AE=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp index 2307ce41..62343514 100644 --- a/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/GlobalSpotAgent.cpp @@ -144,7 +144,7 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { end_minute = close1; } - // 计算当前之前的累积成交金额、成交量 + // 计算当天之前的累积成交金额、成交量 KRecordList klist = stk.getKRecordList(KQuery(today, end_minute, ktype)); price_t sum_amount = 0.0, sum_volume = 0.0; for (const auto& k : klist) { @@ -157,7 +157,11 @@ static void updateStockMinData(const SpotRecord& spot, KQuery::KType ktype) { price_t spot_volume = spot.volume * 100.; // spot 传过来的是手数 price_t volume = spot_volume > sum_volume ? spot_volume - sum_volume : (sum_volume == 0.0 ? spot_volume : 0.0); - KRecord krecord(end_minute, spot.open, spot.high, spot.low, spot.close, amount, volume); + + // 采集时间间隔如果大于等于ktype,则对应的OHLC值均为 spot close + // 采集时间间隔小于ktype, 则多次采集后,其ktype对应值才会体现出 OHLC + // 开盘价、最高价、最低价都须使用当前 spot 收盘价(因为 spot 中的开高低均为当天值) + KRecord krecord(end_minute, spot.close, spot.close, spot.close, spot.close, amount, volume); stk.realtimeUpdate(krecord, ktype); } From 491d176380a9024b5c3729aeaf70dec5a89a66a2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Mon, 9 Sep 2024 23:00:33 +0800 Subject: [PATCH 589/601] fixed DMA --- hikyuu_cpp/hikyuu/indicator/crt/DMA.h | 8 +--- hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp | 59 ++++++++++++++++++++++++ hikyuu_cpp/hikyuu/indicator/imp/IDma.h | 47 +++++++++++++++++++ 3 files changed, 107 insertions(+), 7 deletions(-) create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp create mode 100644 hikyuu_cpp/hikyuu/indicator/imp/IDma.h diff --git a/hikyuu_cpp/hikyuu/indicator/crt/DMA.h b/hikyuu_cpp/hikyuu/indicator/crt/DMA.h index cb0dd17f..5ec05e6e 100644 --- a/hikyuu_cpp/hikyuu/indicator/crt/DMA.h +++ b/hikyuu_cpp/hikyuu/indicator/crt/DMA.h @@ -27,13 +27,7 @@ namespace hku { * @param a 动态系数 * @ingroup Indicator */ -Indicator DMA(const Indicator& ind1, const Indicator& a); - -inline Indicator DMA(const Indicator& ind1, const Indicator& a) { - Indicator dma = a * ind1 + (1 - a) * REF(ind1, 1); - dma.name("DMA"); - return dma; -} +Indicator HKU_API DMA(const Indicator& x, const Indicator& a); } // namespace hku diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp new file mode 100644 index 00000000..9a0bd224 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp @@ -0,0 +1,59 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-03-10 + * Author: fasiondog + */ + +#include "hikyuu/indicator/crt/ALIGN.h" +#include "IDma.h" + +#if HKU_SUPPORT_SERIALIZATION +BOOST_CLASS_EXPORT(hku::IDma) +#endif + +namespace hku { + +IDma::IDma() : IndicatorImp("DMA") {} + +IDma::IDma(const Indicator& ref_ind) : IndicatorImp("DMA"), m_ref_a(ref_ind) {} + +IDma::~IDma() {} + +void IDma::_checkParam(const string& name) const {} + +IndicatorImpPtr IDma::_clone() { + auto p = make_shared(); + p->m_ref_a = m_ref_a.clone(); + return p; +} + +void IDma::_calculate(const Indicator& ind) { + auto k = getContext(); + m_ref_a.setContext(k); + Indicator ref = m_ref_a; + if (m_ref_a.size() != ind.size()) { + ref = ALIGN(m_ref_a, ind); + } + + size_t total = ind.size(); + _readyBuffer(total, 1); + HKU_IF_RETURN(total == 0, void()); + + m_discard = std::max(ind.discard(), ref.discard()); + auto* y = this->data(); + const auto* a = ref.data(); + const auto* x = ind.data(); + y[m_discard] = x[m_discard]; + for (size_t i = m_discard + 1; i < total; i++) { + y[i] = a[i] * x[i] + (1 - a[i]) * y[i - 1]; + } +} + +Indicator HKU_API DMA(const Indicator& x, const Indicator& a) { + auto p = make_shared(a); + Indicator result(p); + return result(x); +} + +} // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDma.h b/hikyuu_cpp/hikyuu/indicator/imp/IDma.h new file mode 100644 index 00000000..062830c0 --- /dev/null +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDma.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024 hikyuu.org + * + * Created on: 2024-09-09 + * Author: fasiondog + */ + +#pragma once + +#include "../Indicator.h" + +namespace hku { + +/* + * 动态移动平均 + * 用法:DMA(X,A),求X的动态移动平均。 + * 算法:若Y=DMA(X,A) 则 Y=A*X+(1-A)*Y',其中Y'表示上一周期Y值。 + * 例如:DMA(CLOSE,VOL/CAPITAL)表示求以换手率作平滑因子的平均价 + */ +class IDma : public IndicatorImp { +public: + IDma(); + explicit IDma(const Indicator& ref_a); + virtual ~IDma(); + + virtual void _checkParam(const string& name) const override; + virtual void _calculate(const Indicator& data) override; + virtual IndicatorImpPtr _clone() override; + +private: + Indicator m_ref_a; + +//============================================ +// 序列化支持 +//============================================ +#if HKU_SUPPORT_SERIALIZATION +private: + friend class boost::serialization::access; + template + void serialize(Archive& ar, const unsigned int version) { + ar& BOOST_SERIALIZATION_BASE_OBJECT_NVP(IndicatorImp); + ar& BOOST_SERIALIZATION_NVP(m_ref_a); + } +#endif +}; + +} \ No newline at end of file From 4961f2484ebb1d14571c689a91396b0bd832a9a8 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 10 Sep 2024 16:18:48 +0800 Subject: [PATCH 590/601] =?UTF-8?q?update=20=E8=A1=A5=E5=85=85etf=E5=89=8D?= =?UTF-8?q?=E7=BC=80=E5=8F=8A=E9=94=99=E8=AF=AF=E6=9D=83=E6=81=AF=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E6=B8=85=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/data/mysql_upgrade/0022.sql | 5 +++++ hikyuu/data/sqlite_upgrade/0023.sql | 7 +++++++ 2 files changed, 12 insertions(+) create mode 100644 hikyuu/data/mysql_upgrade/0022.sql create mode 100644 hikyuu/data/sqlite_upgrade/0023.sql diff --git a/hikyuu/data/mysql_upgrade/0022.sql b/hikyuu/data/mysql_upgrade/0022.sql new file mode 100644 index 00000000..ad72217d --- /dev/null +++ b/hikyuu/data/mysql_upgrade/0022.sql @@ -0,0 +1,5 @@ +DELETE FROM `hku_base`.`stkWeight`; +UPDATE `hku_base`.`coderuletype` SET `codepre`=51 WHERE `codepre`=510 AND `marketid`=1; +UPDATE `hku_base`.`coderuletype` SET `codepre`=50 WHERE `codepre`=500 AND `marketid`=1; +UPDATE `hku_base`.`coderuletype` SET `codepre`=20 WHERE `codepre`=200 AND `marketid`=2; +UPDATE `hku_base`.`version` set `version` = 22; \ No newline at end of file diff --git a/hikyuu/data/sqlite_upgrade/0023.sql b/hikyuu/data/sqlite_upgrade/0023.sql new file mode 100644 index 00000000..e4ede423 --- /dev/null +++ b/hikyuu/data/sqlite_upgrade/0023.sql @@ -0,0 +1,7 @@ +BEGIN TRANSACTION; +DELETE FROM `stkWeight`; +UPDATE `coderuletype` SET `codepre`=51 WHERE `codepre`=510 AND `marketid`=1; +UPDATE `coderuletype` SET `codepre`=50 WHERE `codepre`=500 AND `marketid`=1; +UPDATE `coderuletype` SET `codepre`=20 WHERE `codepre`=200 AND `marketid`=2; +UPDATE `version` set `version` = 23; +COMMIT; \ No newline at end of file From 6f6597d917470270f447c2478b39061c9c9fe305 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Tue, 10 Sep 2024 18:10:57 +0800 Subject: [PATCH 591/601] fixded mysql_upgrade/0022.sql --- hikyuu/data/mysql_upgrade/0022.sql | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu/data/mysql_upgrade/0022.sql b/hikyuu/data/mysql_upgrade/0022.sql index ad72217d..3400c269 100644 --- a/hikyuu/data/mysql_upgrade/0022.sql +++ b/hikyuu/data/mysql_upgrade/0022.sql @@ -1,4 +1,4 @@ -DELETE FROM `hku_base`.`stkWeight`; +DELETE FROM `hku_base`.`stkweight`; UPDATE `hku_base`.`coderuletype` SET `codepre`=51 WHERE `codepre`=510 AND `marketid`=1; UPDATE `hku_base`.`coderuletype` SET `codepre`=50 WHERE `codepre`=500 AND `marketid`=1; UPDATE `hku_base`.`coderuletype` SET `codepre`=20 WHERE `codepre`=200 AND `marketid`=2; From 911311564ec8f7b3dbaef0f96ce9dc95edd45463 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 11 Sep 2024 07:48:57 +0800 Subject: [PATCH 592/601] Release 2.1.5 --- docs/source/release.rst | 9 +++++++++ xmake.lua | 2 +- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/source/release.rst b/docs/source/release.rst index ce05b85e..0c4cd7f4 100644 --- a/docs/source/release.rst +++ b/docs/source/release.rst @@ -4,6 +4,15 @@ 版本发布说明 -------------- +2.1.5 - 2024年9月11日 +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +1. fixed 补充 etf 前缀及扩股错误权息处理 +2. fixed 分钟级别行情数据更新错误 +3. fixed DMA 指标公式错误 +4. 优化 TradeManager checkout 操作精度判断 + + 2.1.4 - 2024年9月4日 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/xmake.lua b/xmake.lua index 3eb05e93..06f010c7 100644 --- a/xmake.lua +++ b/xmake.lua @@ -6,7 +6,7 @@ set_project("hikyuu") add_rules("mode.debug", "mode.release") -- version -set_version("2.1.4", {build = "%Y%m%d%H%M"}) +set_version("2.1.5", {build = "%Y%m%d%H%M"}) set_warnings("all") From 01f80a8bba5f326b219559487902a46731cefa02 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Wed, 11 Sep 2024 18:25:42 +0800 Subject: [PATCH 593/601] =?UTF-8?q?fixed=20portfolio=20=E6=89=93=E5=8D=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp index d5653ef4..daabbdb1 100644 --- a/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp +++ b/hikyuu_cpp/hikyuu/trade_sys/portfolio/Portfolio.cpp @@ -297,7 +297,7 @@ void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool // 强制卖出失败的情况下,如果当前仍有持仓,则需要下一交易日继续进行处理 PositionRecord position = sys.sys->getTM()->getPosition(date, sys.sys->getStock()); if (position.number > 0.0) { - HKU_INFO_IF("[{}] failed to force sell, delay to next day", name()); + HKU_INFO_IF(trace, "[{}] failed to force sell, delay to next day", name()); tmp_continue_adjust_sys_list.emplace_back(sys); } } From 39f8c5cb97791caf390061c36fb90d1660a165e1 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 12 Sep 2024 08:56:57 +0800 Subject: [PATCH 594/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20SpotAgent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 49 ++++++++++++++------ hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 1 + 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 9737f9c2..e011d787 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -38,6 +38,7 @@ void SpotAgent::start() { stop(); if (m_stop) { m_stop = false; + m_receive_data_tg = std::make_unique(1); m_tg = std::make_unique(m_work_num); m_receiveThread = std::thread([this]() { work_thread(); }); } @@ -45,12 +46,18 @@ void SpotAgent::start() { void SpotAgent::stop() { m_stop = true; + if (m_receive_data_tg) { + m_receive_data_tg->stop(); + } if (m_tg) { m_tg->stop(); } if (m_receiveThread.joinable()) { m_receiveThread.join(); } + if (m_receive_data_tg) { + m_receive_data_tg.reset(); + } if (m_tg) { m_tg.reset(); } @@ -140,17 +147,29 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { auto* spot_list = GetSpotList(spot_list_buf); auto* spots = spot_list->spot(); size_t total = spots->size(); - m_batch_count += total; + // m_batch_count += total; + vector> tasks; for (size_t i = 0; i < total; i++) { auto* spot = spots->Get(i); auto spot_record = parseFlatSpot(spot); if (spot_record) { for (const auto& process : m_processList) { - m_process_task_list.emplace_back(m_tg->submit(ProcessTask(process, *spot_record))); + tasks.emplace_back(m_tg->submit(ProcessTask(process, *spot_record))); } } } + for (auto& task : tasks) { + task.get(); + } + HKU_TRACE_IF(m_print, "received count: {}", total); + // m_batch_count = 0; + // 执行后处理 + for (const auto& postProcess : m_postProcessList) { + postProcess(ms_start_rev_time); + } + // m_process_task_list.clear(); + #if defined(_MSC_VER) #pragma warning(pop) #endif @@ -197,20 +216,24 @@ void SpotAgent::work_thread() { case RECEIVING: if (memcmp(buf, ms_endTag, ms_endTagLength) == 0) { m_status = WAITING; - for (auto& task : m_process_task_list) { - task.get(); - } - HKU_TRACE_IF(m_print, "received count: {}", m_batch_count); - m_batch_count = 0; - // 执行后处理 - for (const auto& postProcess : m_postProcessList) { - postProcess(ms_start_rev_time); - } - m_process_task_list.clear(); + // for (auto& task : m_process_task_list) { + // task.get(); + // } + // HKU_TRACE_IF(m_print, "received count: {}", m_batch_count); + // m_batch_count = 0; + // // 执行后处理 + // for (const auto& postProcess : m_postProcessList) { + // postProcess(ms_start_rev_time); + // } + // m_process_task_list.clear(); } else { HKU_CHECK(memcmp(buf, ms_startTag, ms_startTagLength) != 0, "Data not received in time, maybe the send speed is too fast!"); - parseSpotData(buf, length); + std::shared_ptr data_buf(new char[length]); + memcpy(data_buf.get(), buf, length); + m_receive_data_tg->submit([this, length, new_buf = std::move(data_buf)]() { + this->parseSpotData(new_buf.get(), length); + }); } break; } diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index d3231168..8c8c77f3 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -131,6 +131,7 @@ private: std::unique_ptr m_tg; // 数据处理任务线程池 size_t m_work_num = 1; // 数据处理任务线程池线程数 vector> m_process_task_list; + std::unique_ptr m_receive_data_tg; // 数据接收任务组 bool m_print = true; // 是否打印连接信息 string m_server_addr; // 服务器地址 From 5f1961973dea485092571beab1ed4986f3dc8669 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 12 Sep 2024 09:55:38 +0800 Subject: [PATCH 595/601] =?UTF-8?q?=E4=BC=98=E5=8C=96=20SpotAgent?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/demo/demo3.cpp | 2 +- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 20 ++------------------ hikyuu_cpp/hikyuu/global/agent/SpotAgent.h | 10 ++++------ 3 files changed, 7 insertions(+), 25 deletions(-) diff --git a/hikyuu_cpp/demo/demo3.cpp b/hikyuu_cpp/demo/demo3.cpp index 4428d782..07a9480d 100644 --- a/hikyuu_cpp/demo/demo3.cpp +++ b/hikyuu_cpp/demo/demo3.cpp @@ -85,7 +85,7 @@ int main(int argc, char* argv[]) { hkuParam); // 启动行情接收 - startSpotAgent(true, 3); + startSpotAgent(true, 2); server.setAddr("tcp://0.0.0.0:9201"); diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 5ed8b647..3277d286 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -147,7 +147,6 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { auto* spot_list = GetSpotList(spot_list_buf); auto* spots = spot_list->spot(); size_t total = spots->size(); - // m_batch_count += total; vector> tasks; for (size_t i = 0; i < total; i++) { auto* spot = spots->Get(i); @@ -163,12 +162,9 @@ void SpotAgent::parseSpotData(const void* buf, size_t buf_len) { task.get(); } HKU_TRACE_IF(m_print, "received count: {}", total); - // m_batch_count = 0; - // 执行后处理 for (const auto& postProcess : m_postProcessList) { postProcess(ms_start_rev_time); } - // m_process_task_list.clear(); #if defined(_MSC_VER) #pragma warning(pop) @@ -216,25 +212,13 @@ void SpotAgent::work_thread() { case RECEIVING: if (memcmp(buf, ms_endTag, ms_endTagLength) == 0) { m_status = WAITING; - // for (auto& task : m_process_task_list) { - // task.get(); - // } - // HKU_TRACE_IF(m_print, "received count: {}", m_batch_count); - // m_batch_count = 0; - // // 执行后处理 - // for (const auto& postProcess : m_postProcessList) { - // postProcess(ms_start_rev_time); - // } - // m_process_task_list.clear(); - } else { - HKU_CHECK(memcmp(buf, ms_startTag, ms_startTagLength) != 0, - "Data not received in time, maybe the send speed is too fast!"); + } else if (memcmp(buf, ms_startTag, ms_startTagLength) != 0) { std::shared_ptr data_buf(new char[length]); memcpy(data_buf.get(), buf, length); m_receive_data_tg->submit([this, length, new_buf = std::move(data_buf)]() { this->parseSpotData(new_buf.get(), length); }); - } + } // else {继续等待数据} break; } } catch (std::exception& e) { diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h index 8c8c77f3..df0bcfed 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.h @@ -125,12 +125,10 @@ private: enum STATUS m_status = WAITING; // 当前内部状态 std::atomic_bool m_stop = true; // 结束代理工作标识 - int m_revTimeout = 100; // 连接数据服务超时时长(毫秒) - size_t m_batch_count = 0; // 记录本次批次接收的数据数量 - std::thread m_receiveThread; // 数据接收线程 - std::unique_ptr m_tg; // 数据处理任务线程池 - size_t m_work_num = 1; // 数据处理任务线程池线程数 - vector> m_process_task_list; + int m_revTimeout = 100; // 连接数据服务超时时长(毫秒) + std::thread m_receiveThread; // 数据接收线程 + std::unique_ptr m_tg; // 数据处理任务线程池 + size_t m_work_num = 1; // 数据处理任务线程池线程数 std::unique_ptr m_receive_data_tg; // 数据接收任务组 bool m_print = true; // 是否打印连接信息 From 1b477f6664e12a829714f271787b3b028fb8cfb7 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 12 Sep 2024 10:02:34 +0800 Subject: [PATCH 596/601] update --- hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp index 3277d286..c915d043 100644 --- a/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp +++ b/hikyuu_cpp/hikyuu/global/agent/SpotAgent.cpp @@ -216,7 +216,13 @@ void SpotAgent::work_thread() { std::shared_ptr data_buf(new char[length]); memcpy(data_buf.get(), buf, length); m_receive_data_tg->submit([this, length, new_buf = std::move(data_buf)]() { - this->parseSpotData(new_buf.get(), length); + try { + this->parseSpotData(new_buf.get(), length); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR_UNKNOWN; + } }); } // else {继续等待数据} break; From 9f847f0a63e3ff9a76f47ca60a24875f95cc1cb2 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Thu, 12 Sep 2024 14:32:34 +0800 Subject: [PATCH 597/601] =?UTF-8?q?Strategy=20=E6=94=AF=E6=8C=81=E6=8C=87?= =?UTF-8?q?=E5=AE=9A=E5=A4=9A=E4=B8=AA=E6=97=B6=E9=97=B4=E7=82=B9=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy_demo1.py | 3 ++- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 18 ++++++++++++------ hikyuu_cpp/hikyuu/strategy/Strategy.h | 3 +-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/hikyuu/strategy/strategy_demo1.py b/hikyuu/strategy/strategy_demo1.py index 0be90e9a..c69f1e7c 100644 --- a/hikyuu/strategy/strategy_demo1.py +++ b/hikyuu/strategy/strategy_demo1.py @@ -39,8 +39,9 @@ if __name__ == '__main__': # 如需使用交易日历,请记得同时指定 sh000001 s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) - # 当前自动10秒后执行,忽略节假日限制 + # 当前自动延迟10秒/20秒后执行,忽略节假日限制 s.run_daily_at(my_func1, Datetime.now() - Datetime.today() + Seconds(10), False) + s.run_daily_at(my_func1, Datetime.now() - Datetime.today() + Seconds(20), False) # 收到指定 stock 的行情更新 s.on_change(on_change) diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index f6e58a17..e5fba940 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -268,10 +268,13 @@ void Strategy::_runDaily() { void Strategy::runDailyAt(std::function&& func, const TimeDelta& delta, bool ignoreHoliday) { HKU_CHECK(func, "Invalid func!"); - m_run_daily_at_delta = delta; + HKU_CHECK(delta < Days(1), "TimeDelta must < Days(1)!"); + HKU_CHECK(m_run_daily_at_funcs.find(delta) == m_run_daily_at_funcs.end(), + "A task already exists at this point in time!"); + std::function new_func; if (ignoreHoliday) { - m_run_daily_at_func = [=]() { + new_func = [=]() { const auto& sm = StockManager::instance(); auto today = Datetime::today(); int day = today.dayOfWeek(); @@ -281,15 +284,18 @@ void Strategy::runDailyAt(std::function&& func, const TimeDelta& delta, }; } else { - m_run_daily_at_func = [=]() { event(func); }; + new_func = [=]() { event(func); }; } + + m_run_daily_at_funcs[delta] = new_func; } void Strategy::_runDailyAt() { - if (m_run_daily_at_func) { - auto* scheduler = getScheduler(); - scheduler->addFuncAtTimeEveryDay(m_run_daily_at_delta, m_run_daily_at_func); + auto* scheduler = getScheduler(); + for (const auto& [time, func] : m_run_daily_at_funcs) { + scheduler->addFuncAtTimeEveryDay(time, func); } + m_run_daily_at_funcs.clear(); } /* diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index ab391f80..e0ff1a96 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -106,8 +106,7 @@ private: string m_run_daily_market; bool m_ignoreMarket{false}; - std::function m_run_daily_at_func; - TimeDelta m_run_daily_at_delta; + std::map> m_run_daily_at_funcs; private: void _initParam(); From ebc0356c09fee24548506e942bdc86a616fb54f0 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 13 Sep 2024 10:32:59 +0800 Subject: [PATCH 598/601] =?UTF-8?q?=E8=A7=A3=E5=86=B3=20hub=20=E4=B8=AD?= =?UTF-8?q?=E8=87=AA=E5=AE=9A=E4=B9=89=E7=BB=A7=E6=89=BF=E7=B1=BB=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3=E5=9C=A8=E5=8F=A6=E4=B8=80=E4=B8=AApart=E4=B8=AD?= =?UTF-8?q?=E5=BC=95=E7=94=A8=E6=97=B6=E4=B8=A2=E5=A4=B1=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_sys/trade_sys.py | 37 ++++++++++++++++++++++++++--------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/hikyuu/trade_sys/trade_sys.py b/hikyuu/trade_sys/trade_sys.py index 17f2b5d3..6945cb7f 100644 --- a/hikyuu/trade_sys/trade_sys.py +++ b/hikyuu/trade_sys/trade_sys.py @@ -59,7 +59,10 @@ def crtCN(func, params={}, name='crtCN'): """ meta_x = type(name, (ConditionBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func - return meta_x(name, params) + # 强制引入全局空间,避免 hub 使用是自定义继承丢失虚拟函数接口 + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -76,7 +79,9 @@ def crtEV(func, params={}, name='crtEV'): """ meta_x = type(name, (EnvironmentBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -101,7 +106,9 @@ def crtMM(get_buy_num, get_sell_num, params={}, name='crtMM', buy_notify=None, s meta_x._buy_notify = buy_notify if sell_notify is not None: meta_x._sell_notify = sell_notify - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -118,7 +125,9 @@ def crtPG(func, params={}, name='crtPG'): """ meta_x = type(name, (ProfitGoalBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -156,7 +165,9 @@ def crtSE(calculate, get_selected, is_match_af=None, params={}, name='crtSE'): meta_x._calculate = calculate 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 - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -173,7 +184,9 @@ def crtAF(allocate_weight_func, params={}, name='crtAF'): """ meta_x = type(name, (AllocateFundsBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._allocate_weight = allocate_weight_func - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -190,7 +203,9 @@ def crtMF(calculate_func, params={}, name='crtMF'): """ meta_x = type(name, (MultiFactorBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = calculate_func - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -212,7 +227,9 @@ def crtSP(get_real_buy_price, get_real_sell_price, params={}, name='crtSP', calc meta_x.get_real_sell_price = get_real_sell_price if calculate is not None: meta_x._calculate = calculate - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret # ------------------------------------------------------------------ @@ -229,4 +246,6 @@ def crtST(func, params={}, name='crtST'): """ meta_x = type(name, (StoplossBase, ), {'__init__': part_init, '_clone': part_clone}) meta_x._calculate = func - return meta_x(name, params) + ret = meta_x(name, params) + globals().update(dict(_=ret)) + return ret From 0828b86d5dc693b7fd48f3c957cb9ab6dfc1c63f Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 13 Sep 2024 10:33:39 +0800 Subject: [PATCH 599/601] =?UTF-8?q?=E5=BC=95=E5=87=BAadd=5Fsys,add=5Fsys?= =?UTF-8?q?=5Flist=E6=8E=A5=E5=8F=A3=E5=BE=85=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_pywrap/trade_sys/_Selector.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/hikyuu_pywrap/trade_sys/_Selector.cpp b/hikyuu_pywrap/trade_sys/_Selector.cpp index 3fe3f189..d7376cdc 100644 --- a/hikyuu_pywrap/trade_sys/_Selector.cpp +++ b/hikyuu_pywrap/trade_sys/_Selector.cpp @@ -151,6 +151,9 @@ void export_Selector(py::module& m) { :return: 选取的系统实例列表 :rtype: SystemList)") + .def("add_sys", &SelectorBase::addSystem) + .def("add_sys_list", &SelectorBase::addSystemList) + .def("__add__", [](const SelectorPtr& self, const SelectorPtr& other) { return self + other; }) .def("__add__", [](const SelectorPtr& self, double other) { return self + other; }) From 464dc364104616eeee96e9b06bbb3651054b6fb4 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Fri, 13 Sep 2024 15:18:17 +0800 Subject: [PATCH 600/601] =?UTF-8?q?=E4=BC=98=E5=8C=96DMA=E5=92=8CINSUM,?= =?UTF-8?q?=E5=A4=84=E7=90=86=20nan=20=E5=92=8C=20discard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp | 6 +++++- hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp index 9a0bd224..08d614e3 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IDma.cpp @@ -46,7 +46,11 @@ void IDma::_calculate(const Indicator& ind) { const auto* x = ind.data(); y[m_discard] = x[m_discard]; for (size_t i = m_discard + 1; i < total; i++) { - y[i] = a[i] * x[i] + (1 - a[i]) * y[i - 1]; + if (std::isnan(y[i - 1])) { + y[i] = x[i]; + } else { + y[i] = a[i] * x[i] + (1 - a[i]) * y[i - 1]; + } } } diff --git a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp index 4fca6bce..1678d8ef 100644 --- a/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp +++ b/hikyuu_cpp/hikyuu/indicator/imp/IInSum.cpp @@ -195,6 +195,13 @@ void IInSum::_calculate(const Indicator& ind) { } else { HKU_ERROR("Not support mode: {}", mode); } + + for (size_t i = m_discard; i < total; i++) { + if (!std::isnan(dst[i])) { + break; + } + m_discard++; + } } Indicator HKU_API INSUM(const Block& block, const KQuery& query, const Indicator& ind, int mode) { From cfbb00818041dba41fbd2d09015da57f7bae9c91 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sat, 14 Sep 2024 10:42:42 +0800 Subject: [PATCH 601/601] =?UTF-8?q?=E5=B1=8F=E8=94=BD=E5=85=B3=E9=94=AE?= =?UTF-8?q?=E8=AF=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/source/quickstart.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/quickstart.rst b/docs/source/quickstart.rst index 75627909..a6c0b649 100644 --- a/docs/source/quickstart.rst +++ b/docs/source/quickstart.rst @@ -85,7 +85,7 @@ Jupyter notebook(此前被称为 IPython notebook)是一个基于web的交 利用 Jupyter notebook 搭建自己的云量化平台 ------------------------------------------- -搭建自己的云量化平台,首先需要拥有一个可以从外网访问的服务器,可以自行购买云服务器(如阿里云、腾讯云等)。之后需要对 Jupyter notebook 进行配置,使其能够远程进行访问,配置方法如下: +搭建自己的云量化平台,首先需要拥有一个可以从公网访问的服务器,可以自行购买云服务器(如阿里云、腾讯云等)。之后需要对 Jupyter notebook 进行配置,使其能够远程进行访问,配置方法如下: 1. 登陆远程服务器 2. 生成配置文件,在 cmd 下,键入如下命令: