Merge branch 'master' of https://github.com/fasiondog/hikyuu into feature/docs

This commit is contained in:
fasiondog 2024-08-21 16:31:04 +08:00
commit 286fc3fc9f
167 changed files with 7024 additions and 2315 deletions

53
.github/workflows/macos_arm64.yml vendored Normal file
View File

@ -0,0 +1,53 @@
name: macOS_arm64
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [macos-14]
arch: [arm64]
kind: [shared]
runs-on: ${{ matrix.os }}
concurrency:
group: ${{ github.ref }}-${{ github.base_ref }}-${{ github.head_ref }}-macOS-${{ matrix.arch }}-${{ matrix.kind }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Cache packages
id: cache-xmake-macosx
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.xmake
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.4
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'macosx-arm64'
- name: config
run: |
xmake f -c -k ${{ matrix.kind }} -y -vD --mysql=n --hdf5=n
- name: build
run: |
xmake -bvD core

53
.github/workflows/macos_x86_64.yml vendored Normal file
View File

@ -0,0 +1,53 @@
name: macOS_x86_64
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [macos-12]
arch: [x86_64]
kind: [shared]
runs-on: ${{ matrix.os }}
concurrency:
group: ${{ github.ref }}-${{ github.base_ref }}-${{ github.head_ref }}-macOS-${{ matrix.arch }}-${{ matrix.kind }}
cancel-in-progress: true
steps:
- uses: actions/checkout@v4
- name: Cache packages
id: cache-xmake-macosx
uses: actions/cache@v4
env:
cache-name: cache-node-modules
with:
path: ~/.xmake
key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-build-${{ env.cache-name }}-
${{ runner.os }}-build-
${{ runner.os }}-
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.4
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'macosx-x64'
- name: config
run: |
xmake f -c -k ${{ matrix.kind }} -y -vD --mysql=n --hdf5=n
- name: build
run: |
xmake -bvD core

View File

@ -34,7 +34,7 @@ jobs:
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.2
xmake-version: 2.9.3
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'ubuntu'

View File

@ -41,8 +41,9 @@ jobs:
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.2
xmake-version: 2.9.3
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'ubuntu-aarch64'
- name: Installation musl
run: |

View File

@ -34,7 +34,7 @@ jobs:
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.2
xmake-version: 2.9.3
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'ubuntu'

View File

@ -26,17 +26,19 @@ jobs:
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.2
xmake-version: 2.9.3
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'windows'
- name: configure
shell: cmd
run: |
xmake f -c --feedback=n -k shared -y -vD
xmake f -c --feedback=n -y -vD
- name: build
shell: cmd
run: |
xmake -b small-test
xmake -bvD small-test
- name: test
shell: cmd

View File

@ -26,14 +26,16 @@ jobs:
- uses: xmake-io/github-action-setup-xmake@v1
with:
xmake-version: 2.9.2
xmake-version: 2.9.3
actions-cache-folder: '.xmake-cache'
actions-cache-key: 'windows'
- name: configure
shell: cmd
run: |
xmake f -c -k shared -y
xmake f -c -k shared -y --runtimes="MD"
- name: build
shell: cmd
run: |
xmake -b core
xmake -b core

3
.gitignore vendored
View File

@ -77,4 +77,5 @@ hikyuu/cpp/libhku_hdf5_hl.so.200.1.0
hikyuu/cpp/libhku_hdf5.so.200
hikyuu/cpp/libhku_hdf5.so.200.2.0
hikyuu/include
.virtual_documents
.virtual_documents
hikyuu_cpp/hikyuu/utilities/config.h

View File

@ -1,113 +0,0 @@
.. image:: https://img2.imgtp.com/2024/05/29/mSBrbO7R.png
:target: https://gitee.com/fasiondog/hikyuu
:align: left
:alt: Hikyuu
-----------
.. image:: https://static.pepy.tech/badge/hikyuu
:target: https://pepy.tech/project/hikyuu
.. image:: https://static.pepy.tech/badge/hikyuu/month
:target: https://pepy.tech/project/hikyuu
.. image:: https://static.pepy.tech/badge/hikyuu/week
:target: https://pepy.tech/project/hikyuu
.. image:: https://github.com/fasiondog/hikyuu/workflows/win-build/badge.svg
:target: https://github.com/fasiondog/hikyuu/actions
.. image:: https://github.com/fasiondog/hikyuu/workflows/ubuntu-build/badge.svg
:target: https://github.com/fasiondog/hikyuu/actions
.. image:: https://github.com/fasiondog/hikyuu/workflows/ubuntu-aarch64-build/badge.svg
:target: https://github.com/fasiondog/hikyuu/actions
.. image:: https://img.shields.io/github/license/mashape/apistatus.svg
:target: https://github.com/fasiondog/hikyuu/blob/master/LICENSE.txt
:alt: GitHub
Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架用于策略分析及回测目前主要用于国内A股市场。其核心思想基于当前成熟的系统化交易方法将整个系统化交易抽象为由市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法七大组件,你可以分别构建这些组件的策略资产库,在实际研究中对它们自由组合来观察系统的有效性、稳定性以及单一种类策略的效果。
详细文档: `<https://hikyuu.org/>`_
感谢网友提供的 Hikyuu Ubuntu虚拟机环境, 百度网盘下载(提取码: ht8j): `<https://pan.baidu.com/s/1CAiUWDdgV0c0VhPpe4AgVw?pwd=ht8j>`_
示例:
::
#创建模拟交易账户进行回测初始资金30万
my_tm = crtTM(init_cash = 300000)
#创建信号指示器以5日EMA为快线5日EMA自身的10日EMA作为慢线快线向上穿越慢线时买入反之卖出
my_sg = SG_Flex(EMA(CLOSE(), n=5), slow_n=10)
#固定每次买入1000股
my_mm = MM_FixedCount(1000)
#创建交易系统并运行
sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm)
sys.run(sm['sz000001'], Query(-150))
.. figure:: https://img2.imgtp.com/2024/05/29/xTEvXesP.png
:width: 600px
完整示例参见:`<https://nbviewer.jupyter.org/github/fasiondog/hikyuu/blob/master/hikyuu/examples/notebook/000-Index.ipynb?flush_cache=True>`_
为什么选择 Hikyuu
--------------------
- **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象包含了九大策略组件市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下:
.. figure:: https://img2.imgtp.com/2024/05/29/9SrY9vI1.png
:width: 600px
- **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。
- AMD 7950x 实测A股全市场1913万日K线仅加载全部日线计算 20日 MA 并求最后 MA 累积和,首次执行含数据加载 耗时 6秒数据加载完毕后计算耗时 166 毫秒,详见: `性能实测 <https://mp.weixin.qq.com/s?__biz=MzkwMzY1NzYxMA==&mid=2247483768&idx=1&sn=33e40aa9633857fa7b4c7ded51c95ae7&chksm=c093a09df7e4298b3f543121ba01334c0f8bf76e75c643afd6fc53aea1792ebb92de9a32c2be&mpshare=1&scene=23&srcid=05297ByHT6DEv6XAmyje1oOr&sharer_shareinfo=b38f5f91b4efd8fb60303a4ef4774748&sharer_shareinfo_first=b38f5f91b4efd8fb60303a4ef4774748#rd>`_
- C++核心库提供了整体的策略框架在保证性能的同时已经考虑了对多线程和多核处理的支持在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。
- Python库hikyuu提供了对C++库的包装同时集成了talib库如TA_SMA对应talib.SMA可以与numpy、pandas数据结构进行互相转换为使用其他成熟的python数据分析工具提供了便利。
- hikyuu.interactive 交互式探索工具提供了K线、指标、系统信号等的基本绘图功能用于对量化策略的探索和回测。
- **代码简洁,探索更便捷、自由** 同时支持面向对象和命令行编程范式。其中,命令行在进行策略探索时,代码简洁、探索更便捷、自由。
- **安全、自由、隐私,搭建自己的专属云量化平台** 结合 Python + Jupyter 的强大能力与云服务器可以搭建自己专属的云量化平台。将Jupyter部署在云服务器上随时随地的访问自己的云平台即刻实现自己新的想法如下图所示通过手机访问自己的云平台。结合Python强大成熟的数据分析、人工智能工具如 numpy、scipy、pandas、TensorFlow)搭建更强大的人工智能平台。
- **数据存储方式可扩展** 目前支持本地HDF5格式、MySQL存储。默认使用HDF5数据文件体积小、速度更快、备份更便利。截止至2017年4月21日沪市日线数据文件149M、深市日线数据文件184M、5分钟线数据各不到2G。
.. image:: https://api.star-history.com/svg?repos=fasiondog/hikyuu&type=Date
:target: https://star-history.com/#fasiondog/hikyuu&Date
:alt: Star History Chart
想要更多了解Hikyuu请使用以下方式联系
--------------------------------------------------
**加入知识星球** 更多示例与策略部件的及时分享(您的加入将视为对项目的捐赠)
.. figure:: https://img2.imgtp.com/2024/05/29/3sEP6Re0.png
**项目交流和问题答复将转移至知识星球-【Hikyuu量化】。**
- 关注公众号:
.. figure:: https://img2.imgtp.com/2024/05/29/1NQztICj.jpg
- 加入微信群请注明“加入hikyuu”
.. figure:: https://img2.imgtp.com/2024/05/29/HD0dAgbn.jpg
- QQ交流群逐渐废弃114910869, 或扫码加入:
.. figure:: https://img2.imgtp.com/2024/05/29/xAH2PesY.png

View File

@ -19,18 +19,6 @@
// 检查下标越界
#define CHECK_ACCESS_BOUND ${CHECK_ACCESS_BOUND}
// 默认激活的日志级别
#define LOG_ACTIVE_LEVEL ${LOG_ACTIVE_LEVEL}
// 是否使用 spdlog
#define USE_SPDLOG_LOGGER ${USE_SPDLOG_LOGGER}
// 使用异步 logger
#define HKU_USE_SPDLOG_ASYNC_LOGGER ${USE_SPDLOG_ASYNC_LOGGER}
// spdlog默认日志级别
#define SPDLOG_ACTIVE_LEVEL ${LOG_ACTIVE_LEVEL}
// 启用MSVC内存泄漏检查
#define ENABLE_MSVC_LEAK_DETECT ${ENABLE_MSVC_LEAK_DETECT}

41
config_utils.h.in Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#ifndef HKU_UTILS_CONFIG_H_
#define HKU_UTILS_CONFIG_H_
#include "osdef.h"
// clang-format off
${define HKU_ENABLE_MYSQL}
#if HKU_ENABLE_MYSQL && HKU_OS_WINDOWS
#ifndef NOMINMAX
#define NOMINMAX
#endif
#endif
${define HKU_ENABLE_SQLITE}
${define HKU_ENABLE_SQLCIPHER}
${define HKU_SQL_TRACE}
${define HKU_SUPPORT_DATETIME}
${define HKU_ENABLE_INI_PARSER}
${define HKU_ENABLE_STACK_TRACE}
${define HKU_CLOSE_SPEND_TIME}
${define HKU_USE_SPDLOG_ASYNC_LOGGER}
${define HKU_LOG_ACTIVE_LEVEL}
${define HKU_ENABLE_MO}
${define HKU_ENABLE_HTTP_CLIENT}
${define HKU_ENABLE_HTTP_CLIENT_SSL}
${define HKU_ENABLE_HTTP_CLIENT_ZIP}
${define HKU_ENABLE_NODE}
// clang-format on
#endif /* HKU_UTILS_CONFIG_H_ */

84
copy_dependents.lua Normal file
View File

@ -0,0 +1,84 @@
-- 拷贝依赖的第三方库头文件及lib到指定目录
task("copy_dependents")
set_category("plugin")
-- 设置运行脚本
-- destpath 目标目录
-- onlylib 只拷贝lib库
on_run(function(target, destpath, onlylib)
local libdir = destpath .. '/lib'
-- 将依赖的库拷贝至build的输出目录
for libname, pkg in pairs(target:pkgs()) do
if pkg:installdir() == nil then
print(libname .. ": Not found installdir, maybe it is system lib!");
goto continue
end
print("dependent package: " .. pkg:installdir())
--local linkdirs = pkg:get("linkdirs")
-- 部分库没有 linkdirs MNN, Paddle-lite所以使用 includedirs
local pkg_path = pkg:get("includedirs")
if pkg_path == nil then
pkg_path = pkg:get("sysincludedirs") -- xmake 2.3.9 改为了 sysincludedirs
end
if pkg_path == nil then
goto continue
end
-- 安装模式下拷贝所有依赖库的头文件
if not onlylib then
if type(pkg_path) == 'string' then
local pos = string.find(pkg_path, "opencv")
if pos == nil then
os.trycp(pkg_path, destpath)
else
os.trycp(pkg_path .. "/opencv2", destpath .. "/include")
end
elseif type(pkg_path) == 'table' then
for i=1, #pkg_path do
local pos = string.find(pkg_path[i], "hku_utils")
if pos == nil then
pos = string.find(pkg_path[i], "opencv")
if pos == nil then
os.trycp(pkg_path[i], destpath)
else
os.trycp(pkg_path[i] .. "/opencv2", destpath .. "/include")
end
else
for _, filedir in ipairs(os.dirs(pkg_path[i] .. "/*")) do
local pos = string.find(filedir, "hikyuu")
if pos == nil then
os.trycp(filedir, destpath .. "/include")
else
os.trycp(filedir .. "/utilities", destpath .. "/include/hikyuu")
end
end
end
end
end
end
-- 拷贝依赖的库文件
os.trycp(pkg:installdir() .. "/lib/*", libdir)
if is_plat("windows") then
os.trycp(pkg:installdir() .. "/bin/*.dll", libdir)
end
:: continue ::
end
end)
set_menu {
-- usage
usage = "xmake copy_dependents [options]"
-- description
, description = "拷贝依赖的第三方库头文件及lib到指定目录!"
-- options
, options = {}
}
task_end()

View File

@ -1,6 +1,14 @@
版本发布说明
=======================
2.1.1 - 2024年8月9日
-------------------------
1. 预加载历史财务信息
2. fixed windows下 MySQL blob 数据读取错误导致读取历史财务信息时消耗巨大内存
3. HikyuuTdx 读取配置文件放在 output 重定向之前,防止配置文件读取失败没有提示
2.1.0 - 2024年6月18日
-------------------------

View File

@ -1,79 +0,0 @@
.. image:: https://hikyuu.org/images/00000_title.png
:target: https://hikyuu.org
:align: center
:alt: Hikyuu
.. image:: https://travis-ci.org/fasiondog/hikyuu.svg?branch=master
:target: https://travis-ci.org/fasiondog/hikyuu
.. image:: https://img.shields.io/github/license/mashape/apistatus.svg
:target: https://github.com/fasiondog/hikyuu/blob/master/LICENSE.txt
:alt: GitHub
.. image:: https://img.shields.io/badge/license-Anti%20996-blue.svg
:target: https://github.com/996icu/996.ICU/blob/master/LICENSE
:alt: GitHub
-----------
Hikyuu Quant Framework是一款基于C++/Python的开源量化交易研究框架用于策略分析及回测仅受限于数据如有数据也可用于期货等。其核心思想基于当前成熟的系统化交易方法将整个系统化交易抽象为由市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法七大组件,你可以分别构建这些组件的策略资产库,在实际研究中对它们自由组合来观察系统的有效性、稳定性以及单一种类策略的效果。
祝贺 HIKYUU 入选 GITEE 最有价值开源项目 GVP
-----------------------------------------------
.. image:: https://hikyuu.org/images/gitee_GVP.jpg
:target: https://gitee.com/gvp
:alt: Gitee
**给作者加点油,每天扫扫红包,或者请作者喝杯咖啡**
.. image:: https://hikyuu.org/images/juanzeng.jpg
示例:
::
#创建模拟交易账户进行回测初始资金30万
my_tm = crtTM(init_cash = 300000)
#创建信号指示器以5日EMA为快线5日EMA自身的10日EMA作为慢线快线向上穿越慢线时买入反之卖出
my_sg = SG_Flex(EMA(CLOSE(), n=5), slow_n=10)
#固定每次买入1000股
my_mm = MM_FixedCount(1000)
#创建交易系统并运行
sys = SYS_Simple(tm = my_tm, sg = my_sg, mm = my_mm)
sys.run(sm['sz000001'], Query(-150))
.. figure:: https://hikyuu.org/images/10000-overview.png
:width: 600px
完整示例参见:`<https://nbviewer.jupyter.org/github/fasiondog/hikyuu/blob/master/hikyuu/examples/notebook/000-Index.ipynb?flush_cache=True>`_
为什么选择 Hikyuu
--------------------
- **组合灵活,分类构建策略资产库** Hikyuu对系统化交易方法进行了良好的抽象包含了九大策略组件市场环境判断策略、系统有效条件、信号指示器、止损/止盈策略、资金管理策略、盈利目标策略、移滑价差算法、交易对象选择策略、资金分配策略。可以在此基础上构建自己的策略库,并进行灵活的组合和测试。在进行策略探索时,可以更加专注于某一方面的策略性能与影响。其主要功能模块如下:
.. figure:: https://hikyuu.org/images/10002-function-arc.png
:width: 600px
- **性能保障,打造自己的专属应用** 目前项目包含了3个主要组成部分基于C++的核心库、对C++进行包装的Python库(hikyuu)、基于Python的交互式工具。
- C++核心库提供了整体的策略框架在保证性能的同时已经考虑了对多线程和多核处理的支持在未来追求更高运算速度提供便利。C++核心库,可以单独剥离使用,自行构建自己的客户端工具。
- Python库hikyuu提供了对C++库的包装同时集成了talib库如TA_SMA对应talib.SMA可以与numpy、pandas数据结构进行互相转换为使用其他成熟的python数据分析工具提供了便利。
- hikyuu.interactive 交互式探索工具提供了K线、指标、系统信号等的基本绘图功能用于对量化策略的探索和回测。
- **代码简洁,探索更便捷、自由** 同时支持面向对象和命令行编程范式。其中,命令行在进行策略探索时,代码简洁、探索更便捷、自由。
- **安全、自由、隐私,搭建自己的专属云量化平台** 结合 Python + Jupyter 的强大能力与云服务器可以搭建自己专属的云量化平台。将Jupyter部署在云服务器上随时随地的访问自己的云平台即刻实现自己新的想法如下图所示通过手机访问自己的云平台。结合Python强大成熟的数据分析、人工智能工具如 numpy、scipy、pandas、TensorFlow)搭建更强大的人工智能平台。
- **数据存储方式可扩展** 目前支持本地HDF5格式、MySQL存储。默认使用HDF5数据文件体积小、速度更快、备份更便利。截止至2017年4月21日沪市日线数据文件149M、深市日线数据文件184M、5分钟线数据各不到2G。

View File

@ -9,25 +9,25 @@
"name": "stderr",
"output_type": "stream",
"text": [
"2024-04-06 01:24:41,451 [INFO] hikyuu version: 2.0.0_202404040113_RELEASE_windows_x64 [<module>] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:93) [hikyuu::hku_info]\n"
"2024-08-20 16:00:57,364 [INFO] hikyuu version: 2.1.1_202408182226_RELEASE_windows_x64 [<module>] (D:\\workspace\\hikyuu\\hikyuu\\__init__.py:97) [hikyuu::hku_info]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"2024-04-06 01:24:41.867 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n",
"2024-04-06 01:24:41.890 [HKU-I] - Loading market information... (StockManager.cpp:532)\n",
"2024-04-06 01:24:41.902 [HKU-I] - Loading stock type information... (StockManager.cpp:545)\n",
"2024-04-06 01:24:41.914 [HKU-I] - Loading stock information... (StockManager.cpp:460)\n",
"2024-04-06 01:24:42.064 [HKU-I] - Loading stock weight... (StockManager.cpp:562)\n",
"2024-04-06 01:24:43.250 [HKU-I] - Loading KData... (StockManager.cpp:133)\n",
"2024-04-06 01:24:43.958 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:171)\n",
"2024-04-06 01:24:43.959 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:174)\n",
"2024-04-06 01:24:43.959 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:177)\n",
"2024-04-06 01:24:43.963 [HKU-I] - 0.71s Loaded Data. (StockManager.cpp:150)\n",
"CPU times: total: 500 ms\n",
"Wall time: 3.35 s\n"
"2024-08-20 16:00:57.865 [HKU-I] - Using MYSQL BaseInfoDriver (BaseInfoDriver.cpp:58)\n",
"2024-08-20 16:00:57.883 [HKU-I] - Loading market information... (StockManager.cpp:481)\n",
"2024-08-20 16:00:57.890 [HKU-I] - Loading stock type information... (StockManager.cpp:494)\n",
"2024-08-20 16:00:57.897 [HKU-I] - Loading stock information... (StockManager.cpp:409)\n",
"2024-08-20 16:00:58.045 [HKU-I] - Loading stock weight... (StockManager.cpp:511)\n",
"2024-08-20 16:00:59.275 [HKU-I] - Loading KData... (StockManager.cpp:134)\n",
"2024-08-20 16:01:00.831 [HKU-I] - Preloading all day kdata to buffer! (StockManager.cpp:179)\n",
"2024-08-20 16:01:00.832 [HKU-I] - Preloading all week kdata to buffer! (StockManager.cpp:179)\n",
"2024-08-20 16:01:00.832 [HKU-I] - Preloading all month kdata to buffer! (StockManager.cpp:179)\n",
"2024-08-20 16:01:00.902 [HKU-I] - 1.63s Loaded Data. (StockManager.cpp:159)\n",
"CPU times: total: 625 ms\n",
"Wall time: 4.01 s\n"
]
}
],
@ -78,7 +78,7 @@
" current borrow_cash: 0.00,\n",
" current borrow_asset: 0.00,\n",
" Position: \n",
" SZ000001 平安银行 2017-01-03 00:00:00 1762 100.00 911.00 1046.00 135.00 14.82% 0.14%\n",
" SZ000001 平安银行 2017-01-03 00:00:00 1854 100.00 911.00 1029.00 118.00 12.95% 0.12%\n",
" Short Position: \n",
" Borrow Stock: \n",
"}\n"
@ -148,21 +148,21 @@
" <th>SZ000001</th>\n",
" <td>平安银行</td>\n",
" <td>2017-01-03</td>\n",
" <td>1762</td>\n",
" <td>1854</td>\n",
" <td>100</td>\n",
" <td>911.0</td>\n",
" <td>1046.0</td>\n",
" <td>135.0</td>\n",
" <td>14.81888</td>\n",
" <td>1029.0</td>\n",
" <td>118.0</td>\n",
" <td>12.952799</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" 证券名称 买入日期 已持仓天数 持仓数量 投入金额 当前市值 盈亏金额 盈亏比例\n",
"证券代码 \n",
"SZ000001 平安银行 2017-01-03 1762 100 911.0 1046.0 135.0 14.81888"
" 证券名称 买入日期 已持仓天数 持仓数量 投入金额 当前市值 盈亏金额 盈亏比例\n",
"证券代码 \n",
"SZ000001 平安银行 2017-01-03 1854 100 911.0 1029.0 118.0 12.952799"
]
},
"execution_count": 3,
@ -259,7 +259,7 @@
},
{
"cell_type": "code",
"execution_count": 7,
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@ -271,7 +271,7 @@
},
{
"cell_type": "code",
"execution_count": 8,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@ -298,28 +298,30 @@
},
{
"cell_type": "code",
"execution_count": 9,
"execution_count": 8,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"买入SZ000001 11.470 1000\n",
"卖出SZ000001 11.140 1000\n",
"买入SZ000001 11.500 1000\n",
"卖出SZ000001 11.290 1000\n",
"买入SZ000001 10.580 1000\n",
"卖出SZ000001 10.460 1000\n",
"买入SZ000001 9.420 1000\n",
"卖出SZ000001 9.100 1000\n",
"买入SZ000001 9.330 1000\n",
"卖出SZ000001 9.160 1000\n",
"买入SZ000001 9.330 1000\n",
"卖出SZ000001 10.550 1000\n",
"买入SZ000001 10.560 1000\n",
"卖出SZ000001 10.350 1000\n",
"买入SZ000001 10.560 1000\n"
"买入SZ000001, 价格: 9.33, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 9.16, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 9.33, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 10.55, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.56, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 10.35, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.56, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 10.43, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.58, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 11.12, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.4, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 9.94, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.31, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 10.12, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.22, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n",
"卖出SZ000001, 价格: 10.11, 数量: 1000.0, 信号来源: SystemPart.SIGNAL\n",
"买入SZ000001, 价格: 10.13, 数量: 1000.0 预期止损价: 0.0, 预期目标价: nan, 信号来源: SystemPart.SIGNAL\n"
]
}
],

File diff suppressed because one or more lines are too long

View File

@ -1,10 +1,12 @@
#
# 对 C++ 引出类和函数进行扩展, pybind11 对小函数到导出效率不如 python 直接执行
#
# 优先加载 hikyuu 库,防止 windows 公共依赖库不同导致DLL初始化失败
from .core import *
import numpy as np
import pandas as pd
from datetime import *
from .core import *
# ------------------------------------------------------------------
# 增加Datetime、Stock的hash支持以便可做为dict的key

View File

@ -8,6 +8,10 @@ import datetime
import multiprocessing
from configparser import ConfigParser
from logging.handlers import QueueListener
# 优先加载,处理 VS 17.10 升级后依赖 dll 不兼容问题
import hikyuu
import PyQt5
from PyQt5.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
@ -240,6 +244,13 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
self.mp_log_q_lisener.start()
def initUI(self):
# 读取配置文件放在 output 重定向之前,防止配置文件读取失败没有提示
# 读取保存的配置文件信息,如果不存在,则使用默认配置
this_dir = self.getUserConfigDir()
import_config = ConfigParser()
if os.path.exists(this_dir + '/importdata-gui.ini'):
import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8')
self._is_sched_import_running = False
self._is_collect_running = False
self._stream = None
@ -272,12 +283,6 @@ class MyMainWindow(QMainWindow, Ui_MainWindow):
self.time_start_dateEdit.setMinimumDate(today - datetime.timedelta(300))
self.collect_status_label.setText("已停止")
# 读取保存的配置文件信息,如果不存在,则使用默认配置
this_dir = self.getUserConfigDir()
import_config = ConfigParser()
if os.path.exists(this_dir + '/importdata-gui.ini'):
import_config.read(this_dir + '/importdata-gui.ini', encoding='utf-8')
# 初始化导入行情数据类型配置
self.import_stock_checkBox.setChecked(import_config.getboolean('quotation', 'stock', fallback=True))
self.import_fund_checkBox.setChecked(import_config.getboolean('quotation', 'fund', fallback=True))

View File

@ -1,3 +0,0 @@
#!/usr/bin/python
# -*- coding: utf8 -*-
# cp936

View File

@ -2,26 +2,30 @@
# -*- coding: utf8 -*-
# cp936
from hikyuu import StrategyBase, Query
from hikyuu import StrategyContext, StockManager
from hikyuu import Strategy, Query, Datetime, TimeDelta, Seconds, Minutes
from hikyuu import sm
class TestStrategy(StrategyBase):
def __init__(self):
super(self.__class__, self).__init__()
self.stock_list = ['sh600000', 'sz000001']
self.ktype_list = [Query.MIN, Query.DAY]
def init(self):
print("strategy init")
def on_bar(self, ktype):
print("on bar {}".format(ktype))
print("{}".format(len(StockManager.instance())))
for s in self.sm:
print(s)
def on_change(stk, spot):
print(stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1)
def on_spot(rev_time):
print("rev_time:", rev_time)
def my_func():
print("calculate:", Datetime.now())
for s in sm:
print(s)
# 注意每一个Strategy 只能作为独立进程执行,即 python xxx.py 的方式执行!
# 以 Strategy 方式运行示例
if __name__ == '__main__':
s = TestStrategy()
s.run()
s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY])
# s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5))
s.on_change(on_change)
s.on_received_spot(on_spot)
s.run_daily(my_func, Minutes(1))
s.start()

View File

@ -29,7 +29,9 @@
# 1. 20170704, Added by fasiondog
# ===============================================================================
import json
from hikyuu import OrderBrokerBase
from hikyuu.util import hku_error
class OrderBrokerWrap(OrderBrokerBase):
@ -44,15 +46,23 @@ class OrderBrokerWrap(OrderBrokerBase):
super(OrderBrokerWrap, self).__init__(name)
self._broker = broker
def _buy(self, datetime, market, code, price, num):
def _buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from):
"""实现 OrderBrokerBase 的 _buy 接口"""
self._broker.buy('{}{}'.format(market, code), price, num)
return datetime
self._broker.buy('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from)
def _sell(self, datetime, market, code, price, num):
def _sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from):
"""实现 OrderBrokerBase 的 _sell 接口"""
self._broker.sell('{}{}'.format(market, code), price, num)
return datetime
self._broker.sell('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from)
def _get_asset_info(self):
try:
if hasattr(self._broker, "get_asset_info"):
ret = self._broker.get_asset_info()
return json.dumps(ret) if type(ret) == dict else str(ret)
return str()
except Exception as e:
hku_error(e)
return str()
class TestOrderBroker:
@ -61,11 +71,11 @@ class TestOrderBroker:
def __init__(self):
pass
def buy(self, code, price, num):
print("买入:%s %.3f %i" % (code, price, num))
def buy(self, code, price, num, stoploss, goal_price, part_from):
print(f"买入:{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}")
def sell(self, code, price, num):
print("卖出:%s %.3f %i" % (code, price, num))
def sell(self, code, price, num, stoploss, goal_price, part_from):
print(f"卖出:{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}")
def crtOB(broker, name="NO_NAME"):

View File

@ -4,14 +4,52 @@
# Create on: 2024-01-30
# Author: fasiondog
from hikyuu import Datetime, hku_info
class EasyTraderOrderBroker:
def __init__(self, user):
self.user = user
self.buffer = {}
def buy(self, code, price, num):
def buy(self, code, price, num, stoploss, goal_price, part_from):
self.user.buy(code[2:], price=price, amount=num)
print("买入:%s %.3f %i" % (code, price, num))
self.buffer[code] = (num, stoploss, goal_price)
def sell(self, code, price, num):
def sell(self, code, price, num, stoploss, goal_price, part_from):
self.user.sell(code[2:], price=price, amount=num)
print("卖出:%s %.3f %i" % (code, price, num))
if code in self.buffer:
old_num = self.buffer[code][0]
if old_num == num:
self.buffer.pop(code)
else:
self.buffer[code] = (old_num - num, stoploss, goal_price)
def get_asset_info(self):
balance = self.user.balance
cash = 0.0
for item in balance:
cash += item['可用资金']
positions = []
for v in self.user.position:
if v["交易市场"] == "沪A":
market = "SH"
elif v["交易市场"] == "深A":
market = "SZ"
else:
hku_info(f"Ignored not supported market: {v['交易市场']}")
continue
code = v["证券代码"]
market_code = f"{market}{code}"
if market_code in self.buffer:
stoploss, goal_price = self.buffer[market_code]
else:
stoploss, goal_price = 0.0, 0.0
positions.append(dict(market=market, code=code, number=(
v['当前持仓'] + v['买入冻结'] - v['卖出冻结']), stoploss=stoploss, goal_price=goal_price))
return dict(datetime=str(Datetime.now()), cash=cash, positions=positions)

View File

@ -24,24 +24,25 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
#===============================================================================
# ===============================================================================
# History
# 1. 20170704, Added by fasiondog
#===============================================================================
# ===============================================================================
import smtplib
from email.mime.text import MIMEText
from email.header import Header
class MailOrderBroker:
"""
邮件订单代理
"""
def __init__(self, host, sender, pwd, receivers):
"""
邮件订单代理执行买入/卖出操作时发送 Email
:param str host: smtp服务器地址
:param int port: smtp服务器端口
:param str sender: 发件邮箱既用户名
@ -52,10 +53,10 @@ class MailOrderBroker:
self._pwd = pwd
self._sender = sender
self._receivers = receivers
def _sendmail(self, title, msg):
"""发送邮件
:param str title: 邮件标题
:param str msg: 邮件内容
"""
@ -70,14 +71,13 @@ class MailOrderBroker:
smtpObj.connect(self._host, 25)
smtpObj.login(self._sender, self._pwd)
smtpObj.sendmail(self._sender, self._receivers, message.as_string())
def buy(self, code, price, num):
def buy(self, code, price, num, stoploss, goal_price, part_from):
"""执行买入操作,向指定的邮箱发送邮件,格式如下::
邮件标题Hkyuu提醒买入 证券代码
邮件内容买入证券代码价格买入的价格数量买入数量
:param str code: 证券代码
:param float price: 买入价格
:param int num: 买入数量
@ -85,14 +85,13 @@ class MailOrderBroker:
action = "买入:{},价格:{},数量:{} ".format(code, price, num)
title = "【Hkyuu提醒】买入 {}".format(code)
self._sendmail(title, action)
def sell(self, code, price, num):
def sell(self, code, price, num, stoploss, goal_price, part_from):
"""执行卖出操作,向指定的邮箱发送邮件,格式如下::
邮件标题Hkyuu提醒卖出 证券代码
邮件内容卖出证券代码价格卖出的价格数量卖出数量
:param str code: 证券代码
:param float price: 卖出价格
:param int num: 卖出数量
@ -100,5 +99,3 @@ class MailOrderBroker:
title = "【Hkyuu提醒】卖出 {}".format(code)
action = "卖出:{},价格:{},数量:{} ".format(code, price, num)
self._sendmail(title, action)

View File

@ -35,6 +35,7 @@ int main(int argc, char* argv[]) {
std::cout << k[i] << std::endl;
}
// 启动行情接收(只是计算回测可以不需要)
startSpotAgent(true);
while (true) {

66
hikyuu_cpp/demo/demo2.cpp Normal file
View File

@ -0,0 +1,66 @@
// demo.cpp : 定义控制台应用程序的入口点。
//
#include <hikyuu/hikyuu.h>
#include <thread>
#include <chrono>
#if defined(_WIN32)
#include <Windows.h>
#endif
using namespace hku;
static void changed(const Stock& stk, const SpotRecord& spot) {
HKU_INFO("{} {} 当前收盘价: {}", stk.market_code(), stk.name(), spot.close);
}
static void changed2(const Stock& stk, const SpotRecord& spot) {
if (stk.market_code() == "SZ000001") {
HKU_INFO("strategy 2 process sz000001");
}
}
static void my_process1() {
HKU_INFO("{}", getStock("sh000001"));
}
static void my_process2() {
HKU_INFO("run at time: {} {}", Datetime::now(), getStock("sh000001").name());
}
int main(int argc, char* argv[]) {
#if defined(_WIN32)
// Windows 下设置控制台程序输出代码页为 UTF8
auto old_cp = GetConsoleOutputCP();
SetConsoleOutputCP(CP_UTF8);
#endif
Strategy stg({"sh000001", "sz000001"}, {KQuery::DAY}, "test");
// stock 数据变化接收,通常用于调测,直接一般不需要
stg.onChange(changed);
// 每日开盘期间,按间隔时间循环执行
stg.runDaily(my_process1, Minutes(1));
// 每日定点执行
stg.runDailyAt(my_process2, Datetime::now() - Datetime::today() + Seconds(20));
auto t = std::thread([]() {
// 以线程的方式执行另一个策略
// 注意:同一进程内的所有 strategy 共享的是同一个上下文,
// 即使后续创建的 strategy 指定了新的上下文,但不会生效!!!
Strategy stg2("stratege2");
stg2.onChange(changed2);
stg2.start();
});
// 启动策略
stg.start();
#if defined(_WIN32)
SetConsoleOutputCP(old_cp);
#endif
return 0;
}

View File

@ -1,10 +1,8 @@
target("demo")
target("demo1")
set_kind("binary")
set_default(false)
add_options("hdf5", "mysql", "sqlite", "tdx", "feedback", "stacktrace", "spend_time")
add_packages("boost", "spdlog", "fmt", "flatbuffers")
add_packages("boost", "spdlog", "fmt")
add_includedirs("..")
if is_plat("windows") then
@ -14,13 +12,35 @@ target("demo")
if is_plat("windows") and get_config("kind") == "shared" then
add_defines("HKU_API=__declspec(dllimport)")
add_defines("HKU_UTILS_API=__declspec(dllimport)")
add_defines("SQLITE_API=__declspec(dllimport)")
end
-- add files
add_files("./*.cpp")
add_deps("hikyuu")
add_files("./demo1.cpp")
target_end()
target("demo2")
set_kind("binary")
set_default(false)
add_packages("boost", "spdlog", "fmt")
add_includedirs("..")
if is_plat("windows") then
add_cxflags("-wd4267")
add_cxflags("-wd4251")
end
if is_plat("windows") and get_config("kind") == "shared" then
add_defines("HKU_API=__declspec(dllimport)")
add_defines("HKU_UTILS_API=__declspec(dllimport)")
add_defines("SQLITE_API=__declspec(dllimport)")
end
add_deps("hikyuu")
add_files("./demo2.cpp")
target_end()

View File

@ -29,7 +29,8 @@
#include <map>
#include <unordered_map>
#include "Log.h"
#include "config.h"
#include "utilities/Log.h"
#include "utilities/osdef.h"
#include "utilities/cppdef.h"
#include "utilities/datetime/Datetime.h"

View File

@ -20,7 +20,8 @@
#include <H5public.h>
#endif
#include "Log.h"
#include "utilities/Log.h"
#include "utilities/os.h"
#include "hikyuu.h"
#include "GlobalInitializer.h"
#include "StockManager.h"
@ -56,12 +57,11 @@ void GlobalInitializer::init() {
fmt::print("Initialize hikyuu_{} ...\n", getVersionWithBuild());
#endif
initLogger();
#if defined(_DEBUG) || defined(DEBUG)
set_log_level(LOG_LEVEL::LOG_TRACE);
#else
set_log_level(LOG_LEVEL::LOG_INFO);
#endif
if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) {
initLogger(false, fmt::format("{}/.hikyuu/hikyuu.log", getUserDir()));
} else {
initLogger();
}
#if HKU_ENABLE_SEND_FEEDBACK
sendFeedback();
@ -74,18 +74,20 @@ void GlobalInitializer::init() {
}
void GlobalInitializer::clean() {
if (CanUpgrade()) {
#if HKU_ENABLE_SEND_FEEDBACK
if (runningInPython() && CanUpgrade()) {
fmt::print(
"\n====================================================================\n"
"The new version of Hikyuu is {}, and you can run the upgrade command:\n"
"Hikyuu 的最新版本是 {}, 您可以运行升级命令:\n"
"Hikyuu 的最新版本是 {}, 您可以运行升级命令:\n"
"pip install hikyuu --upgrade\n"
"========================================================\n\n",
getLatestVersion(), getLatestVersion());
}
#endif
releaseGlobalTaskGroup();
releaseScheduler();
releaseGlobalTaskGroup();
releaseGlobalSpotAgent();
IndicatorImp::releaseDynEngine();
@ -103,9 +105,7 @@ void GlobalInitializer::clean() {
H5close();
#endif
#if USE_SPDLOG_LOGGER
spdlog::drop_all();
#endif
#ifdef MSVC_LEAKER_DETECT
// MSVC 内存泄露检测,输出至 VS 的输出窗口

View File

@ -238,7 +238,6 @@ void KDataImp::_recoverBackward() {
Datetime end_date(m_buffer.back().datetime.date() + bd::days(1));
StockWeightList weightList = m_stock.getWeight(start_date, end_date);
StockWeightList::const_reverse_iterator weightIter = weightList.rbegin();
StockWeightList::const_reverse_iterator pre_weightIter;
size_t pre_pos = total - 1;
for (; weightIter != weightList.rend(); ++weightIter) {
@ -360,7 +359,6 @@ void KDataImp::_recoverEqualBackward() {
Datetime end_date(m_buffer.back().datetime.date() + bd::days(1));
StockWeightList weightList = m_stock.getWeight(start_date, end_date);
StockWeightList::const_reverse_iterator weightIter = weightList.rbegin();
StockWeightList::const_reverse_iterator pre_weightIter;
size_t pre_pos = total - 1;
for (; weightIter != weightList.rend(); ++weightIter) {

View File

@ -23,7 +23,7 @@ public:
price_t openPrice; ///< 开盘价
price_t highPrice; ///< 最高价
price_t lowPrice; ///< 最低价
price_t closePrice; ///< 最低
price_t closePrice; ///< 收盘
price_t transAmount; ///< 成交金额(千元)
price_t transCount; ///< 成交量(手),日线以下为股数

View File

@ -1,143 +0,0 @@
/*
* Log.cpp
*
* Created on: 2013-2-1
* Author: fasiondog
*/
#include <thread>
#include "config.h"
#include "utilities/os.h"
#include "GlobalInitializer.h"
#include "Log.h"
#if USE_SPDLOG_LOGGER
// 使用 stdout_color 将无法将日志输出重定向至 python
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
#include "spdlog/sinks/ostream_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#if HKU_USE_SPDLOG_ASYNC_LOGGER
#include <spdlog/async.h>
#endif /* HKU_USE_SPDLOG_ASYNC_LOGGER */
#endif /* #if USE_SPDLOG_LOGGER */
namespace hku {
static std::thread::id g_main_thread_id = std::this_thread::get_id();
bool isLogInMainThread() {
return std::this_thread::get_id() == g_main_thread_id;
}
static LOG_LEVEL g_log_level = LOG_LEVEL::LOG_TRACE;
std::string g_unknown_error_msg{"Unknown error!"};
LOG_LEVEL get_log_level() {
return g_log_level;
}
/**********************************************
* Use SPDLOG for logging
*********************************************/
#if USE_SPDLOG_LOGGER
std::shared_ptr<spdlog::logger> getHikyuuLogger() {
return spdlog::get("hikyuu");
}
#if HKU_USE_SPDLOG_ASYNC_LOGGER
void initLogger(bool inJupyter) {
std::string logname = "hikyuu";
spdlog::drop(logname);
std::shared_ptr<spdlog::logger> logger = spdlog::get(logname);
if (logger) {
spdlog::drop(logname);
}
spdlog::sink_ptr stdout_sink;
if (inJupyter) {
stdout_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(std::cout, true);
} else {
stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
}
stdout_sink->set_level(spdlog::level::trace);
spdlog::init_thread_pool(8192, 1);
std::vector<spdlog::sink_ptr> sinks{stdout_sink};
logger = std::make_shared<spdlog::async_logger>(logname, sinks.begin(), sinks.end(),
spdlog::thread_pool(),
spdlog::async_overflow_policy::block);
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::trace);
logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v [%!]");
spdlog::register_logger(logger);
}
#else /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */
void initLogger(bool inJupyter) {
std::string logname = "hikyuu";
spdlog::drop(logname);
std::shared_ptr<spdlog::logger> logger = spdlog::get(logname);
if (logger) {
spdlog::drop(logname);
}
spdlog::sink_ptr stdout_sink;
if (inJupyter) {
stdout_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(std::cout, true);
} else {
stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
}
stdout_sink->set_level(spdlog::level::trace);
std::string log_filename = fmt::format("{}/.hikyuu/hikyuu.log", getUserDir());
auto rotating_sink =
std::make_shared<spdlog::sinks::rotating_file_sink_mt>(log_filename, 1024 * 1024 * 10, 3);
rotating_sink->set_level(spdlog::level::warn);
std::vector<spdlog::sink_ptr> sinks{stdout_sink, rotating_sink};
logger = std::make_shared<spdlog::logger>(logname, sinks.begin(), sinks.end());
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::trace);
// logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v [%!] (%@)");
// logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v (%@)");
logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v (%s:%#)");
spdlog::register_logger(logger);
}
#endif /* #if HKU_USE_SPDLOG_ASYNC_LOGGER */
void set_log_level(LOG_LEVEL level) {
g_log_level = level;
getHikyuuLogger()->set_level((spdlog::level::level_enum)level);
}
#else /* #if USE_SPDLOG_LOGGER */
/**********************************************
* Use SPDLOG for logging
*********************************************/
void initLogger(bool inJupyter) {}
void set_log_level(LOG_LEVEL level) {
g_log_level = level;
}
std::string HKU_API getLocalTime() {
auto now = std::chrono::system_clock::now();
uint64_t dis_millseconds =
std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() -
std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000;
time_t tt = std::chrono::system_clock::to_time_t(now);
#ifdef _WIN32
struct tm now_time;
localtime_s(&now_time, &tt);
#else
struct tm now_time;
localtime_r(&tt, &now_time);
#endif
return fmt::format("{:%Y-%m-%d %H:%M:%S}.{:<3d}", now_time, dis_millseconds);
}
#endif /* #if USE_SPDLOG_LOGGER */
} // namespace hku

View File

@ -278,7 +278,7 @@ struct HKU_API Stock::Data {
mutable vector<HistoryFinanceInfo>
m_history_finance; // 历史财务信息 [财务报告日期, 字段1, 字段2, ...]
mutable bool m_history_finance_ready{false};
mutable std::atomic_bool m_history_finance_ready{false};
mutable std::mutex m_history_finance_mutex;
price_t m_tick;

View File

@ -15,8 +15,8 @@
#include <boost/lexical_cast.hpp>
#include <boost/algorithm/string.hpp>
#include "utilities/IniParser.h"
#include "utilities/thread/ThreadPool.h"
#include "hikyuu/utilities/ini_parser/IniParser.h"
#include "hikyuu/utilities/thread/ThreadPool.h"
#include "StockManager.h"
#include "global/GlobalTaskGroup.h"
#include "global/schedule/inner_tasks.h"
@ -98,8 +98,14 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa
const Parameter& hikyuuParam, const StrategyContext& context) {
HKU_WARN_IF_RETURN(m_initializing, void(),
"The last initialization has not finished. Please try again later!");
// 防止重复 init
if (m_thread_id != std::thread::id()) {
return;
}
m_initializing = true;
m_thread_id = std::this_thread::get_id();
m_baseInfoDriverParam = baseInfoParam;
m_blockDriverParam = blockParam;
m_kdataDriverParam = kdataParam;
@ -134,7 +140,12 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa
HKU_INFO("Loading KData...");
std::chrono::system_clock::time_point start_time = std::chrono::system_clock::now();
setKDataDriver(DataDriverFactory::getKDataDriverPool(m_kdataDriverParam));
auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam);
HKU_CHECK(driver, "driver is null!");
if (m_kdataDriverParam != driver->getPrototype()->getParameter()) {
m_kdataDriverParam = driver->getPrototype()->getParameter();
}
setKDataDriver(driver);
// 加载 block须在 stock 的 kdatadriver 被设置之后调用
m_blockDriver->load();
@ -142,6 +153,11 @@ void StockManager::init(const Parameter& baseInfoParam, const Parameter& blockPa
// 加载 K 线至缓存
loadAllKData();
// 加载历史财务信息
loadHistoryFinance();
initInnerTask();
// add special Market, for temp csv file
m_marketInfoDict["TMP"] =
MarketInfo("TMP", "Temp Csv file", "temp load from csv file", "000001", Null<Datetime>(),
@ -161,114 +177,47 @@ void StockManager::setKDataDriver(const KDataDriverConnectPoolPtr& driver) {
}
void StockManager::loadAllKData() {
auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam);
HKU_ERROR_IF_RETURN(!driver, void(), "kdata driver is null!");
if (m_kdataDriverParam != driver->getPrototype()->getParameter()) {
m_kdataDriverParam = driver->getPrototype()->getParameter();
const auto& ktypes = KQuery::getAllKType();
vector<string> low_ktypes;
low_ktypes.reserve(ktypes.size());
for (const auto& ktype : ktypes) {
auto& back = low_ktypes.emplace_back(ktype);
to_lower(back);
HKU_INFO_IF(m_preloadParam.tryGet<bool>(back, false), "Preloading all {} kdata to buffer!",
back);
}
bool preload_day = m_preloadParam.tryGet<bool>("day", false);
HKU_INFO_IF(preload_day, "Preloading all day kdata to buffer!");
bool preload_week = m_preloadParam.tryGet<bool>("week", false);
HKU_INFO_IF(preload_week, "Preloading all week kdata to buffer!");
bool preload_month = m_preloadParam.tryGet<bool>("month", false);
HKU_INFO_IF(preload_month, "Preloading all month kdata to buffer!");
bool preload_quarter = m_preloadParam.tryGet<bool>("quarter", false);
HKU_INFO_IF(preload_quarter, "Preloading all quarter kdata to buffer!");
bool preload_halfyear = m_preloadParam.tryGet<bool>("halfyear", false);
HKU_INFO_IF(preload_halfyear, "Preloading all halfyear kdata to buffer!");
bool preload_year = m_preloadParam.tryGet<bool>("year", false);
HKU_INFO_IF(preload_year, "Preloading all year kdata to buffer!");
bool preload_min = m_preloadParam.tryGet<bool>("min", false);
HKU_INFO_IF(preload_min, "Preloading all 1 min kdata to buffer!");
bool preload_min5 = m_preloadParam.tryGet<bool>("min5", false);
HKU_INFO_IF(preload_min5, "Preloading all 5 min kdata to buffer!");
bool preload_min15 = m_preloadParam.tryGet<bool>("min15", false);
HKU_INFO_IF(preload_min15, "Preloading all 15 min kdata to buffer!");
bool preload_min30 = m_preloadParam.tryGet<bool>("min30", false);
HKU_INFO_IF(preload_min30, "Preloading all 30 min kdata to buffer!");
bool preload_min60 = m_preloadParam.tryGet<bool>("min60", false);
HKU_INFO_IF(preload_min60, "Preloading all 60 min kdata to buffer!");
bool preload_hour2 = m_preloadParam.tryGet<bool>("hour2", false);
HKU_INFO_IF(preload_hour2, "Preloading all 120 min kdata to buffer!");
auto driver = DataDriverFactory::getKDataDriverPool(m_kdataDriverParam);
if (!driver->getPrototype()->canParallelLoad()) {
for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
if (preload_day)
iter->second.loadKDataToBuffer(KQuery::DAY);
if (preload_week)
iter->second.loadKDataToBuffer(KQuery::WEEK);
if (preload_month)
iter->second.loadKDataToBuffer(KQuery::MONTH);
if (preload_quarter)
iter->second.loadKDataToBuffer(KQuery::QUARTER);
if (preload_halfyear)
iter->second.loadKDataToBuffer(KQuery::HALFYEAR);
if (preload_year)
iter->second.loadKDataToBuffer(KQuery::YEAR);
if (preload_min)
iter->second.loadKDataToBuffer(KQuery::MIN);
if (preload_min5)
iter->second.loadKDataToBuffer(KQuery::MIN5);
if (preload_min15)
iter->second.loadKDataToBuffer(KQuery::MIN15);
if (preload_min30)
iter->second.loadKDataToBuffer(KQuery::MIN30);
if (preload_min60)
iter->second.loadKDataToBuffer(KQuery::MIN60);
if (preload_hour2)
iter->second.loadKDataToBuffer(KQuery::HOUR2);
for (size_t i = 0, len = ktypes.size(); i < len; i++) {
const auto& low_ktype = low_ktypes[i];
if (m_preloadParam.tryGet<bool>(low_ktype, false)) {
iter->second.loadKDataToBuffer(ktypes[i]);
}
}
}
} else {
// 异步并行加载
auto& tg = *getGlobalTaskGroup();
auto* tg = getGlobalTaskGroup();
for (auto iter = m_stockDict.begin(); iter != m_stockDict.end(); ++iter) {
if (preload_day)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::DAY); });
if (preload_week)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::WEEK); });
if (preload_month)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MONTH); });
if (preload_quarter)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::QUARTER); });
if (preload_halfyear)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::HALFYEAR); });
if (preload_year)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::YEAR); });
if (preload_min)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN); });
if (preload_min5)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN5); });
if (preload_min15)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN15); });
if (preload_min30)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN30); });
if (preload_min60)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::MIN60); });
if (preload_hour2)
tg.submit([=]() mutable { iter->second.loadKDataToBuffer(KQuery::HOUR2); });
for (size_t i = 0, len = ktypes.size(); i < len; i++) {
const auto& low_ktype = low_ktypes[i];
if (m_preloadParam.tryGet<bool>(low_ktype, false)) {
tg->submit(
[=, ktype = ktypes[i]]() mutable { iter->second.loadKDataToBuffer(ktype); });
}
}
}
}
initInnerTask();
}
void StockManager::reload() {
loadAllHolidays();
HKU_IF_RETURN(m_initializing, void());
m_initializing = true;
loadAllHolidays();
loadAllMarketInfos();
loadAllStockTypeInfo();
loadAllStocks();
@ -310,6 +259,9 @@ void StockManager::reload() {
}
}
}
loadHistoryFinance();
m_initializing = false;
}
string StockManager::tmpdir() const {
@ -529,6 +481,7 @@ void StockManager::loadAllStocks() {
stock.m_data->m_precision = info.precision;
stock.m_data->m_minTradeNumber = info.minTradeNumber;
stock.m_data->m_maxTradeNumber = info.maxTradeNumber;
stock.m_data->m_history_finance_ready = false;
}
}
}
@ -615,4 +568,12 @@ 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

@ -255,6 +255,9 @@ private:
/** 加载历史财经字段索引 */
void loadHistoryFinanceField();
/** 加载历史财务数据 */
void loadHistoryFinance();
private:
StockManager();

View File

@ -29,11 +29,15 @@ void StrategyContext::setKTypeList(const vector<KQuery::KType>& ktypeList) {
});
}
bool StrategyContext::isAll() const {
bool StrategyContext::isAll() const noexcept {
return std::find_if(m_stockCodeList.begin(), m_stockCodeList.end(), [](string val) {
to_upper(val);
return val == "ALL";
}) != m_stockCodeList.end();
}
bool StrategyContext::isValid() const noexcept {
return m_stockCodeList.empty() || m_ktypeList.empty();
}
} // namespace hku

View File

@ -24,9 +24,11 @@ public:
virtual ~StrategyContext() = default;
bool isAll() const;
bool isAll() const noexcept;
Datetime startDatetime() const {
bool isValid() const noexcept;
Datetime startDatetime() const noexcept {
return m_startDatetime;
}

View File

@ -10,7 +10,7 @@
#include "hikyuu/indicator/Indicator.h"
#include "hikyuu/trade_sys/system/System.h"
#include "hikyuu/trade_manage/Performance.h"
#include "../Log.h"
#include "hikyuu/utilities/Log.h"
namespace hku {

View File

@ -9,8 +9,8 @@
#include <boost/algorithm/string.hpp>
#include "MySQLBaseInfoDriver.h"
#include "hikyuu/utilities/Log.h"
#include "../../../StockManager.h"
#include "../../../Log.h"
#include "../table/MarketInfoTable.h"
#include "../table/StockTypeInfoTable.h"
#include "../table/StockWeightTable.h"
@ -281,12 +281,9 @@ Parameter MySQLBaseInfoDriver::getFinanceInfo(const string &market, const string
<< "f.zhuyinglirun, f.yingshouzhangkuan, f.yingyelirun, f.touzishouyu,"
<< "f.jingyingxianjinliu, f.zongxianjinliu, f.cunhuo, f.lirunzonghe,"
<< "f.shuihoulirun, f.jinglirun, f.weifenpeilirun, f.meigujingzichan,"
<< "f.baoliu2 from stkfinance f, stock s, market m "
<< "where m.market='" << market << "'"
<< " and s.code = '" << code << "'"
<< " and s.marketid = m.marketid"
<< " and f.stockid = s.stockid"
<< " order by updated_date DESC limit 1";
<< "f.baoliu2 from stkfinance f, stock s, market m " << "where m.market='" << market << "'"
<< " and s.code = '" << code << "'" << " and s.marketid = m.marketid"
<< " and f.stockid = s.stockid" << " order by updated_date DESC limit 1";
auto con = m_pool->getConnect();

View File

@ -13,8 +13,8 @@ namespace hku {
struct HistoryFinanceTable {
TABLE_BIND4(HistoryFinanceTable, HistoryFinance, file_date, report_date, market_code, values)
uint64_t file_date;
uint64_t report_date;
uint64_t file_date{0};
uint64_t report_date{0};
std::string market_code;
// std::vector<float> values;
std::vector<char> values;

View File

@ -8,10 +8,9 @@
#include <fstream>
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp>
#include "hikyuu/utilities/Log.h"
#include "KDataTempCsvDriver.h"
#include "../../../Log.h"
namespace hku {
KDataTempCsvDriver::~KDataTempCsvDriver() {}

View File

@ -6,7 +6,7 @@
*/
#include <boost/lexical_cast.hpp>
#include "../../../Log.h"
#include "hikyuu/utilities/Log.h"
#include "MySQLKDataDriver.h"
#include "KRecordTable.h"

View File

@ -137,8 +137,8 @@
* @details
* @ingroup TradeSystem
*
* @defgroup SystemInstance SystemInstance
* @details
* @defgroup Stratgy Strategy
* @details
* @ingroup Hikyuu
*
* @defgroup Agent Agent

View File

@ -171,68 +171,74 @@ void HKU_API startSpotAgent(bool print) {
agent.setPrintFlag(print);
const auto& preloadParam = sm.getPreloadParameter();
if (preloadParam.tryGet<bool>("min", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN));
}
// 防止调用 stopSpotAgent 后重新 startSpotAgent
static std::atomic_bool g_init_spot_agent{false};
if (!g_init_spot_agent) {
const auto& preloadParam = sm.getPreloadParameter();
if (preloadParam.tryGet<bool>("min", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN));
}
if (preloadParam.tryGet<bool>("day", false)) {
agent.addProcess(updateStockDayData);
}
if (preloadParam.tryGet<bool>("day", false)) {
agent.addProcess(updateStockDayData);
}
if (preloadParam.tryGet<bool>("week", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::WEEK));
}
if (preloadParam.tryGet<bool>("week", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::WEEK));
}
if (preloadParam.tryGet<bool>("month", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::MONTH));
}
if (preloadParam.tryGet<bool>("month", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::MONTH));
}
if (preloadParam.tryGet<bool>("quarter", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::QUARTER));
}
if (preloadParam.tryGet<bool>("quarter", false)) {
agent.addProcess(
std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::QUARTER));
}
if (preloadParam.tryGet<bool>("halfyear", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::HALFYEAR));
}
if (preloadParam.tryGet<bool>("halfyear", false)) {
agent.addProcess(
std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::HALFYEAR));
}
if (preloadParam.tryGet<bool>("year", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::YEAR));
}
if (preloadParam.tryGet<bool>("year", false)) {
agent.addProcess(std::bind(updateStockDayUpData, std::placeholders::_1, KQuery::YEAR));
}
if (preloadParam.tryGet<bool>("min5", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN5));
}
if (preloadParam.tryGet<bool>("min5", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN5));
}
if (preloadParam.tryGet<bool>("min15", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN15));
}
if (preloadParam.tryGet<bool>("min15", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN15));
}
if (preloadParam.tryGet<bool>("min30", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN30));
}
if (preloadParam.tryGet<bool>("min30", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN30));
}
if (preloadParam.tryGet<bool>("min60", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN60));
}
if (preloadParam.tryGet<bool>("min3", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN3));
}
if (preloadParam.tryGet<bool>("min60", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN60));
}
if (preloadParam.tryGet<bool>("min3", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::MIN3));
}
if (preloadParam.tryGet<bool>("hour2", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR2));
}
if (preloadParam.tryGet<bool>("hour2", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR2));
}
if (preloadParam.tryGet<bool>("hour4", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR4));
}
if (preloadParam.tryGet<bool>("hour4", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR4));
}
if (preloadParam.tryGet<bool>("hour6", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR6));
}
if (preloadParam.tryGet<bool>("hour6", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR6));
}
if (preloadParam.tryGet<bool>("hour12", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR12));
if (preloadParam.tryGet<bool>("hour12", false)) {
agent.addProcess(std::bind(updateStockMinData, std::placeholders::_1, KQuery::HOUR12));
}
}
agent.start();

View File

@ -5,6 +5,7 @@
* Author: fasiondog
*/
#pragma once
#include "agent/SpotAgent.h"
namespace hku {

View File

@ -8,14 +8,14 @@
*/
#include "hikyuu/GlobalInitializer.h"
#include "hikyuu/utilities/Log.h"
#include "GlobalTaskGroup.h"
#include "../Log.h"
namespace hku {
static StealThreadPool* g_threadPool;
static TaskGroup* g_threadPool;
StealThreadPool* getGlobalTaskGroup() {
TaskGroup* getGlobalTaskGroup() {
static std::once_flag oc;
std::call_once(oc, [&]() {
auto cpu_num = std::thread::hardware_concurrency();
@ -24,7 +24,7 @@ StealThreadPool* getGlobalTaskGroup() {
} else if (cpu_num > 1) {
cpu_num--;
}
g_threadPool = new StealThreadPool(cpu_num);
g_threadPool = new TaskGroup(cpu_num);
});
return g_threadPool;
}

View File

@ -11,15 +11,17 @@
#ifndef HKU_GLOBAL_TASK_GROUP
#define HKU_GLOBAL_TASK_GROUP
#include "../utilities/thread/StealThreadPool.h"
#include "../utilities/thread/thread.h"
namespace hku {
using TaskGroup = ThreadPool;
/**
* 线
* @note 使 future
*/
StealThreadPool* getGlobalTaskGroup();
TaskGroup* getGlobalTaskGroup();
template <typename ResultType>
using task_handle = std::future<ResultType>;

View File

@ -0,0 +1,52 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-08-15
* Author: fasiondog
*/
#pragma once
#include "hikyuu/DataType.h"
namespace hku {
/**
*
* @ingroup Agent
*/
struct HKU_API SpotRecord {
string market; ///< 市场标识
string code; ///< 证券代码
string name; ///< 证券名称
Datetime datetime; ///< 数据时间
price_t yesterday_close; ///< 昨日收盘价
price_t open; ///< 开盘价
price_t high; ///< 最高价
price_t low; ///< 最低价
price_t close; ///< 收盘价
price_t amount; ///< 成交金额 (千元)
price_t volume; ///< 成交量(手)
price_t bid1; ///< 买一价
price_t bid1_amount; ///< 买一数量(手)
price_t bid2; ///< 买二价
price_t bid2_amount; ///< 买二数量
price_t bid3; ///< 买三价
price_t bid3_amount; ///< 买三数量
price_t bid4; ///< 买四价
price_t bid4_amount; ///< 买四数量
price_t bid5; ///< 买五价
price_t bid5_amount; ///< 买五数量
price_t ask1; ///< 卖一价
price_t ask1_amount; ///< 卖一数量
price_t ask2; ///< 卖二价
price_t ask2_amount; ///< 卖二数量
price_t ask3; ///< 卖三价
price_t ask3_amount; ///< 卖三数量
price_t ask4; ///< 卖四价
price_t ask4_amount; ///< 卖四数量
price_t ask5; ///< 卖五价
price_t ask5_amount; ///< 卖五数量
};
} // namespace hku

View File

@ -8,6 +8,7 @@
#include <chrono>
#include <nng/nng.h>
#include <nng/protocol/pubsub0/sub.h>
#include "spot_generated.h"
#include "SpotAgent.h"
using namespace hikyuu::flat;
@ -33,6 +34,7 @@ SpotAgent::~SpotAgent() {
}
void SpotAgent::start() {
stop();
if (m_stop) {
m_stop = false;
m_receiveThread = std::thread([this]() { work_thread(); });
@ -156,13 +158,11 @@ void SpotAgent::work_thread() {
while (!m_stop && rv != 0) {
rv = nng_dial(sock, ms_pubUrl.c_str(), nullptr, 0);
HKU_WARN_IF(m_print && rv != 0,
"Faied connect quotation server {}, will retry after 5 seconds! You Maybe need "
"start the collection service first.",
ms_pubUrl);
"Faied connect quotation server {}, will retry after 5 seconds!", ms_pubUrl);
std::this_thread::sleep_for(std::chrono::seconds(5));
}
HKU_INFO_IF(m_print, "Ready to receive quotation ...");
HKU_INFO_IF(m_print, "Ready to receive quotation from {} ...", ms_pubUrl);
while (!m_stop) {
char* buf = nullptr;
@ -186,7 +186,7 @@ void SpotAgent::work_thread() {
for (auto& task : m_process_task_list) {
task.get();
}
HKU_INFO_IF(m_print, "received count: {}", m_batch_count);
HKU_TRACE_IF(m_print, "received count: {}", m_batch_count);
m_batch_count = 0;
// 执行后处理
for (const auto& postProcess : m_postProcessList) {
@ -215,21 +215,25 @@ void SpotAgent::work_thread() {
void SpotAgent::addProcess(std::function<void(const SpotRecord&)> process) {
HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!");
std::lock_guard<std::mutex> lock(m_mutex);
m_processList.push_back(process);
}
void SpotAgent::addPostProcess(std::function<void(Datetime)> func) {
HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!");
std::lock_guard<std::mutex> lock(m_mutex);
m_postProcessList.push_back(func);
}
void SpotAgent::clearProcessList() {
HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!");
std::lock_guard<std::mutex> lock(m_mutex);
m_processList.clear();
}
void SpotAgent::clearPostProcessList() {
HKU_CHECK(m_stop, "SpotAgent is running, please stop agent first!");
std::lock_guard<std::mutex> lock(m_mutex);
m_postProcessList.clear();
}

View File

@ -9,50 +9,18 @@
#include <thread>
#include <functional>
#include "spot_generated.h"
#include "../../DataType.h"
#include "../../utilities/thread/ThreadPool.h"
#include "../SpotRecord.h"
namespace hikyuu {
namespace flat {
struct Spot;
}
} // namespace hikyuu
namespace hku {
/**
*
* @ingroup Agent
*/
struct HKU_API SpotRecord {
string market; ///< 市场标识
string code; ///< 证券代码
string name; ///< 证券名称
Datetime datetime; ///< 数据时间
price_t yesterday_close; ///< 昨日收盘价
price_t open; ///< 开盘价
price_t high; ///< 最高价
price_t low; ///< 最低价
price_t close; ///< 收盘价
price_t amount; ///< 成交金额 (千元)
price_t volume; ///< 成交量(手)
price_t bid1; ///< 买一价
price_t bid1_amount; ///< 买一数量(手)
price_t bid2; ///< 买二价
price_t bid2_amount; ///< 买二数量
price_t bid3; ///< 买三价
price_t bid3_amount; ///< 买三数量
price_t bid4; ///< 买四价
price_t bid4_amount; ///< 买四数量
price_t bid5; ///< 买五价
price_t bid5_amount; ///< 买五数量
price_t ask1; ///< 卖一价
price_t ask1_amount; ///< 卖一数量
price_t ask2; ///< 卖二价
price_t ask2_amount; ///< 卖二数量
price_t ask3; ///< 卖三价
price_t ask3_amount; ///< 卖三数量
price_t ask4; ///< 卖四价
price_t ask4_amount; ///< 卖四数量
price_t ask5; ///< 卖五价
price_t ask5_amount; ///< 卖五数量
};
/**
*
* @ingroup Agent
@ -77,6 +45,7 @@ public:
/** 设置是否打印数据接收进展情况,主要用于在交互环境下关闭打印 */
void setPrintFlag(bool print) {
std::lock_guard<std::mutex> lock(m_mutex);
m_print = print;
}
@ -136,14 +105,18 @@ private:
enum STATUS { WAITING, RECEIVING }; // 等待新的批次数据,正在接收批次数据中
enum STATUS m_status = WAITING; // 当前内部状态
std::atomic_bool m_stop = true; // 结束代理工作标识
bool m_print = true; // 是否打印接收进度,防止的交互模式的影响
int m_revTimeout = 100; // 连接数据服务超时时长(毫秒)
size_t m_batch_count = 0; // 记录本次批次接收的数据数量
std::thread m_receiveThread; // 数据接收线程
ThreadPool m_tg; // 数据处理任务线程池
vector<std::future<void>> m_process_task_list;
// 下面属性被修改时需要加锁,以便可以使用多线程方式运行 strategy
std::mutex m_mutex;
bool m_print = true; // 是否打印接收进度,防止的交互模式的影响
list<std::function<void(const SpotRecord&)>> m_processList; // 已注册的 spot 处理函数列表
list<std::function<void(Datetime)>> m_postProcessList; // 已注册的批次后处理函数列表
vector<std::future<void>> m_process_task_list;
};
} // namespace hku

View File

@ -15,7 +15,6 @@ namespace hku {
void initInnerTask() {
auto* tm = getScheduler();
tm->addFuncAtTimeEveryDay(Datetime::min(), Datetime::max(), TimeDelta(), reloadHikyuuTask);
tm->start();
}
void reloadHikyuuTask() {

View File

@ -5,10 +5,11 @@
* Author: fasiondog
*/
#include <hikyuu/GlobalInitializer.h>
#include "hikyuu/GlobalInitializer.h"
#include <mutex>
#include "hikyuu/utilities/Log.h"
#include "hikyuu/global/GlobalTaskGroup.h"
#include "scheduler.h"
#include "../../Log.h"
namespace hku {
@ -16,7 +17,8 @@ static TimerManager *g_scheduler;
TimerManager *getScheduler() {
static std::once_flag oc;
std::call_once(oc, [&]() { g_scheduler = new TimerManager(2); });
// 使用内部公共任务组,减少内部线程
std::call_once(oc, [&]() { g_scheduler = new TimerManager(getGlobalTaskGroup()); });
return g_scheduler;
}

View File

@ -10,17 +10,16 @@
#include <boost/uuid/uuid.hpp>
#include <boost/uuid/uuid_io.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <httplib.h>
#include <nlohmann/json.hpp>
#include "hikyuu/version.h"
#include "hikyuu/DataType.h"
#include "hikyuu/utilities/os.h"
#include "node/NodeClient.h"
#include "hikyuu/utilities/http_client/HttpClient.h"
#include "sysinfo.h"
using json = nlohmann::json;
#define FEEDBACK_SERVER_ADDR "tcp://1.tcp.cpolar.cn:20981"
#define FEEDBACK_SERVER_ADDR "http://hikyuu.cpolar.cn"
namespace hku {
@ -51,7 +50,11 @@ bool HKU_API pythonInJupyter() {
void HKU_API setPythonInJupyter(bool injupyter) {
g_pythonInJupyter = injupyter;
initLogger(injupyter);
if (createDir(fmt::format("{}/.hikyuu", getUserDir()))) {
initLogger(injupyter, fmt::format("{}/.hikyuu/hikyuu.log", getUserDir()));
} else {
initLogger(injupyter);
}
}
bool HKU_API CanUpgrade() {
@ -113,27 +116,17 @@ void sendFeedback() {
saveUUID(uid);
}
NodeClient client(FEEDBACK_SERVER_ADDR);
client.dial();
json req, res;
req["cmd"] = 2;
client.post(req, res);
std::string host = res["host"].get<std::string>();
uint64_t port = res["port"].get<uint64_t>();
g_latest_version = res.contains("last_version") ? res["last_version"].get<int>() : 0;
client.close();
client.setServerAddr(fmt::format("tcp://{}:{}", host, port));
client.dial();
req["cmd"] = 1;
HttpClient client(FEEDBACK_SERVER_ADDR, 2000);
json req;
req["uid"] = boost::uuids::to_string(uid);
req["part"] = "hikyuu";
req["version"] = HKU_VERSION;
req["build"] = fmt::format("{}", HKU_VERSION_BUILD);
req["platform"] = getPlatform();
req["arch"] = getCpuArch();
client.post(req, res);
auto res = client.post("/hku/visit", req);
json r = res.json();
g_latest_version = r["data"]["last_version"].get<int>();
} catch (...) {
// do nothing
@ -146,24 +139,12 @@ void sendFeedback() {
void sendPythonVersionFeedBack(int major, int minor, int micro) {
std::thread t([=]() {
try {
NodeClient client(FEEDBACK_SERVER_ADDR);
client.dial();
json req, res;
req["cmd"] = 2;
client.post(req, res);
std::string host = res["host"].get<std::string>();
uint64_t port = res["port"].get<uint64_t>();
g_latest_version = res.contains("last_version") ? res["last_version"].get<int>() : 0;
client.close();
client.setServerAddr(fmt::format("tcp://{}:{}", host, port));
client.dial();
req["cmd"] = 3;
HttpClient client(FEEDBACK_SERVER_ADDR, 2000);
json req;
req["major"] = major;
req["minor"] = minor;
req["micro"] = micro;
client.post(req, res);
client.post("/hku/pyver", req);
} catch (...) {
// do nothing
}

View File

@ -11,7 +11,7 @@
#include <set>
#include <fmt/format.h>
#include "utilities/IniParser.h"
#include "utilities/ini_parser/IniParser.h"
#include "hikyuu.h"
#include "version.h"
@ -40,8 +40,8 @@ void hikyuu_init(const string& config_file_name, bool ignore_preload,
hkuParam.set<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<string>("quotation_server",
config.get("hikyuu", "quotation_server", "ipc:///tmp/hikyuu_real.ipc"));
if (!config.hasSection("baseinfo")) {
HKU_FATAL("Missing configure of baseinfo!");
@ -73,17 +73,17 @@ void hikyuu_init(const string& config_file_name, bool ignore_preload,
for (auto iter = option->begin(); iter != option->end(); ++iter) {
try {
preloadParam.set<bool>(*iter,
ignore_preload ? false : config.getBool("preload", *iter));
} catch (...) {
if (!ignore_preload) {
// 获取预加载的最大数量
try {
preloadParam.set<int>(*iter, config.getInt("preload", *iter));
} catch (...) {
HKU_WARN("Invalid option: {}", *iter);
}
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));
}
} catch (const std::exception& e) {
HKU_ERROR("proload param ({}) error! {}!", *iter, e.what());
} catch (...) {
HKU_ERROR("proload param ({})! Unknown error!", *iter);
}
}

View File

@ -16,6 +16,7 @@
#include "indicator/build_in.h"
#include "trade_manage/build_in.h"
#include "trade_sys/all.h"
#include "strategy/Strategy.h"
namespace hku {

View File

@ -7,10 +7,10 @@
#include <stdexcept>
#include <algorithm>
#include <forward_list>
#include "hikyuu/utilities/Log.h"
#include "Indicator.h"
#include "IndParam.h"
#include "../Stock.h"
#include "../Log.h"
#include "../GlobalInitializer.h"
#include "imp/ICval.h"

View File

@ -1,84 +0,0 @@
/*
* Copyright(C) 2021 hikyuu.org
*
* Create on: 2021-03-23
* Author: fasiondog
*/
#include <nlohmann/json.hpp>
#include "../utilities/arithmetic.h"
#include "AccountTradeManager.h"
using nlohmann::json;
namespace hku {
#define HKU_SERVER_URL "http://localhost:9001"
#define HKU_SERVER_LOGIN_API "/hku/account/v1/login"
#define HTTP_STATUS_OK 200
#define HTTP_STATUS_BAD_REQUEST 400
static string getHttpClientErrorMsg(httplib::Error err) {
string result;
if (httplib::Error::Success == err) {
result = "Success";
} else if (httplib::Error::Unknown == err) {
result = "Unknonw error";
} else if (httplib::Error::Connection == err) {
result = "Connection error";
} else if (httplib::Error::BindIPAddress == err) {
result = "BindIPAddress error";
} else if (httplib::Error::Read == err) {
result = "Read error";
} else if (httplib::Error::Write == err) {
result = "Write error";
} else if (httplib::Error::ExceedRedirectCount == err) {
result = "ExceedRedirectCount error";
} else if (httplib::Error::Canceled == err) {
result = "Canceled error";
} else if (httplib::Error::SSLConnection == err) {
result = "SSLConnection error";
} else if (httplib::Error::SSLLoadingCerts == err) {
result = "SSLLoadingCerts error";
} else if (httplib::Error::SSLServerVerification == err) {
result = "SSLServerVerification error";
} else if (httplib::Error::UnsupportedMultipartBoundaryChars == err) {
result = "UnsupportedMultipartBoundaryChars error";
} else if (httplib::Error::Compression == err) {
result = "Compression error";
} else {
result = "Other unknonw error";
}
return result;
}
AccountTradeManager::AccountTradeManager(const string& name, const string& pwd)
: TradeManagerBase(name, TC_Zero()),
m_client(std::make_unique<httplib::Client>(HKU_SERVER_URL)),
m_user(name),
m_password(pwd) {
trim(m_user);
if (m_user.empty()) {
HKU_ERROR("User name is empty.");
return;
}
string req(fmt::format(R"({{"user":"{}", "password":"{}"}})", name, pwd));
auto res = m_client->Post(HKU_SERVER_LOGIN_API, req.c_str(), req.size(), "application/json");
if (!res) {
HKU_ERROR("http client err: {}", getHttpClientErrorMsg(res.error()));
return;
}
if (HTTP_STATUS_OK == res->status) {
auto data = json::parse(res->body);
m_token = data["hku_token"].get<string>();
} else if (HTTP_STATUS_BAD_REQUEST == res->status) {
HKU_ERROR("Bad request: {}", res->body);
} else {
HKU_ERROR("http response status: {}", res->status);
}
}
} // namespace hku

View File

@ -0,0 +1,282 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-08-16
* Author: fasiondog
*/
#include <nlohmann/json.hpp>
#include "hikyuu/trade_manage/crt/TC_Zero.h"
#include "BrokerTradeManager.h"
namespace hku {
using json = nlohmann::json;
BrokerTradeManager::BrokerTradeManager(const OrderBrokerPtr& broker, const TradeCostPtr& costfunc,
const string& name)
: TradeManagerBase(name, costfunc) {
HKU_ASSERT(broker);
m_broker_list.emplace_back(broker);
m_datetime = Datetime::now();
m_broker_last_datetime = m_datetime;
}
void BrokerTradeManager::_reset() {
HKU_WARN("The subclass does not implement a reset method");
m_datetime = Datetime::max();
m_cash = 0.0;
m_position.clear();
}
shared_ptr<TradeManagerBase> BrokerTradeManager::_clone() {
BrokerTradeManager* p = new BrokerTradeManager();
p->m_datetime = m_datetime;
p->m_cash = m_cash;
p->m_position = m_position;
return shared_ptr<TradeManagerBase>(p);
}
void BrokerTradeManager::fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) {
HKU_CHECK(broker, "broker is null!");
auto brk_asset = broker->getAssetInfo();
if (brk_asset.empty()) {
m_datetime = Datetime::now();
m_cash = 0.0;
m_position.clear();
return;
}
try {
json asset(brk_asset);
m_datetime = asset.contains("datetime")
? m_datetime = Datetime(asset["datetime"].get<string>())
: m_datetime = Datetime::now();
m_cash = asset["cash"];
auto& positions = asset["positions"];
for (auto iter = positions.cbegin(); iter != positions.cend(); ++iter) {
const auto& jpos = *iter;
auto market = jpos["market"].get<string>();
auto code = jpos["code"].get<string>();
Stock stock = getStock(fmt::format("{}{}", market, code));
if (stock.isNull()) {
HKU_WARN("Not found stock: {}{}", market, code);
continue;
}
PositionRecord pos;
pos.stock = stock;
pos.takeDatetime = m_datetime;
pos.number = jpos["number"].get<double>();
pos.stoploss = jpos["stoploss"].get<price_t>();
pos.goalPrice = jpos["goal_price"].get<price_t>();
pos.totalNumber = pos.number;
price_t cost_price = jpos["cost_price"].get<price_t>();
pos.buyMoney = pos.number * cost_price;
pos.totalRisk = (pos.stoploss - cost_price) * pos.number;
m_position[stock.id()] = pos;
}
} catch (const std::exception& e) {
HKU_ERROR(e.what());
}
m_broker_last_datetime = m_datetime;
}
PositionRecordList BrokerTradeManager::getPositionList() const {
PositionRecordList result;
position_map_type::const_iterator iter = m_position.begin();
for (; iter != m_position.end(); ++iter) {
result.push_back(iter->second);
}
return result;
}
bool BrokerTradeManager::checkin(const Datetime& datetime, price_t cash) {
HKU_IF_RETURN(datetime < m_datetime, false);
m_cash += cash;
return true;
}
TradeRecord BrokerTradeManager::buy(const Datetime& datetime, const Stock& stock, price_t realPrice,
double number, price_t stoploss, price_t goalPrice,
price_t planPrice, SystemPart from) {
TradeRecord result;
result.business = BUSINESS_INVALID;
HKU_ERROR_IF_RETURN(stock.isNull(), result, "{} Stock is Null!", datetime);
HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result,
"{} {} datetime must be >= lastDatetime({})!", datetime,
stock.market_code(), lastDatetime());
HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} numer is zero!", datetime,
stock.market_code());
HKU_ERROR_IF_RETURN(number < stock.minTradeNumber(), result,
"{} {} Buy number({}) must be >= minTradeNumber({})!", datetime,
stock.market_code(), number, stock.minTradeNumber());
HKU_ERROR_IF_RETURN(number > stock.maxTradeNumber(), result,
"{} {} Buy number({}) must be <= maxTradeNumber({})!", datetime,
stock.market_code(), number, stock.maxTradeNumber());
CostRecord cost = getBuyCost(datetime, stock, realPrice, number);
// 实际交易需要的现金=交易数量*实际交易价格+交易总成本
int precision = getParam<int>("precision");
// price_t money = roundEx(realPrice * number * stock.unit() + cost.total, precision);
price_t money = roundEx(realPrice * number * stock.unit(), precision);
HKU_WARN_IF_RETURN(m_cash < roundEx(money + cost.total, precision), result,
"{} {} Can't buy, need cash({:<.4f}) > current cash({:<.4f})!", datetime,
stock.market_code(), roundEx(money + cost.total, precision), m_cash);
// 更新现金
m_cash = roundEx(m_cash - money - cost.total, precision);
// 加入交易记录
result = TradeRecord(stock, datetime, BUSINESS_BUY, planPrice, realPrice, goalPrice, number,
cost, stoploss, m_cash, from);
// 更新当前持仓记录
position_map_type::iterator pos_iter = m_position.find(stock.id());
if (pos_iter == m_position.end()) {
m_position[stock.id()] = PositionRecord(
stock, datetime, Null<Datetime>(), number, stoploss, goalPrice, number, money, cost.total,
roundEx((realPrice - stoploss) * number * stock.unit(), precision), 0.0);
} else {
PositionRecord& position = pos_iter->second;
position.number += number;
position.stoploss = stoploss;
position.goalPrice = goalPrice;
position.totalNumber += number;
position.buyMoney = roundEx(money + position.buyMoney, precision);
position.totalCost = roundEx(cost.total + position.totalCost, precision);
position.totalRisk =
roundEx(position.totalRisk + (realPrice - stoploss) * number * stock.unit(), precision);
}
if (datetime > m_broker_last_datetime) {
list<OrderBrokerPtr>::const_iterator broker_iter = m_broker_list.begin();
for (; broker_iter != m_broker_list.end(); ++broker_iter) {
(*broker_iter)
->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice,
from);
if (datetime > m_broker_last_datetime) {
m_broker_last_datetime = datetime;
}
}
}
return result;
}
TradeRecord BrokerTradeManager::sell(const Datetime& datetime, const Stock& stock,
price_t realPrice, double number, price_t stoploss,
price_t goalPrice, price_t planPrice, SystemPart from) {
HKU_CHECK(!std::isnan(number), "sell number should be a valid double!");
TradeRecord result;
HKU_ERROR_IF_RETURN(stock.isNull(), result, "{} Stock is Null!", datetime);
HKU_ERROR_IF_RETURN(datetime < lastDatetime(), result,
"{} {} datetime must be >= lastDatetime({})!", datetime,
stock.market_code(), lastDatetime());
HKU_ERROR_IF_RETURN(number == 0.0, result, "{} {} number is zero!", datetime,
stock.market_code());
// 对于分红扩股造成不满足最小交易量整数倍的情况只能通过number=MAX_DOUBLE的方式全仓卖出
HKU_ERROR_IF_RETURN(number < stock.minTradeNumber(), result,
"{} {} Sell number({}) must be >= minTradeNumber({})!", datetime,
stock.market_code(), number, stock.minTradeNumber());
HKU_ERROR_IF_RETURN(number != MAX_DOUBLE && number > stock.maxTradeNumber(), result,
"{} {} Sell number({}) must be <= maxTradeNumber({})!", datetime,
stock.market_code(), number, stock.maxTradeNumber());
// 未持仓
position_map_type::iterator pos_iter = m_position.find(stock.id());
HKU_TRACE_IF_RETURN(pos_iter == m_position.end(), result,
"{} {} This stock was not bought never! ({}, {:<.4f}, {}, {})", datetime,
stock.market_code(), datetime, realPrice, number, getSystemPartName(from));
PositionRecord& position = pos_iter->second;
// 调整欲卖出的数量如果卖出数量等于MAX_DOUBLE则表示卖出全部
double real_number = number == MAX_DOUBLE ? position.number : number;
// 欲卖出的数量大于当前持仓的数量
HKU_ERROR_IF_RETURN(position.number < real_number, result,
"{} {} Try to sell number({}) > number of position({})!", datetime,
stock.market_code(), real_number, position.number);
CostRecord cost = getSellCost(datetime, stock, realPrice, real_number);
int precision = getParam<int>("precision");
price_t money = roundEx(realPrice * real_number * stock.unit(), precision);
// 更新现金余额
m_cash = roundEx(m_cash + money - cost.total, precision);
// 更新交易记录
result = TradeRecord(stock, datetime, BUSINESS_SELL, planPrice, realPrice, goalPrice,
real_number, cost, stoploss, m_cash, from);
// 更新当前持仓情况
position.number -= real_number;
position.stoploss = stoploss;
position.goalPrice = goalPrice;
// position.buyMoney = position.buyMoney;
position.totalCost = roundEx(position.totalCost + cost.total, precision);
position.sellMoney = roundEx(position.sellMoney + money, precision);
if (position.number == 0) {
// 删除当前持仓
m_position.erase(stock.id());
}
if (datetime > m_broker_last_datetime) {
list<OrderBrokerPtr>::const_iterator broker_iter = m_broker_list.begin();
for (; broker_iter != m_broker_list.end(); ++broker_iter) {
(*broker_iter)
->sell(datetime, stock.market(), stock.code(), realPrice, real_number, stoploss,
goalPrice, from);
if (datetime > m_broker_last_datetime) {
m_broker_last_datetime = datetime;
}
}
}
return result;
}
FundsRecord BrokerTradeManager::getFunds(KQuery::KType inktype) const {
FundsRecord funds;
int precision = getParam<int>("precision");
string ktype(inktype);
to_upper(ktype);
price_t value{0.0}; // 当前市值
position_map_type::const_iterator iter = m_position.begin();
for (; iter != m_position.end(); ++iter) {
const PositionRecord& record = iter->second;
auto price = record.stock.getMarketValue(lastDatetime(), ktype);
value = roundEx((value + record.number * price * record.stock.unit()), precision);
}
funds.cash = m_cash;
funds.market_value = value;
funds.short_market_value = 0.0;
funds.base_cash = m_cash;
funds.base_asset = 0.0;
funds.borrow_cash = 0.0;
funds.borrow_asset = 0.0;
return funds;
}
FundsRecord BrokerTradeManager::getFunds(const Datetime& datetime, KQuery::KType ktype) {
return (datetime >= m_datetime) ? getFunds(ktype) : FundsRecord();
}
} // namespace hku

View File

@ -1,28 +1,36 @@
/*
* Copyright(C) 2021 hikyuu.org
* Copyright (c) 2024 hikyuu.org
*
* Create on: 2021-03-23
* Author: fasiondog
* Created on: 2024-08-16
* Author: fasiondog
*/
#pragma once
#include <httplib.h>
#include "../trade_manage/TradeManagerBase.h"
#include "hikyuu/trade_manage/TradeManagerBase.h"
namespace hku {
class HKU_API AccountTradeManager : public TradeManagerBase {
class HKU_API BrokerTradeManager : public TradeManagerBase {
public:
AccountTradeManager() = default;
AccountTradeManager(const string& name, const string& pwd);
virtual ~AccountTradeManager() = default;
BrokerTradeManager() = default;
explicit BrokerTradeManager(const OrderBrokerPtr& broker,
const TradeCostPtr& costfunc = TC_Zero(),
const string& name = "SYS");
virtual ~BrokerTradeManager() {}
virtual void _reset() override {}
virtual void _reset() override;
virtual shared_ptr<TradeManagerBase> _clone() override {
return std::make_shared<AccountTradeManager>();
}
virtual shared_ptr<TradeManagerBase> _clone() override;
virtual void fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) override;
/**
*
* @note
* @param datetime
*/
virtual void updateWithWeight(const Datetime& datetime) override {}
/**
*
@ -36,26 +44,22 @@ public:
/** 初始资金 */
virtual price_t initCash() const override {
HKU_WARN("The subclass does not implement this method");
return 0.0;
return m_cash;
}
/** 账户建立日期 */
virtual Datetime initDatetime() const override {
HKU_WARN("The subclass does not implement this method");
return Datetime();
return m_datetime;
}
/** 第一笔买入交易发生日期如未发生交易返回Null<Datetime>() */
virtual Datetime firstDatetime() const override {
HKU_WARN("The subclass does not implement this method");
return Datetime();
return m_datetime;
}
/** 最后一笔交易日期,注意和交易类型无关,如未发生交易返回账户建立日期 */
virtual Datetime lastDatetime() const override {
HKU_WARN("The subclass does not implement this method");
return Datetime();
return m_datetime;
}
/**
@ -63,8 +67,7 @@ public:
* @note
*/
virtual price_t currentCash() const override {
HKU_WARN("The subclass does not implement this method");
return 0.0;
return m_cash;
}
/**
@ -72,8 +75,7 @@ public:
* @note
*/
virtual price_t cash(const Datetime& datetime, KQuery::KType ktype = KQuery::DAY) override {
HKU_WARN("The subclass does not implement this method");
return 0.0;
return (datetime >= m_datetime) ? currentCash() : 0.0;
}
/**
@ -83,8 +85,7 @@ public:
* @return true | false
*/
virtual bool have(const Stock& stock) const override {
HKU_WARN("The subclass does not implement this method");
return false;
return m_position.count(stock.id()) ? true : false;
}
/**
@ -93,49 +94,48 @@ public:
* @param stock
* @return true | false
*/
virtual bool haveShort(const Stock& stock) const override {
virtual bool haveShort(const Stock& stock) const {
HKU_WARN("The subclass does not implement this method");
return false;
}
/** 当前持有的证券种类数量 */
virtual size_t getStockNumber() const override {
HKU_WARN("The subclass does not implement this method");
return 0;
virtual size_t getStockNumber() const {
return m_position.size();
}
/** 当前空头持有的证券种类数量 */
virtual size_t getShortStockNumber() const override {
virtual size_t getShortStockNumber() const {
HKU_WARN("The subclass does not implement this method");
return 0;
}
/** 获取指定时刻的某证券持有数量 */
virtual double getHoldNumber(const Datetime& datetime, const Stock& stock) override {
virtual double getHoldNumber(const Datetime& datetime, const Stock& stock) {
HKU_WARN("The subclass does not implement this method");
return 0.0;
}
/** 获取指定时刻的空头某证券持有数量 */
virtual double getShortHoldNumber(const Datetime& datetime, const Stock& stock) override {
virtual double getShortHoldNumber(const Datetime& datetime, const Stock& stock) {
HKU_WARN("The subclass does not implement this method");
return 0.0;
}
/** 获取指定时刻已借入的股票数量 */
virtual double getDebtNumber(const Datetime& datetime, const Stock& stock) override {
virtual double getDebtNumber(const Datetime& datetime, const Stock& stock) {
HKU_WARN("The subclass does not implement this method");
return 0.0;
}
/** 获取指定时刻已借入的现金额 */
virtual price_t getDebtCash(const Datetime& datetime) override {
virtual price_t getDebtCash(const Datetime& datetime) {
HKU_WARN("The subclass does not implement this method");
return 0.0;
}
/** 获取全部交易记录 */
virtual TradeRecordList getTradeList() const override {
virtual TradeRecordList getTradeList() const {
HKU_WARN("The subclass does not implement this method");
return TradeRecordList();
}
@ -146,50 +146,54 @@ public:
* @param end
* @return
*/
virtual TradeRecordList getTradeList(const Datetime& start,
const Datetime& end) const override {
virtual TradeRecordList getTradeList(const Datetime& start, const Datetime& end) const {
HKU_WARN("The subclass does not implement this method");
return TradeRecordList();
}
/** 获取当前全部持仓记录 */
virtual PositionRecordList getPositionList() const override {
HKU_WARN("The subclass does not implement this method");
return PositionRecordList();
}
virtual PositionRecordList getPositionList() const override;
/** 获取全部历史持仓记录,即已平仓记录 */
virtual PositionRecordList getHistoryPositionList() const override {
virtual PositionRecordList getHistoryPositionList() const {
HKU_WARN("The subclass does not implement this method");
return PositionRecordList();
}
/** 获取当前全部空头仓位记录 */
virtual PositionRecordList getShortPositionList() const override {
virtual PositionRecordList getShortPositionList() const {
HKU_WARN("The subclass does not implement this method");
return PositionRecordList();
}
/** 获取全部空头历史仓位记录 */
virtual PositionRecordList getShortHistoryPositionList() const override {
virtual PositionRecordList getShortHistoryPositionList() const {
HKU_WARN("The subclass does not implement this method");
return PositionRecordList();
}
/** 获取指定证券的当前持仓记录如当前未持有该票返回Null<PositionRecord>() */
virtual PositionRecord getPosition(const Datetime&, const Stock&) override {
/**
*
* @param date
* @param stock
*/
virtual PositionRecord getPosition(const Datetime& date, const Stock& stock) {
HKU_WARN("The subclass does not implement this method");
return PositionRecord();
}
/** 获取指定证券的当前空头仓位持仓记录如当前未持有该票返回Null<PositionRecord>() */
virtual PositionRecord getShortPosition(const Stock&) const override {
/**
*
* @param date
* @param stock
*/
virtual PositionRecord getShortPosition(const Stock&) const {
HKU_WARN("The subclass does not implement this method");
return PositionRecord();
}
/** 获取当前借入的股票列表 */
virtual BorrowRecordList getBorrowStockList() const override {
virtual BorrowRecordList getBorrowStockList() const {
HKU_WARN("The subclass does not implement this method");
return BorrowRecordList();
}
@ -200,10 +204,7 @@ public:
* @param cash
* @return true | false
*/
virtual bool checkin(const Datetime& datetime, price_t cash) override {
HKU_WARN("The subclass does not implement this method");
return false;
}
virtual bool checkin(const Datetime& datetime, price_t cash) override;
/**
*
@ -212,8 +213,8 @@ public:
* @return true | false
*/
virtual bool checkout(const Datetime& datetime, price_t cash) override {
HKU_WARN("The subclass does not implement this method");
return false;
m_cash = (cash > m_cash) ? 0.0 : m_cash - cash;
return true;
}
/**
@ -225,7 +226,7 @@ public:
* @return true | false
*/
virtual bool checkinStock(const Datetime& datetime, const Stock& stock, price_t price,
double number) override {
double number) {
HKU_WARN("The subclass does not implement this method");
return false;
}
@ -240,7 +241,7 @@ public:
* @note
*/
virtual bool checkoutStock(const Datetime& datetime, const Stock& stock, price_t price,
double number) override {
double number) {
HKU_WARN("The subclass does not implement this method");
return false;
}
@ -259,10 +260,7 @@ public:
*/
virtual TradeRecord buy(const Datetime& datetime, const Stock& stock, price_t realPrice,
double number, price_t stoploss = 0.0, price_t goalPrice = 0.0,
price_t planPrice = 0.0, SystemPart from = PART_INVALID) override {
HKU_WARN("The subclass does not implement this method");
return TradeRecord();
}
price_t planPrice = 0.0, SystemPart from = PART_INVALID) override;
/**
*
@ -279,10 +277,7 @@ public:
virtual TradeRecord sell(const Datetime& datetime, const Stock& stock, price_t realPrice,
double number = MAX_DOUBLE, price_t stoploss = 0.0,
price_t goalPrice = 0.0, price_t planPrice = 0.0,
SystemPart from = PART_INVALID) override {
HKU_WARN("The subclass does not implement this method");
return TradeRecord();
}
SystemPart from = PART_INVALID) override;
/**
*
@ -298,8 +293,7 @@ public:
*/
virtual TradeRecord sellShort(const Datetime& datetime, const Stock& stock, price_t realPrice,
double number, price_t stoploss = 0.0, price_t goalPrice = 0.0,
price_t planPrice = 0.0,
SystemPart from = PART_INVALID) override {
price_t planPrice = 0.0, SystemPart from = PART_INVALID) {
HKU_WARN("The subclass does not implement this method");
return TradeRecord();
}
@ -319,7 +313,7 @@ public:
virtual TradeRecord buyShort(const Datetime& datetime, const Stock& stock, price_t realPrice,
double number = MAX_DOUBLE, price_t stoploss = 0.0,
price_t goalPrice = 0.0, price_t planPrice = 0.0,
SystemPart from = PART_INVALID) override {
SystemPart from = PART_INVALID) {
HKU_WARN("The subclass does not implement this method");
return TradeRecord();
}
@ -330,7 +324,7 @@ public:
* @param cash
* @return true | false
*/
virtual bool borrowCash(const Datetime& datetime, price_t cash) override {
virtual bool borrowCash(const Datetime& datetime, price_t cash) {
HKU_WARN("The subclass does not implement this method");
return false;
}
@ -341,7 +335,7 @@ public:
* @param cash
* @return true | false
*/
virtual bool returnCash(const Datetime& datetime, price_t cash) override {
virtual bool returnCash(const Datetime& datetime, price_t cash) {
HKU_WARN("The subclass does not implement this method");
return false;
}
@ -355,7 +349,7 @@ public:
* @return true | false
*/
virtual bool borrowStock(const Datetime& datetime, const Stock& stock, price_t price,
double number) override {
double number) {
HKU_WARN("The subclass does not implement this method");
return false;
}
@ -369,11 +363,18 @@ public:
* @return true | false
*/
virtual bool returnStock(const Datetime& datetime, const Stock& stock, price_t price,
double number) override {
double number) {
HKU_WARN("The subclass does not implement this method");
return false;
}
/**
*
* @param ktype
* @return
*/
virtual FundsRecord getFunds(KQuery::KType ktype = KQuery::DAY) const override;
/**
*
* @param datetime Null<Datetime>()
@ -382,10 +383,7 @@ public:
* @note datetime等于Null<Datetime>()getFunds(KType)
*/
virtual FundsRecord getFunds(const Datetime& datetime,
KQuery::KType ktype = KQuery::DAY) override {
HKU_WARN("The subclass does not implement this method");
return FundsRecord();
}
KQuery::KType ktype = KQuery::DAY) override;
/**
*
@ -393,13 +391,24 @@ public:
* @param tr
* @return bool true | false
*/
virtual bool addTradeRecord(const TradeRecord& tr) override {
virtual bool addTradeRecord(const TradeRecord& tr) {
HKU_WARN("The subclass does not implement this method");
return false;
}
/**
*
* @param pr
* @return true
* @return false
*/
virtual bool addPosition(const PositionRecord& pr) {
HKU_WARN("The subclass does not implement this method");
return false;
}
/** 字符串输出 */
virtual string str() const override {
virtual string str() const {
HKU_WARN("The subclass does not implement this method");
return string();
}
@ -408,22 +417,17 @@ public:
* csv格式输出交易记录线
* @param path
*/
virtual void tocsv(const string& path) override {
virtual void tocsv(const string& path) {
HKU_WARN("The subclass does not implement this method");
}
private:
string getToken();
Datetime m_datetime; // 当前日期
private:
std::unique_ptr<httplib::Client> m_client;
string m_user;
string m_password;
string m_token;
price_t m_cash{0.0}; // 当前可用现金
typedef map<uint64_t, PositionRecord> position_map_type;
position_map_type m_position; // 当前持仓交易对象的持仓记录
};
inline TMPtr crtAccountTM(const string& name, const string& pwd) {
return std::make_shared<AccountTradeManager>(name, pwd);
}
} // namespace hku

View File

@ -0,0 +1,264 @@
/*
* Copyright(C) 2021 hikyuu.org
*
* Create on: 2021-02-16
* Author: fasiondog
*/
#include <csignal>
#include <unordered_set>
#include "hikyuu/utilities/os.h"
#include "hikyuu/utilities/ini_parser/IniParser.h"
#include "hikyuu/global/GlobalSpotAgent.h"
#include "hikyuu/global/schedule/scheduler.h"
#include "hikyuu/global/GlobalTaskGroup.h"
#include "hikyuu/global/sysinfo.h"
#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;
void Strategy::sig_handler(int sig) {
if (sig == SIGINT) {
ms_keep_running = false;
exit(0);
}
}
Strategy::Strategy() : Strategy("Strategy", "") {}
Strategy::Strategy(const string& name, const string& config_file)
: m_name(name), m_config_file(config_file) {
if (m_config_file.empty()) {
string home = getUserDir();
HKU_ERROR_IF(home == "", "Failed get user home path!");
#if HKU_OS_WINOWS
m_config_file = format("{}\\{}", home, ".hikyuu\\hikyuu.ini");
#else
m_config_file = format("{}/{}", home, ".hikyuu/hikyuu.ini");
#endif
}
}
Strategy::Strategy(const vector<string>& codeList, const vector<KQuery::KType>& ktypeList,
const string& name, const string& config_file)
: Strategy(name, config_file) {
m_context.setStockCodeList(codeList);
m_context.setKTypeList(ktypeList);
}
Strategy::~Strategy() {
ms_keep_running = false;
CLS_INFO("Quit Strategy {}!", m_name);
}
void Strategy::run() {
CLS_IF_RETURN(m_running, void());
StockManager& sm = StockManager::instance();
// sm 尚未初始化,则初始化
if (sm.thread_id() == std::thread::id()) {
// 注册 ctrl-c 终止信号
std::signal(SIGINT, sig_handler);
CLS_INFO("{} is running! You can press Ctrl-C to terminte ...", m_name);
// 初始化
hikyuu_init(m_config_file, false, m_context);
} else {
m_context = sm.getStrategyContext();
}
CLS_CHECK(!m_context.getStockCodeList().empty(), "The context does not contain any stocks!");
CLS_CHECK(!m_context.getKTypeList().empty(), "The K type list was empty!");
// 先将行情接收代理停止,以便后面加入处理函数
stopSpotAgent();
auto& agent = *getGlobalSpotAgent();
agent.addProcess([this](const SpotRecord& spot) { receivedSpot(spot); });
agent.addPostProcess([this](Datetime revTime) {
if (m_on_recieved_spot) {
EVENT([=]() { m_on_recieved_spot(revTime); });
}
});
startSpotAgent(true);
m_running = true;
}
void Strategy::start() {
CLS_CHECK(m_running, "No handler functions are registered!");
CLS_INFO("start even loop ...");
_startEventLoop();
}
void Strategy::onChange(std::function<void(const Stock&, const SpotRecord& spot)>&& changeFunc) {
if (!m_running) {
run();
}
m_on_change = changeFunc;
}
void Strategy::onReceivedSpot(std::function<void(const Datetime&)>&& recievedFucn) {
if (!m_running) {
run();
}
m_on_recieved_spot = recievedFucn;
}
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); });
}
}
}
void Strategy::runDaily(std::function<void()>&& func, const TimeDelta& delta,
const std::string& market) {
if (!m_running) {
run();
}
try {
auto* scheduler = getScheduler();
auto new_func = [=]() {
const auto& sm = StockManager::instance();
auto today = Datetime::today();
int day = today.dayOfWeek();
if (day == 0 || day == 6 || sm.isHoliday(today)) {
return;
}
auto market_info = sm.getMarketInfo(market);
Datetime open1 = today + market_info.openTime1();
Datetime close1 = today + market_info.closeTime1();
Datetime open2 = today + market_info.openTime2();
Datetime close2 = today + market_info.closeTime2();
Datetime now = Datetime::now();
if ((now >= open1 && now <= close1) || (now >= open2 && now <= close2)) {
EVENT(func);
}
};
const auto& sm = StockManager::instance();
auto market_info = sm.getMarketInfo(market);
auto today = Datetime::today();
auto now = Datetime::now();
TimeDelta now_time = now - today;
if (now_time >= market_info.closeTime2()) {
scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() {
new_func();
auto* sched = getScheduler();
sched->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
});
} else if (now_time >= market_info.openTime2()) {
int64_t ticks = now_time.ticks() - market_info.openTime2().ticks();
int64_t delta_ticks = delta.ticks();
if (ticks % delta_ticks == 0) {
scheduler->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
} else {
auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks);
scheduler->addFuncAtTime(now + delay, [=]() {
new_func();
auto* sched = getScheduler();
sched->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
});
}
} else if (now_time >= market_info.closeTime1()) {
scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() {
new_func();
auto* sched = getScheduler();
sched->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
});
} else if (now_time < market_info.closeTime1() && now_time >= market_info.openTime1()) {
int64_t ticks = now_time.ticks() - market_info.openTime1().ticks();
int64_t delta_ticks = delta.ticks();
if (ticks % delta_ticks == 0) {
scheduler->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
} else {
auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks);
scheduler->addFuncAtTime(now + delay, [=]() {
new_func();
auto* sched = getScheduler();
sched->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
});
}
} else if (now_time < market_info.openTime1()) {
scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() {
new_func();
auto* sched = getScheduler();
sched->addDurationFunc(std::numeric_limits<int>::max(), delta, new_func);
});
} else {
CLS_ERROR("Unknown process! now_time: {}", now_time);
}
} catch (const std::exception& e) {
CLS_THROW(e.what());
}
}
void Strategy::runDailyAt(std::function<void()>&& func, const TimeDelta& delta,
bool ignoreHoliday) {
if (!m_running) {
run();
}
auto new_func = [=]() {
if (!ignoreHoliday) {
EVENT(func);
return;
}
const auto& sm = StockManager::instance();
auto today = Datetime::today();
int day = today.dayOfWeek();
if (day != 0 && day != 6 && !sm.isHoliday(today)) {
EVENT(func);
}
};
auto* scheduler = getScheduler();
scheduler->addFuncAtTimeEveryDay(delta, new_func);
}
/*
* 线 python GIL
*/
void Strategy::_startEventLoop() {
while (ms_keep_running) {
event_type task;
m_event_queue.wait_and_pop(task);
if (task.isNullTask()) {
ms_keep_running = false;
} else {
try {
task();
} catch (const std::exception& e) {
CLS_ERROR("Failed run task! {}", e.what());
} catch (...) {
CLS_ERROR("Failed run task! Unknow error!");
}
}
}
}
} // namespace hku

View File

@ -0,0 +1,132 @@
/*
* Copyright(C) 2021 hikyuu.org
*
* Create on: 2021-02-16
* Author: fasiondog
*/
#pragma once
#include <future>
#include "../DataType.h"
#include "../StrategyContext.h"
#include "../global/SpotRecord.h"
#include "../utilities/thread/FuncWrapper.h"
#include "../utilities/thread/ThreadSafeQueue.h"
#include "../trade_sys/portfolio/Portfolio.h"
namespace hku {
/**
* @brief
* @ingroup Stratgy
*/
class HKU_API Strategy {
CLASS_LOGGER_IMP(Strategy)
public:
Strategy();
explicit Strategy(const string& name, const string& config_file = "");
Strategy(const vector<string>& codeList, const vector<KQuery::KType>& ktypeList,
const string& name = "Strategy", const string& config_file = "");
virtual ~Strategy();
const string& name() const {
return m_name;
}
void name(const string& name) {
m_name = name;
}
const StrategyContext& context() const {
return m_context;
}
/**
* delta
* @param func
* @param delta
* @param market
*/
void runDaily(std::function<void()>&& func, const TimeDelta& delta,
const std::string& market = "SH");
/**
*
* @param func
* @param delta
* @param ignoreHoliday
*/
void runDailyAt(std::function<void()>&& func, const TimeDelta& delta,
bool ignoreHoliday = true);
/**
*
* @note
* @param stk stock
* @param spot
*/
void onChange(std::function<void(const Stock&, const SpotRecord& spot)>&& changeFunc);
/**
*
* @note stock
*
*/
void onReceivedSpot(std::function<void(const Datetime&)>&& recievedFucn);
/**
*
*/
void start();
private:
string m_name;
string m_config_file;
StrategyContext m_context;
std::function<void(const Datetime&)> m_on_recieved_spot;
std::function<void(const Stock&, const SpotRecord& spot)> m_on_change;
bool m_running{false};
private:
void run();
void receivedSpot(const SpotRecord& spot);
private:
static std::atomic_bool ms_keep_running;
static void sig_handler(int sig);
typedef FuncWrapper event_type;
ThreadSafeQueue<event_type> m_event_queue; // 消息队列
/** 先消息队列提交任务后返回的对应 future 的类型 */
template <typename ResultType>
using event_handle = std::future<ResultType>;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
/** 向线程池提交任务 */
template <typename FunctionType>
event_handle<typename std::result_of<FunctionType()>::type> event(FunctionType f) {
typedef typename std::result_of<FunctionType()>::type result_type;
std::packaged_task<result_type()> task(f);
event_handle<result_type> res(task.get_future());
m_event_queue.push(std::move(task));
return res;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
void _startEventLoop();
};
typedef shared_ptr<Strategy> StrategyPtr;
} // namespace hku

View File

@ -1,300 +0,0 @@
/*
* Copyright(C) 2021 hikyuu.org
*
* Create on: 2021-02-16
* Author: fasiondog
*/
#include <csignal>
#include <unordered_set>
#include "../utilities/os.h"
#include "../utilities/IniParser.h"
#include "../global/schedule/scheduler.h"
#include "StrategyBase.h"
namespace hku {
std::atomic_bool StrategyBase::ms_keep_running = true;
void StrategyBase::sig_handler(int sig) {
if (sig == SIGINT) {
ms_keep_running = false;
exit(0);
}
}
StrategyBase::StrategyBase() : StrategyBase("Strategy") {}
StrategyBase::StrategyBase(const string& name) {
string home = getUserDir();
HKU_ERROR_IF(home == "", "Failed get user home path!");
#if HKU_OS_WINOWS
m_config_file = format("{}\\{}", home, ".hikyuu\\hikyuu.ini");
#else
m_config_file = format("{}/{}", home, ".hikyuu/hikyuu.ini");
#endif
_initDefaultParam();
}
StrategyBase::StrategyBase(const string& name, const string& config_file)
: m_name(name), m_config_file(config_file) {
_initDefaultParam();
}
StrategyBase::~StrategyBase() {
HKU_INFO("[Strategy {}] Quit Strategy!", m_name);
}
void StrategyBase::_initDefaultParam() {
setParam<bool>("enable_market_event", false);
setParam<bool>("enable_30_seconds_clock", false);
setParam<bool>("enable_1min_clock", false);
setParam<bool>("enable_3min_clock", false);
setParam<bool>("enable_5min_clock", false);
setParam<bool>("enable_10min_clock", false);
setParam<bool>("enable_15min_clock", false);
setParam<bool>("enable_30min_clock", false);
setParam<bool>("enable_60min_clock", false);
setParam<bool>("enable_2hour_clock", false);
}
void StrategyBase::_run(bool forTest) {
// 调用 strategy 自身的初始化方法
init();
StockManager& sm = StockManager::instance();
// 非独立进程方式运行 Stratege 或 重复执行,则直接返回
if (sm.thread_id() == std::this_thread::get_id()) {
return;
}
// 注册 ctrl-c 终止信号
std::signal(SIGINT, sig_handler);
HKU_INFO("[Strategy {}] strategy is running! You can press Ctrl-C to terminte ...", m_name);
// 加载上下文指定的证券数据
IniParser config;
try {
config.read(m_config_file);
} catch (std::exception& e) {
HKU_FATAL("[Strategy {}] Failed read configure file (\"{}\")! {}", m_name, m_config_file,
e.what());
HKU_INFO("[Strategy {}] Exit Strategy", m_name);
exit(1);
} catch (...) {
HKU_FATAL("[Strategy {}] Failed read configure file (\"{}\")! Unknow error!", m_name,
m_config_file);
HKU_INFO("[Strategy {}] Exit Strategy", m_name);
exit(1);
}
Parameter baseParam, blockParam, kdataParam, preloadParam, hkuParam;
hkuParam.set<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"));
if (!config.hasSection("baseinfo")) {
HKU_FATAL("Missing configure of baseinfo!");
exit(1);
}
IniParser::StringListPtr option = config.getOptionList("baseinfo");
for (auto iter = option->begin(); iter != option->end(); ++iter) {
string value = config.get("baseinfo", *iter);
baseParam.set<string>(*iter, value);
}
IniParser::StringListPtr block_config = config.getOptionList("block");
for (auto iter = block_config->begin(); iter != block_config->end(); ++iter) {
string value = config.get("block", *iter);
blockParam.set<string>(*iter, value);
}
option = config.getOptionList("kdata");
for (auto iter = option->begin(); iter != option->end(); ++iter) {
kdataParam.set<string>(*iter, config.get("kdata", *iter));
}
// 设置预加载参数,只加载指定的 ktype 至内存
auto ktype_list = m_context.getKTypeList();
if (ktype_list.empty()) {
// 如果为空,则默认加载日线数据
ktype_list.push_back(KQuery::DAY);
}
// 不使用默认的预加载模式
for (auto ktype : ktype_list) {
to_lower(ktype);
preloadParam.set<bool>(ktype, false);
}
sm.init(baseParam, blockParam, kdataParam, preloadParam, hkuParam, m_context);
const auto& stk_code_list = getStockCodeList();
m_stock_list.reserve(stk_code_list.size());
for (const auto& code : stk_code_list) {
Stock stk = getStock(code);
if (!stk.isNull()) {
m_stock_list.push_back(stk);
} else {
HKU_WARN("[Strategy {}] Invalid code: {}, can't find the stock!", m_name, code);
}
}
HKU_WARN_IF(m_stock_list.empty(), "[Strategy {}] stock list is empty!", m_name);
// 借助 Stock.setKRecordList 方法进行预加载(同步方式,不需要异步加载)
// 只从 context 指定起始日期开始加载
size_t ktype_count = ktype_list.size();
vector<KRecordList> k_buffer(ktype_count);
for (auto& stk : m_stock_list) {
// 保留原始 KDataDriver因为使用 stock.setKRecordList 将会把 stock 的 KDataDriver 设置为
// DoNothing
auto old_driver = stk.getKDataDirver();
for (size_t i = 0; i < ktype_count; i++) {
k_buffer[i] = stk.getKRecordList(
KQueryByDate(m_context.startDatetime(), Null<Datetime>(), ktype_list[i]));
}
for (size_t i = 0; i < ktype_count; i++) {
stk.setKRecordList(std::move(k_buffer[i]), ktype_list[i]);
}
// 恢复 KDataDriver
stk.setKDataDriver(old_driver);
}
// 计算每个类型当前最后的日期
for (const auto& ktype : ktype_list) {
Datetime last_date = Datetime::min();
for (auto& stk : m_stock_list) {
size_t count = stk.getCount(ktype);
if (count > 1) {
auto kr = stk.getKRecord(count - 1, ktype);
if (kr.datetime > last_date) {
last_date = kr.datetime;
}
}
}
m_ref_last_time[ktype] = last_date == Datetime::min() ? Null<Datetime>() : last_date;
}
if (!forTest) {
// 启动行情接收代理
auto& agent = *getGlobalSpotAgent();
agent.addProcess([this](const SpotRecord& spot) { this->receivedSpot(spot); });
agent.addPostProcess([this](Datetime revTime) { this->finishReceivedSpot(revTime); });
startSpotAgent(false);
_addTimer();
HKU_INFO("start even loop ...");
_startEventLoop();
}
}
void StrategyBase::receivedSpot(const SpotRecord& spot) {
Stock stk = getStock(format("{}{}", spot.market, spot.code));
if (!stk.isNull()) {
m_spot_map[stk] = spot;
}
}
void StrategyBase::finishReceivedSpot(Datetime revTime) {
HKU_IF_RETURN(m_stock_list.empty(), void());
event([this]() { this->onTick(); });
Stock& ref_stk = m_stock_list[0];
const auto& ktype_list = getKTypeList();
for (const auto& ktype : ktype_list) {
size_t count = ref_stk.getCount(ktype);
if (count > 0) {
KRecord k = ref_stk.getKRecord(count - 1, ktype);
if (k.datetime != m_ref_last_time[ktype]) {
m_ref_last_time[ktype] = k.datetime;
event([this, ktype]() { this->onBar(ktype); });
}
}
}
}
void StrategyBase::_addTimer() {
std::unordered_set<string> market_set;
for (auto& stk : m_stock_list) {
market_set.insert(stk.market());
}
const auto& sm = StockManager::instance();
TimeDelta openTime(0, 23, 59, 59, 999, 999), closeTime(0);
for (const auto& market : market_set) {
auto market_info = sm.getMarketInfo(market);
if (market_info.openTime1() < market_info.closeTime1()) {
if (market_info.openTime1() < openTime) {
openTime = market_info.openTime1();
}
if (market_info.closeTime1() > closeTime) {
closeTime = market_info.closeTime1();
}
}
if (market_info.openTime2() < market_info.closeTime2()) {
if (market_info.openTime2() < openTime) {
openTime = market_info.openTime2();
}
if (market_info.closeTime2() > closeTime) {
closeTime = market_info.closeTime2();
}
}
}
HKU_ERROR_IF_RETURN(openTime >= closeTime, void(), "Invalid market openTime: {}, closeTime: {}",
openTime, closeTime);
auto* scheduler = getScheduler();
if (getParam<bool>("enable_market_event")) {
scheduler->addFuncAtTimeEveryDay(
openTime, [this]() { this->event([this]() { this->onMarketOpen(); }); });
scheduler->addFuncAtTimeEveryDay(
closeTime, [this]() { this->event([this]() { this->onMarketClose(); }); });
}
_addClockEvent("enable_30_seconds_clock", Seconds(30), openTime, closeTime);
_addClockEvent("enable_1min_clock", Minutes(1), openTime, closeTime);
_addClockEvent("enable_3min_clock", Minutes(3), openTime, closeTime);
_addClockEvent("enable_5min_clock", Minutes(5), openTime, closeTime);
_addClockEvent("enable_10min_clock", Minutes(10), openTime, closeTime);
_addClockEvent("enable_15min_clock", Minutes(15), openTime, closeTime);
_addClockEvent("enable_30min_clock", Minutes(30), openTime, closeTime);
_addClockEvent("enable_60min_clock", Minutes(60), openTime, closeTime);
_addClockEvent("enable_2hour_clock", Hours(2), openTime, closeTime);
}
void StrategyBase::_addClockEvent(const string& enable, TimeDelta delta, TimeDelta openTime,
TimeDelta closeTime) {
auto* scheduler = getScheduler();
if (getParam<bool>(enable)) {
int repeat = static_cast<int>((closeTime - openTime) / delta);
scheduler->addFunc(Datetime::min(), Datetime::max(), openTime, closeTime, repeat, delta,
[this, delta]() { this->onClock(delta); });
}
}
/*
* 线 python GIL
*/
void StrategyBase::_startEventLoop() {
while (ms_keep_running) {
event_type task;
m_event_queue.wait_and_pop(task);
if (task.isNullTask()) {
ms_keep_running = false;
} else {
task();
}
}
}
} // namespace hku

View File

@ -1,156 +0,0 @@
/*
* Copyright(C) 2021 hikyuu.org
*
* Create on: 2021-02-16
* Author: fasiondog
*/
#pragma once
#include <future>
#include "../DataType.h"
#include "../StrategyContext.h"
#include "../utilities/Parameter.h"
#include "../utilities/thread/FuncWrapper.h"
#include "../utilities/thread/ThreadSafeQueue.h"
#include "../global/GlobalSpotAgent.h"
#include "../trade_sys/portfolio/Portfolio.h"
namespace hku {
class HKU_API StrategyBase {
PARAMETER_SUPPORT
public:
StrategyBase();
explicit StrategyBase(const string& name);
StrategyBase(const string& name, const string& config_file);
virtual ~StrategyBase();
const string& name() const {
return m_name;
}
void name(const string& name) {
m_name = name;
}
StockManager& getSM() {
return StockManager::instance();
}
TMPtr getTM() const {
return m_tm;
}
void setTM(const TMPtr& tm) {
m_tm = tm;
}
const StrategyContext& context() const {
return m_context;
}
void context(const StrategyContext& context) {
m_context = context;
}
Datetime startDatetime() const {
return m_context.startDatetime();
}
void startDatetime(const Datetime& d) {
m_context.startDatetime(d);
}
void setStockCodeList(vector<string>&& stockList) {
m_context.setStockCodeList(std::move(stockList));
}
void setStockCodeList(const vector<string>& stockList) {
m_context.setStockCodeList(stockList);
}
const vector<string>& getStockCodeList() const {
return m_context.getStockCodeList();
}
void setKTypeList(const vector<KQuery::KType>& ktypeList) {
m_context.setKTypeList(ktypeList);
}
const vector<KQuery::KType>& getKTypeList() const {
return m_context.getKTypeList();
}
void run() {
_run(false);
}
void receivedSpot(const SpotRecord& spot);
void finishReceivedSpot(Datetime revTime);
virtual void init() {}
virtual void onTick() {}
virtual void onBar(const KQuery::KType& ktype){};
virtual void onMarketOpen() {}
virtual void onMarketClose() {}
virtual void onClock(TimeDelta detla) {}
private:
string m_name;
string m_config_file;
StrategyContext m_context;
TMPtr m_tm;
StockList m_stock_list;
std::unordered_map<KQuery::KType, Datetime> m_ref_last_time;
std::unordered_map<Stock, SpotRecord> m_spot_map;
private:
void _initDefaultParam();
void _addTimer();
void _addClockEvent(const string& enable, TimeDelta delta, TimeDelta openTime,
TimeDelta closeTime);
void _run(bool forTest);
private:
static std::atomic_bool ms_keep_running;
static void sig_handler(int sig);
typedef FuncWrapper event_type;
ThreadSafeQueue<event_type> m_event_queue; // 消息队列
/** 先消息队列提交任务后返回的对应 future 的类型 */
template <typename ResultType>
using event_handle = std::future<ResultType>;
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4996)
#endif
/** 向线程池提交任务 */
template <typename FunctionType>
event_handle<typename std::result_of<FunctionType()>::type> event(FunctionType f) {
typedef typename std::result_of<FunctionType()>::type result_type;
std::packaged_task<result_type()> task(f);
event_handle<result_type> res(task.get_future());
m_event_queue.push(std::move(task));
return res;
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
void _startEventLoop();
};
typedef shared_ptr<StrategyBase> StrategyPtr;
} // namespace hku

View File

@ -23,18 +23,9 @@ HKU_API std::ostream& operator<<(std::ostream& os, const FundsRecord& funds) {
return os;
}
FundsRecord::FundsRecord()
: cash(0.0),
market_value(0.0),
short_market_value(0.0),
base_cash(0.0),
base_asset(0.0),
borrow_cash(0.0),
borrow_asset(0.0) {}
FundsRecord ::FundsRecord(price_t cash, price_t market_value, price_t short_market_value,
price_t base_cash, price_t base_asset, price_t borrow_cash,
price_t borrow_asset)
FundsRecord::FundsRecord(price_t cash, price_t market_value, price_t short_market_value,
price_t base_cash, price_t base_asset, price_t borrow_cash,
price_t borrow_asset)
: cash(cash),
market_value(market_value),
short_market_value(short_market_value),

View File

@ -20,17 +20,17 @@ namespace hku {
*/
class HKU_API FundsRecord {
public:
FundsRecord();
FundsRecord() = default;
FundsRecord(price_t cash, price_t market_value, price_t short_market_value, price_t base_cash,
price_t base_asset, price_t borrow_cash, price_t borrow_asset);
price_t cash; /**< 当前现金 */
price_t market_value; /**< 当前多头市值 */
price_t short_market_value; /**< 当前空头仓位市值 */
price_t base_cash; /**< 当前投入本金principal */
price_t base_asset; /**< 当前投入的资产价值 */
price_t borrow_cash; /**< 当前借入的资金,即负债 */
price_t borrow_asset; /**< 当前借入证券资产价值 */
price_t cash{0.0}; /**< 当前现金 */
price_t market_value{0.0}; /**< 当前多头市值 */
price_t short_market_value{0.0}; /**< 当前空头仓位市值 */
price_t base_cash{0.0}; /**< 当前投入本金principal */
price_t base_asset{0.0}; /**< 当前投入的资产价值 */
price_t borrow_cash{0.0}; /**< 当前借入的资金,即负债 */
price_t borrow_asset{0.0}; /**< 当前借入证券资产价值 */
// 当前总资产 = 现金 + 多头市值 + 空头数量×(借入价格 - 当前价格)
// = cash + market_value + borrow_asset - short_market_value

View File

@ -5,10 +5,43 @@
* Author: fasiondog
*/
#include <nlohmann/json.hpp>
#include "OrderBrokerBase.h"
namespace hku {
using json = nlohmann::json;
BrokerPositionRecord::BrokerPositionRecord(const Stock& stock_, price_t number_, price_t money_)
: stock(stock_), number(number_), money(money_) {}
BrokerPositionRecord::BrokerPositionRecord(BrokerPositionRecord&& rv)
: stock(std::move(rv.stock)), number(rv.number), money(rv.money) {
rv.number = 0.0;
rv.money = 0.0;
}
BrokerPositionRecord& BrokerPositionRecord::operator=(BrokerPositionRecord&& rv) {
if (this != &rv) {
stock = std::move(rv.stock);
number = rv.number;
money = rv.money;
rv.number = 0.0;
rv.money = 0.0;
}
return *this;
}
string BrokerPositionRecord::str() const {
return fmt::format("BrokerPositionRecord({}, {:<.4f}, {:<.4f})", stock.market_code(), number,
money);
}
HKU_API std::ostream& operator<<(std::ostream& os, const BrokerPositionRecord& pos) {
os << pos.str();
return os;
}
HKU_API std::ostream& operator<<(std::ostream& os, const OrderBrokerBase& broker) {
os << "OrderBroker(" << broker.name() << ")";
return os;
@ -25,34 +58,40 @@ OrderBrokerBase::OrderBrokerBase(const string& name) : m_name(name) {}
OrderBrokerBase::~OrderBrokerBase() {}
Datetime OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code,
price_t price, double num) {
Datetime tradetime;
void OrderBrokerBase::buy(Datetime datetime, const string& market, const string& code,
price_t price, double num, price_t stoploss, price_t goalPrice,
SystemPart from) noexcept {
try {
tradetime = _buy(datetime, market, code, price, num);
_buy(datetime, market, code, price, num, stoploss, goalPrice, from);
} catch (const std::exception& e) {
HKU_ERROR(e.what());
tradetime = Null<Datetime>();
} catch (...) {
HKU_ERROR_UNKNOWN;
tradetime = Null<Datetime>();
}
return tradetime;
}
Datetime OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code,
price_t price, double num) {
Datetime tradetime;
void OrderBrokerBase::sell(Datetime datetime, const string& market, const string& code,
price_t price, double num, price_t stoploss, price_t goalPrice,
SystemPart from) noexcept {
try {
tradetime = _sell(datetime, market, code, price, num);
_sell(datetime, market, code, price, num, stoploss, goalPrice, from);
} catch (const std::exception& e) {
HKU_ERROR(e.what());
tradetime = Null<Datetime>();
} catch (...) {
HKU_ERROR_UNKNOWN;
tradetime = Null<Datetime>();
}
return tradetime;
}
string OrderBrokerBase::getAssetInfo() noexcept {
string ret;
try {
ret = _getAssetInfo();
} catch (const std::exception& e) {
HKU_ERROR(e.what());
} catch (...) {
HKU_ERROR_UNKNOWN;
}
return ret;
}
} /* namespace hku */

View File

@ -11,9 +11,28 @@
#include "../DataType.h"
#include "../utilities/Parameter.h"
#include "../trade_sys/system/SystemPart.h"
namespace hku {
struct HKU_API BrokerPositionRecord {
Stock stock;
price_t number{0.0}; // 数量
price_t money{0.0}; // 买入花费总资金
BrokerPositionRecord() = default;
BrokerPositionRecord(const Stock& stock, price_t number, price_t money);
BrokerPositionRecord(const BrokerPositionRecord&) = default;
BrokerPositionRecord(BrokerPositionRecord&& rv);
BrokerPositionRecord& operator=(const BrokerPositionRecord&) = default;
BrokerPositionRecord& operator=(BrokerPositionRecord&& rv);
string str() const;
};
HKU_API std::ostream& operator<<(std::ostream& os, const BrokerPositionRecord&);
/**
*
* @details TradeManager.regBroker TradeManager
@ -49,10 +68,12 @@ public:
* @param code
* @param price
* @param num
* @return
* @param stoploss
* @param goalPrice
* @param from
*/
Datetime buy(Datetime datetime, const string& market, const string& code, price_t price,
double num);
void buy(Datetime datetime, const string& market, const string& code, price_t price, double num,
price_t stoploss, price_t goalPrice, SystemPart from) noexcept;
/**
*
@ -61,34 +82,65 @@ public:
* @param code
* @param price
* @param num
* @return
* @param stoploss
* @param goalPrice
* @param from
*/
Datetime sell(Datetime datetime, const string& market, const string& code, price_t price,
double num);
void sell(Datetime datetime, const string& market, const string& code, price_t price,
double num, price_t stoploss, price_t goalPrice, SystemPart from) noexcept;
/**
*
*
* @return string json字符串
* <pre>
*
* {
* "datetime": "2001-01-01 18:00:00.12345",
* "cash": 0.0,
* "positions": [
* {"market": "SZ", "code": "000001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0,
* "cost_price": 0.0},
* {"market": "SH", "code": "600001", "number": 100.0, "stoploss": 0.0, "goal_price": 0.0,
* "cost_price": 0.0},
* ]
* }
*
*
* cash:
* number + -
* cost_price:
* </pre>
*/
string getAssetInfo() noexcept;
/**
*
* @param datetime
* @param market
* @param code
* @param price
* @param num
* @return
* @param stoploss
* @param goalPrice
* @param from
*/
virtual Datetime _buy(Datetime datetime, const string& market, const string& code,
price_t price, double num) = 0;
virtual void _buy(Datetime datetime, const string& market, const string& code, price_t price,
double num, price_t stoploss, price_t goalPrice, SystemPart from) = 0;
/**
*
*
* @param datetime
* @param market
* @param code
* @param price
* @param num
* @return
*/
virtual Datetime _sell(Datetime datetime, const string& market, const string& code,
price_t price, double num) = 0;
virtual void _sell(Datetime datetime, const string& market, const string& code, price_t price,
double num, price_t stoploss, price_t goalPrice, SystemPart from) = 0;
virtual string _getAssetInfo() {
return string();
}
protected:
string m_name;

View File

@ -65,7 +65,7 @@ HKU_API std::ostream& operator<<(std::ostream& os, const PositionRecord& record)
return os;
}
string PositionRecord::toString() const {
string PositionRecord::str() const {
int precision = 2;
std::string market(""), code(""), name("");
if (!stock.isNull()) {

View File

@ -26,19 +26,19 @@ public:
price_t buyMoney, price_t totalCost, price_t totalRisk, price_t sellMoney);
/** 仅用于python的__str__ */
string toString() const;
string str() const;
Stock stock; ///< 交易对象
Datetime takeDatetime; ///< 初次建仓日期
Datetime cleanDatetime; ///< 平仓日期当前持仓记录中为Null<Datetime>()
double number; ///< 当前持仓数量
price_t stoploss; ///< 当前止损价
price_t goalPrice; ///< 当前的目标价格
double totalNumber; ///< 累计持仓数量
price_t buyMoney; ///< 累计买入资金
price_t totalCost; ///< 累计交易总成本
price_t totalRisk; ///< 累计交易风险 = 各次 (买入价格-止损)*买入数量, 不包含交易成本
price_t sellMoney; ///< 累计卖出资金
Stock stock; ///< 交易对象
Datetime takeDatetime; ///< 初次建仓日期
Datetime cleanDatetime; ///< 平仓日期当前持仓记录中为Null<Datetime>()
double number{0.0}; ///< 当前持仓数量
price_t stoploss{0.0}; ///< 当前止损价
price_t goalPrice{0.0}; ///< 当前的目标价格
double totalNumber{0.0}; ///< 累计持仓数量
price_t buyMoney{0.0}; ///< 累计买入资金
price_t totalCost{0.0}; ///< 累计交易总成本
price_t totalRisk{0.0}; ///< 累计交易风险 = 各次 (买入价格-止损)*买入数量, 不包含交易成本
price_t sellMoney{0.0}; ///< 累计卖出资金
//===================
// 序列化支持

View File

@ -865,14 +865,14 @@ TradeRecord TradeManager::buy(const Datetime& datetime, const Stock& stock, pric
roundEx(position.totalRisk + (realPrice - stoploss) * number * stock.unit(), precision);
}
if (result.datetime > m_broker_last_datetime) {
if (datetime > m_broker_last_datetime) {
list<OrderBrokerPtr>::const_iterator broker_iter = m_broker_list.begin();
Datetime realtime, nulltime;
for (; broker_iter != m_broker_list.end(); ++broker_iter) {
realtime =
(*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number);
if (realtime != nulltime && realtime > m_broker_last_datetime) {
m_broker_last_datetime = realtime;
(*broker_iter)
->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice,
from);
if (datetime > m_broker_last_datetime) {
m_broker_last_datetime = datetime;
}
}
}
@ -955,14 +955,14 @@ TradeRecord TradeManager::sell(const Datetime& datetime, const Stock& stock, pri
returnCash(datetime, m_borrow_cash < m_cash ? m_borrow_cash : m_cash);
}
if (result.datetime > m_broker_last_datetime) {
if (datetime > m_broker_last_datetime) {
list<OrderBrokerPtr>::const_iterator broker_iter = m_broker_list.begin();
Datetime realtime, nulltime;
for (; broker_iter != m_broker_list.end(); ++broker_iter) {
realtime =
(*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, real_number);
if (realtime != nulltime && realtime > m_broker_last_datetime) {
m_broker_last_datetime = realtime;
(*broker_iter)
->sell(datetime, stock.market(), stock.code(), realPrice, real_number, stoploss,
goalPrice, from);
if (datetime > m_broker_last_datetime) {
m_broker_last_datetime = datetime;
}
}
}
@ -1070,14 +1070,14 @@ TradeRecord TradeManager::sellShort(const Datetime& datetime, const Stock& stock
position.sellMoney = roundEx(position.sellMoney + money, precision);
}
if (result.datetime > m_broker_last_datetime) {
if (datetime > m_broker_last_datetime) {
list<OrderBrokerPtr>::const_iterator broker_iter = m_broker_list.begin();
Datetime realtime, nulltime;
for (; broker_iter != m_broker_list.end(); ++broker_iter) {
realtime =
(*broker_iter)->sell(datetime, stock.market(), stock.code(), realPrice, number);
if (realtime != nulltime && realtime > m_broker_last_datetime) {
m_broker_last_datetime = realtime;
(*broker_iter)
->sell(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice,
from);
if (datetime > m_broker_last_datetime) {
m_broker_last_datetime = datetime;
}
}
}
@ -1144,14 +1144,14 @@ TradeRecord TradeManager::buyShort(const Datetime& datetime, const Stock& stock,
m_short_position.erase(stock.id());
}
if (result.datetime > m_broker_last_datetime) {
if (datetime > m_broker_last_datetime) {
list<OrderBrokerPtr>::const_iterator broker_iter = m_broker_list.begin();
Datetime realtime, nulltime;
for (; broker_iter != m_broker_list.end(); ++broker_iter) {
realtime =
(*broker_iter)->buy(datetime, stock.market(), stock.code(), realPrice, number);
if (realtime != nulltime && realtime > m_broker_last_datetime) {
m_broker_last_datetime = realtime;
(*broker_iter)
->buy(datetime, stock.market(), stock.code(), realPrice, number, stoploss, goalPrice,
from);
if (datetime > m_broker_last_datetime) {
m_broker_last_datetime = datetime;
}
}
}
@ -1709,6 +1709,26 @@ void TradeManager::tocsv(const string& path) {
file.close();
}
bool TradeManager::addPosition(const PositionRecord& pr) {
HKU_ERROR_IF_RETURN(pr.stock.isNull(), false, "Invalid postion record! stock is null!");
HKU_ERROR_IF_RETURN(pr.cleanDatetime != Null<Datetime>(), false,
"Position cleanDatetime({}) must be Null!", pr.cleanDatetime);
HKU_ERROR_IF_RETURN(pr.takeDatetime < initDatetime(), false,
"Poistion takeDatetime({}) > initDatetime({})", pr.takeDatetime,
initDatetime());
HKU_ERROR_IF_RETURN(!m_trade_list.empty(), false, "Exist trade list!");
auto iter = m_position.find(pr.stock.id());
HKU_ERROR_IF_RETURN(iter != m_position.end(), false, "The stock({}) has position!",
pr.stock.market_code());
m_position[pr.stock.id()] = pr;
if (pr.takeDatetime > m_init_datetime) {
m_init_datetime = pr.takeDatetime;
}
return true;
}
bool TradeManager::addTradeRecord(const TradeRecord& tr) {
HKU_IF_RETURN(BUSINESS_INIT == tr.business, _add_init_tr(tr));
HKU_ERROR_IF_RETURN(tr.datetime < lastDatetime(), false,

View File

@ -344,6 +344,15 @@ public:
*/
virtual bool addTradeRecord(const TradeRecord& tr) override;
/**
*
* @note
* @param pr
* @return true
* @return false
*/
virtual bool addPosition(const PositionRecord& pr) override;
/** 字符串输出 */
virtual string str() const override;

View File

@ -668,6 +668,17 @@ public:
return false;
}
/**
*
* @param pr
* @return true
* @return false
*/
virtual bool addPosition(const PositionRecord& pr) {
HKU_WARN("The subclass does not implement this method");
return false;
}
/** 字符串输出 */
virtual string str() const {
HKU_WARN("The subclass does not implement this method");
@ -682,6 +693,14 @@ public:
HKU_WARN("The subclass does not implement this method");
}
/**
*
* @param broker
*/
virtual void fetchAssetInfoFromBroker(const OrderBrokerPtr& broker) {
HKU_WARN("The subclass does not implement this method");
}
protected:
string m_name; // 账户名称
TradeCostPtr m_costfunc; // 成本算法

View File

@ -6,8 +6,8 @@
*/
#include "FixedATradeCost.h"
#include "hikyuu/utilities/Log.h"
#include "../../StockTypeInfo.h"
#include "../../Log.h"
#if HKU_SUPPORT_SERIALIZATION
BOOST_CLASS_EXPORT(hku::FixedATradeCost)

View File

@ -382,8 +382,15 @@ void System::run(const KData& kdata, bool reset, bool resetAll) {
size_t total = kdata.size();
auto const* ks = kdata.data();
auto const* src_ks = m_src_kdata.data();
// 适应 strategy 模式下运行时同步资产信息可能造成的偏差
Datetime tm_init_datetime = m_tm->initDatetime();
if (KQuery::getKTypeInMin(kdata.getQuery().kType()) >= 1440) {
tm_init_datetime = tm_init_datetime.startOfDay();
}
for (size_t i = 0; i < total; ++i) {
if (ks[i].datetime >= m_tm->initDatetime()) {
if (ks[i].datetime >= tm_init_datetime) {
auto tr = _runMoment(ks[i], src_ks[i]);
if (trace) {
HKU_INFO_IF(!tr.isNull(), "{}", tr);

View File

@ -0,0 +1,267 @@
/*
* Copyright (c) 2023 hikyuu.org
*
* Created on: 2023-01-13
* Author: fasiondog
*/
#pragma once
#include <memory>
#include <functional>
#include <forward_list>
#include <unordered_map>
#include "thread/ThreadPool.h"
#include "any_to_string.h"
#include "Log.h"
namespace hku {
/**
* @brief
*/
class FilterNode {
public:
FilterNode() = default;
FilterNode(const FilterNode&) = default;
virtual ~FilterNode() = default;
/**
* @brief
* @param exclusive true
*/
explicit FilterNode(bool exclusive) : m_exclusive(exclusive) {}
FilterNode(FilterNode&& rv)
: m_value(std::move(rv.m_value)),
m_children(std::move(rv.m_children)),
m_exclusive(rv.m_exclusive) {}
FilterNode& operator=(const FilterNode& rv) {
if (this == &rv)
return *this;
m_value = rv.m_value;
m_children = rv.m_children;
m_exclusive = rv.m_exclusive;
return *this;
}
FilterNode& operator=(FilterNode&& rv) {
if (this == &rv)
return *this;
m_value = std::move(rv.m_value);
m_children = std::move(rv.m_children);
m_exclusive = rv.m_exclusive;
return *this;
}
using ptr_t = std::shared_ptr<FilterNode>;
ptr_t addChild(const ptr_t& child) {
HKU_CHECK(child, "Invalid input child! child is null!");
m_children.push_front(child);
return child;
}
bool exclusive() const {
return m_exclusive;
}
void exclusive(bool exclusive) {
m_exclusive = exclusive;
}
using const_iterator = std::forward_list<ptr_t>::const_iterator;
using iterator = std::forward_list<ptr_t>::iterator;
const_iterator cbegin() const {
return m_children.cbegin();
}
const_iterator cend() const {
return m_children.cend();
}
iterator begin() {
return m_children.begin();
}
iterator end() {
return m_children.end();
}
bool run(const any_t& data) noexcept {
if (_filter(data)) {
_process(data);
for (auto& node : m_children) {
if (node->run(data) && m_exclusive) {
return true;
}
}
return true;
}
return false;
}
virtual bool filter(const any_t& data) {
return true;
}
virtual void process(const any_t& data) {}
template <typename ValueT>
ValueT value() const {
return any_cast<ValueT>(m_value);
}
template <typename ValueT>
void value(const ValueT& value) {
m_value = value;
}
bool has_value() const {
#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17
return m_value.has_value();
#else
return !m_value.empty();
#endif
}
private:
bool _filter(const any_t& data) noexcept {
try {
return filter(data);
} catch (const std::exception& e) {
HKU_WARN("Node filter exist error! {}", e.what());
} catch (...) {
HKU_WARN("Node filter exist unknown error!");
}
return false;
}
void _process(const any_t& data) noexcept {
try {
process(data);
} catch (const std::exception& e) {
HKU_WARN("Node process exist error! {}", e.what());
} catch (...) {
HKU_WARN("Node process exist unknown error!");
}
}
protected:
any_t m_value;
private:
std::forward_list<ptr_t> m_children;
bool m_exclusive = false;
};
template <>
inline const any_t& FilterNode::value() const {
return m_value;
}
typedef std::shared_ptr<FilterNode> FilterNodePtr;
/**
* @brief std::function filter process
*/
class BindFilterNode : public FilterNode {
public:
BindFilterNode() = default;
virtual ~BindFilterNode() = default;
using filter_func = std::function<bool(FilterNode*, const any_t&)>;
using process_func = std::function<void(FilterNode*, const any_t&)>;
explicit BindFilterNode(const process_func& process) : FilterNode(false), m_process(process) {}
explicit BindFilterNode(process_func&& process)
: FilterNode(false), m_process(std::move(process)) {}
BindFilterNode(const filter_func& filter, const process_func& process, bool exclusive = false)
: FilterNode(exclusive), m_filter(filter), m_process(process) {}
BindFilterNode(filter_func&& filter, process_func&& process, bool exclusive = false)
: FilterNode(exclusive), m_filter(std::move(filter)), m_process(std::move(process)) {}
virtual bool filter(const any_t& data) {
return m_filter ? m_filter(this, data) : true;
}
virtual void process(const any_t& data) {
if (m_process) {
m_process(this, data);
}
}
private:
filter_func m_filter;
process_func m_process;
};
/**
* @brief
* @tparam EventT
*/
template <class EventT>
class AsyncSerialEventProcessor {
public:
/**
* @brief
* @param quit_wait 退
*/
explicit AsyncSerialEventProcessor(bool quit_wait = true) : m_quit_wait(quit_wait) {
m_tg = std::unique_ptr<ThreadPool>(new ThreadPool(1));
}
/** 析构函数 */
virtual ~AsyncSerialEventProcessor() {
if (m_quit_wait) {
m_tg->join();
} else {
m_tg->stop();
}
}
/**
* @brief
*
* @param event
* @param action
* @return
*/
FilterNodePtr addAction(const EventT& event, const FilterNodePtr& action) {
HKU_CHECK(action, "Input action is null!");
std::lock_guard<std::mutex> lock(m_mutex);
auto iter = m_trees.find(event);
if (iter != m_trees.end()) {
iter->second->addChild(action);
} else {
m_trees[event] = action;
}
return action;
}
/**
* @brief
*
* @param event
* @param data
*/
void dispatch(const EventT& event, const any_t& data) {
m_tg->submit([=] {
auto iter = m_trees.find(event);
HKU_WARN_IF_RETURN(iter == m_trees.end(), void(),
"There is no matching handling method for the event({})!", event);
iter->second->run(data);
});
}
private:
mutable std::mutex m_mutex;
std::unordered_map<EventT, FilterNodePtr> m_trees;
std::unique_ptr<ThreadPool> m_tg;
bool m_quit_wait = true;
};
} // namespace hku

View File

@ -0,0 +1,230 @@
/*
* LRUCache11 - a templated C++11 based LRU cache class that allows
* specification of
* key, value and optionally the map container type (defaults to
* std::unordered_map)
* By using the std::unordered_map and a linked list of keys it allows O(1) insert, delete
* and
* refresh operations.
*
* This is a header-only library and all you need is the LRUCache11.hpp file
*
* Github: https://github.com/mohaps/lrucache11
*
* This is a follow-up to the LRUCache project -
* https://github.com/mohaps/lrucache
*
* Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <algorithm>
#include <cstdint>
#include <list>
#include <mutex>
#include <stdexcept>
#include <thread>
#include <unordered_map>
namespace lru11 {
/*
* a noop lockable concept that can be used in place of std::mutex
*/
class NullLock {
public:
void lock() {}
void unlock() {}
bool try_lock() {
return true;
}
};
/**
* error raised when a key not in cache is passed to get()
*/
class KeyNotFound : public std::invalid_argument {
public:
KeyNotFound() : std::invalid_argument("key_not_found") {}
};
template <typename K, typename V>
struct KeyValuePair {
public:
K key;
V value;
KeyValuePair(K k, V v) : key(std::move(k)), value(std::move(v)) {}
};
/**
* The LRU Cache class templated by
* Key - key type
* Value - value type
* MapType - an associative container like std::unordered_map
* LockType - a lock type derived from the Lock class (default:
*NullLock = no synchronization)
*
* The default NullLock based template is not thread-safe, however passing
*Lock=std::mutex will make it
* thread-safe
*/
template <class Key, class Value, class Lock = NullLock,
class Map =
std::unordered_map<Key, typename std::list<KeyValuePair<Key, Value>>::iterator>>
class Cache {
public:
typedef KeyValuePair<Key, Value> node_type;
typedef std::list<KeyValuePair<Key, Value>> list_type;
typedef Map map_type;
typedef Lock lock_type;
using Guard = std::lock_guard<lock_type>;
/**
* the maxSize is the soft limit of keys and (maxSize + elasticity) is the
* hard limit
* the cache is allowed to grow till (maxSize + elasticity) and is pruned back
* to maxSize keys
* set maxSize = 0 for an unbounded cache (but in that case, you're better off
* using a std::unordered_map
* directly anyway! :)
*/
explicit Cache(size_t maxSize = 64, size_t elasticity = 10)
: maxSize_(maxSize), elasticity_(elasticity) {}
virtual ~Cache() = default;
size_t size() const {
Guard g(lock_);
return cache_.size();
}
bool empty() const {
Guard g(lock_);
return cache_.empty();
}
void clear() {
Guard g(lock_);
cache_.clear();
keys_.clear();
}
void insert(const Key& k, const Value& v) {
Guard g(lock_);
const auto iter = cache_.find(k);
if (iter != cache_.end()) {
iter->second->value = v;
keys_.splice(keys_.begin(), keys_, iter->second);
return;
}
keys_.emplace_front(k, v);
cache_[k] = keys_.begin();
prune();
}
void insert(const Key& k, Value&& v) {
Guard g(lock_);
const auto iter = cache_.find(k);
if (iter != cache_.end()) {
iter->second->value = std::move(v);
keys_.splice(keys_.begin(), keys_, iter->second);
return;
}
keys_.emplace_front(k, std::move(v));
cache_[k] = keys_.begin();
prune();
}
bool tryGet(const Key& kIn, Value& vOut) {
Guard g(lock_);
const auto iter = cache_.find(kIn);
if (iter == cache_.end()) {
return false;
}
keys_.splice(keys_.begin(), keys_, iter->second);
vOut = iter->second->value;
return true;
}
/**
* The const reference returned here is only
* guaranteed to be valid till the next insert/delete
* 便
*/
Value& get(const Key& k) {
Guard g(lock_);
const auto iter = cache_.find(k);
if (iter == cache_.end()) {
throw KeyNotFound();
}
keys_.splice(keys_.begin(), keys_, iter->second);
return iter->second->value;
}
/**
* returns a copy of the stored object (if found)
*/
Value getCopy(const Key& k) {
return get(k);
}
bool remove(const Key& k) {
Guard g(lock_);
auto iter = cache_.find(k);
if (iter == cache_.end()) {
return false;
}
keys_.erase(iter->second);
cache_.erase(iter);
return true;
}
bool contains(const Key& k) const {
Guard g(lock_);
return cache_.find(k) != cache_.end();
}
size_t getMaxSize() const {
return maxSize_;
}
size_t getElasticity() const {
return elasticity_;
}
size_t getMaxAllowedSize() const {
return maxSize_ + elasticity_;
}
template <typename F>
void cwalk(F& f) const {
Guard g(lock_);
std::for_each(keys_.begin(), keys_.end(), f);
}
protected:
size_t prune() {
size_t maxAllowed = maxSize_ + elasticity_;
if (maxSize_ == 0 || cache_.size() < maxAllowed) {
return 0;
}
size_t count = 0;
while (cache_.size() > maxSize_) {
cache_.erase(keys_.back().key);
keys_.pop_back();
++count;
}
return count;
}
private:
// Disallow copying.
Cache(const Cache&) = delete;
Cache& operator=(const Cache&) = delete;
mutable Lock lock_;
Map cache_;
list_type keys_;
size_t maxSize_;
size_t elasticity_;
};
} // namespace lru11

View File

@ -0,0 +1,86 @@
/*
* Log.cpp
*
* Created on: 2013-2-1
* Author: fasiondog
*/
#include <thread>
#include "hikyuu/GlobalInitializer.h"
#include "os.h"
#include "Log.h"
// 使用 stdout_color 将无法将日志输出重定向至 python
#include <spdlog/sinks/stdout_color_sinks.h>
#include <iostream>
#include "spdlog/sinks/ostream_sink.h"
#include "spdlog/sinks/rotating_file_sink.h"
#if HKU_USE_SPDLOG_ASYNC_LOGGER
#include <spdlog/async.h>
#endif /* HKU_USE_SPDLOG_ASYNC_LOGGER */
namespace hku {
static LOG_LEVEL g_log_level = LOG_LEVEL::LOG_TRACE;
std::string g_unknown_error_msg{"Unknown error!"};
LOG_LEVEL get_log_level() {
return g_log_level;
}
void set_log_level(LOG_LEVEL level) {
g_log_level = level;
getHikyuuLogger()->set_level((spdlog::level::level_enum)level);
}
std::shared_ptr<spdlog::logger> getHikyuuLogger() {
auto logger = spdlog::get("hikyuu");
return logger ? logger : spdlog::default_logger();
}
void HKU_UTILS_API initLogger(bool not_use_color, const std::string& filename) {
std::string logname("hikyuu");
spdlog::drop(logname);
std::shared_ptr<spdlog::logger> logger = spdlog::get(logname);
if (logger) {
spdlog::drop(logname);
}
spdlog::sink_ptr stdout_sink;
if (not_use_color) {
stdout_sink = std::make_shared<spdlog::sinks::ostream_sink_mt>(std::cout, true);
} else {
stdout_sink = std::make_shared<spdlog::sinks::stdout_color_sink_mt>();
}
stdout_sink->set_level(spdlog::level::trace);
std::string logfile = filename.empty() ? "./hikyuu.log" : filename;
auto rotating_sink =
std::make_shared<spdlog::sinks::rotating_file_sink_mt>(logfile, 1024 * 1024 * 10, 3);
rotating_sink->set_level(spdlog::level::warn);
std::vector<spdlog::sink_ptr> sinks{stdout_sink};
if (rotating_sink) {
sinks.emplace_back(rotating_sink);
}
#if HKU_USE_SPDLOG_ASYNC_LOGGER
spdlog::init_thread_pool(8192, 1);
logger = std::make_shared<spdlog::async_logger>(logname, sinks.begin(), sinks.end(),
spdlog::thread_pool(),
spdlog::async_overflow_policy::block);
#else
logger = std::make_shared<spdlog::logger>(logname, sinks.begin(), sinks.end());
#endif
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::trace);
logger->set_pattern("%Y-%m-%d %H:%M:%S.%e [%^HKU-%L%$] - %v (%s:%#)");
// logger->set_pattern("%^%Y-%m-%d %H:%M:%S.%e [HKU-%L] - %v (%s:%#)%$");
// spdlog::register_logger(logger);
spdlog::set_default_logger(logger);
}
} // namespace hku

View File

@ -16,13 +16,19 @@
#include "config.h"
#include "exception.h"
#ifndef HKU_LOG_ACTIVE_LEVEL
#define HKU_LOG_ACTIVE_LEVEL 0
#endif
// clang-format off
#if USE_SPDLOG_LOGGER
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
#if HKU_USE_SPDLOG_ASYNC_LOGGER
#include "spdlog/async.h"
#endif
#ifndef SPDLOG_ACTIVE_LEVEL
#define SPDLOG_ACTIVE_LEVEL HKU_LOG_ACTIVE_LEVEL
#endif
#include <spdlog/spdlog.h>
#include <spdlog/fmt/ostr.h>
#if HKU_USE_SPDLOG_ASYNC_LOGGER
#include "spdlog/async.h"
#endif
// clang-format on
@ -30,30 +36,24 @@
#include <fmt/format.h>
#include <fmt/chrono.h>
#ifdef HKU_ENABLE_STACK_TRACE
#ifndef HKU_ENABLE_STACK_TRACE
#define HKU_ENABLE_STACK_TRACE 0
#endif
#if HKU_ENABLE_STACK_TRACE
#include <boost/stacktrace.hpp>
#endif
#ifndef HKU_API
#define HKU_API
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
namespace hku {
/**
* @ingroup Utilities
* @addtogroup logging Logging tools
* @details
* TRACE < DEBUG < INFO < WARN < ERROR < FATAL
* @{
*/
bool HKU_API isLogInMainThread();
/**********************************************
* Use SPDLOG for logging
*********************************************/
#if USE_SPDLOG_LOGGER
/** 日志级别 */
enum LOG_LEVEL {
LOG_TRACE = SPDLOG_LEVEL_TRACE, ///< 跟踪
@ -65,19 +65,27 @@ enum LOG_LEVEL {
LOG_OFF = SPDLOG_LEVEL_OFF, ///< 关闭日志打印
};
/**
* logger
* @param not_use_color 使
* @param filename "./hikyuu.log"
*/
void HKU_UTILS_API initLogger(bool not_use_color = false,
const std::string& filename = std::string());
/**
*
* @return
*/
LOG_LEVEL HKU_API get_log_level();
LOG_LEVEL HKU_UTILS_API get_log_level();
/**
*
* @param level
*/
void HKU_API set_log_level(LOG_LEVEL level);
void HKU_UTILS_API set_log_level(LOG_LEVEL level);
std::shared_ptr<spdlog::logger> HKU_API getHikyuuLogger();
std::shared_ptr<spdlog::logger> HKU_UTILS_API getHikyuuLogger();
#define HKU_TRACE(...) SPDLOG_LOGGER_TRACE(hku::getHikyuuLogger(), __VA_ARGS__)
#define HKU_DEBUG(...) SPDLOG_LOGGER_DEBUG(hku::getHikyuuLogger(), __VA_ARGS__)
@ -86,76 +94,6 @@ std::shared_ptr<spdlog::logger> HKU_API getHikyuuLogger();
#define HKU_ERROR(...) SPDLOG_LOGGER_ERROR(hku::getHikyuuLogger(), __VA_ARGS__)
#define HKU_FATAL(...) SPDLOG_LOGGER_CRITICAL(hku::getHikyuuLogger(), __VA_ARGS__)
void initLogger(bool inJupyter = false);
#else
enum LOG_LEVEL {
LOG_TRACE = 0,
LOG_DEBUG = 1,
LOG_INFO = 2,
LOG_WARN = 3,
LOG_ERROR = 4,
LOG_FATAL = 5,
LOG_OFF = 6,
};
LOG_LEVEL HKU_API get_log_level();
void HKU_API set_log_level(LOG_LEVEL level);
void initLogger(bool inJupyter = false);
/** 获取系统当前时间精确到毫秒2001-01-02 13:01:02.001 */
std::string HKU_API getLocalTime();
#if LOG_ACTIVE_LEVEL <= 0
#define HKU_TRACE(...) \
fmt::print("[{}] [HKU-T] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \
__LINE__);
#else
#define HKU_TRACE(...)
#endif
#if LOG_ACTIVE_LEVEL <= 1
#define HKU_DEBUG(...) \
fmt::print("[{}] [HKU-D] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \
__LINE__);
#else
#define HKU_DEBUG(...)
#endif
#if LOG_ACTIVE_LEVEL <= 2
#define HKU_INFO(...) \
fmt::print("[{}] [HKU-I] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \
__LINE__);
#else
#define HKU_INFO(...)
#endif
#if LOG_ACTIVE_LEVEL <= 3
#define HKU_WARN(...) \
fmt::print("[{}] [HKU-W] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \
__LINE__);
#else
#define HKU_WARN(...)
#endif
#if LOG_ACTIVE_LEVEL <= 4
#define HKU_ERROR(...) \
fmt::print("[{}] [HKU-E] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \
__LINE__);
#else
#define HKU_ERROR(...)
#endif
#if LOG_ACTIVE_LEVEL <= 5
#define HKU_FATAL(...) \
fmt::print("[{}] [HKU-F] - {} ({}:{})\n", getLocalTime(), fmt::format(__VA_ARGS__), __FILE__, \
__LINE__);
#else
#define HKU_FATAL(...)
#endif
#endif /* USE_SPDLOG_LOGGER */
///////////////////////////////////////////////////////////////////////////////
//
// clang/gcc 下使用 __PRETTY_FUNCTION__ 会包含函数参数,可以在编译时指定
@ -166,7 +104,7 @@ std::string HKU_API getLocalTime();
#define HKU_FUNCTION __FUNCTION__
#endif
#ifndef HKU_ENABLE_STACK_TRACE
#if !HKU_ENABLE_STACK_TRACE
/**
* false hku::exception ,
* @note
@ -212,23 +150,9 @@ std::string HKU_API getLocalTime();
__FILE__, __LINE__)); \
} \
} while (0)
#endif // #ifndef HKU_ENABLE_STACK_TRACE
#endif // #if !HKU_ENABLE_STACK_TRACE
#if HKU_DEBUG_MODE
#define HKU_ASSERT_DEBUG(expr)
#else
/** 仅在 debug 模式下生效 */
#define HKU_ASSERT_DEBUG(expr) \
do { \
if (!(expr)) { \
std::string err_msg(fmt::format("HKU_ASSERT({})", #expr)); \
throw hku::exception( \
fmt::format("{} [{}] ({}:{})", err_msg, HKU_FUNCTION, __FILE__, __LINE__)); \
} \
} while (0)
#endif /* #if HKU_DEBUG_MODE */
#ifdef HKU_ENABLE_STACK_TRACE
#if HKU_ENABLE_STACK_TRACE
/**
* false hku::exception
* @note HKU_DISABLE_ASSERT
@ -253,9 +177,9 @@ std::string HKU_API getLocalTime();
} \
} while (0)
#endif // #ifndef HKU_ENABLE_STACK_TRACE
#endif // #if HKU_ENABLE_STACK_TRACE
#ifndef HKU_ENABLE_STACK_TRACE
#if !HKU_ENABLE_STACK_TRACE
/** 抛出 hku::exception 及传入信息 */
#define HKU_THROW(...) \
do { \
@ -286,7 +210,7 @@ std::string HKU_API getLocalTime();
throw except( \
fmt::format("EXCEPTION: {} [{}] ({}:{})", errmsg, HKU_FUNCTION, __FILE__, __LINE__)); \
} while (0)
#endif // #ifndef HKU_ENABLE_STACK_TRACE
#endif // #if !HKU_ENABLE_STACK_TRACE
/**
* TRACE
@ -420,12 +344,66 @@ std::string HKU_API getLocalTime();
/** 用于 catch (...) 中打印,减少编译后代码大小 */
extern std::string g_unknown_error_msg;
#define HKU_THROW_UNKNOWN HKU_THROW(g_unknown_error_msg);
#define HKU_TRACE_UNKNOWN HKU_TRACE(g_unknown_error_msg)
#define HKU_DEBUG_UNKNOWN HKU_DEBUG(g_unknown_error_msg)
#define HKU_INFO_UNKNOWN HKU_INFO(g_unknown_error_msg)
#define HKU_ERROR_UNKNOWN HKU_ERROR(g_unknown_error_msg)
#define HKU_FATAL_UNKNOWN HKU_FATAL(g_unknown_error_msg)
#if CPP_STANDARD >= CPP_STANDARD_17
#define CLASS_LOGGER_IMP(cls) \
protected: \
inline static const char* ms_logger = #cls;
#else
#define CLASS_LOGGER_IMP(cls) \
protected: \
const char* ms_logger = #cls;
#endif
#define CLS_TRACE(...) HKU_TRACE(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_DEBUG(...) HKU_DEBUG(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_INFO(...) HKU_INFO(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_WARN(...) HKU_WARN(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_ERROR(...) HKU_ERROR(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_FATAL(...) HKU_FATAL(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_TRACE_IF(expr, ...) \
HKU_TRACE_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_DEBUG_IF(expr, ...) \
HKU_DEBUG_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_INFO_IF(expr, ...) \
HKU_INFO_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_WARN_IF(expr, ...) \
HKU_WARN_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_ERROR_IF(expr, ...) \
HKU_ERROR_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_FATAL_IF(expr, ...) \
HKU_FATAL_IF(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_IF_RETURN(expr, ret) HKU_IF_RETURN(expr, ret)
#define CLS_TRACE_IF_RETURN(expr, ret, ...) \
HKU_TRACE_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_DEBUG_IF_RETURN(expr, ret, ...) \
HKU_DEBUG_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_INFO_IF_RETURN(expr, ret, ...) \
HKU_INFO_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_WARN_IF_RETURN(expr, ret, ...) \
HKU_WARN_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_ERROR_IF_RETURN(expr, ret, ...) \
HKU_ERROR_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_FATAL_IF_RETURN(expr, ret, ...) \
HKU_FATAL_IF_RETURN(expr, ret, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_ASSERT HKU_ASSERT
#define CLS_CHECK(expr, ...) \
HKU_CHECK(expr, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_CHECK_THROW(expr, except, ...) \
HKU_CHECK_THROW(expr, except, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_THROW(...) HKU_THROW(fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
#define CLS_THROW_EXCEPTION(except, ...) \
HKU_THROW_EXCEPTION(except, fmt::format("[{}] {}", ms_logger, fmt::format(__VA_ARGS__)))
/** @} */
} /* namespace hku */

View File

@ -10,6 +10,7 @@
#define NULL_H_
#include <cstddef>
#include <cstdint>
#include <limits>
#include <type_traits>
#include "osdef.h"

View File

@ -6,8 +6,8 @@
*/
#include <boost/algorithm/string.hpp>
#include "hikyuu/utilities/Log.h"
#include "hikyuu/Block.h"
#include "../Log.h"
#include "Parameter.h"
namespace hku {

View File

@ -17,7 +17,8 @@
#include <map>
#include <boost/any.hpp>
#include "../config.h"
#include "hikyuu/config.h"
#include "hikyuu/utilities/config.h"
#if HKU_SUPPORT_SERIALIZATION
#include <boost/lexical_cast.hpp>

View File

@ -0,0 +1,636 @@
/*
* ResourcePool.h
*
* Copyright (c) 2019, hikyuu.org
*
* Created on: 2019-8-5
* Author: fasiondog
*/
#pragma once
#ifndef HKU_UTILS_RESOURCE_POOL_H
#define HKU_UTILS_RESOURCE_POOL_H
#include <thread>
#include <mutex>
#include <condition_variable>
#include <queue>
#include <chrono>
#include <unordered_set>
#include "Parameter.h"
#include "Log.h"
namespace hku {
/**
*
*/
class GetResourceTimeoutException : public hku::exception {
public:
GetResourceTimeoutException(const char *msg)
: hku::exception(fmt::format("GetResourceTimeoutException {}", msg)) {}
GetResourceTimeoutException(const std::string &msg)
: hku::exception(fmt::format("GetResourceTimeoutException {}", msg)) {}
virtual ~GetResourceTimeoutException() {}
};
/**
*
*/
class CreateResourceException : public hku::exception {
public:
CreateResourceException(const char *msg)
: hku::exception(fmt::format("CreateResourceException {}", msg)) {}
CreateResourceException(const std::string &msg)
: hku::exception(fmt::format("CreateResourceException {}", msg)) {}
virtual ~CreateResourceException() {}
};
/**
*
* @ingroup Utilities
*/
template <typename ResourceType>
class ResourcePool {
public:
ResourcePool() = delete;
ResourcePool(const ResourcePool &) = delete;
ResourcePool &operator=(const ResourcePool &) = delete;
/**
*
* @param param
* @param maxPoolSize 0
* @param maxIdleNum 0
*/
explicit ResourcePool(const Parameter &param, size_t maxPoolSize = 0, size_t maxIdleNum = 100)
: m_maxPoolSize(maxPoolSize), m_maxIdelSize(maxIdleNum), m_count(0), m_param(param) {}
/**
*
*/
virtual ~ResourcePool() {
std::unique_lock<std::mutex> lock(m_mutex);
// 将所有已分配资源的 closer 和 pool 解绑
for (auto iter = m_closer_set.begin(); iter != m_closer_set.end(); ++iter) {
(*iter)->unbind();
}
// 删除所有空闲资源
while (!m_resourceList.empty()) {
ResourceType *p = m_resourceList.front();
m_resourceList.pop();
if (p) {
delete p;
}
}
}
/** 获取当前允许的最大资源数 */
size_t maxPoolSize() const {
return m_maxIdelSize;
}
/** 获取当前允许的最大空闲资源数 */
size_t maxIdleSize() const {
return m_maxIdelSize;
}
/** 设置最大资源数 */
void maxPoolSize(size_t num) {
std::lock_guard<std::mutex> lock(m_mutex);
m_maxPoolSize = num;
}
/** 设置允许的最大空闲资源数 */
void maxIdleSize(size_t num) {
std::lock_guard<std::mutex> lock(m_mutex);
m_maxIdelSize = num;
}
/** 资源实例指针类型 */
typedef std::shared_ptr<ResourceType> ResourcePtr;
/**
*
* @exception CreateResourceException
*/
ResourcePtr get() {
std::lock_guard<std::mutex> lock(m_mutex);
ResourcePtr result;
ResourceType *p = nullptr;
if (m_resourceList.empty()) {
if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) {
return result;
}
try {
p = new ResourceType(m_param);
} catch (const std::exception &e) {
HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}",
e.what());
} catch (...) {
HKU_THROW_EXCEPTION(CreateResourceException,
"Failed create a new Resource! Unknown error!");
}
m_count++;
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
p = m_resourceList.front();
m_resourceList.pop();
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
/**
*
* @param ms_timeout
* @exception GetResourceTimeoutException, CreateResourceException
*/
ResourcePtr getWaitFor(uint64_t ms_timeout) { // NOSONAR
std::unique_lock<std::mutex> lock(m_mutex);
ResourcePtr result;
ResourceType *p = nullptr;
if (m_resourceList.empty()) {
if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) {
// HKU_TRACE("超出最大资源数,等待空闲资源");
if (ms_timeout > 0) {
if (m_cond.wait_for(lock,
std::chrono::duration<uint64_t, std::milli>(ms_timeout),
[&] { return !m_resourceList.empty(); })) {
HKU_CHECK_THROW(!m_resourceList.empty(), GetResourceTimeoutException,
"Failed get resource!");
} else {
HKU_THROW_EXCEPTION(GetResourceTimeoutException, "Failed get resource!");
}
} else {
m_cond.wait(lock, [this] { return !m_resourceList.empty(); });
}
} else {
try {
p = new ResourceType(m_param);
} catch (const std::exception &e) {
HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}",
e.what());
} catch (...) {
HKU_THROW_EXCEPTION(CreateResourceException,
"Failed create a new Resource! Unknown error!");
}
m_count++;
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
}
p = m_resourceList.front();
m_resourceList.pop();
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
/**
*
* @exception CreateResourceException
*/
ResourcePtr getAndWait() {
return getWaitFor(0);
}
/** 当前活动的资源数, 即全部资源数(含空闲及被使用的资源) */
size_t count() const {
return m_count;
}
/** 当前空闲的资源数 */
size_t idleCount() const {
return m_resourceList.size();
}
/** 释放当前所有的空闲资源 */
void releaseIdleResource() {
std::lock_guard<std::mutex> lock(m_mutex);
_releaseIdleResourceNoLock();
}
private:
void _releaseIdleResourceNoLock() {
while (!m_resourceList.empty()) {
ResourceType *p = m_resourceList.front();
m_resourceList.pop();
m_count--;
if (p) {
delete p;
}
}
}
private:
size_t m_maxPoolSize; // 允许的最大共享资源数
size_t m_maxIdelSize; // 允许的最大空闲资源数
size_t m_count; // 当前活动的资源数
Parameter m_param;
std::mutex m_mutex;
std::condition_variable m_cond;
std::queue<ResourceType *> m_resourceList;
class ResourceCloser {
public:
explicit ResourceCloser(ResourcePool *pool) : m_pool(pool) { // NOSONAR
}
void operator()(ResourceType *conn) { // NOSONAR
if (conn) {
// 如果绑定了 pool则归还资源否则删除
if (m_pool) {
// HKU_DEBUG("retuan to pool");
m_pool->returnResource(conn, this);
} else {
// HKU_DEBUG("delete resource not in pool");
delete conn;
}
}
}
// 解绑资源池
void unbind() {
m_pool = nullptr;
}
private:
ResourcePool *m_pool;
};
/** 归还至资源池 */
void returnResource(ResourceType *p, ResourceCloser *closer) {
std::unique_lock<std::mutex> lock(m_mutex);
if (p) {
if (m_resourceList.size() < m_maxIdelSize) {
m_resourceList.push(p);
m_cond.notify_all();
} else {
delete p;
m_count--;
}
} else {
m_count--;
// HKU_WARN("Trying to return an empty pointer!");
}
if (closer) {
m_closer_set.erase(closer); // 移除该 closer
}
}
std::unordered_set<ResourceCloser *> m_closer_set; // 占用资源的 closer
};
/**
* @brief
* @details getVersion setVerion ResourceVersionPool
*/
class ResourceWithVersion {
public:
/** 默认构造函数 */
ResourceWithVersion() : m_version(0) {}
/** 析构函数 */
virtual ~ResourceWithVersion() {}
/** 获取资源版本 */
int getVersion() const {
return m_version;
}
/** 设置资源版本 **/
void setVersion(int version) {
m_version = version;
}
protected:
int m_version;
};
/**
* 使使
* @details int getVersion() void setVersion(int)
* @ingroup Utilities
*/
template <typename ResourceType>
class ResourceVersionPool {
public:
ResourceVersionPool() = delete;
ResourceVersionPool(const ResourceVersionPool &) = delete;
ResourceVersionPool &operator=(const ResourceVersionPool &) = delete;
/**
*
* @param param
* @param maxPoolSize 0
* @param maxIdleNum 0
*/
explicit ResourceVersionPool(const Parameter &param, size_t maxPoolSize = 0,
size_t maxIdleNum = 100)
: m_maxPoolSize(maxPoolSize),
m_maxIdelSize(maxIdleNum),
m_count(0),
m_param(param),
m_version(0) {}
/**
*
*/
virtual ~ResourceVersionPool() {
std::unique_lock<std::mutex> lock(m_mutex);
// 将所有已分配资源的 closer 和 pool 解绑
for (auto iter = m_closer_set.begin(); iter != m_closer_set.end(); ++iter) {
(*iter)->unbind();
}
// 删除所有空闲资源
while (!m_resourceList.empty()) {
ResourceType *p = m_resourceList.front();
m_resourceList.pop();
if (p) {
delete p;
}
}
}
/** 获取当前允许的最大资源数 */
size_t maxPoolSize() const {
return m_maxIdelSize;
}
/** 获取当前允许的最大空闲资源数 */
size_t maxIdleSize() const {
return m_maxIdelSize;
}
/** 设置最大资源数 */
void maxPoolSize(size_t num) {
std::lock_guard<std::mutex> lock(m_mutex);
m_maxPoolSize = num;
}
/** 设置允许的最大空闲资源数 */
void maxIdleSize(size_t num) {
std::lock_guard<std::mutex> lock(m_mutex);
m_maxIdelSize = num;
}
/** 指定参数是否存在 */
bool haveParam(const std::string &name) {
std::lock_guard<std::mutex> lock(m_mutex);
return m_param.have(name);
}
/** 获取指定参数的值,如参数不存在或类型不匹配抛出异常 */
template <typename ValueType>
ValueType getParam(const std::string &name) {
std::lock_guard<std::mutex> lock(m_mutex);
return m_param.get<ValueType>(name);
}
/**
* @brief
* @details
* @param name
* @param value
* @exception std::logic_error
*/
template <typename ValueType>
void setParam(const std::string &name, const ValueType &value) {
std::lock_guard<std::mutex> lock(m_mutex);
// 如果参数未实际发送变化,则直接返回
HKU_IF_RETURN(m_param.have(name) && value == m_param.get<ValueType>(name), void());
m_param.set<ValueType>(name, value);
m_version++;
_releaseIdleResourceNoLock(); // 释放当前空闲资源,以便新参数值生效
}
/**
* @brief
* @param param
*/
void setParameter(const Parameter &param) {
std::lock_guard<std::mutex> lock(m_mutex);
m_param = param;
m_version++;
_releaseIdleResourceNoLock(); // 释放当前空闲资源,以便新参数值生效
}
/**
* @brief
* @param param
*/
void setParameter(Parameter &&param) {
std::lock_guard<std::mutex> lock(m_mutex);
m_param = std::move(param);
m_version++;
_releaseIdleResourceNoLock(); // 释放当前空闲资源,以便新参数值生效
}
/** 获取当前资源池版本 */
int getVersion() {
std::lock_guard<std::mutex> lock(m_mutex);
return m_version;
}
/** 递增当前资源池版本,相当于通知资源池资源版本发生变化 */
void incVersion(int version) {
std::lock_guard<std::mutex> lock(m_mutex);
m_version++;
}
/** 资源实例指针类型 */
typedef std::shared_ptr<ResourceType> ResourcePtr;
/**
*
* @exception CreateResourceException
*/
ResourcePtr get() {
std::lock_guard<std::mutex> lock(m_mutex);
ResourcePtr result;
ResourceType *p = nullptr;
if (m_resourceList.empty()) {
if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) {
return result;
}
try {
p = new ResourceType(m_param);
p->setVersion(m_version);
} catch (const std::exception &e) {
HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}",
e.what());
} catch (...) {
HKU_THROW_EXCEPTION(CreateResourceException,
"Failed create a new Resource! Unknown error!");
}
m_count++;
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
p = m_resourceList.front();
m_resourceList.pop();
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
/**
*
* @param ms_timeout
* @exception GetResourceTimeoutException, CreateResourceException
*/
ResourcePtr getWaitFor(uint64_t ms_timeout) { // NOSONAR
std::unique_lock<std::mutex> lock(m_mutex);
ResourcePtr result;
ResourceType *p = nullptr;
if (m_resourceList.empty()) {
if (m_maxPoolSize > 0 && m_count >= m_maxPoolSize) {
// HKU_TRACE("超出最大资源数,等待空闲资源");
if (ms_timeout > 0) {
if (m_cond.wait_for(lock,
std::chrono::duration<uint64_t, std::milli>(ms_timeout),
[&] { return !m_resourceList.empty(); })) {
HKU_CHECK_THROW(!m_resourceList.empty(), GetResourceTimeoutException,
"Failed get resource!");
} else {
HKU_THROW_EXCEPTION(GetResourceTimeoutException, "Failed get resource!");
}
} else {
m_cond.wait(lock, [this] { return !m_resourceList.empty(); });
}
} else {
try {
p = new ResourceType(m_param);
p->setVersion(m_version);
} catch (const std::exception &e) {
HKU_THROW_EXCEPTION(CreateResourceException, "Failed create a new Resource! {}",
e.what());
} catch (...) {
HKU_THROW_EXCEPTION(CreateResourceException,
"Failed create a new Resource! Unknown error!");
}
m_count++;
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
}
p = m_resourceList.front();
m_resourceList.pop();
result = ResourcePtr(p, ResourceCloser(this));
m_closer_set.insert(std::get_deleter<ResourceCloser>(result));
return result;
}
/**
*
* @exception CreateResourceException
*/
ResourcePtr getAndWait() {
return getWaitFor(0);
}
/** 当前活动的资源数, 即全部资源数(含空闲及被使用的资源) */
size_t count() const {
return m_count;
}
/** 当前空闲的资源数 */
size_t idleCount() const {
return m_resourceList.size();
}
/** 释放当前所有的空闲资源 */
void releaseIdleResource() {
std::lock_guard<std::mutex> lock(m_mutex);
_releaseIdleResourceNoLock();
}
private:
void _releaseIdleResourceNoLock() {
while (!m_resourceList.empty()) {
ResourceType *p = m_resourceList.front();
m_resourceList.pop();
m_count--;
if (p) {
delete p;
}
}
}
private:
size_t m_maxPoolSize; // 允许的最大共享资源数
size_t m_maxIdelSize; // 允许的最大空闲资源数
size_t m_count; // 当前活动的资源数
Parameter m_param;
std::mutex m_mutex;
std::condition_variable m_cond;
std::queue<ResourceType *> m_resourceList;
int m_version;
class ResourceCloser {
public:
explicit ResourceCloser(ResourceVersionPool *pool) : m_pool(pool) { // NOSONAR
}
void operator()(ResourceType *conn) { // NOSONAR
if (conn) {
// 如果绑定了 pool则归还资源否则删除
if (m_pool) {
// HKU_DEBUG("retuan to pool");
m_pool->returnResource(conn, this);
} else {
// HKU_DEBUG("delete resource not in pool");
delete conn;
}
}
}
// 解绑资源池
void unbind() {
m_pool = nullptr;
}
private:
ResourceVersionPool *m_pool;
};
/** 归还至资源池 */
void returnResource(ResourceType *p, ResourceCloser *closer) {
std::unique_lock<std::mutex> lock(m_mutex);
if (p) {
// 当前归还资源的版本和资源池版本相等,且空闲资源列表小于最大空闲资源数时,接受归还的资源
if (p->getVersion() == m_version && m_resourceList.size() < m_maxIdelSize) {
m_resourceList.push(p);
m_cond.notify_all();
} else {
delete p;
m_count--;
}
} else {
m_count--;
// HKU_WARN("Trying to return an empty pointer!");
}
if (closer) {
m_closer_set.erase(closer); // 移除该 closer
}
}
std::unordered_set<ResourceCloser *> m_closer_set; // 占用资源的 closer
};
} // namespace hku
#endif /* HKU_UTILS_RESOURCE_POOL_H */

View File

@ -126,15 +126,15 @@ double SpendTimer::value() const {
return sec.count();
}
void HKU_API close_spend_time() { // NOSONAR
void HKU_UTILS_API close_spend_time() { // NOSONAR
SpendTimer::ms_closed = true;
}
void HKU_API open_spend_time() { // NOSONAR
void HKU_UTILS_API open_spend_time() { // NOSONAR
SpendTimer::ms_closed = false;
}
bool HKU_API get_spend_time_status() {
bool HKU_UTILS_API get_spend_time_status() {
return !SpendTimer::ms_closed;
}

View File

@ -21,9 +21,10 @@
#include <chrono>
#include <vector>
#include <fmt/format.h>
#include "hikyuu/utilities/config.h"
#ifndef HKU_API
#define HKU_API
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
#ifdef __ANDROID__
#include <android/log.h>
@ -138,7 +139,7 @@ namespace hku {
* @note 使使
* @see SPEND_TIME, SPEND_TIME_MSG, SPEND_TIMG_CONTROL
*/
class HKU_API SpendTimer {
class HKU_UTILS_API SpendTimer {
public:
/** 构造函数,记录当前系统时间 */
explicit SpendTimer()
@ -237,19 +238,19 @@ private:
std::vector<std::string> m_keep_desc;
static bool ms_closed;
friend void HKU_API close_spend_time();
friend void HKU_API open_spend_time();
friend bool HKU_API get_spend_time_status();
friend void HKU_UTILS_API close_spend_time();
friend void HKU_UTILS_API open_spend_time();
friend bool HKU_UTILS_API get_spend_time_status();
};
/** 全局关闭耗时打印输出 */
void HKU_API close_spend_time();
void HKU_UTILS_API close_spend_time();
/** 全局开启耗时打印输出 */
void HKU_API open_spend_time();
void HKU_UTILS_API open_spend_time();
/** 获取全局耗时打印输出状态true - 开启false - 关闭 */
bool HKU_API get_spend_time_status();
bool HKU_UTILS_API get_spend_time_status();
/**
*

View File

@ -11,7 +11,7 @@
#include <unordered_map>
#include <functional>
#include "hikyuu/utilities/datetime/Datetime.h"
#include "hikyuu/Log.h"
#include "hikyuu/utilities/Log.h"
#include "thread/ThreadPool.h"
#include "cppdef.h"
@ -35,7 +35,23 @@ public:
* @param work_num 线线
*/
explicit TimerManager(size_t work_num = 1)
: m_stop(true), m_current_timer_id(-1), m_work_num(work_num) {
: m_stop(true),
m_current_timer_id(-1),
m_work_num(work_num),
m_tg(nullptr),
m_use_extend_tg(false) {
HKU_ASSERT(work_num >= 1);
start();
}
/**
* 线便线
* @note tg TimerManager
* @param tg 线
*/
explicit TimerManager(ThreadPool* tg)
: m_stop(true), m_current_timer_id(-1), m_work_num(1), m_tg(tg), m_use_extend_tg(true) {
HKU_ASSERT(m_tg);
start();
}
@ -60,11 +76,7 @@ public:
std::priority_queue<IntervalS> new_queue;
m_queue.swap(new_queue);
if (!m_tg) {
#if CPP_STANDARD >= CPP_STANDARD_14
m_tg = std::make_unique<ThreadPool>(m_work_num);
#else
m_tg = std::unique_ptr<ThreadPool>(new ThreadPool(m_work_num));
#endif
m_tg = new ThreadPool(m_work_num);
}
/*
@ -152,9 +164,10 @@ public:
m_detect_thread.join();
}
if (m_tg) {
if (!m_use_extend_tg && m_tg) {
m_tg->stop();
m_tg.reset();
delete m_tg;
m_tg = nullptr;
}
}
@ -399,7 +412,7 @@ private:
// 分配 timer_id
int getNewTimerId() {
int max_int = std::numeric_limits<int>::max();
HKU_WARN_IF_RETURN(m_timers.size() >= max_int, -1, "Timer queue is full!");
HKU_WARN_IF_RETURN(m_timers.size() >= size_t(max_int), -1, "Timer queue is full!");
if (m_current_timer_id >= max_int) {
m_current_timer_id = 0;
@ -532,7 +545,8 @@ private:
std::unordered_map<int, Timer*> m_timers;
int m_current_timer_id;
size_t m_work_num; // 任务执行线程池线程数量
std::unique_ptr<ThreadPool> m_tg;
ThreadPool* m_tg{nullptr};
bool m_use_extend_tg{false};
};
} // namespace hku

View File

@ -0,0 +1,142 @@
/*
* Copyright (c) 2023 hikyuu.org
*
* Created on: 2023-01-15
* Author: fasiondog
*/
#pragma once
#include <string>
#include "osdef.h"
#include "cppdef.h"
#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17
#include <any>
#else
#include <boost/any.hpp>
#endif
#if defined(HKU_SUPPORT_DATETIME)
#include "hikyuu/utilities/datetime/Datetime.h"
#endif
namespace hku {
#if !HKU_OS_IOS && CPP_STANDARD >= CPP_STANDARD_17
using any_t = std::any;
using std::any_cast;
#else
using any_t = boost::any;
using boost::any_cast;
#endif
//------------------------------------------------------------------------------
//
// 常见基本类型包装的 any_t 和 std::string 的互相转换函数
// any_to_string 要用户自定义类型需包含从 std::string 进行构造的构造函数
// string_to_any 需要用户自定义实现 std::to_string 特化方法
//
//------------------------------------------------------------------------------
template <typename ValueT>
inline std::string any_to_string(const any_t& data) {
return any_cast<ValueT>(data).str();
}
template <typename ValueT>
inline any_t string_to_any(const std::string& data) {
return any_t(ValueT(data));
}
template <>
inline std::string any_to_string<int>(const any_t& data) {
return std::to_string(any_cast<int>(data));
}
template <>
inline std::string any_to_string<long>(const any_t& data) {
return std::to_string(any_cast<long>(data));
}
template <>
inline std::string any_to_string<long long>(const any_t& data) {
return std::to_string(any_cast<long long>(data));
}
template <>
inline std::string any_to_string<unsigned int>(const any_t& data) {
return std::to_string(any_cast<unsigned int>(data));
}
template <>
inline std::string any_to_string<unsigned long>(const any_t& data) {
return std::to_string(any_cast<unsigned long>(data));
}
template <>
inline std::string any_to_string<unsigned long long>(const any_t& data) {
return std::to_string(any_cast<unsigned long long>(data));
}
template <>
inline std::string any_to_string<float>(const any_t& data) {
return std::to_string(any_cast<float>(data));
}
template <>
inline std::string any_to_string<double>(const any_t& data) {
return std::to_string(any_cast<double>(data));
}
template <>
inline std::string any_to_string<long double>(const any_t& data) {
return std::to_string(any_cast<long double>(data));
}
template <>
inline any_t string_to_any<int>(const std::string& data) {
return any_t(std::stoi(data));
}
template <>
inline any_t string_to_any<long>(const std::string& data) {
return any_t(std::stol(data));
}
template <>
inline any_t string_to_any<long long>(const std::string& data) {
return any_t(std::stoll(data));
}
template <>
inline any_t string_to_any<unsigned int>(const std::string& data) {
return any_t((unsigned int)(std::stoul(data)));
}
template <>
inline any_t string_to_any<unsigned long>(const std::string& data) {
return any_t(std::stoul(data));
}
template <>
inline any_t string_to_any<unsigned long long>(const std::string& data) {
return any_t(std::stoull(data));
}
template <>
inline any_t string_to_any<float>(const std::string& data) {
return any_t(std::stof(data));
}
template <>
inline any_t string_to_any<double>(const std::string& data) {
return any_t(std::stod(data));
}
template <>
inline any_t string_to_any<long double>(const std::string& data) {
return any_t(std::stold(data));
}
} // namespace hku

View File

@ -17,6 +17,13 @@
namespace hku {
template double HKU_UTILS_API roundEx(double number, int ndigits);
template float HKU_UTILS_API roundEx(float number, int ndigits);
template double HKU_UTILS_API roundUp(double number, int ndigits);
template float HKU_UTILS_API roundUp(float number, int ndigits);
template double HKU_UTILS_API roundDown(double number, int ndigits);
template float HKU_UTILS_API roundDown(float number, int ndigits);
#if defined(_MSC_VER)
/**
* UTF8编码的字符串转换为GB2312编码的字符串
@ -24,7 +31,7 @@ namespace hku {
* @return GB2312编码的字符串
* @note Windows平台下生效
*/
std::string HKU_API utf8_to_gb(const char *szinput) {
std::string HKU_UTILS_API utf8_to_gb(const char *szinput) {
wchar_t *strSrc;
char *szRes;
std::string nullStr;
@ -60,7 +67,7 @@ std::string HKU_API utf8_to_gb(const char *szinput) {
return result;
}
std::string HKU_API utf8_to_gb(const std::string &szinput) {
std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput) {
return utf8_to_gb(szinput.c_str());
}
@ -70,7 +77,7 @@ std::string HKU_API utf8_to_gb(const std::string &szinput) {
* @return UTF8编码的字符串
* @note Windows平台下生效
*/
std::string HKU_API gb_to_utf8(const char *szinput) {
std::string HKU_UTILS_API gb_to_utf8(const char *szinput) {
wchar_t *strSrc;
char *szRes;
std::string nullstr;
@ -107,12 +114,12 @@ std::string HKU_API gb_to_utf8(const char *szinput) {
return result;
}
std::string HKU_API gb_to_utf8(const std::string &szinput) {
std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput) {
return gb_to_utf8(szinput.c_str());
}
#else /* else for defined(_MSC_VER) */
std::string HKU_API utf8_to_gb(const std::string &szinput) {
std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput) {
char *inbuf = const_cast<char *>(szinput.c_str());
size_t inlen = strlen(inbuf);
size_t outlen = inlen;
@ -128,7 +135,7 @@ std::string HKU_API utf8_to_gb(const std::string &szinput) {
return result;
}
std::string HKU_API gb_to_utf8(const std::string &szinput) {
std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput) {
char *inbuf = const_cast<char *>(szinput.c_str());
size_t inlen = strlen(inbuf);
size_t outlen = inlen * 2;

View File

@ -15,11 +15,12 @@
#include <cctype>
#include <vector>
#include <string>
#include <string_view>
#include <algorithm>
#ifndef HKU_API
#define HKU_API
#include "string_view.h"
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
namespace hku {
@ -30,13 +31,13 @@ namespace hku {
*/
#if defined(_MSC_VER)
std::string HKU_API utf8_to_gb(const char* szinput);
std::string HKU_API utf8_to_gb(const std::string& szinput);
std::string HKU_API gb_to_utf8(const char* szinput);
std::string HKU_API gb_to_utf8(const std::string& szinput);
std::string HKU_UTILS_API utf8_to_gb(const char *szinput);
std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput);
std::string HKU_UTILS_API gb_to_utf8(const char *szinput);
std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput);
#else
std::string HKU_API utf8_to_gb(const std::string& szinput);
std::string HKU_API gb_to_utf8(const std::string& szinput);
std::string HKU_UTILS_API utf8_to_gb(const std::string &szinput);
std::string HKU_UTILS_API gb_to_utf8(const std::string &szinput);
#endif
#define UTF8ToGB hku::utf8_to_gb
@ -81,18 +82,17 @@ std::string HKU_API gb_to_utf8(const std::string& szinput);
* @param ndigits
* @return
*/
// double HKU_API roundEx(double number, int ndigits = 0);
template <typename ValueT>
ValueT roundEx(ValueT number, int ndigits = 0) {
// 切换至ROUND_HALF_EVEN 银行家舍入法
ValueT pow1, pow2, y, z;
ValueT x = number;
if (ndigits >= 0) {
pow1 = std::pow<ValueT>(10.0, (ValueT)ndigits);
pow1 = pow(ValueT(10.0), ValueT(ndigits));
pow2 = 1.0;
y = (x * pow1) * pow2;
} else {
pow1 = std::pow<ValueT>(10.0, (ValueT)-ndigits);
pow1 = pow(ValueT(10.0), ValueT(-ndigits));
pow2 = 1.0;
y = x / pow1;
}
@ -189,17 +189,17 @@ ValueT roundDown(ValueT number, int ndigits = 0) {
#endif
/** 转小写字符串 */
inline void to_lower(std::string& s) {
inline void to_lower(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::tolower(c); });
}
/** 转大写字符串 */
inline void to_upper(std::string& s) {
inline void to_upper(std::string &s) {
std::transform(s.begin(), s.end(), s.begin(), [](unsigned char c) { return std::toupper(c); });
}
/** 删除字符串两端空格 */
inline void trim(std::string& s) {
inline void trim(std::string &s) {
if (s.empty()) {
return;
}
@ -210,12 +210,13 @@ inline void trim(std::string& s) {
s.erase(s.find_last_not_of("\n") + 1);
}
#if CPP_STANDARD >= CPP_STANDARD_17
/**
*
* @param str
* @param c
*/
inline std::vector<std::string_view> split(const std::string& str, char c) {
inline std::vector<std::string_view> split(const std::string &str, char c) {
std::vector<std::string_view> result;
std::string_view view(str);
size_t prepos = 0;
@ -226,9 +227,7 @@ inline std::vector<std::string_view> split(const std::string& str, char c) {
pos = view.find_first_of(c, prepos);
}
if (prepos < str.size() - 1) {
result.emplace_back(str.substr(prepos));
}
result.emplace_back(view.substr(prepos));
return result;
}
@ -239,7 +238,7 @@ inline std::vector<std::string_view> split(const std::string& str, char c) {
* @return string_view vector
* @note
*/
inline std::vector<std::string_view> split(const std::string_view& view, char c) {
inline std::vector<std::string_view> split(const std::string_view &view, char c) {
std::vector<std::string_view> result;
size_t prepos = 0;
size_t pos = view.find_first_of(c);
@ -249,14 +248,12 @@ inline std::vector<std::string_view> split(const std::string_view& view, char c)
pos = view.find_first_of(c, prepos);
}
if (prepos < view.size() - 1) {
result.emplace_back(view.substr(prepos));
}
result.emplace_back(view.substr(prepos));
return result;
}
inline std::vector<std::string_view> split(const std::string_view& str,
const std::string& split_str) {
inline std::vector<std::string_view> split(const std::string_view &str,
const std::string &split_str) {
std::vector<std::string_view> result;
size_t split_str_len = split_str.size();
if (split_str_len == 0) {
@ -272,25 +269,64 @@ inline std::vector<std::string_view> split(const std::string_view& str,
pos = str.find(split_str, prepos);
}
if (prepos < str.size() - 1) {
result.emplace_back(str.substr(prepos));
}
result.emplace_back(str.substr(prepos));
return result;
}
#else
/**
*
* @param str
* @param c
*/
inline std::vector<std::string> split(const std::string &str, char c) {
std::vector<std::string> result;
size_t prepos = 0;
size_t pos = str.find_first_of(c);
while (pos != std::string::npos) {
result.emplace_back(str.substr(prepos, pos - prepos));
prepos = pos + 1;
pos = str.find_first_of(c, prepos);
}
result.emplace_back(str.substr(prepos));
return result;
}
inline std::vector<std::string> split(const std::string &str, const std::string &split_str) {
std::vector<std::string> result;
size_t split_str_len = split_str.size();
if (split_str_len == 0) {
result.emplace_back(str);
return result;
}
size_t prepos = 0;
size_t pos = str.find(split_str);
while (pos != std::string::npos) {
result.emplace_back(str.substr(prepos, pos - prepos));
prepos = pos + split_str_len;
pos = str.find(split_str, prepos);
}
result.emplace_back(str.substr(prepos));
return result;
}
#endif /* #if CPP_STANDARD >= CPP_STANDARD_17 */
/**
* byte 16 , "abcd" "61626364"
* @param in_byte byte
* @param in_len byte
*/
inline std::string byteToHexStr(const char* bytes, size_t in_len) {
inline std::string byteToHexStr(const char *bytes, size_t in_len) {
std::string hexstr;
const unsigned char* in_byte = (const unsigned char*)bytes;
const unsigned char *in_byte = (const unsigned char *)bytes;
if (in_byte == nullptr) {
return hexstr;
}
char* buf = new char[2 * in_len + 1];
char *buf = new char[2 * in_len + 1];
size_t buf_ix = 0;
for (size_t i = 0; i < in_len; ++i) {
@ -311,7 +347,7 @@ inline std::string byteToHexStr(const char* bytes, size_t in_len) {
* byte 16 , "abcd" "61626364"
* @param in_byte std::string
*/
inline std::string byteToHexStr(const std::string& bytes) {
inline std::string byteToHexStr(const std::string &bytes) {
return byteToHexStr(bytes.c_str(), bytes.size());
}
@ -320,14 +356,14 @@ inline std::string byteToHexStr(const std::string& bytes) {
* @param in_byte byte
* @param in_len byte
*/
inline std::string byteToHexStrForPrint(const char* bytes, size_t in_len) {
inline std::string byteToHexStrForPrint(const char *bytes, size_t in_len) {
std::string hexstr;
const unsigned char* in_byte = (const unsigned char*)bytes;
const unsigned char *in_byte = (const unsigned char *)bytes;
if (in_byte == nullptr) {
return hexstr;
}
char* buf = new char[5 * in_len + 1];
char *buf = new char[5 * in_len + 1];
size_t buf_ix = 0;
for (size_t i = 0; i < in_len; ++i) {
@ -355,7 +391,7 @@ inline std::string byteToHexStrForPrint(const char* bytes, size_t in_len) {
* byte 16 , "abcd" "61626364"
* @param in_byte byte
*/
inline std::string byteToHexStrForPrint(const std::string& bytes) {
inline std::string byteToHexStrForPrint(const std::string &bytes) {
return byteToHexStrForPrint(bytes.c_str(), bytes.size());
}

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) hikyuu.org
*
* Created on: 2020-6-2
* Author: fasiondog
*/
#include "base64.h"
#include "Log.h"
namespace hku {
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len) { // NOSONAR
HKU_CHECK(bytes_to_encode, "Input null ptr!");
std::string ret;
HKU_IF_RETURN(in_len == 0, ret);
int i = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (i = 0; (i < 4); i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i) {
for (int j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (int j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while ((i++ < 3))
ret += '=';
}
return ret;
}
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_decode(unsigned char const* encoded_string, size_t in_len) {
HKU_CHECK(encoded_string, "Input null ptr!");
std::string ret;
HKU_IF_RETURN(in_len == 0, ret);
int i = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
while (in_len-- && (encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_];
in_++;
if (i == 4) {
for (i = 0; i < 4; i++)
char_array_4[i] = (unsigned char)base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (int j = i; j < 4; j++)
char_array_4[j] = 0;
for (int j = 0; j < 4; j++)
char_array_4[j] = (unsigned char)base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (int j = 0; (j < i - 1); j++)
ret += char_array_3[j];
}
return ret;
}
} // namespace hku

View File

@ -0,0 +1,59 @@
/*
* Copyright (c) hikyuu.org
*
* Created on: 2020-6-2
* Author: fasiondog
*/
#pragma once
#ifndef HKU_UTILS_BASE64_H
#define HKU_UTILS_BASE64_H
#include <memory>
#include "string_view.h"
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
namespace hku {
/**
* bytes base64
* @param bytes_to_encode
* @param in_len
*/
std::string HKU_UTILS_API base64_encode(unsigned char const* bytes_to_encode, size_t in_len);
/**
* base64
* @param bytes_to_encode
* @param in_len
* @note func(unsigned char *, unsigned int) string_view
* c++17 string_view nullptr
*/
inline std::string base64_encode(string_view src) {
return base64_encode((unsigned char const*)src.data(), src.size());
}
/**
* base64
* @param encoded_string base64
* @param in_len
* @return string
* @note base64编码字符串中含有非法字符
*/
std::string HKU_UTILS_API base64_decode(unsigned char const* encoded_string, size_t in_len);
/**
* base64
* @param encoded_string base64
* @return string
*/
inline std::string base64_decode(string_view encoded_string) {
return base64_decode((unsigned char const*)encoded_string.data(), encoded_string.size());
}
} // namespace hku
#endif // HKU_UTILS_BASE64_H

View File

@ -11,18 +11,22 @@
#include <fmt/format.h>
#include "hikyuu/utilities/Null.h"
#include "hikyuu/Log.h"
#include "hikyuu/utilities/Log.h"
#include "hikyuu/utilities/arithmetic.h"
#include "Datetime.h"
namespace hku {
HKU_API std::ostream& operator<<(std::ostream& out, const Datetime& d) {
HKU_UTILS_API std::ostream &operator<<(std::ostream &out, const Datetime &d) {
out << d.str();
return out;
}
Datetime Datetime::fromHex(uint64_t time) {
if (Null<unsigned long long>() == time) {
return Datetime();
}
uint64_t second = 0xFFULL & time;
uint64_t minute = (0xFF00ULL & time) >> 8;
uint64_t hour = (0xFF0000ULL & time) >> 16;
@ -88,7 +92,7 @@ Datetime::Datetime(unsigned long long datetime) {
}
}
Datetime::Datetime(const std::string& ts) {
Datetime::Datetime(const std::string &ts) {
std::string timeStr(ts);
trim(timeStr);
if ("+infinity" == timeStr) {
@ -113,7 +117,7 @@ bool Datetime::isNull() const {
return (m_data == null_date) ? true : false;
}
Datetime& Datetime::operator=(const Datetime& d) {
Datetime &Datetime::operator=(const Datetime &d) {
if (this == &d)
return *this;
m_data = d.m_data;
@ -155,7 +159,7 @@ uint64_t Datetime::number() const noexcept {
return (unsigned long long)year() * 100000000ULL +
(unsigned long long)month() * 1000000ULL + (unsigned long long)day() * 10000ULL +
(unsigned long long)hour() * 100ULL + (unsigned long long)minute();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -169,7 +173,7 @@ uint64_t Datetime::ym() const noexcept {
try {
HKU_IF_RETURN(isNull(), Null<unsigned long long>());
return (unsigned long long)year() * 100ULL + (unsigned long long)month();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -184,7 +188,7 @@ uint64_t Datetime::ymd() const noexcept {
HKU_IF_RETURN(isNull(), Null<unsigned long long>());
return (unsigned long long)year() * 10000ULL + (unsigned long long)month() * 100ULL +
(unsigned long long)day();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -199,7 +203,7 @@ uint64_t Datetime::ymdh() const noexcept {
HKU_IF_RETURN(isNull(), Null<unsigned long long>());
return (unsigned long long)year() * 1000000ULL + (unsigned long long)month() * 10000ULL +
(unsigned long long)day() * 100ULL + (unsigned long long)hour();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -215,7 +219,7 @@ uint64_t Datetime::ymdhm() const noexcept {
return (unsigned long long)year() * 100000000LL + (unsigned long long)month() * 1000000LL +
(unsigned long long)day() * 10000LL + (unsigned long long)hour() * 100LL +
(unsigned long long)minute();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -232,7 +236,7 @@ uint64_t Datetime::ymdhms() const noexcept {
(unsigned long long)month() * 100000000ULL + (unsigned long long)day() * 1000000ULL +
(unsigned long long)hour() * 10000ULL + (unsigned long long)minute() * 100ULL +
(unsigned long long)second();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -255,7 +259,7 @@ uint64_t Datetime::hex() const noexcept {
ret |= (low_y << 40);
ret |= (high_y << 48);
return ret;
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -269,7 +273,7 @@ uint64_t Datetime::ticks() const noexcept {
HKU_IF_RETURN(isNull(), Null<uint64_t>());
TimeDelta d = (*this) - Datetime::min();
return d.ticks();
} catch (const std::exception& e) {
} catch (const std::exception &e) {
HKU_ERROR(e.what());
return Null<uint64_t>();
} catch (...) {
@ -337,7 +341,7 @@ Datetime Datetime::today() {
return Datetime(x.year(), x.month(), x.day());
}
DatetimeList HKU_API getDateRange(const Datetime& start, const Datetime& end) {
DatetimeList HKU_UTILS_API getDateRange(const Datetime &start, const Datetime &end) {
DatetimeList result;
bd::date start_day = start.date();
bd::date end_day = end.date();

View File

@ -17,14 +17,19 @@
#include <string>
#include <vector>
#include <fmt/ostream.h>
#include "hikyuu/utilities/config.h"
#if !HKU_SUPPORT_DATETIME
#error "Don't support datetime, you can config with --datetime=y"
#endif
#include "TimeDelta.h"
#if defined(_MSC_VER)
#pragma warning(disable : 4251)
#endif
#ifndef HKU_API
#define HKU_API
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
namespace hku {
@ -37,7 +42,7 @@ namespace bd = boost::gregorian;
* @details std::out_of_range
* @ingroup DataType
*/
class HKU_API Datetime {
class HKU_UTILS_API Datetime {
public:
/** 返回所能表示的最小日期1400-Jan-01 00:00:00 */
static Datetime min();
@ -61,7 +66,7 @@ public:
/** 默认构造函数Null<Datetime> */
Datetime();
Datetime(const Datetime&);
Datetime(const Datetime &);
/**
*
@ -78,10 +83,10 @@ public:
long millisec = 0, long microsec = 0);
/** 从boost::gregorian::date构造日期类型 */
explicit Datetime(const bd::date&);
explicit Datetime(const bd::date &);
/** 从boost::posix_time::ptime构造 */
explicit Datetime(const bt::ptime&);
explicit Datetime(const bt::ptime &);
/**
*
@ -103,9 +108,9 @@ public:
* 4"20010101T181159"
* </pre>
*/
explicit Datetime(const std::string&);
explicit Datetime(const std::string &);
Datetime& operator=(const Datetime&);
Datetime &operator=(const Datetime &);
/** 年份,如果是 Null 将抛出异常 */
long year() const;
@ -283,7 +288,7 @@ private:
bt::ptime m_data;
};
HKU_API std::ostream& operator<<(std::ostream&, const Datetime&);
HKU_UTILS_API std::ostream &operator<<(std::ostream &, const Datetime &);
/**
*
@ -297,41 +302,41 @@ typedef std::vector<Datetime> DatetimeList;
* @param end
* @return [start, end)
*/
DatetimeList HKU_API getDateRange(const Datetime& start, const Datetime& end);
DatetimeList HKU_UTILS_API getDateRange(const Datetime &start, const Datetime &end);
///////////////////////////////////////////////////////////////////////////////
//
// 关系比较函数, 不直接在类中定义是为了支持 Null<>() == dNull可以放在左边
//
///////////////////////////////////////////////////////////////////////////////
bool operator==(const Datetime&, const Datetime&);
bool operator!=(const Datetime&, const Datetime&);
bool operator>(const Datetime&, const Datetime&);
bool operator<(const Datetime&, const Datetime&);
bool operator>=(const Datetime&, const Datetime&);
bool operator<=(const Datetime&, const Datetime&);
bool operator==(const Datetime &, const Datetime &);
bool operator!=(const Datetime &, const Datetime &);
bool operator>(const Datetime &, const Datetime &);
bool operator<(const Datetime &, const Datetime &);
bool operator>=(const Datetime &, const Datetime &);
bool operator<=(const Datetime &, const Datetime &);
inline bool operator==(const Datetime& d1, const Datetime& d2) {
inline bool operator==(const Datetime &d1, const Datetime &d2) {
return d1.ptime() == d2.ptime();
}
inline bool operator!=(const Datetime& d1, const Datetime& d2) {
inline bool operator!=(const Datetime &d1, const Datetime &d2) {
return d1.ptime() != d2.ptime();
}
inline bool operator>(const Datetime& d1, const Datetime& d2) {
inline bool operator>(const Datetime &d1, const Datetime &d2) {
return d1.ptime() > d2.ptime();
}
inline bool operator<(const Datetime& d1, const Datetime& d2) {
inline bool operator<(const Datetime &d1, const Datetime &d2) {
return d1.ptime() < d2.ptime();
}
inline bool operator>=(const Datetime& d1, const Datetime& d2) {
inline bool operator>=(const Datetime &d1, const Datetime &d2) {
return d1.ptime() >= d2.ptime();
}
inline bool operator<=(const Datetime& d1, const Datetime& d2) {
inline bool operator<=(const Datetime &d1, const Datetime &d2) {
return d1.ptime() <= d2.ptime();
}
@ -340,11 +345,11 @@ inline bool operator<=(const Datetime& d1, const Datetime& d2) {
// 加、减法运算补充
//
///////////////////////////////////////////////////////////////////////////////
inline Datetime operator+(const TimeDelta& delta, const Datetime& date) {
inline Datetime operator+(const TimeDelta &delta, const Datetime &date) {
return date + delta;
}
inline TimeDelta operator-(const Datetime& d1, const Datetime& d2) {
inline TimeDelta operator-(const Datetime &d1, const Datetime &d2) {
return TimeDelta(d1.ptime() - d2.ptime());
}
@ -359,11 +364,11 @@ inline Datetime::Datetime() {
m_data = bt::ptime(d, bt::time_duration(0, 0, 0));
}
inline Datetime::Datetime(const Datetime& d) : m_data(d.m_data) {}
inline Datetime::Datetime(const Datetime &d) : m_data(d.m_data) {}
inline Datetime::Datetime(const bd::date& d) : m_data(bt::ptime(d, bt::time_duration(0, 0, 0))) {}
inline Datetime::Datetime(const bd::date &d) : m_data(bt::ptime(d, bt::time_duration(0, 0, 0))) {}
inline Datetime::Datetime(const bt::ptime& d) : m_data(d) {}
inline Datetime::Datetime(const bt::ptime &d) : m_data(d) {}
inline bt::ptime Datetime::ptime() const {
return m_data;
@ -404,21 +409,26 @@ namespace std {
template <>
class hash<hku::Datetime> {
public:
size_t operator()(hku::Datetime const& d) const noexcept {
size_t operator()(hku::Datetime const &d) const noexcept {
return d.ticks(); // or use boost::hash_combine
}
};
inline string to_string(const hku::Datetime &date) {
return date.str();
}
} // namespace std
#if FMT_VERSION >= 90000
template <>
struct fmt::formatter<hku::Datetime> {
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
return ctx.end();
}
template <typename FormatContext>
auto format(const hku::Datetime& d, FormatContext& ctx) const -> decltype(ctx.out()) {
auto format(const hku::Datetime &d, FormatContext &ctx) const -> decltype(ctx.out()) {
return fmt::format_to(ctx.out(), "{}", d.str());
}
};

View File

@ -10,7 +10,7 @@
#include <cstdint>
#include "TimeDelta.h"
#include "hikyuu/utilities/arithmetic.h"
#include "hikyuu/Log.h"
#include "hikyuu/utilities/Log.h"
namespace hku {
@ -39,6 +39,24 @@ TimeDelta::TimeDelta(bt::time_duration td) {
m_duration = td;
}
/** 从字符串构造,格式:-1 days, hh:mm:ss.000000) */
TimeDelta::TimeDelta(const std::string& delta) {
std::string val(delta);
std::string errmsg(fmt::format("Invalid format: {}", delta));
to_lower(val);
auto vals = split(val, ' ');
HKU_CHECK(vals.size() == 3, errmsg);
int64_t days = std::stoll(std::string(vals[0]));
vals = split(vals[2], ':');
HKU_CHECK(vals.size() == 3, errmsg);
int64_t hours = std::stoll(std::string(vals[0]));
int64_t minutes = std::stoll(std::string(vals[1]));
int64_t microseconds = static_cast<int64_t>(std::stod(std::string(vals[2])) * 1000000.0);
int64_t total = (((days * 24) + hours) * 60 + minutes) * 60000000LL + microseconds;
HKU_CHECK(total >= m_min_micro_seconds && total <= m_max_micro_seconds, "Out of total range!");
m_duration = bt::time_duration(0, 0, 0, total);
}
TimeDelta TimeDelta::fromTicks(int64_t ticks) {
HKU_CHECK(ticks >= m_min_micro_seconds && ticks <= m_max_micro_seconds, "Out of total range!");
return TimeDelta(bt::time_duration(0, 0, 0, ticks));
@ -120,28 +138,28 @@ int64_t TimeDelta::microseconds() const {
return std::abs(ticks() % 1000);
}
TimeDelta HKU_API Hours(int64_t hours) {
TimeDelta HKU_UTILS_API Hours(int64_t hours) {
HKU_CHECK(hours >= TimeDelta::minTicks() / 3600000000LL &&
hours <= TimeDelta::maxTicks() / 3600000000LL,
"Out of total range!");
return TimeDelta::fromTicks(hours * 3600000000LL);
}
TimeDelta HKU_API Minutes(int64_t mins) {
TimeDelta HKU_UTILS_API Minutes(int64_t mins) {
HKU_CHECK(
mins >= TimeDelta::minTicks() / 60000000LL && mins <= TimeDelta::maxTicks() / 60000000LL,
"Out of total range!");
return TimeDelta::fromTicks(mins * 60000000LL);
}
TimeDelta HKU_API Seconds(int64_t secs) {
TimeDelta HKU_UTILS_API Seconds(int64_t secs) {
HKU_CHECK(
secs >= TimeDelta::minTicks() / 1000000LL && secs <= TimeDelta::maxTicks() / 1000000LL,
"Out of total range!");
return TimeDelta::fromTicks(secs * 1000000LL);
}
TimeDelta HKU_API Milliseconds(int64_t milliseconds) {
TimeDelta HKU_UTILS_API Milliseconds(int64_t milliseconds) {
HKU_CHECK(milliseconds >= TimeDelta::minTicks() / 1000LL &&
milliseconds <= TimeDelta::maxTicks() / 1000LL,
"Out of total range!");
@ -149,12 +167,12 @@ TimeDelta HKU_API Milliseconds(int64_t milliseconds) {
}
TimeDelta TimeDelta::operator*(double p) const {
return TimeDelta::fromTicks(static_cast<int64_t>(roundEx(double(ticks()) * p, 0)));
return TimeDelta::fromTicks(static_cast<int64_t>(roundEx<double>(double(ticks()) * p, 0)));
}
TimeDelta TimeDelta::operator/(double p) const {
HKU_CHECK(p != 0, "Attempt to divide by 0!");
return TimeDelta::fromTicks(static_cast<int64_t>(roundEx(double(ticks()) / p, 0)));
return TimeDelta::fromTicks(static_cast<int64_t>(roundEx<double>(double(ticks()) / p, 0)));
}
TimeDelta TimeDelta::floorDiv(double p) const {

View File

@ -20,8 +20,8 @@
#include <fmt/ostream.h>
#include <boost/date_time/posix_time/posix_time.hpp>
#ifndef HKU_API
#define HKU_API
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
namespace hku {
@ -33,7 +33,7 @@ namespace bd = boost::gregorian;
*
* @ingroup DataType
*/
class HKU_API TimeDelta {
class HKU_UTILS_API TimeDelta {
public:
/**
*
@ -58,11 +58,14 @@ public:
/** 通过 boost::posix_time::time_duration 构造 */
explicit TimeDelta(bt::time_duration td);
/** 从字符串构造,格式:-1 days, hh:mm:ss.000000) */
explicit TimeDelta(const std::string& delta);
/** 赋值构造函数 */
TimeDelta(const TimeDelta&) = default;
TimeDelta(const TimeDelta &) = default;
/** 赋值拷贝函数 */
TimeDelta& operator=(const TimeDelta& other) {
TimeDelta &operator=(const TimeDelta &other) {
if (this != &other) {
m_duration = other.m_duration;
}
@ -259,8 +262,8 @@ private:
static constexpr const int64_t m_one_day_ticks = 24 * 60 * 60 * 1000000LL;
};
std::ostream& operator<<(std::ostream& out, TimeDelta td);
inline std::ostream& operator<<(std::ostream& out, TimeDelta td) {
std::ostream &operator<<(std::ostream &out, TimeDelta td);
inline std::ostream &operator<<(std::ostream &out, TimeDelta td) {
out << td.str();
return out;
}
@ -280,28 +283,28 @@ inline TimeDelta Days(int64_t days) {
* @param hours
* @ingroup DataType
*/
TimeDelta HKU_API Hours(int64_t hours);
TimeDelta HKU_UTILS_API Hours(int64_t hours);
/**
* TimeDelta
* @param mins
* @ingroup DataType
*/
TimeDelta HKU_API Minutes(int64_t mins);
TimeDelta HKU_UTILS_API Minutes(int64_t mins);
/**
* TimeDelta
* @param secs
* @ingroup DataType
*/
TimeDelta HKU_API Seconds(int64_t secs);
TimeDelta HKU_UTILS_API Seconds(int64_t secs);
/**
* TimeDelta
* @param milliseconds
* @ingroup DataType
*/
TimeDelta HKU_API Milliseconds(int64_t milliseconds);
TimeDelta HKU_UTILS_API Milliseconds(int64_t milliseconds);
/**
* TimeDelta
@ -315,15 +318,23 @@ inline TimeDelta Microseconds(int64_t microsecs) {
} /* namespace hku */
namespace std {
inline string to_string(const hku::TimeDelta &delta) {
return delta.str();
}
} // namespace std
#if FMT_VERSION >= 90000
template <>
struct fmt::formatter<hku::TimeDelta> {
constexpr auto parse(format_parse_context& ctx) -> decltype(ctx.begin()) {
constexpr auto parse(format_parse_context &ctx) -> decltype(ctx.begin()) {
return ctx.end();
}
template <typename FormatContext>
auto format(const hku::TimeDelta& d, FormatContext& ctx) const -> decltype(ctx.out()) {
auto format(const hku::TimeDelta &d, FormatContext &ctx) const -> decltype(ctx.out()) {
return fmt::format_to(ctx.out(), "{}", d.str());
}
};

View File

@ -13,23 +13,23 @@
#include <vector>
#include <fmt/format.h>
#include <fmt/ranges.h>
#include "../../Log.h"
#include "hikyuu/utilities/Log.h"
#ifndef HKU_API
#define HKU_API
#ifndef HKU_UTILS_API
#define HKU_UTILS_API
#endif
namespace hku {
struct ASC {
explicit ASC(const char* name) : name(name) {}
explicit ASC(const std::string& name) : name(name) {}
explicit ASC(const char *name) : name(name) {}
explicit ASC(const std::string &name) : name(name) {}
std::string name;
};
struct DESC {
explicit DESC(const char* name) : name(name) {}
explicit DESC(const std::string& name) : name(name) {}
explicit DESC(const char *name) : name(name) {}
explicit DESC(const std::string &name) : name(name) {}
std::string name;
};
@ -38,50 +38,50 @@ struct LIMIT {
int limit = 1;
};
class HKU_API DBCondition {
class HKU_UTILS_API DBCondition {
public:
DBCondition() = default;
DBCondition(const DBCondition&) = default;
DBCondition(DBCondition&& rv) : m_condition(std::move(rv.m_condition)) {}
DBCondition(const DBCondition &) = default;
DBCondition(DBCondition &&rv) : m_condition(std::move(rv.m_condition)) {}
explicit DBCondition(const char* cond) : m_condition(cond) {}
explicit DBCondition(const std::string& cond) : m_condition(cond) {}
explicit DBCondition(const char *cond) : m_condition(cond) {}
explicit DBCondition(const std::string &cond) : m_condition(cond) {}
DBCondition& operator=(const DBCondition&) = default;
DBCondition& operator=(DBCondition&& rv) {
DBCondition &operator=(const DBCondition &) = default;
DBCondition &operator=(DBCondition &&rv) {
if (this != &rv) {
m_condition = std::move(rv.m_condition);
}
return *this;
}
DBCondition& operator&(const DBCondition& other);
DBCondition& operator|(const DBCondition& other);
DBCondition &operator&(const DBCondition &other);
DBCondition &operator|(const DBCondition &other);
enum ORDERBY { ORDER_ASC, ORDER_DESC };
void orderBy(const std::string& field, ORDERBY order) {
void orderBy(const std::string &field, ORDERBY order) {
m_condition = order == ORDERBY::ORDER_ASC
? fmt::format("{} order by {} ASC", m_condition, field)
: fmt::format("{} order by {} DESC", m_condition, field);
}
DBCondition& operator+(const ASC& asc) {
DBCondition &operator+(const ASC &asc) {
orderBy(asc.name, ORDER_ASC);
return *this;
}
DBCondition& operator+(const DESC& desc) {
DBCondition &operator+(const DESC &desc) {
orderBy(desc.name, ORDER_DESC);
return *this;
}
DBCondition& operator+(const LIMIT& limit) {
DBCondition &operator+(const LIMIT &limit) {
m_condition = fmt::format("{} limit {}", m_condition, limit.limit);
return *this;
}
const std::string& str() const {
const std::string &str() const {
return m_condition;
}
@ -90,27 +90,27 @@ private:
};
struct Field {
explicit Field(const char* name) : name(name) {}
explicit Field(const std::string& name) : name(name) {}
explicit Field(const char *name) : name(name) {}
explicit Field(const std::string &name) : name(name) {}
// in 和 not_in 不支持 字符串,一般不会用到 in ("stra", "strb") 的 SQL 操作
template <typename T>
DBCondition in(const std::vector<T>& vals) {
DBCondition in(const std::vector<T> &vals) {
HKU_CHECK(!vals.empty(), "input vals can't be empty!");
return DBCondition(fmt::format("({} in ({}))", name, fmt::join(vals, ",")));
}
template <typename T>
DBCondition not_in(const std::vector<T>& vals) {
DBCondition not_in(const std::vector<T> &vals) {
HKU_CHECK(!vals.empty(), "input vals can't be empty!");
return DBCondition(fmt::format("({} not in ({}))", name, fmt::join(vals, ",")));
}
DBCondition like(const std::string& pattern) {
DBCondition like(const std::string &pattern) {
return DBCondition(fmt::format(R"(({} like "{}"))", name, pattern));
}
DBCondition like(const char* pattern) {
DBCondition like(const char *pattern) {
return DBCondition(fmt::format(R"(({} like "{}"))", name, pattern));
}
@ -120,7 +120,7 @@ struct Field {
// linux下类成员函数模板特化必须放在类外实现
// 否则编译时会报explicit specialization in non-namespace scope
template <>
inline DBCondition Field::in<std::string>(const std::vector<std::string>& vals) {
inline DBCondition Field::in<std::string>(const std::vector<std::string> &vals) {
HKU_CHECK(!vals.empty(), "input vals can't be empty!");
std::ostringstream out;
out << "(" << name << " in (";
@ -133,7 +133,7 @@ inline DBCondition Field::in<std::string>(const std::vector<std::string>& vals)
}
template <>
inline DBCondition Field::not_in<std::string>(const std::vector<std::string>& vals) {
inline DBCondition Field::not_in<std::string>(const std::vector<std::string> &vals) {
HKU_CHECK(!vals.empty(), "input vals can't be empty!");
std::ostringstream out;
out << "(" << name << " not in (";
@ -145,103 +145,103 @@ inline DBCondition Field::not_in<std::string>(const std::vector<std::string>& va
return DBCondition(out.str());
}
inline std::ostream& operator<<(std::ostream& out, const DBCondition& d) {
inline std::ostream &operator<<(std::ostream &out, const DBCondition &d) {
out << d.str();
return out;
}
template <typename T>
inline DBCondition operator==(const Field& field, T val) {
inline DBCondition operator==(const Field &field, T val) {
std::ostringstream out;
out << "(" << field.name << "=" << val << ")";
return DBCondition(out.str());
}
template <typename T>
inline DBCondition operator!=(const Field& field, T val) {
inline DBCondition operator!=(const Field &field, T val) {
std::ostringstream out;
out << "(" << field.name << "<>" << val << ")";
return DBCondition(out.str());
}
template <typename T>
inline DBCondition operator>(const Field& field, T val) {
inline DBCondition operator>(const Field &field, T val) {
std::ostringstream out;
out << "(" << field.name << ">" << val << ")";
return DBCondition(out.str());
}
template <typename T>
inline DBCondition operator>=(const Field& field, T val) {
inline DBCondition operator>=(const Field &field, T val) {
std::ostringstream out;
out << "(" << field.name << ">=" << val << ")";
return DBCondition(out.str());
}
template <typename T>
inline DBCondition operator<(const Field& field, T val) {
inline DBCondition operator<(const Field &field, T val) {
std::ostringstream out;
out << "(" << field.name << "<" << val << ")";
return DBCondition(out.str());
}
template <typename T>
inline DBCondition operator<=(const Field& field, T val) {
inline DBCondition operator<=(const Field &field, T val) {
std::ostringstream out;
out << "(" << field.name << "<=" << val << ")";
return DBCondition(out.str());
}
template <>
inline DBCondition operator!=(const Field& field, const char* val) {
inline DBCondition operator!=(const Field &field, const char *val) {
return DBCondition(fmt::format(R"(({}<>"{}"))", field.name, val));
}
template <>
inline DBCondition operator>(const Field& field, const char* val) {
inline DBCondition operator>(const Field &field, const char *val) {
return DBCondition(fmt::format(R"(({}>"{}"))", field.name, val));
}
template <>
inline DBCondition operator<(const Field& field, const char* val) {
inline DBCondition operator<(const Field &field, const char *val) {
return DBCondition(fmt::format(R"(({}<"{}"))", field.name, val));
}
template <>
inline DBCondition operator>=(const Field& field, const char* val) {
inline DBCondition operator>=(const Field &field, const char *val) {
return DBCondition(fmt::format(R"(({}>="{}"))", field.name, val));
}
template <>
inline DBCondition operator<=(const Field& field, const char* val) {
inline DBCondition operator<=(const Field &field, const char *val) {
return DBCondition(fmt::format(R"(({}<="{}"))", field.name, val));
}
inline DBCondition operator==(const Field& field, const std::string& val) {
inline DBCondition operator==(const Field &field, const std::string &val) {
return DBCondition(fmt::format(R"(({}="{}"))", field.name, val));
}
inline DBCondition operator!=(const Field& field, const std::string& val) {
inline DBCondition operator!=(const Field &field, const std::string &val) {
return DBCondition(fmt::format(R"(({}<>"{}"))", field.name, val));
}
inline DBCondition operator>(const Field& field, const std::string& val) {
inline DBCondition operator>(const Field &field, const std::string &val) {
return DBCondition(fmt::format(R"(({}>"{}"))", field.name, val));
}
inline DBCondition operator<(const Field& field, const std::string& val) {
inline DBCondition operator<(const Field &field, const std::string &val) {
return DBCondition(fmt::format(R"(({}<"{}"))", field.name, val));
}
inline DBCondition operator>=(const Field& field, const std::string& val) {
inline DBCondition operator>=(const Field &field, const std::string &val) {
return DBCondition(fmt::format(R"(({}>="{}"))", field.name, val));
}
inline DBCondition operator<=(const Field& field, const std::string& val) {
inline DBCondition operator<=(const Field &field, const std::string &val) {
return DBCondition(fmt::format(R"(({}<="{}"))", field.name, val));
}
inline DBCondition operator==(const Field& field, const char* val) {
inline DBCondition operator==(const Field &field, const char *val) {
return DBCondition(fmt::format(R"(({}="{}"))", field.name, val));
}

View File

@ -17,4 +17,14 @@
#include "TableMacro.h"
#include "DBUpgrade.h"
#include "hikyuu/utilities/config.h"
#if HKU_ENABLE_MYSQL
#include "mysql/MySQLConnect.h"
#endif
#if HKU_ENABLE_SQLITE
#include "sqlite/SQLiteConnect.h"
#include "sqlite/SQLiteUtil.h"
#endif
#endif /* HIKYUU_DB_CONNECT_H */

View File

@ -10,8 +10,8 @@
#ifndef HIKYUU_DB_CONNECT_DBCONNECTBASE_H
#define HIKYUU_DB_CONNECT_DBCONNECTBASE_H
#include "../../DataType.h"
#include "../../utilities/Parameter.h"
#include "../Null.h"
#include "DBCondition.h"
#include "SQLStatementBase.h"
#include "SQLException.h"
@ -25,7 +25,7 @@ class SQLResultSet;
*
* @ingroup DBConnect
*/
class HKU_API DBConnectBase : public std::enable_shared_from_this<DBConnectBase> {
class HKU_UTILS_API DBConnectBase : public std::enable_shared_from_this<DBConnectBase> {
PARAMETER_SUPPORT // NOSONAR
public :
@ -297,7 +297,7 @@ private:
};
/** @ingroup DBConnect */
typedef shared_ptr<DBConnectBase> DBConnectPtr;
typedef std::shared_ptr<DBConnectBase> DBConnectPtr;
//-------------------------------------------------------------------------
// inline方法实现
@ -366,19 +366,16 @@ void DBConnectBase::save(T &item, bool autotrans) {
if (autotrans) {
commit();
}
} catch (hku::SQLException &e) {
if (autotrans) {
rollback();
}
SQL_THROW(e.errcode(), "failed save! sql: {}! {}", st->getSqlString(), e.what());
} catch (std::exception &e) {
if (autotrans) {
rollback();
}
HKU_THROW("failed save! sql: {}! {}", st->getSqlString(), e.what());
} catch (...) {
if (autotrans) {
rollback();
@ -409,19 +406,16 @@ void DBConnectBase::batchSave(InputIterator first, InputIterator last, bool auto
if (autotrans) {
commit();
}
} catch (hku::SQLException &e) {
if (autotrans) {
rollback();
}
SQL_THROW(e.errcode(), "failed batch save! sql: {}! {}", st->getSqlString(), e.what());
} catch (std::exception &e) {
if (autotrans) {
rollback();
}
HKU_THROW("failed batch save! sql: {}! {}", st->getSqlString(), e.what());
} catch (...) {
if (autotrans) {
rollback();
@ -431,7 +425,7 @@ void DBConnectBase::batchSave(InputIterator first, InputIterator last, bool auto
}
template <typename T>
void DBConnectBase::load(T &item, const string &where) {
void DBConnectBase::load(T &item, const std::string &where) {
std::ostringstream sql;
if (where != "") {
sql << T::getSelectSQL() << " where " << where << " limit 1";
@ -451,7 +445,7 @@ void DBConnectBase::load(T &item, const DBCondition &cond) {
}
template <typename Container>
void DBConnectBase::batchLoad(Container &container, const string &where) {
void DBConnectBase::batchLoad(Container &container, const std::string &where) {
std::ostringstream sql;
if (where != "") {
sql << Container::value_type::getSelectSQL() << " where " << where;
@ -513,19 +507,16 @@ void DBConnectBase::batchUpdate(InputIterator first, InputIterator last, bool au
if (autotrans) {
commit();
}
} catch (hku::SQLException &e) {
if (autotrans) {
rollback();
}
SQL_THROW(e.errcode(), "failed batch save! sql: {}! {}", st->getSqlString(), e.what());
} catch (std::exception &e) {
if (autotrans) {
rollback();
}
HKU_THROW("failed batch update! sql: {}! {}", st->getSqlString(), e.what());
} catch (...) {
if (autotrans) {
rollback();
@ -570,19 +561,16 @@ void DBConnectBase::remove(T &item, bool autotrans) {
commit();
}
item.rowid(0);
} catch (hku::SQLException &e) {
if (autotrans) {
rollback();
}
SQL_THROW(e.errcode(), "failed delete! sql: {}! {}", st->getSqlString(), e.what());
} catch (std::exception &e) {
if (autotrans) {
rollback();
}
HKU_THROW("failed delete! sql: {}! {}", st->getSqlString(), e.what());
} catch (...) {
if (autotrans) {
rollback();
@ -610,19 +598,16 @@ void DBConnectBase::batchRemove(InputIterator first, InputIterator last, bool au
if (autotrans) {
commit();
}
} catch (hku::SQLException &e) {
if (autotrans) {
rollback();
}
SQL_THROW(e.errcode(), "failed batch delete! {}", e.what());
} catch (std::exception &e) {
if (autotrans) {
rollback();
}
HKU_THROW("failed batch delete! {}", e.what());
} catch (...) {
if (autotrans) {
rollback();
@ -645,13 +630,11 @@ inline void DBConnectBase::remove(const std::string &tablename, const std::strin
if (autotrans) {
commit();
}
} catch (hku::SQLException &e) {
if (autotrans) {
rollback();
}
SQL_THROW(e.errcode(), "Failed exec sql: {}! {}", sql, e.what());
} catch (std::exception &e) {
if (autotrans) {
rollback();

View File

@ -6,23 +6,51 @@
*/
#include "DBUpgrade.h"
#include "../../debug.h"
#include "hikyuu/utilities/config.h"
#if HKU_ENABLE_MYSQL
#include "mysql/MySQLConnect.h"
#endif
#if HKU_ENABLE_SQLITE
#include "sqlite/SQLiteConnect.h"
#endif
namespace hku {
/*
*
*/
void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name,
const std::vector<string> &upgrade_scripts, int start_version,
const char *create_script) {
void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name,
const std::vector<std::string> &upgrade_scripts, int start_version,
const char *create_script) {
HKU_TRACE("check {} database version ...", module_name);
// 如果模块版本表不存在,则创建该表
if (!driver->tableExist("module_version")) {
driver->exec(
"CREATE TABLE `module_version` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,`module` TEXT, "
"`version` INTEGER NOT NULL);");
bool need_create = true;
#if HKU_ENABLE_SQLITE
if (need_create && typeid(driver.get()) == typeid(SQLiteConnect *)) {
driver->exec(
"CREATE TABLE `module_version` (`id` INTEGER PRIMARY KEY AUTOINCREMENT,`module` "
"TEXT, "
"`version` INTEGER NOT NULL);");
need_create = false;
}
#endif
#if HKU_ENABLE_MYSQL
if (need_create && typeid(driver.get()) == typeid(MySQLConnect *)) {
driver->exec(
R"(CREATE TABLE `module_version` (
`id` int NOT NULL AUTO_INCREMENT,
`module` varchar(20) DEFAULT NULL,
`version` int NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;)");
need_create = false;
}
#endif
}
// 如果没有升级脚本,也没有创建脚本,则直接返回
@ -75,7 +103,7 @@ void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name,
// 当前版本已经大于等于待升至的版本,无需升级,直接返回
if (version >= to_version) {
HKU_TRACE("current version({}) greater the upgrade version({}), ignored!", version,
to_version);
to_version);
return;
}

View File

@ -21,8 +21,8 @@ namespace hku {
* @param create_script 使
* @ingroup DataDriver
*/
void HKU_API DBUpgrade(const DBConnectPtr &driver, const char *module_name,
const std::vector<std::string> &upgrade_scripts, int start_version = 2,
const char *create_script = nullptr);
void HKU_UTILS_API DBUpgrade(const DBConnectPtr &driver, const char *module_name,
const std::vector<std::string> &upgrade_scripts, int start_version = 2,
const char *create_script = nullptr);
} // namespace hku

View File

@ -8,7 +8,7 @@
#pragma once
#include <fmt/format.h>
#include "hikyuu/exception.h"
#include "hikyuu/utilities/exception.h"
namespace hku {

View File

@ -9,7 +9,7 @@
#include <iterator>
#include "hikyuu/utilities/arithmetic.h"
#include "hikyuu/Log.h"
#include "hikyuu/utilities/Log.h"
#include "hikyuu/utilities/osdef.h"
#include "DBConnectBase.h"

Some files were not shown because too many files have changed in this diff Show More