Merge pull request #286 from fasiondog/feature/spot

优化数据加载
This commit is contained in:
fasiondog 2024-08-31 10:54:15 +08:00 committed by GitHub
commit baf3f2a0b6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 364 additions and 376 deletions

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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']
@ -114,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)
# 启动行情接收代理

View File

@ -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,11 +85,16 @@ void GlobalInitializer::clean() {
#endif
releaseScheduler();
releaseGlobalTaskGroup();
releaseGlobalSpotAgent();
IndicatorImp::releaseDynEngine();
// 主动停止异步数据加载任务组,否则 hdf5 在 linux 下会报关闭异常
auto *tg = StockManager::instance().getLoadTaskGroup();
if (tg) {
tg->stop();
}
#if HKU_ENABLE_LEAK_DETECT || defined(MSVC_LEAKER_DETECT)
// 非内存泄漏检测时,内存让系统自动释放,避免某些场景下 windows 下退出速度过慢
StockManager::quit();

View File

@ -55,7 +55,7 @@ static const unordered_map<string, int32_t> g_ktype2min{
};
// 获取所有的 KType
vector<string>& KQuery::getAllKType() {
const vector<KQuery::KType>& KQuery::getAllKType() {
return g_all_ktype;
}

View File

@ -70,7 +70,7 @@ public:
// static const string INVALID_KTYPE;
/** 获取所有的 KType */
static vector<string>& getAllKType();
static const vector<KType>& getAllKType();
static int32_t getKTypeInMin(KType);
@ -93,7 +93,7 @@ public:
m_end(Null<int64_t>()),
m_queryType(INDEX),
m_dataType(DAY),
m_recoverType(NO_RECOVER){};
m_recoverType(NO_RECOVER) {};
/**
* K线查询[start, end)

View File

@ -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) {
@ -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);
}
}

View File

@ -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;
@ -55,44 +54,6 @@ StockManager& StockManager::instance() {
return (*m_sm);
}
Parameter default_preload_param() {
Parameter param;
param.set<bool>("day", true);
param.set<bool>("week", false);
param.set<bool>("month", false);
param.set<bool>("quarter", false);
param.set<bool>("halfyear", false);
param.set<bool>("year", false);
param.set<bool>("min", false);
param.set<bool>("min5", false);
param.set<bool>("min15", false);
param.set<bool>("min30", false);
param.set<bool>("min60", false);
param.set<bool>("hour2", false);
param.set<bool>("ticks", false);
param.set<int>("day_max", 100000);
param.set<int>("week_max", 100000);
param.set<int>("month_max", 100000);
param.set<int>("quarter_max", 100000);
param.set<int>("halfyear_max", 100000);
param.set<int>("year_max", 100000);
param.set<int>("min_max", 5120);
param.set<int>("min5_max", 5120);
param.set<int>("min15_max", 5120);
param.set<int>("min30_max", 5120);
param.set<int>("min60_max", 5120);
param.set<int>("hour2_max", 5120);
param.set<int>("ticks_max", 5120);
return param;
}
Parameter default_other_param() {
Parameter param;
param.set<string>("tmpdir", ".");
param.set<string>("logger", "");
return param;
}
void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockParam,
const Parameter& kdataParam, const Parameter& preloadParam,
const Parameter& hikyuuParam, const StrategyContext& context) {
@ -125,72 +86,90 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa
m_baseInfoDriver = DataDriverFactory::getBaseInfoDriver(baseInfoParam);
HKU_CHECK(m_baseInfoDriver, "Failed get base info driver!");
loadAllHolidays();
loadAllMarketInfos();
loadAllStockTypeInfo();
loadAllStocks();
loadAllStockWeights();
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();
loadData();
initInnerTask();
// add special Market, for temp csv file
m_marketInfoDict["TMP"] =
MarketInfo("TMP", "Temp Csv file", "temp load from csv file", "000001", Null<Datetime>(),
TimeDelta(0), TimeDelta(0), TimeDelta(0), TimeDelta(0));
std::chrono::duration<double> 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::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();
loadAllMarketInfos();
// loadAllStockTypeInfo();
loadAllStocks();
loadAllStockWeights();
// loadAllZhBond10();
// loadHistoryFinanceField();
HKU_INFO("Loading block...");
// m_blockDriver->load();
tg.submit([this]() { this->m_blockDriver->load(); });
// 获取K线数据驱动并预加载指定的数据
HKU_INFO("Loading KData...");
auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam);
// 加载K线及历史财务信息
loadAllKData();
tg.join();
std::chrono::duration<double> sec = std::chrono::system_clock::now() - start_time;
HKU_INFO("{:<.2f}s Loaded Data.", sec.count());
}
void StockManager::loadAllKData() {
const auto& ktypes = KQuery::getAllKType();
// 按 K 线类型控制加载顺序
vector<KQuery::KType> 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<KQuery::KType> ktypes;
vector<string> 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);
to_lower(back);
HKU_INFO_IF(m_preloadParam.tryGet<bool>(back, false), "Preloading all {} kdata to buffer!",
HKU_INFO_IF(m_preloadParam.tryGet<bool>(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 (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
const auto& low_ktype = low_ktypes[i];
if (m_preloadParam.tryGet<bool>(low_ktype, false)) {
iter->second.loadKDataToBuffer(ktypes[i]);
@ -198,18 +177,44 @@ void StockManager::loadAllKData() {
}
}
if (m_hikyuuParam.tryGet<bool>("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 (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
std::thread t = std::thread([this, ktypes, low_ktypes]() {
this->m_load_tg = std::make_unique<ThreadPool>();
for (size_t i = 0, len = ktypes.size(); i < len; i++) {
const auto& low_ktype = low_ktypes[i];
if (m_preloadParam.tryGet<bool>(low_ktype, false)) {
tg->submit(
[=, ktype = ktypes[i]]() mutable { iter->second.loadKDataToBuffer(ktype); });
std::lock_guard<std::mutex> lock(*m_stockDict_mutex);
for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
if (m_preloadParam.tryGet<bool>(low_ktypes[i], false)) {
m_load_tg->submit(
[stk = iter->second, ktype = std::move(ktypes[i])]() mutable {
stk.loadKDataToBuffer(ktype);
});
}
}
}
if (m_hikyuuParam.tryGet<bool>("load_history_finance", true)) {
std::lock_guard<std::mutex> lock(*m_stockDict_mutex);
for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
m_load_tg->submit([stk = iter->second]() { stk.getHistoryFinance(); });
}
}
m_load_tg->join();
m_load_tg.reset();
m_data_ready = true;
});
t.detach();
}
}
@ -217,50 +222,8 @@ void StockManager::reload() {
HKU_IF_RETURN(m_initializing, void());
m_initializing = true;
loadAllHolidays();
loadAllMarketInfos();
loadAllStockTypeInfo();
loadAllStocks();
loadAllStockWeights();
loadAllZhBond10();
loadHistoryFinanceField();
m_blockDriver->load();
HKU_INFO("start reload kdata to buffer");
std::vector<Stock> can_not_parallel_stk_list; // 记录不支持并行加载的Stock
{
auto* tg = getGlobalTaskGroup();
std::lock_guard<std::mutex> 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;
}
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);
}
}
}
loadHistoryFinance();
HKU_INFO("start reload ...");
loadData();
m_initializing = false;
}
@ -420,9 +383,9 @@ void StockManager::loadAllStocks() {
if (m_context.isAll()) {
stockInfos = m_baseInfoDriver->getAllStockInfo();
} else {
const vector<string>& 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) {
@ -439,6 +402,8 @@ void StockManager::loadAllStocks() {
}
}
auto kdriver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam);
std::lock_guard<std::mutex> lock(*m_stockDict_mutex);
for (auto& info : stockInfos) {
Datetime startDate, endDate;
@ -452,15 +417,17 @@ void StockManager::loadAllStocks() {
} catch (...) {
endDate = Null<Datetime>();
}
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) {
@ -483,6 +450,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);
}
}
}
}
@ -498,6 +468,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<Datetime>(),
TimeDelta(0), TimeDelta(0), TimeDelta(0), TimeDelta(0));
}
void StockManager::loadAllStockTypeInfo() {
@ -518,6 +493,7 @@ void StockManager::loadAllHolidays() {
}
void StockManager::loadAllStockWeights() {
HKU_IF_RETURN(!m_hikyuuParam.tryGet<bool>("load_stock_weight", true), void());
HKU_INFO("Loading stock weight...");
if (m_context.isAll()) {
auto all_stkweight_dict = m_baseInfoDriver->getAllStockWeightList();
@ -549,11 +525,13 @@ void StockManager::loadAllZhBond10() {
}
void StockManager::loadHistoryFinanceField() {
if (m_hikyuuParam.tryGet<bool>("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;
}
}
}
vector<std::pair<size_t, string>> StockManager::getHistoryFinanceAllFields() const {
@ -568,12 +546,4 @@ vector<std::pair<size_t, string>> StockManager::getHistoryFinanceAllFields() con
return ret;
}
void StockManager::loadHistoryFinance() {
auto* tg = getGlobalTaskGroup();
std::lock_guard<std::mutex> lock1(*m_stockDict_mutex);
for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
tg->submit([=]() { iter->second.getHistoryFinance(); });
}
}
} // namespace hku

View File

@ -10,8 +10,9 @@
#include <mutex>
#include <thread>
#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"
@ -21,9 +22,6 @@ namespace hku {
typedef vector<string> MarketList;
Parameter default_preload_param();
Parameter default_other_param();
/**
*
* @ingroup StockManage
@ -47,8 +45,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"}));
/** 重新加载 */
@ -90,6 +88,9 @@ public:
/** 获取证券数量 */
size_t size() const;
/** 是否所有数据准备完毕 */
bool dataReady() const;
/**
* "市场简称证券代码"
* @param querystr "sh000001"
@ -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 {
@ -228,8 +234,8 @@ public:
}
private:
/* 设置K线驱动 */
void setKDataDriver(const KDataDriverConnectPoolPtr&);
/* 加载全部数据 */
void loadData();
/* 加载 K线数据至缓存 */
void loadAllKData();
@ -255,15 +261,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;
@ -295,12 +299,18 @@ private:
Parameter m_preloadParam;
Parameter m_hikyuuParam;
StrategyContext m_context;
std::unique_ptr<ThreadPool> m_load_tg; // 异步数据加载辅助线程组
};
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);
}

View File

@ -21,12 +21,6 @@ void StrategyContext::setKTypeList(const vector<KQuery::KType>& 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<string> StrategyContext::getAllNeedLoadStockCodeList() const {
vector<string> ret{m_stockCodeList};
for (const auto& code : m_mustLoad) {
ret.push_back(code);
}
return ret;
}
} // namespace hku

View File

@ -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<string>& getMustLoadStockCodeList() const {
return m_mustLoad;
}
vector<string> getAllNeedLoadStockCodeList() const;
private:
Datetime m_startDatetime{19901219};
vector<string> m_mustLoad{"sh000001", "sh000300"}; // 默认必须加载的 stock
vector<string> m_stockCodeList;
vector<KQuery::KType> m_ktypeList;
};

View File

@ -7,7 +7,6 @@
#include <boost/algorithm/string.hpp>
#include "../StockManager.h"
#include "../global/GlobalTaskGroup.h"
#include "BaseInfoDriver.h"
namespace hku {

View File

@ -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;
}
@ -37,8 +37,8 @@ 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.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);
stk.realtimeUpdate(krecord, KQuery::DAY);
@ -46,8 +46,8 @@ 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.isNull() || !stk.isBuffer(ktype), void());
HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void());
std::function<Datetime(Datetime*)> endOfPhase;
std::function<Datetime(Datetime*)> startOfPhase;
@ -120,8 +120,8 @@ 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.isNull() || !stk.isBuffer(ktype), void());
HKU_IF_RETURN(!stk.isTransactionTime(spot.datetime), void());
TimeDelta gap;
if (KQuery::MIN == ktype) {
@ -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,15 +165,15 @@ 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);
}
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<string>("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);

View File

@ -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,8 +24,8 @@ 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();
void HKU_API releaseGlobalSpotAgent();
} // namespace hku

View File

@ -1,41 +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"
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--;
}
g_threadPool = new TaskGroup(cpu_num);
});
return g_threadPool;
}
void releaseGlobalTaskGroup() {
HKU_TRACE("releaseGlobalTaskGroup");
if (g_threadPool) {
g_threadPool->stop();
delete g_threadPool;
g_threadPool = nullptr;
}
}
} /* namespace hku */

View File

@ -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 <typename ResultType>
using task_handle = std::future<ResultType>;
/**
*
*/
template <typename FunctionType>
task_handle<typename std::result_of<FunctionType()>::type> addTask(FunctionType f) {
return getGlobalTaskGroup()->submit(f);
}
/**
* 退
*/
void releaseGlobalTaskGroup();
} /* namespace hku */
#endif /* HKU_GLOBAL_TASK_GROUP */

View File

@ -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(size_t worker_num) {
m_tg = std::make_unique<ThreadPool>(worker_num);
}
SpotAgent::~SpotAgent() {
stop();
}
void SpotAgent::setQuotationServer(const string& server) {
ms_pubUrl = server;
}
void SpotAgent::start() {
stop();
if (m_stop) {
@ -128,7 +132,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 +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.push_back(m_tg.submit(ProcessTask(process, *spot_record)));
m_process_task_list.emplace_back(m_tg->submit(ProcessTask(process, *spot_record)));
}
}
}

View File

@ -27,7 +27,7 @@ namespace hku {
*/
class HKU_API SpotAgent {
public:
SpotAgent() = default;
SpotAgent(size_t worker_num);
/** 析构函数 */
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<ThreadPool> m_tg;
vector<std::future<void>> m_process_task_list;
// 下面属性被修改时需要加锁,以便可以使用多线程方式运行 strategy

View File

@ -7,7 +7,6 @@
#include "inner_tasks.h"
#include "scheduler.h"
#include "../GlobalTaskGroup.h"
#include "../../StockManager.h"
namespace hku {

View File

@ -8,7 +8,6 @@
#include "hikyuu/GlobalInitializer.h"
#include <mutex>
#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;
}

View File

@ -21,32 +21,37 @@ 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<bool>(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<string>("tmpdir", config.get("hikyuu", "tmpdir", "."));
hkuParam.set<string>("datadir", config.get("hikyuu", "datadir", "."));
hkuParam.set<string>("quotation_server",
config.get("hikyuu", "quotation_server", "ipc:///tmp/hikyuu_real.ipc"));
// 加载权息数据
hkuParam.set<bool>("load_stock_weight", config.getBool("hikyuu", "load_stock_weight", "True"));
if (!config.hasSection("baseinfo")) {
HKU_FATAL("Missing configure of baseinfo!");
exit(1);
}
// 加载历史财务数据
hkuParam.set<bool>("load_history_finance",
config.getBool("hikyuu", "load_history_finance", "True"));
IniParser::StringListPtr option = config.getOptionList("baseinfo");
for (auto iter = option->begin(); iter != option->end(); ++iter) {
@ -69,26 +74,14 @@ void hikyuu_init(const string& config_file_name, bool ignore_preload,
kdataParam.set<string>(*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<bool>(*iter,
ignore_preload ? false : config.getBool("preload", *iter));
} else if (!ignore_preload) {
preloadParam.set<int>(*iter, config.getInt("preload", *iter));
const auto& ktypes = KQuery::getAllKType();
for (const auto& ktype : ktypes) {
string low_ktype = ktype;
to_lower(low_ktype);
preloadParam.set<bool>(low_ktype, config.getBool("preload", low_ktype, "False"));
string num_preload = fmt::format("{}_max", low_ktype);
preloadParam.set<int>(num_preload, config.getInt("preload", num_preload, "4096"));
}
} catch (const std::exception& e) {
HKU_ERROR("proload param ({}) error! {}!", *iter, e.what());
} catch (...) {
HKU_ERROR("proload param ({})! Unknown error!", *iter);
}
}
StockManager& sm = StockManager::instance();
sm.init(baseParam, blockParam, kdataParam, preloadParam, hkuParam, context);
}
} // namespace hku

View File

@ -35,6 +35,19 @@ 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);
/** @} */
} // namespace hku

View File

@ -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;
@ -102,19 +93,28 @@ void Strategy::_init() {
stopSpotAgent();
}
void Strategy::start() {
void Strategy::start(bool autoRecieveSpot) {
_init();
_runDailyAt();
auto& agent = *getGlobalSpotAgent();
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(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); });
event([=]() { m_on_recieved_spot(revTime); });
}
});
startSpotAgent(true);
}
_runDaily();
@ -136,7 +136,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 +149,7 @@ void Strategy::runDaily(std::function<void()>&& 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 +167,7 @@ void Strategy::runDaily(std::function<void()>&& 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 +264,12 @@ void Strategy::runDailyAt(std::function<void()>&& 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); };
}
}
@ -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<bool>(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<string>());
const auto& jdata = res["data"];
// HKU_INFO("{}", to_string(jdata));
for (auto iter = jdata.cbegin(); iter != jdata.cend(); ++iter) {
const auto& r = *iter;
try {

View File

@ -28,6 +28,7 @@ namespace hku {
*/
class HKU_API Strategy {
CLASS_LOGGER_IMP(Strategy)
PARAMETER_SUPPORT
public:
Strategy();
@ -88,7 +89,7 @@ public:
/**
*
*/
void start();
void start(bool autoRecieveSpot = true);
private:
string m_name;

View File

@ -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<System*, SYSPtr> 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();

View File

@ -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 <class Archive>
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 <class Archive>
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
};

View File

@ -36,12 +36,29 @@ private:
//============================================
#if HKU_SUPPORT_SERIALIZATION
friend class boost::serialization::access;
// template <class Archive>
// 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 <class Archive>
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 <class Archive>
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
};

View File

@ -20,8 +20,8 @@ TEST_CASE("test_StrategyContext") {
sc.setKTypeList(
{KQuery::MONTH, KQuery::MIN5, KQuery::DAY, KQuery::MIN, KQuery::WEEK, KQuery::MIN60});
vector<KQuery::KType> expect{KQuery::MIN, KQuery::MIN5, KQuery::MIN60,
KQuery::DAY, KQuery::WEEK, KQuery::MONTH};
vector<KQuery::KType> 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]);

View File

@ -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

View File

@ -32,9 +32,16 @@ void export_StrategeContext(py::module& m) {
.def(py::init<const vector<string>&, const vector<KQuery::KType>&>(), 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<string>(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<string>(ktype_list));
},
"需要的K线类型");
}

View File

@ -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);
}

View File

@ -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!");