完善资产组合

This commit is contained in:
fasiondog 2020-08-09 16:51:01 +08:00
parent 70ccca946c
commit 7145dfdb66
9 changed files with 185 additions and 241 deletions

View File

@ -28,16 +28,14 @@ HKU_API std::ostream& operator<<(std::ostream& os, const AFPtr& af) {
AllocateFundsBase::AllocateFundsBase()
: m_name("AllocateMoneyBase"), m_count(0), m_pre_date(Datetime::min()), m_reserve_percent(0) {
//是否调整之前已经持仓策略的持仓。不调整时,仅使用总账户当前剩余资金进行分配,否则将使用总市值进行分配
setParam<bool>("adjust_hold_sys", false);
setParam<bool>("adjust_running_sys", false);
setParam<int>("max_sys_num", 100000); //最大系统实例数
setParam<int>("freq", 1); //调仓频率
}
AllocateFundsBase::AllocateFundsBase(const string& name)
: m_name("AllocateMoneyBase"), m_count(0), m_pre_date(Datetime::min()), m_reserve_percent(0) {
setParam<bool>("adjust_hold_sys", false);
setParam<bool>("adjust_running_sys", false);
setParam<int>("max_sys_num", 100000); //最大系统实例数
setParam<int>("freq", 1); //调仓频率
}
AllocateFundsBase::~AllocateFundsBase() {}
@ -83,49 +81,23 @@ void AllocateFundsBase::setReserverPercent(double percent) {
}
}
bool AllocateFundsBase::changed(Datetime date) {
if (date <= m_pre_date || date == Null<Datetime>())
return false;
int freq = getParam<int>("freq");
if (freq <= 0) {
freq = 1;
}
m_count++;
if (m_count >= freq) {
m_count = 0;
m_pre_date = date;
return true;
}
return false;
}
SystemList AllocateFundsBase ::getAllocatedSystemList(const Datetime& date,
const SystemList& se_list,
const SystemList& hold_list) {
SystemList result;
void AllocateFundsBase ::adjustFunds(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list) {
int max_num = getParam<int>("max_sys_num");
if (max_num <= 0) {
HKU_ERROR("param(max_sys_num) need > 0!");
return result;
return;
}
if (getParam<bool>("adjust_hold_sys")) {
_getAllocatedSystemList_adjust_hold(date, se_list, hold_list, result);
if (getParam<bool>("adjust_running_sys")) {
_adjust_with_running(date, se_list, running_list);
} else {
_getAllocatedSystemList_not_adjust_hold(date, se_list, hold_list, result);
_adjust_without_running(date, se_list, running_list);
}
return result;
}
void AllocateFundsBase::_getAllocatedSystemList_adjust_hold(const Datetime& date,
const SystemList& se_list,
const SystemList& hold_list,
SystemList& out_sys_list) {
void AllocateFundsBase::_adjust_with_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list) {
//计算当前选中系统列表的权重
SystemWeightList sw_list = _allocateWeight(date, se_list);
if (sw_list.size() == 0) {
@ -143,7 +115,7 @@ void AllocateFundsBase::_getAllocatedSystemList_adjust_hold(const Datetime& date
}
//如果当前持仓的系统不在实际的选中系统集合,则强制清仓卖出,如果账户有现金则同时回收现金
for (auto iter = hold_list.begin(); iter != hold_list.end(); ++iter) {
for (auto iter = running_list.begin(); iter != running_list.end(); ++iter) {
const SYSPtr& sys = *iter;
if (selected_sets.find(sys) == selected_sets.end()) {
KRecord record = sys->getTO().getKRecordByDate(date);
@ -251,7 +223,7 @@ void AllocateFundsBase::_getAllocatedSystemList_adjust_hold(const Datetime& date
need_sell_num =
size_t(need_sell_num / stock.minTradeNumber()) * stock.minTradeNumber();
if (position.number >= need_sell_num) {
sys->_sellFromAllocateFunds(k, need_sell_num);
sys->_sellForce(k, need_sell_num, PART_ALLOCATEFUNDS);
}
}
@ -284,30 +256,25 @@ void AllocateFundsBase::_getAllocatedSystemList_adjust_hold(const Datetime& date
}
}
void AllocateFundsBase::_getAllocatedSystemList_not_adjust_hold(const Datetime& date,
const SystemList& se_list,
const SystemList& hold_list,
SystemList& out_sys_list) {
void AllocateFundsBase::_adjust_without_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list) {
if (se_list.size() == 0) {
return;
}
//不调整持仓,先将所有持仓系统加入到输出系统列表中
out_sys_list.insert(out_sys_list.end(), hold_list.begin(), hold_list.end());
//如果持仓的系统数已大于等于最大持仓系统数,直接输出已持仓系统列表,并返回
int max_num = getParam<int>("max_sys_num");
if (hold_list.size() >= max_num) {
if (running_list.size() >= max_num) {
return;
}
//从当前选中的系统列表中将持仓的系统排除
//从当前选中的系统列表中将运行中的子系统排除
std::set<SYSPtr> hold_sets;
for (auto iter = hold_list.begin(); iter != hold_list.end(); ++iter) {
for (auto iter = running_list.begin(); iter != running_list.end(); ++iter) {
hold_sets.insert(*iter);
}
SystemList pure_se_list;
SystemList pure_se_list; // 不包含运行中系统的子系统列表
for (auto iter = se_list.begin(); iter != se_list.end(); ++iter) {
if (hold_sets.find(*iter) == hold_sets.end()) {
pure_se_list.push_back(*iter);
@ -325,8 +292,8 @@ void AllocateFundsBase::_getAllocatedSystemList_not_adjust_hold(const Datetime&
boost::bind(std::less<double>(), boost::bind(&SystemWeight::m_weight, _1),
boost::bind(&SystemWeight::m_weight, _2)));
//倒序遍历计算总权重并在遇到权重为0或等于最大持仓时
size_t remain = max_num - hold_list.size();
//倒序遍历计算总权重并在遇到权重为0或等于最大持仓时结束遍历
size_t remain = max_num - running_list.size();
price_t total_weight = 0.0;
auto sw_iter = sw_list.rbegin();
for (size_t count = 0; sw_iter != sw_list.rend(); ++sw_iter, count++) {
@ -348,33 +315,17 @@ void AllocateFundsBase::_getAllocatedSystemList_not_adjust_hold(const Datetime&
sw_iter = sw_list.rbegin();
for (; sw_iter != end_iter; ++sw_iter) {
// 该系统期望分配的资金
price_t will_cash = per_cash * sw_iter->getWeight();
if (will_cash <= 0.0) {
price_t will_cash = roundDown(per_cash * sw_iter->getWeight(), precision);
if (will_cash <= std::abs(roundDown(0.0, precision))) {
break;
}
// 尝试从总账户中取出资金存入子账户
SYSPtr sub_sys = sw_iter->getSYS();
TMPtr sub_tm = sub_sys->getTM();
assert(sub_tm);
// 计算实际的价格精度(总账户和当前系统账号之间的最小值)
int real_precision = sub_tm->getParam<int>("precision");
if (precision < real_precision) {
real_precision = precision;
}
// 计算该系统实际期望分配的资金,并将总账户中的资金移入该系统账户中
will_cash = roundDown(will_cash - sub_tm->currentCash(), real_precision);
if (will_cash > 0) {
if (!m_tm->checkout(date, will_cash)) {
HKU_ERROR("m_tm->checkout failed!");
continue;
}
if (m_tm->checkout(date, will_cash)) {
sub_tm->checkin(date, will_cash);
}
out_sys_list.push_back(sub_sys);
}
}

View File

@ -32,14 +32,14 @@ public:
void name(const string& name);
/**
* Portfolio获取实际获得资产分配的系统策略实例
*
* @param date
* @param se_list
* @param hold_list
* @param running_list
* @return
*/
SystemList getAllocatedSystemList(const Datetime& date, const SystemList& se_list,
const SystemList& hold_list);
void adjustFunds(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list);
/** 获取交易账户 */
TMPtr getTM();
@ -59,8 +59,6 @@ public:
*/
void setReserverPercent(double p);
bool changed(Datetime date);
/** 复位 */
void reset();
@ -85,11 +83,10 @@ public:
virtual SystemWeightList _allocateWeight(const Datetime& date, const SystemList& se_list) = 0;
void _getAllocatedSystemList_adjust_hold(const Datetime& date, const SystemList& se_list,
const SystemList& hold_list, SystemList& out_sys_list);
void _getAllocatedSystemList_not_adjust_hold(const Datetime& date, const SystemList& se_list,
const SystemList& hold_list,
SystemList& out_sys_list);
void _adjust_with_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list);
void _adjust_without_running(const Datetime& date, const SystemList& se_list,
const std::list<SYSPtr>& running_list);
private:
string m_name;

View File

@ -27,188 +27,184 @@ HKU_API std::ostream& operator<<(std::ostream& os, const PortfolioPtr& pf) {
return os;
}
Portfolio::Portfolio() : m_name("Portfolio") {}
Portfolio::Portfolio() : m_name("Portfolio"), m_is_ready(false) {}
Portfolio::Portfolio(const string& name) : m_name(name) {}
Portfolio::Portfolio(const string& name) : m_name(name), m_is_ready(false) {}
Portfolio::Portfolio(const TradeManagerPtr& tm, const SelectorPtr& se, const AFPtr& af)
: m_name("Portfolio"), m_tm(tm), m_se(se), m_af(af) {}
: m_name("Portfolio"), m_tm(tm), m_se(se), m_af(af), m_is_ready(false) {}
Portfolio::~Portfolio() {}
void Portfolio::reset() {
m_is_ready = false;
m_running_sys_set.clear();
m_running_sys_list.clear();
m_all_sys_set.clear();
if (m_tm)
m_tm->reset();
if (m_se)
m_se->reset();
if (m_af)
m_af->reset();
if (m_tm_shadow)
m_tm_shadow->reset();
}
PortfolioPtr Portfolio::clone() {
PortfolioPtr p = make_shared<Portfolio>();
p->m_params = m_params;
p->m_name = m_name;
p->m_query = m_query;
p->m_running_sys_set = m_running_sys_set;
p->m_running_sys_list = m_running_sys_list;
p->m_all_sys_set = m_all_sys_set;
p->m_is_ready = m_is_ready;
if (m_se)
p->m_se = m_se->clone();
if (m_af)
p->m_af = m_af->clone();
if (m_tm)
p->m_tm = m_tm->clone();
if (m_tm_shadow)
p->m_tm_shadow = m_tm_shadow->clone();
return p;
}
bool Portfolio::readyForRun() {
if (!m_se) {
HKU_WARN("m_se is null!");
m_is_ready = false;
return false;
}
if (!m_tm) {
HKU_WARN("m_tm is null!");
m_is_ready = false;
return false;
}
if (!m_af) {
HKU_WARN("m_am is null!");
m_is_ready = false;
return false;
}
reset();
//将影子账户指定给资产分配器
m_tm_shadow = m_tm->clone();
m_af->setTM(m_tm_shadow);
// 将影子账户指定给资产分配器
// m_tm_shadow = m_tm->clone();
// m_af->setTM(m_tm_shadow);
m_af->setTM(m_tm);
return true;
}
void Portfolio::runMoment(const Datetime& datetime) {}
void Portfolio::run(const KQuery& query) {
if (!readyForRun()) {
return;
}
m_af->setQuery(query);
// 为资金分配器设置关联查询条件
m_af->setQuery(m_query);
// 获取所有备选子系统,为无关联账户的子系统分配子账号,对所有子系统做好启动准备
SystemList all_sys_list = m_se->getAllSystemList();
TMPtr pro_tm = crtTM(m_tm->initDatetime(), 0.0, m_tm->costFunc(), "SUB");
TMPtr pro_tm = crtTM(m_tm->initDatetime(), 0.0, m_tm->costFunc(), "TM_SUB");
auto sys_iter = all_sys_list.begin();
for (; sys_iter != all_sys_list.end(); ++sys_iter) {
//为每一个系统实例分配子账户
SystemPtr& sys = *sys_iter;
sys->setTM(pro_tm->clone());
// 如果子系统没有关联账户,则为其分配一个子账户
if (!sys->getTM()) {
sys->setTM(pro_tm->clone());
}
if (sys->readyForRun()) {
KData k = sys->getStock().getKData(query);
KData k = sys->getStock().getKData(m_query);
sys->setTO(k);
// 保存记录子系统
m_running_sys_set.insert(sys);
m_running_sys_list.push_back(sys);
m_all_sys_set.insert(sys);
} else {
HKU_WARN("Exist invalid system, it could not ready for run!");
}
}
m_is_ready = true;
return true;
}
void Portfolio::runMoment(const Datetime& date) {
HKU_CHECK(isReady(), "Not ready to run! Please perform readyForRun() first!");
// 当前日期小于账户建立日期,直接忽略
if (date < m_tm->initDatetime()) {
return;
}
int precision = m_tm->getParam<int>("precision");
SystemList cur_selected_list; //当前选中系统列表
std::set<SYSPtr> cur_selected_sets; //当前选中系统集合,方便计算使用
SystemList cur_allocated_list; //当前分配了资金的系统
SystemList cur_hold_sys_list; //当前时刻有持仓的系统,每个时刻重新收集
// 从选股策略获取当前选中的系统列表
cur_selected_list = m_se->getSelectedSystemList(date);
// 资产分配算法调整各子系统资产分配
m_af->adjustFunds(date, cur_selected_list, m_running_sys_list);
// 遍历当前运行中的子系统,如果已没有分配资金和持仓,则移除
SystemList will_remove_sys;
for (auto& running_sys : m_running_sys_list) {
Stock stock = running_sys->getStock();
TMPtr sub_tm = running_sys->getTM();
KRecord krecord = running_sys->getTO().getKRecordByDate(date);
PositionRecord position = sub_tm->getPosition(stock);
running_sys->_sellForce(krecord, position.number, PART_PORTFOLIO);
price_t cash = sub_tm->currentCash();
if (cash > 0) {
if (sub_tm->checkout(date, cash)) {
m_tm->checkin(date, cash);
// 重新获取此时的子账户资金
cash = sub_tm->currentCash();
}
}
position = sub_tm->getPosition(stock);
// 已没有持仓且没有现金,则放入待移除列表
if (position.number == 0 && cash <= std::abs(roundDown(0, precision))) {
will_remove_sys.push_back(running_sys);
}
}
for (auto& sub_sys : will_remove_sys) {
m_running_sys_list.remove(sub_sys);
m_running_sys_set.erase(sub_sys);
}
// 遍历本次选择的系统列表,如果存在分配资金且不在运行中列表内,则加入运行列表
for (auto& sub_sys : cur_selected_list) {
price_t cash = sub_sys->getTM()->currentCash();
if (cash > 0 && m_running_sys_set.find(sub_sys) == m_running_sys_set.end()) {
m_running_sys_list.push_back(sub_sys);
m_running_sys_set.insert(sub_sys);
if (m_all_sys_set.find(sub_sys) == m_all_sys_set.end()) {
m_all_sys_set.insert(sub_sys);
}
}
}
// 执行所有运行中的系统
for (auto& sub_sys : m_running_sys_list) {
sub_sys->runMoment(date);
}
}
void Portfolio::run(const KQuery& query) {
HKU_CHECK(readyForRun(),
"readyForRun fails, check to see if a valid TradeManager, Selector, or "
"AllocateFunds instance have been specified.");
DatetimeList datelist = StockManager::instance().getTradingCalendar(query);
DatetimeList::const_iterator date_iter = datelist.begin();
for (; date_iter != datelist.end(); ++date_iter) {
const Datetime& cur_date = *date_iter;
//忽略小于账户初始建立日期的交易日
if (cur_date < m_tm->initDatetime()) {
continue;
}
cur_hold_sys_list.clear();
//----------------------------------------------------------
//如果当前时刻选择标的发生变化(此时也一定是资金调整分配的时刻)
//----------------------------------------------------------
bool selected_changed = m_se->changed(cur_date);
if (selected_changed) {
//重新计算当前时刻选择的系统实例
cur_selected_list = m_se->getSelectedSystemList(cur_date);
//构建当前时刻选择的系统实例集合,便于后续计算
cur_selected_sets.clear();
sys_iter = cur_selected_list.begin();
for (; sys_iter != cur_selected_list.end(); ++sys_iter) {
cur_selected_sets.insert(*sys_iter);
}
}
//----------------------------------------------------------
//查找当前已分配资金的系统,如果不在当前选中的系统范围内,则回收期没有持仓的系统资金
//----------------------------------------------------------
sys_iter = cur_allocated_list.begin();
for (; sys_iter != cur_allocated_list.end(); ++sys_iter) {
SYSPtr& sys = *sys_iter;
TMPtr tm = sys->getTM();
if (tm->getStockNumber() != 0) {
//收集当前仍有持仓的系统
cur_hold_sys_list.push_back(sys);
} else {
//如果该系统已没有持仓,且不在当前的选中系统范围内,则回收分配的资金
if (cur_selected_sets.find(sys) == cur_selected_sets.end()) {
price_t cash = tm->currentCash();
if (cash > 0) {
//要先存入影子账户再取出子账户资金,否则后续交易记录同步会错误
m_tm_shadow->checkin(cur_date, cash);
tm->checkout(cur_date, cash);
}
}
}
}
//----------------------------------------------------------
//如果选择列表更新或调整资金时刻,则调整资金分配
//----------------------------------------------------------
if (selected_changed || m_af->changed(cur_date)) {
cur_allocated_list =
m_af->getAllocatedSystemList(cur_date, cur_selected_list, cur_hold_sys_list);
}
//----------------------------------------------------------
// 运行当前分配了资金的系统
//----------------------------------------------------------
sys_iter = cur_allocated_list.begin();
for (; sys_iter != cur_allocated_list.end(); ++sys_iter) {
SYSPtr& sys = *sys_iter;
sys->runMoment(cur_date);
//同步交易记录
TradeRecordList tr_list = sys->getTM()->getTradeList(cur_date, Null<Datetime>());
auto tr_iter = tr_list.begin();
for (; tr_iter != tr_list.end(); ++tr_iter) {
m_tm_shadow->addTradeRecord(*tr_iter);
}
}
//同步总账户和影子账户交易记录
TradeRecordList tr_list = m_tm_shadow->getTradeList(cur_date, Null<Datetime>());
auto tr_iter = tr_list.begin();
for (; tr_iter != tr_list.end(); ++tr_iter) {
if (tr_iter->business == BUSINESS_CHECKIN || tr_iter->business == BUSINESS_CHECKOUT) {
continue;
}
m_tm->addTradeRecord(*tr_iter);
}
if (m_tm->currentCash() != m_tm_shadow->currentCash()) {
HKU_INFO("m_tm->currentCash() != m_tm_shadow->currentCash()");
HKU_INFO("{:<.4f} == {:<.4f}", m_tm->currentCash(), m_tm_shadow->currentCash());
}
} // for datelist
for (auto& date : datelist) {
runMoment(date);
}
}
} /* namespace hku */

View File

@ -40,9 +40,22 @@ public:
m_name = name;
}
bool isReady() const {
return m_is_ready;
}
bool readyForRun();
void run(const KQuery& query);
void runMoment(const Datetime& datetime);
void setQuery(const KQuery& query) {
m_query = query;
}
KQuery getQuery() const {
return m_query;
}
TMPtr getTM() {
return m_tm;
}
@ -63,24 +76,30 @@ public:
m_af = af;
}
SystemList getAllSystem() const {
SystemList result;
for (auto& sys : m_all_sys_set) {
result.push_back(sys);
}
return result;
}
void reset();
typedef shared_ptr<Portfolio> PortfolioPtr;
PortfolioPtr clone();
private:
bool readyForRun();
void rebuildOnlyTotalTM();
protected:
string m_name;
TMPtr m_tm;
SEPtr m_se;
AFPtr m_af;
//以下为临时变量
TMPtr m_tm_shadow; //影子账户,用于内部协调分配资金
std::set<SYSPtr> m_running_sys_set; // 当前仍在运行的子系统集合
std::list<SYSPtr> m_running_sys_list; // 当前仍在运行的子系统列表
std::set<SYSPtr> m_all_sys_set; // 记录所有运行过或运行中的子系统集合
KQuery m_query; // 关联的查询条件
bool m_is_ready; // 是否已做好运行准备
//============================================
// 序列化支持

View File

@ -24,14 +24,10 @@ HKU_API std::ostream& operator<<(std::ostream& os, const SelectorPtr& st) {
return os;
}
SelectorBase::SelectorBase() : m_name("SelectorBase"), m_count(0), m_pre_date(Datetime::min()) {
setParam<int>("freq", 1); //已Bar为单位
}
SelectorBase::SelectorBase() : m_name("SelectorBase"), m_count(0), m_pre_date(Datetime::min()) {}
SelectorBase::SelectorBase(const string& name)
: m_name(name), m_count(0), m_pre_date(Datetime::min()) {
setParam<int>("freq", 1);
}
: m_name(name), m_count(0), m_pre_date(Datetime::min()) {}
SelectorBase::~SelectorBase() {}
@ -75,6 +71,7 @@ SelectorPtr SelectorBase::clone() {
SystemList::const_iterator iter = m_sys_list.begin();
for (; iter != m_sys_list.end(); ++iter) {
// TODO
p->m_sys_list.push_back((*iter)->clone(true, false));
}
@ -115,23 +112,4 @@ void SelectorBase::addStockList(const StockList& stkList, const SystemPtr& proto
}
}
bool SelectorBase::changed(Datetime date) {
if (date <= m_pre_date || date == Null<Datetime>())
return false;
int freq = getParam<int>("freq");
if (freq <= 0) {
freq = 1;
}
m_count++;
if (m_count >= freq) {
m_count = 0;
m_pre_date = date;
return true;
}
return false;
}
} /* namespace hku */

View File

@ -55,8 +55,6 @@ public:
return m_sys_list;
}
bool changed(Datetime date);
void reset();
void clear();

View File

@ -600,7 +600,9 @@ void System::_submitBuyRequest(const KRecord& today, Part from) {
}
}
void System::_sellFromAllocateFunds(const KRecord& today, double num) {
void System::_sellForce(const KRecord& today, double num, Part from) {
HKU_ASSERT_M(from == PART_ALLOCATEFUNDS || from == PART_PORTFOLIO,
"Only Allocator or Portfolis can perform this operation!");
if (getParam<bool>("delay")) {
if (m_sellRequest.valid) {
if (m_sellRequest.count > getParam<int>("max_delay_count")) {
@ -617,7 +619,7 @@ void System::_sellFromAllocateFunds(const KRecord& today, double num) {
}
PositionRecord position = m_tm->getPosition(m_stock);
m_sellRequest.from = PART_ALLOCATEFUNDS;
m_sellRequest.from = from;
m_sellRequest.datetime = today.datetime;
m_sellRequest.stoploss = position.stoploss;
m_sellRequest.goal = position.goalPrice;
@ -627,7 +629,7 @@ void System::_sellFromAllocateFunds(const KRecord& today, double num) {
PositionRecord position = m_tm->getPosition(m_stock);
price_t realPrice = _getRealSellPrice(today.datetime, today.closePrice);
TradeRecord record = m_tm->sell(today.datetime, m_stock, realPrice, num, position.stoploss,
position.goalPrice, today.closePrice, PART_ALLOCATEFUNDS);
position.goalPrice, today.closePrice, from);
m_trade_list.push_back(record);
_sellNotifyAll(record);
}

View File

@ -211,7 +211,9 @@ public:
void _sellNow(const KRecord& today, Part from);
void _sellDelay(const KRecord& today);
void _submitSellRequest(const KRecord& today, Part from);
void _sellFromAllocateFunds(const KRecord& today, double num);
// 强制卖出,用于资金分配管理器和资产组合指示系统进行强制卖出操作
void _sellForce(const KRecord& today, double num, Part from);
void _sellShort(const KRecord& today, Part from);
void _sellShortNow(const KRecord& today, Part from);

View File

@ -28,8 +28,9 @@ enum SystemPart {
PART_SLIPPAGE = 7, /**< 移滑价差算法 */
PART_ALLOCATEFUNDS = 8, /**< 资产分配算法 */
PART_PORTFOLIO = 9, /**< 资产组合 */
PART_INVALID = 9, /**< 无效值 */
PART_INVALID = 10, /**< 无效值 */
};
/**