diff --git a/hikyuu/draw/drawplot/matplotlib_draw.py b/hikyuu/draw/drawplot/matplotlib_draw.py index 84884da9c..79d38483c 100644 --- a/hikyuu/draw/drawplot/matplotlib_draw.py +++ b/hikyuu/draw/drawplot/matplotlib_draw.py @@ -24,6 +24,10 @@ def set_mpl_params(): if in_interactive_session(): rcParams['interactive'] = True + if sys.platform == 'darwin': + matplotlib.rcParams['font.sans-serif'] = 'Arial Unicode MS' + return + rcParams['font.family'] = 'sans-serif' rcParams['axes.unicode_minus'] = False diff --git a/hikyuu_cpp/hikyuu/StockManager.cpp b/hikyuu_cpp/hikyuu/StockManager.cpp index 16a433f10..2f65226ab 100644 --- a/hikyuu_cpp/hikyuu/StockManager.cpp +++ b/hikyuu_cpp/hikyuu/StockManager.cpp @@ -298,6 +298,20 @@ Block StockManager::getBlock(const string& category, const string& name) { return m_blockDriver ? m_blockDriver->getBlock(category, name) : Block(); } +void StockManager::saveBlock(const Block& blk) { + if (m_blockDriver) { + HKU_CHECK(!blk.category().empty(), "block's category can not be empty!"); + HKU_CHECK(!blk.name().empty(), "block's name can not be empty!"); + HKU_CHECK(!blk.empty(), "Can't save empty block!"); + m_blockDriver->save(blk); + } +} +void StockManager::removeBlock(const string& category, const string& name) { + if (m_blockDriver) { + m_blockDriver->remove(category, name); + } +} + BlockList StockManager::getBlockList(const string& category) { return m_blockDriver ? m_blockDriver->getBlockList(category) : BlockList(); } diff --git a/hikyuu_cpp/hikyuu/StockManager.h b/hikyuu_cpp/hikyuu/StockManager.h index 44beb6b37..4d3ac28eb 100644 --- a/hikyuu_cpp/hikyuu/StockManager.h +++ b/hikyuu_cpp/hikyuu/StockManager.h @@ -129,6 +129,16 @@ class HKU_API StockManager { */ Block getBlock(const string& category, const string& name); + void addBlock(const Block& blk) { + saveBlock(blk); + } + + void saveBlock(const Block& blk); + void removeBlock(const string& category, const string& name); + void removeBlock(const Block& blk) { + removeBlock(blk.category(), blk.name()); + } + /** * 获取指定分类的板块列表 * @param category 板块分类 diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp index dcc6e8012..d509cc6e4 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.cpp @@ -6,26 +6,40 @@ */ #include "hikyuu/utilities/db_connect/mysql/MySQLConnect.h" -#include "hikyuu/utilities/db_connect/TableMacro.h" +#include "hikyuu/utilities/db_connect/DBConnect.h" #include "MySQLBlockInfoDriver.h" namespace hku { -struct MySQLBlockTable { - TABLE_BIND4(MySQLBlockTable, block, category, name, market_code, index_code) +struct MySQLBlockView { + TABLE_BIND4(MySQLBlockView, block, category, name, market_code, index_code) string category; string name; string market_code; string index_code; }; +struct MySQLBlockTable { + TABLE_BIND3(MySQLBlockTable, block, category, name, market_code) + string category; + string name; + string market_code; +}; + +struct MySQLBlockIndexTable { + TABLE_BIND3(MySQLBlockIndexTable, BlockIndex, category, name, market_code) + string category; + string name; + string market_code; +}; + MySQLBlockInfoDriver::~MySQLBlockInfoDriver() {} bool MySQLBlockInfoDriver::_init() { return true; } -void MySQLBlockInfoDriver::load() { +DBConnectPtr MySQLBlockInfoDriver::getConnect() { Parameter connect_param; connect_param.set("host", getParamFromOther(m_params, "host", "127.0.0.1")); connect_param.set("usr", getParamFromOther(m_params, "usr", "root")); @@ -34,15 +48,19 @@ void MySQLBlockInfoDriver::load() { string port_str = getParamFromOther(m_params, "port", "3306"); unsigned int port = boost::lexical_cast(port_str); connect_param.set("port", port); - MySQLConnect connect(connect_param); + return std::make_shared(connect_param); +} - vector records; - connect.batchLoadView( +void MySQLBlockInfoDriver::load() { + auto connect = getConnect(); + vector records; + connect->batchLoadView( records, "select a.id, a.category, a.name, a.market_code, b.market_code as " "index_code from `hku_base`.`block` a left " "join `hku_base`.`BlockIndex` b on a.category=b.category and a.name = b.name"); + std::unique_lock lock(m_buffer_mutex); for (auto& record : records) { auto category_iter = m_buffer.find(record.category); if (category_iter == m_buffer.end()) { @@ -59,6 +77,7 @@ void MySQLBlockInfoDriver::load() { Block MySQLBlockInfoDriver::getBlock(const string& category, const string& name) { Block ret; + std::shared_lock lock(m_buffer_mutex); auto category_iter = m_buffer.find(category); HKU_IF_RETURN(category_iter == m_buffer.end(), ret); @@ -71,6 +90,7 @@ Block MySQLBlockInfoDriver::getBlock(const string& category, const string& name) BlockList MySQLBlockInfoDriver::getBlockList(const string& category) { BlockList ret; + std::shared_lock lock(m_buffer_mutex); auto category_iter = m_buffer.find(category); HKU_IF_RETURN(category_iter == m_buffer.end(), ret); @@ -84,6 +104,7 @@ BlockList MySQLBlockInfoDriver::getBlockList(const string& category) { BlockList MySQLBlockInfoDriver::getBlockList() { BlockList ret; + std::shared_lock lock(m_buffer_mutex); for (auto category_iter = m_buffer.begin(); category_iter != m_buffer.end(); ++category_iter) { const auto& category_blocks = category_iter->second; for (auto iter = category_blocks.begin(); iter != category_blocks.end(); ++iter) { @@ -93,8 +114,56 @@ BlockList MySQLBlockInfoDriver::getBlockList() { return ret; } -void MySQLBlockInfoDriver::save(const Block& block) {} +void MySQLBlockInfoDriver::save(const Block& block) { + std::unique_lock lock(m_buffer_mutex); + auto category_iter = m_buffer.find(block.category()); + if (category_iter == m_buffer.end()) { + m_buffer.emplace(block.category(), unordered_map{{block.name(), block}}); + } else { + category_iter->second.emplace(block.name(), block); + } + + auto connect = getConnect(); + AutoTransAction trans(connect); + auto condition = (Field("category") == block.category()) & (Field("name") == block.name()); + connect->remove(MySQLBlockView::getTableName(), condition, false); + connect->remove(MySQLBlockIndexTable::getTableName(), condition, false); + + if (!block.getIndexStock().isNull()) { + MySQLBlockIndexTable index; + index.category = block.category(); + index.name = block.name(); + index.market_code = block.getIndexStock().market_code(); + connect->save(index, false); + } + + for (auto iter = block.begin(); iter != block.end(); ++iter) { + MySQLBlockTable record; + record.category = block.category(); + record.name = block.name(); + record.market_code = iter->market_code(); + connect->save(record, false); + } +} + +void MySQLBlockInfoDriver::remove(const string& category, const string& name) { + { + auto connect = getConnect(); + AutoTransAction trans(connect); + auto condition = (Field("category") == category) & (Field("name") == name); + connect->remove(MySQLBlockTable::getTableName(), condition, false); + connect->remove(MySQLBlockIndexTable::getTableName(), condition, false); + } + + std::unique_lock lock(m_buffer_mutex); + auto category_iter = m_buffer.find(category); + HKU_IF_RETURN(category_iter == m_buffer.end(), void()); -void MySQLBlockInfoDriver::remove(const string& category, const string& name) {} + auto block_iter = category_iter->second.find(name); + HKU_IF_RETURN(block_iter == category_iter->second.end(), void()); + + category_iter->second.erase(block_iter); + m_buffer.erase(category_iter); +} } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.h index 2555368c3..e124bc13b 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/mysql/MySQLBlockInfoDriver.h @@ -7,6 +7,7 @@ #pragma once +#include #include "../../BlockInfoDriver.h" namespace hku { @@ -24,8 +25,12 @@ class MySQLBlockInfoDriver : public BlockInfoDriver { virtual void save(const Block& block) override; virtual void remove(const string& category, const string& name) override; +private: + DBConnectPtr getConnect(); + private: unordered_map> m_buffer; + std::shared_mutex m_buffer_mutex; }; } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/qianlong/QLBlockInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/block_info/qianlong/QLBlockInfoDriver.cpp index b0460b56c..aa63ddd37 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/qianlong/QLBlockInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/qianlong/QLBlockInfoDriver.cpp @@ -190,11 +190,11 @@ BlockList QLBlockInfoDriver::getBlockList() { } void QLBlockInfoDriver::save(const Block& block) { - HKU_THROW("Not support save block info!"); + HKU_THROW("Not support save block info! You can use ini file to do it!"); } void QLBlockInfoDriver::remove(const string& category, const string& name) { - HKU_THROW("Not support remove block info!"); + HKU_THROW("Not support save block info! You can use ini file to do it!"); } } /* namespace hku */ diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp b/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp index 22b0561e1..9d5a12d98 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.cpp @@ -5,20 +5,34 @@ * Author: fasiondog */ +#include "hikyuu/utilities/db_connect/DBConnect.h" #include "hikyuu/utilities/db_connect/sqlite/SQLiteConnect.h" -#include "hikyuu/utilities/db_connect/TableMacro.h" #include "SQLiteBlockInfoDriver.h" namespace hku { -struct SQLiteBlockTable { - TABLE_BIND4(SQLiteBlockTable, block, category, name, market_code, index_code) +struct SQLiteBlockView { + TABLE_BIND4(SQLiteBlockView, block, category, name, market_code, index_code) string category; string name; string market_code; string index_code; }; +struct SQLiteBlockTable { + TABLE_BIND3(SQLiteBlockTable, block, category, name, market_code) + string category; + string name; + string market_code; +}; + +struct SQLiteBlockIndexTable { + TABLE_BIND3(SQLiteBlockIndexTable, BlockIndex, category, name, market_code) + string category; + string name; + string market_code; +}; + SQLiteBlockInfoDriver::~SQLiteBlockInfoDriver() {} bool SQLiteBlockInfoDriver::_init() { @@ -26,18 +40,22 @@ bool SQLiteBlockInfoDriver::_init() { return !(dbname == ""); } -void SQLiteBlockInfoDriver::load() { +DBConnectPtr SQLiteBlockInfoDriver::getConnect() { string dbname = tryGetParam("db", ""); - HKU_ERROR_IF_RETURN(dbname == "", void(), "Can't get Sqlite3 filename!"); + HKU_CHECK(!dbname.empty(), "Can't get Sqlite3 filename!"); HKU_TRACE("SQLITE3: {}", dbname); + return std::make_shared(m_params); +} - SQLiteConnect connect(m_params); - vector records; - connect.batchLoadView(records, - "select a.id, a.category, a.name, a.market_code, b.market_code as " - "index_code from block a left " - "join BlockIndex b on a.category=b.category and a.name = b.name"); - +void SQLiteBlockInfoDriver::load() { + vector records; + auto connect = getConnect(); + connect->batchLoadView(records, + "select a.id, a.category, a.name, a.market_code, b.market_code as " + "index_code from block a left " + "join BlockIndex b on a.category=b.category and a.name = b.name"); + + std::unique_lock lock(m_buffer_mutex); for (auto& record : records) { auto category_iter = m_buffer.find(record.category); if (category_iter == m_buffer.end()) { @@ -54,6 +72,7 @@ void SQLiteBlockInfoDriver::load() { Block SQLiteBlockInfoDriver::getBlock(const string& category, const string& name) { Block ret; + std::shared_lock lock(m_buffer_mutex); auto category_iter = m_buffer.find(category); HKU_IF_RETURN(category_iter == m_buffer.end(), ret); @@ -66,6 +85,7 @@ Block SQLiteBlockInfoDriver::getBlock(const string& category, const string& name BlockList SQLiteBlockInfoDriver::getBlockList(const string& category) { BlockList ret; + std::shared_lock lock(m_buffer_mutex); auto category_iter = m_buffer.find(category); HKU_IF_RETURN(category_iter == m_buffer.end(), ret); @@ -79,6 +99,7 @@ BlockList SQLiteBlockInfoDriver::getBlockList(const string& category) { BlockList SQLiteBlockInfoDriver::getBlockList() { BlockList ret; + std::shared_lock lock(m_buffer_mutex); for (auto category_iter = m_buffer.begin(); category_iter != m_buffer.end(); ++category_iter) { const auto& category_blocks = category_iter->second; for (auto iter = category_blocks.begin(); iter != category_blocks.end(); ++iter) { @@ -89,11 +110,55 @@ BlockList SQLiteBlockInfoDriver::getBlockList() { } void SQLiteBlockInfoDriver::save(const Block& block) { - HKU_THROW("Not support save block info!"); + std::unique_lock lock(m_buffer_mutex); + auto category_iter = m_buffer.find(block.category()); + if (category_iter == m_buffer.end()) { + m_buffer.emplace(block.category(), unordered_map{{block.name(), block}}); + } else { + category_iter->second.emplace(block.name(), block); + } + + auto connect = getConnect(); + AutoTransAction trans(connect); + auto condition = (Field("category") == block.category()) & (Field("name") == block.name()); + connect->remove(SQLiteBlockView::getTableName(), condition, false); + connect->remove(SQLiteBlockIndexTable::getTableName(), condition, false); + + if (!block.getIndexStock().isNull()) { + SQLiteBlockIndexTable index; + index.category = block.category(); + index.name = block.name(); + index.market_code = block.getIndexStock().market_code(); + connect->save(index, false); + } + + for (auto iter = block.begin(); iter != block.end(); ++iter) { + SQLiteBlockTable record; + record.category = block.category(); + record.name = block.name(); + record.market_code = iter->market_code(); + connect->save(record, false); + } } void SQLiteBlockInfoDriver::remove(const string& category, const string& name) { - HKU_THROW("Not support save block info!"); + { + auto connect = getConnect(); + AutoTransAction trans(connect); + auto condition = (Field("category") == category) & (Field("name") == name); + connect->remove(SQLiteBlockTable::getTableName(), condition, false); + connect->remove(SQLiteBlockIndexTable::getTableName(), condition, false); + } + + std::unique_lock lock(m_buffer_mutex); + auto category_iter = m_buffer.find(category); + HKU_IF_RETURN(category_iter == m_buffer.end(), void()); + + auto block_iter = category_iter->second.find(name); + HKU_IF_RETURN(block_iter == category_iter->second.end(), void()); + + category_iter->second.erase(block_iter); + m_buffer.erase(category_iter); } } // namespace hku \ No newline at end of file diff --git a/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.h b/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.h index 367d4ea0b..a0f105789 100644 --- a/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.h +++ b/hikyuu_cpp/hikyuu/data_driver/block_info/sqlite/SQLiteBlockInfoDriver.h @@ -7,6 +7,7 @@ #pragma once +#include #include "../../BlockInfoDriver.h" namespace hku { @@ -24,8 +25,12 @@ class SQLiteBlockInfoDriver : public BlockInfoDriver { virtual void save(const Block& block) override; virtual void remove(const string& category, const string& name) override; +private: + DBConnectPtr getConnect(); + private: unordered_map> m_buffer; + std::shared_mutex m_buffer_mutex; }; } // namespace hku diff --git a/hikyuu_pywrap/_StockManager.cpp b/hikyuu_pywrap/_StockManager.cpp index 5d6451c34..fd7caabe4 100644 --- a/hikyuu_pywrap/_StockManager.cpp +++ b/hikyuu_pywrap/_StockManager.cpp @@ -117,6 +117,14 @@ void export_StockManager(py::module& m) { :return: 板块,如找不到返回空Block :rtype: Block)") + .def("add_block", &StockManager::addBlock, R"(add_block(self, block))") + .def("save_block", &StockManager::saveBlock, R"(save_block(self, block))") + .def("remove_block", + py::overload_cast(&StockManager::removeBlock), + py::arg("category"), py::arg("name"), R"(remove_block(self, category, name))") + .def("remove_block", py::overload_cast(&StockManager::removeBlock), + py::arg("block"), R"(remove_block(self, block))") + .def("get_block_list", py::overload_cast<>(&StockManager::getBlockList)) .def("get_block_list", py::overload_cast(&StockManager::getBlockList), R"(get_block_list(self[, category])