mirror of
https://gitee.com/fasiondog/hikyuu.git
synced 2024-11-30 02:48:57 +08:00
commit
baf3f2a0b6
4
hikyuu/data/mysql_upgrade/0020.sql
Normal file
4
hikyuu/data/mysql_upgrade/0020.sql
Normal 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;
|
6
hikyuu/data/sqlite_upgrade/0021.sql
Normal file
6
hikyuu/data/sqlite_upgrade/0021.sql
Normal 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;
|
@ -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
|
||||
|
@ -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)
|
||||
|
||||
# 启动行情接收代理
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
|
@ -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;
|
||||
};
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include "../StockManager.h"
|
||||
#include "../global/GlobalTaskGroup.h"
|
||||
#include "BaseInfoDriver.h"
|
||||
|
||||
namespace hku {
|
||||
|
@ -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);
|
||||
|
@ -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
|
@ -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 */
|
@ -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 */
|
@ -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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include "inner_tasks.h"
|
||||
#include "scheduler.h"
|
||||
#include "../GlobalTaskGroup.h"
|
||||
#include "../../StockManager.h"
|
||||
|
||||
namespace hku {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
@ -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]);
|
||||
|
@ -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
|
||||
|
@ -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线类型");
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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!");
|
||||
|
Loading…
Reference in New Issue
Block a user