From 02a8c00f2ae3a3aff40b6f1fe6149cafa5bb975e Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 14:08:54 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BC=98=E5=8C=96=20Strategy?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/strategy/strategy.py | 8 +- hikyuu_cpp/hikyuu/strategy/Strategy.cpp | 135 ++++++++++++++---------- hikyuu_cpp/hikyuu/strategy/Strategy.h | 18 +++- hikyuu_pywrap/strategy/_Strategy.cpp | 34 +++--- 4 files changed, 116 insertions(+), 79 deletions(-) diff --git a/hikyuu/strategy/strategy.py b/hikyuu/strategy/strategy.py index 4cd8d76e..575f2909 100644 --- a/hikyuu/strategy/strategy.py +++ b/hikyuu/strategy/strategy.py @@ -7,11 +7,11 @@ from hikyuu import sm def on_change(stk, spot): - print(stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1) + print("[on_change]:", stk.market_code, stk.name, spot.close, spot.bid1, spot.ask1) def on_spot(rev_time): - print("rev_time:", rev_time) + print("[on_received_spot] rev_time:", rev_time) def my_func(): @@ -24,8 +24,8 @@ def my_func(): # 以 Strategy 方式运行示例 if __name__ == '__main__': s = Strategy(['sh600000', 'sz000001'], [Query.MIN, Query.DAY]) - # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(5)) + # s.run_daily_at(my_func, Datetime.now() - Datetime.today() + Seconds(10), False) s.on_change(on_change) s.on_received_spot(on_spot) - s.run_daily(my_func, Minutes(1)) + s.run_daily(my_func, Minutes(1)) # , ignore_market=True) s.start() diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp index 9307f250..58803124 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.cpp +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.cpp @@ -62,9 +62,7 @@ Strategy::~Strategy() { CLS_INFO("Quit Strategy {}!", m_name); } -void Strategy::run() { - CLS_IF_RETURN(m_running, void()); - +void Strategy::_init() { StockManager& sm = StockManager::instance(); // sm 尚未初始化,则初始化 @@ -85,9 +83,15 @@ void Strategy::run() { // 先将行情接收代理停止,以便后面加入处理函数 stopSpotAgent(); +} + +void Strategy::start() { + _init(); + + _runDailyAt(); auto& agent = *getGlobalSpotAgent(); - agent.addProcess([this](const SpotRecord& spot) { receivedSpot(spot); }); + agent.addProcess([this](const SpotRecord& spot) { _receivedSpot(spot); }); agent.addPostProcess([this](Datetime revTime) { if (m_on_recieved_spot) { EVENT([=]() { m_on_recieved_spot(revTime); }); @@ -95,30 +99,23 @@ void Strategy::run() { }); startSpotAgent(true); - m_running = true; -} + _runDaily(); -void Strategy::start() { - CLS_CHECK(m_running, "No handler functions are registered!"); CLS_INFO("start even loop ..."); _startEventLoop(); } void Strategy::onChange(std::function&& changeFunc) { - if (!m_running) { - run(); - } + HKU_CHECK(changeFunc, "Invalid changeFunc!"); m_on_change = changeFunc; } void Strategy::onReceivedSpot(std::function&& recievedFucn) { - if (!m_running) { - run(); - } + HKU_CHECK(recievedFucn, "Invalid recievedFucn!"); m_on_recieved_spot = recievedFucn; } -void Strategy::receivedSpot(const SpotRecord& spot) { +void Strategy::_receivedSpot(const SpotRecord& spot) { Stock stk = getStock(format("{}{}", spot.market, spot.code)); if (!stk.isNull()) { if (m_on_change) { @@ -128,14 +125,17 @@ void Strategy::receivedSpot(const SpotRecord& spot) { } void Strategy::runDaily(std::function&& func, const TimeDelta& delta, - const std::string& market) { - if (!m_running) { - run(); - } + const std::string& market, bool ignoreMarket) { + HKU_CHECK(func, "Invalid func!"); + m_run_daily_delta = delta; + m_run_daily_market = market; + m_ignoreMarket = ignoreMarket; - try { - auto* scheduler = getScheduler(); - auto new_func = [=]() { + if (ignoreMarket) { + m_run_daily_func = [=]() { EVENT(func); }; + + } else { + m_run_daily_func = [=]() { const auto& sm = StockManager::instance(); auto today = Datetime::today(); int day = today.dayOfWeek(); @@ -143,7 +143,7 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, return; } - auto market_info = sm.getMarketInfo(market); + auto market_info = sm.getMarketInfo(m_run_daily_market); Datetime open1 = today + market_info.openTime1(); Datetime close1 = today + market_info.closeTime1(); Datetime open2 = today + market_info.openTime2(); @@ -153,59 +153,79 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, EVENT(func); } }; + } +} +void Strategy::_runDaily() { + HKU_IF_RETURN(!m_run_daily_func, void()); + + auto* scheduler = getScheduler(); + if (m_ignoreMarket) { + scheduler->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); + return; + } + + try { const auto& sm = StockManager::instance(); - auto market_info = sm.getMarketInfo(market); + auto market_info = sm.getMarketInfo(m_run_daily_market); auto today = Datetime::today(); auto now = Datetime::now(); TimeDelta now_time = now - today; if (now_time >= market_info.closeTime2()) { scheduler->addFuncAtTime(today.nextDay() + market_info.openTime1(), [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } else if (now_time >= market_info.openTime2()) { int64_t ticks = now_time.ticks() - market_info.openTime2().ticks(); - int64_t delta_ticks = delta.ticks(); + int64_t delta_ticks = m_run_daily_delta.ticks(); if (ticks % delta_ticks == 0) { - scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + scheduler->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); } else { auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks); scheduler->addFuncAtTime(now + delay, [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } } else if (now_time >= market_info.closeTime1()) { scheduler->addFuncAtTime(today + market_info.openTime2(), [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } else if (now_time < market_info.closeTime1() && now_time >= market_info.openTime1()) { int64_t ticks = now_time.ticks() - market_info.openTime1().ticks(); - int64_t delta_ticks = delta.ticks(); + int64_t delta_ticks = m_run_daily_delta.ticks(); if (ticks % delta_ticks == 0) { - scheduler->addDurationFunc(std::numeric_limits::max(), delta, new_func); + scheduler->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); } else { auto delay = TimeDelta::fromTicks((ticks / delta_ticks + 1) * delta_ticks - ticks); scheduler->addFuncAtTime(now + delay, [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } } else if (now_time < market_info.openTime1()) { scheduler->addFuncAtTime(today + market_info.openTime1(), [=]() { - new_func(); + m_run_daily_func(); auto* sched = getScheduler(); - sched->addDurationFunc(std::numeric_limits::max(), delta, new_func); + sched->addDurationFunc(std::numeric_limits::max(), m_run_daily_delta, + m_run_daily_func); }); } else { @@ -218,26 +238,29 @@ void Strategy::runDaily(std::function&& func, const TimeDelta& delta, void Strategy::runDailyAt(std::function&& func, const TimeDelta& delta, bool ignoreHoliday) { - if (!m_running) { - run(); + HKU_CHECK(func, "Invalid func!"); + m_run_daily_at_delta = delta; + + if (ignoreHoliday) { + m_run_daily_at_func = [=]() { + const auto& sm = StockManager::instance(); + auto today = Datetime::today(); + int day = today.dayOfWeek(); + if (day != 0 && day != 6 && !sm.isHoliday(today)) { + EVENT(func); + } + }; + + } else { + m_run_daily_at_func = [=]() { EVENT(func); }; } +} - auto new_func = [=]() { - if (!ignoreHoliday) { - EVENT(func); - return; - } - - const auto& sm = StockManager::instance(); - auto today = Datetime::today(); - int day = today.dayOfWeek(); - if (day != 0 && day != 6 && !sm.isHoliday(today)) { - EVENT(func); - } - }; - - auto* scheduler = getScheduler(); - scheduler->addFuncAtTimeEveryDay(delta, new_func); +void Strategy::_runDailyAt() { + if (m_run_daily_at_func) { + auto* scheduler = getScheduler(); + scheduler->addFuncAtTimeEveryDay(m_run_daily_at_delta, m_run_daily_at_func); + } } /* diff --git a/hikyuu_cpp/hikyuu/strategy/Strategy.h b/hikyuu_cpp/hikyuu/strategy/Strategy.h index 94a144b7..1143277d 100644 --- a/hikyuu_cpp/hikyuu/strategy/Strategy.h +++ b/hikyuu_cpp/hikyuu/strategy/Strategy.h @@ -50,9 +50,10 @@ public: * @param func 待执行的任务 * @param delta 间隔时间 * @param market 指定的市场 + * @param ignoreMarket 是否忽略市场时间限制,如为 true,则为定时循环不受开闭市时间限制 */ void runDaily(std::function&& func, const TimeDelta& delta, - const std::string& market = "SH"); + const std::string& market = "SH", bool ignoreMarket = false); /** * 每日在指定时刻执行任务 @@ -89,11 +90,20 @@ private: StrategyContext m_context; std::function m_on_recieved_spot; std::function m_on_change; - bool m_running{false}; + + std::function m_run_daily_func; + TimeDelta m_run_daily_delta; + string m_run_daily_market; + bool m_ignoreMarket{false}; + + std::function m_run_daily_at_func; + TimeDelta m_run_daily_at_delta; private: - void run(); - void receivedSpot(const SpotRecord& spot); + void _init(); + void _receivedSpot(const SpotRecord& spot); + void _runDaily(); + void _runDailyAt(); private: static std::atomic_bool ms_keep_running; diff --git a/hikyuu_pywrap/strategy/_Strategy.cpp b/hikyuu_pywrap/strategy/_Strategy.cpp index 8280ed70..18ccb657 100644 --- a/hikyuu_pywrap/strategy/_Strategy.cpp +++ b/hikyuu_pywrap/strategy/_Strategy.cpp @@ -58,21 +58,25 @@ void export_Strategy(py::module& m) { }; self.onReceivedSpot(new_func); }) - .def("run_daily", - [](Strategy& self, py::object func, const TimeDelta& time) { - HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); - py::object c_func = func.attr("__call__"); - auto new_func = [=]() { - try { - c_func(); - } catch (const std::exception& e) { - HKU_ERROR(e.what()); - } catch (...) { - HKU_ERROR("Unknown error!"); - } - }; - self.runDaily(new_func, time); - }) + .def( + "run_daily", + [](Strategy& self, py::object func, const TimeDelta& time, std::string market, + bool ignore_market) { + HKU_CHECK(py::hasattr(func, "__call__"), "func is not callable!"); + py::object c_func = func.attr("__call__"); + auto new_func = [=]() { + try { + c_func(); + } catch (const std::exception& e) { + HKU_ERROR(e.what()); + } catch (...) { + HKU_ERROR("Unknown error!"); + } + }; + self.runDaily(new_func, time, market, ignore_market); + }, + py::arg("func"), py::arg("time"), py::arg("market") = "SH", + py::arg("ignore_market") = false) .def( "run_daily_at", [](Strategy& self, py::object func, const TimeDelta& time, bool ignore_holiday) { From ee8e7b0fb803a552c9e4b142ef795dd8632f8d03 Mon Sep 17 00:00:00 2001 From: fasiondog Date: Sun, 25 Aug 2024 15:47:39 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E8=B0=83=E6=95=B4=20python=20borker=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hikyuu/trade_manage/broker.py | 12 ++++++------ hikyuu/trade_manage/broker_easytrader.py | 24 +++++++++++++----------- hikyuu/trade_manage/broker_mail.py | 8 ++++---- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/hikyuu/trade_manage/broker.py b/hikyuu/trade_manage/broker.py index a55f8770..1b855ed5 100644 --- a/hikyuu/trade_manage/broker.py +++ b/hikyuu/trade_manage/broker.py @@ -48,11 +48,11 @@ class OrderBrokerWrap(OrderBrokerBase): def _buy(self, datetime, market, code, price, num, stoploss, goal_price, part_from): """实现 OrderBrokerBase 的 _buy 接口""" - self._broker.buy('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) + self._broker.buy(market, code, price, num, stoploss, goal_price, part_from) def _sell(self, datetime, market, code, price, num, stoploss, goal_price, part_from): """实现 OrderBrokerBase 的 _sell 接口""" - self._broker.sell('{}{}'.format(market, code), price, num, stoploss, goal_price, part_from) + self._broker.sell(market, code, price, num, stoploss, goal_price, part_from) def _get_asset_info(self): try: @@ -71,11 +71,11 @@ class TestOrderBroker: def __init__(self): pass - def buy(self, code, price, num, stoploss, goal_price, part_from): - print(f"买入:{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}") + def buy(self, market, code, price, num, stoploss, goal_price, part_from): + print(f"买入:{market}{code}, 价格: {price}, 数量: {num} 预期止损价: {stoploss}, 预期目标价: {goal_price}, 信号来源: {part_from}") - def sell(self, code, price, num, stoploss, goal_price, part_from): - print(f"卖出:{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}") + def sell(self, market, code, price, num, stoploss, goal_price, part_from): + print(f"卖出:{market}{code}, 价格: {price}, 数量: {num}, 信号来源: {part_from}") def crtOB(broker, name="NO_NAME"): diff --git a/hikyuu/trade_manage/broker_easytrader.py b/hikyuu/trade_manage/broker_easytrader.py index 81d7ded9..2f9f9ba4 100644 --- a/hikyuu/trade_manage/broker_easytrader.py +++ b/hikyuu/trade_manage/broker_easytrader.py @@ -12,20 +12,22 @@ class EasyTraderOrderBroker: self.user = user self.buffer = {} - def buy(self, code, price, num, stoploss, goal_price, part_from): - self.user.buy(code[2:], price=price, amount=num) - print("买入:%s %.3f %i" % (code, price, num)) - self.buffer[code] = (num, stoploss, goal_price) + def buy(self, market, code, price, num, stoploss, goal_price, part_from): + self.user.buy(code, price=price, amount=num) + market_code = f"{market}{code}" + print(f"买入:{market_code} {price} {num}") + self.buffer[market_code] = (num, stoploss, goal_price) - def sell(self, code, price, num, stoploss, goal_price, part_from): - self.user.sell(code[2:], price=price, amount=num) - print("卖出:%s %.3f %i" % (code, price, num)) - if code in self.buffer: - old_num = self.buffer[code][0] + def sell(self, market, code, price, num, stoploss, goal_price, part_from): + self.user.sell(code, price=price, amount=num) + market_code = f"{market}{code}" + print(f"卖出:{market_code} {price} {num}") + if market_code in self.buffer: + old_num = self.buffer[market_code][0] if old_num == num: - self.buffer.pop(code) + self.buffer.pop(market_code) else: - self.buffer[code] = (old_num - num, stoploss, goal_price) + self.buffer[market_code] = (old_num - num, stoploss, goal_price) def get_asset_info(self): '''以下只适用于华泰''' diff --git a/hikyuu/trade_manage/broker_mail.py b/hikyuu/trade_manage/broker_mail.py index 83889bf8..f6437f88 100644 --- a/hikyuu/trade_manage/broker_mail.py +++ b/hikyuu/trade_manage/broker_mail.py @@ -72,7 +72,7 @@ class MailOrderBroker: smtpObj.login(self._sender, self._pwd) smtpObj.sendmail(self._sender, self._receivers, message.as_string()) - def buy(self, code, price, num, stoploss, goal_price, part_from): + def buy(self, market, code, price, num, stoploss, goal_price, part_from): """执行买入操作,向指定的邮箱发送邮件,格式如下:: 邮件标题:【Hkyuu提醒】买入 证券代码 @@ -82,11 +82,11 @@ class MailOrderBroker: :param float price: 买入价格 :param int num: 买入数量 """ - action = "买入:{},价格:{},数量:{} ".format(code, price, num) + action = "买入:{}{},价格:{},数量:{} ".format(market, code, price, num) title = "【Hkyuu提醒】买入 {}".format(code) self._sendmail(title, action) - def sell(self, code, price, num, stoploss, goal_price, part_from): + def sell(self, market, code, price, num, stoploss, goal_price, part_from): """执行卖出操作,向指定的邮箱发送邮件,格式如下:: 邮件标题:【Hkyuu提醒】卖出 证券代码 @@ -96,6 +96,6 @@ class MailOrderBroker: :param float price: 卖出价格 :param int num: 卖出数量 """ - title = "【Hkyuu提醒】卖出 {}".format(code) + title = "【Hkyuu提醒】卖出 {}{}".format(market, code) action = "卖出:{},价格:{},数量:{} ".format(code, price, num) self._sendmail(title, action)