Signal add Cycle

This commit is contained in:
fasiondog 2024-04-02 11:57:01 +08:00
parent 8d28ef9dd6
commit 2db4bf7692
9 changed files with 139 additions and 49 deletions

View File

@ -173,15 +173,18 @@ void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) {
HKU_IF_RETURN(datelist.empty(), void());
size_t cur_adjust_ix = 0;
Datetime cur_cycle_end;
for (size_t i = 0, total = datelist.size(); i < total; i++) {
bool adjust = false;
if (i == cur_adjust_ix) {
adjust = true;
cur_adjust_ix += adjust_cycle;
cur_cycle_end =
cur_adjust_ix < total ? datelist[cur_adjust_ix] : datelist.back() + Seconds(1);
}
const auto& date = datelist[i];
_runMoment(date, adjust);
_runMoment(date, cur_cycle_end, adjust);
}
m_need_calculate = false;
@ -191,7 +194,7 @@ void Portfolio::run(const KQuery& query, int adjust_cycle, bool force) {
m_tmp_will_remove_sys = SystemWeightList();
}
void Portfolio::_runMoment(const Datetime& date, bool adjust) {
void Portfolio::_runMoment(const Datetime& date, const Datetime& nextCycle, bool adjust) {
// 当前日期小于账户建立日期,直接忽略
HKU_IF_RETURN(date < m_cash_tm->initDatetime(), void());
@ -295,44 +298,6 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
m_delay_adjust_sys_list.swap(tmp_continue_adjust_sys_list);
//---------------------------------------------------------------
// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则回收
//---------------------------------------------------------------
#if 0
m_tmp_will_remove_sys.clear();
for (auto& running_sys : m_running_sys_set) {
Stock stock = running_sys->getStock();
TMPtr sub_tm = running_sys->getTM();
PositionRecord position = sub_tm->getPosition(date, stock);
price_t cash = sub_tm->currentCash();
price_t min_cash = 1.0;
KRecord krecord = stock.getKRecord(date);
if (krecord.isValid()) {
min_cash = krecord.openPrice * stock.minTradeNumber();
}
// 如果系统的剩余资金小于交易一手的资金,则回收资金??? TODO 放到 AF 中进行处理不足一手的
if (cash != 0 && cash <= min_cash) {
sub_tm->checkout(date, cash);
m_cash_tm->checkin(date, cash);
HKU_INFO_IF(trace, "Recycle cash ({:<.2f}) from {}, current cash: {}", cash,
running_sys->name(), m_cash_tm->currentCash());
// 如果已经没有持仓,则回收
if (position.number <= 0.0) {
m_tmp_will_remove_sys.emplace_back(running_sys, 0.);
HKU_INFO_IF(trace, "[PF] Recycle running sys: {}", running_sys->name());
}
}
}
// 依据待移除列表将系统从运行中系统列表里删除
for (auto& sub_sys : m_tmp_will_remove_sys) {
HKU_INFO_IF(trace, "Recycling system {}", sub_sys.sys->name());
m_running_sys_set.erase(sub_sys.sys);
}
#endif
//---------------------------------------------------
// 调仓日,进行资金分配调整
//---------------------------------------------------
@ -395,6 +360,11 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
//----------------------------------------------------------------------------
for (auto& sub_sys : m_running_sys_set) {
HKU_TRACE_IF(trace, "run: {}", sub_sys->name());
if (adjust) {
auto sg = sub_sys->getSG();
sg->startCycle(date, nextCycle);
}
auto tr = sub_sys->runMoment(date);
if (!tr.isNull()) {
HKU_INFO_IF(trace, "[PF] {}", tr);
@ -449,9 +419,13 @@ void Portfolio::_runMoment(const Datetime& date, bool adjust) {
#endif
// clang-format off
HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+");
if (++count >= getParam<int>("trace_max_num")) {
count++;
int trace_max_num = getParam<int>("trace_max_num");
if (count >= trace_max_num) {
if (count > trace_max_num) {
HKU_INFO("+ ... ... more +");
HKU_INFO("+------------+------------+------------+--------------+--------------+-------------+-------------+");
}
break;
}
// clang-format on

View File

@ -101,7 +101,7 @@ private:
/** 运行前准备 */
void _readyForRun();
void _runMoment(const Datetime& date, bool adjust);
void _runMoment(const Datetime& date, const Datetime& nextCycle, bool adjust);
protected:
string m_name;

View File

@ -25,17 +25,21 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SignalPtr& sg) {
}
SignalBase::SignalBase() : m_name("SignalBase"), m_hold_long(false), m_hold_short(false) {
setParam<bool>("alternate", true); // 买入卖出信号交替出现
setParam<bool>("support_borrow_stock", false); // 支持发出空头信号
initParam();
}
SignalBase::SignalBase(const string& name) : m_name(name), m_hold_long(false), m_hold_short(false) {
setParam<bool>("alternate", true);
setParam<bool>("support_borrow_stock", false);
initParam();
}
SignalBase::~SignalBase() {}
void SignalBase::initParam() {
setParam<bool>("cycle", false); // 仅在指定周期范围内计算
setParam<bool>("alternate", true); // 买入卖出信号交替出现
setParam<bool>("support_borrow_stock", false); // 支持发出空头信号
}
void SignalBase::baseCheckParam(const string& name) const {}
void SignalBase::paramChanged() {}
@ -66,7 +70,12 @@ SignalPtr SignalBase::clone() {
void SignalBase::setTO(const KData& kdata) {
HKU_IF_RETURN(m_kdata == kdata, void());
m_kdata = kdata;
if (!kdata.empty()) {
bool cycle = getParam<bool>("cycle");
if (!cycle) {
m_cycle_start = Datetime::min();
m_cycle_end = Datetime::max();
}
if (!cycle && !kdata.empty()) {
_calculate();
}
}
@ -80,6 +89,17 @@ void SignalBase::reset() {
_reset();
}
void SignalBase::startCycle(const Datetime& start, const Datetime& close) {
HKU_IF_RETURN(!getParam<bool>("cycle"), void());
HKU_ASSERT(start != Null<Datetime>() && close != Null<Datetime>() && start < close);
HKU_CHECK(start >= m_cycle_end, "curretn start: {}, pre cycle end: {}", start, m_cycle_end);
m_cycle_start = start;
m_cycle_end = close;
if (!m_kdata.empty()) {
_calculate();
}
}
DatetimeList SignalBase::getBuySignal() const {
DatetimeList result(m_buySig.size());
std::copy(m_buySig.begin(), m_buySig.end(), result.begin());

View File

@ -84,6 +84,10 @@ public:
*/
const KData& getTO() const;
void startCycle(const Datetime& start, const Datetime& end);
const Datetime& getCycleStart() const;
const Datetime& getCycleEnd() const;
/** 复位操作 */
void reset();
@ -106,6 +110,9 @@ public:
/** 子类计算接口在setTO中调用 */
virtual void _calculate() = 0;
private:
void initParam();
protected:
string m_name;
KData m_kdata;
@ -118,6 +125,9 @@ protected:
std::set<Datetime> m_buySig;
std::set<Datetime> m_sellSig;
Datetime m_cycle_start{Datetime::min()};
Datetime m_cycle_end{Datetime::min()};
//============================================
// 序列化支持
//============================================
@ -218,6 +228,14 @@ inline bool SignalBase::shouldSell(const Datetime& datetime) const {
return m_sellSig.count(datetime) ? true : false;
}
inline const Datetime& SignalBase::getCycleStart() const {
return m_cycle_start;
}
inline const Datetime& SignalBase::getCycleEnd() const {
return m_cycle_end;
}
} /* namespace hku */
#if FMT_VERSION >= 90000

View File

@ -12,6 +12,7 @@
#include "crt/SG_AllwaysBuy.h"
#include "crt/SG_Cross.h"
#include "crt/SG_CrossGold.h"
#include "crt/SG_Cycle.h"
#include "crt/SG_Flex.h"
#include "crt/SG_Single.h"
#include "crt/SG_Bool.h"

View File

@ -0,0 +1,16 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-04-01
* Author: fasiondog
*/
#pragma once
#include "../SignalBase.h"
namespace hku {
SignalPtr HKU_API SG_Cycle();
}

View File

@ -0,0 +1,35 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-03-30
* Author: fasiondog
*/
#include "CycleSignal.h"
#if HKU_SUPPORT_SERIALIZATION
BOOST_CLASS_EXPORT(hku::CycleSignal)
#endif
namespace hku {
CycleSignal::CycleSignal() : SignalBase("SG_AllwaysBuy") {
setParam<bool>("cycle", true);
}
void CycleSignal::_checkParam(const string& name) const {
if ("cycle" == name) {
bool cycle = getParam<bool>(name);
HKU_CHECK(cycle, "param cycle must be true!");
}
}
void CycleSignal::_calculate() {
_addBuySignal(getCycleStart());
}
SignalPtr HKU_API SG_Cycle() {
return make_shared<CycleSignal>();
}
} // namespace hku

View File

@ -0,0 +1,25 @@
/*
* Copyright (c) 2024 hikyuu.org
*
* Created on: 2024-04-01
* Author: fasiondog
*/
#pragma once
#include "../SignalBase.h"
namespace hku {
class CycleSignal : public SignalBase {
SIGNAL_IMP(CycleSignal)
SIGNAL_NO_PRIVATE_MEMBER_SERIALIZATION
public:
CycleSignal();
virtual ~CycleSignal() = default;
virtual void _checkParam(const string& name) const override;
};
}

View File

@ -209,4 +209,5 @@ void export_Signal(py::module& m) {
)");
m.def("SG_AllwaysBuy", SG_AllwaysBuy);
m.def("SG_Cycle", SG_Cycle);
}