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

This commit is contained in:
fasiondog 2024-08-25 15:48:52 +08:00
commit 8452b1a0b3
7 changed files with 139 additions and 100 deletions

View File

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

View File

@ -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"):

View File

@ -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):
'''以下只适用于华泰'''

View File

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

View File

@ -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<void(const Stock&, const SpotRecord& spot)>&& changeFunc) {
if (!m_running) {
run();
}
HKU_CHECK(changeFunc, "Invalid changeFunc!");
m_on_change = changeFunc;
}
void Strategy::onReceivedSpot(std::function<void(const Datetime&)>&& 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<void()>&& 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<void()>&& 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<void()>&& 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<int>::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<int>::max(), delta, new_func);
sched->addDurationFunc(std::numeric_limits<int>::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<int>::max(), delta, new_func);
scheduler->addDurationFunc(std::numeric_limits<int>::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<int>::max(), delta, new_func);
sched->addDurationFunc(std::numeric_limits<int>::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<int>::max(), delta, new_func);
sched->addDurationFunc(std::numeric_limits<int>::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<int>::max(), delta, new_func);
scheduler->addDurationFunc(std::numeric_limits<int>::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<int>::max(), delta, new_func);
sched->addDurationFunc(std::numeric_limits<int>::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<int>::max(), delta, new_func);
sched->addDurationFunc(std::numeric_limits<int>::max(), m_run_daily_delta,
m_run_daily_func);
});
} else {
@ -218,26 +238,29 @@ void Strategy::runDaily(std::function<void()>&& func, const TimeDelta& delta,
void Strategy::runDailyAt(std::function<void()>&& 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);
}
}
/*

View File

@ -50,9 +50,10 @@ public:
* @param func
* @param delta
* @param market
* @param ignoreMarket true
*/
void runDaily(std::function<void()>&& 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<void(const Datetime&)> m_on_recieved_spot;
std::function<void(const Stock&, const SpotRecord& spot)> m_on_change;
bool m_running{false};
std::function<void()> m_run_daily_func;
TimeDelta m_run_daily_delta;
string m_run_daily_market;
bool m_ignoreMarket{false};
std::function<void()> 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;

View File

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