Commit 34e8471f authored by wenwen.tang's avatar wenwen.tang 😕

实盘

parent 88204c62
......@@ -60,6 +60,7 @@ basic: # 基础信息模块
- 'TEMUSGI LX Equity'
real:
- 'FGFSACU LX Equity'
- 'TEMUSGI LX Equity'
# navs: # 净值模块
# exrate: # 汇率,如果不开启,整个这块注释掉
# - from: EUR # 需要转换的货币类型
......
......@@ -42,10 +42,10 @@ py-jftech:
hold-report: portfolios.holder.DivHoldReportor
mpt: portfolios.builder.PoemPortfoliosBuilder
dividend-holder: portfolios.holder.InvTrustPortfoliosHolder
# email:
# server: smtphz.qiye.163.com
# user: jft-ra@thizgroup.com
# password: 5dbb#30ec6d3
email:
server: smtphz.qiye.163.com
user: jft-ra@thizgroup.com
password: 5dbb#30ec6d3
mulit-process:
max-workers: ${MAX_PROCESS:4}
basic: # 基础信息模块
......@@ -60,6 +60,7 @@ basic: # 基础信息模块
- 'TEMUSGI LX Equity'
real:
- 'FGFSACU LX Equity'
- 'TEMUSGI LX Equity'
# navs: # 净值模块
# exrate: # 汇率,如果不开启,整个这块注释掉
# - from: EUR # 需要转换的货币类型
......@@ -183,7 +184,7 @@ reports: # 报告模块相关
- month-div-rate-report # 月度配息率比较
- year-div-rate-report # 年度配息率比较
real-daily:
file-name: svROBO5_portfolios
file-name: svROBO6_portfolios
include-report:
- daily-hold-report
- daily-signal-report
......@@ -192,13 +193,13 @@ reports: # 报告模块相关
- wenwen.tang@thizgroup.com
copies: ${DAILY_EMAIL_COPIES}
subject:
default: "ROBO5_TAIBEI-实盘版-每日投組推薦_{today}"
rebalance: "ROBO5_TAIBEI-实盘版-每日投組推薦_{today}_今日有調倉信號!!!"
default: "ROBO6_TAIBEI-实盘版-每日投組推薦_{today}"
rebalance: "ROBO6_TAIBEI-实盘版-每日投組推薦_{today}_今日有調倉信號!!!"
content:
default: "Dear All: 附件是今天生成的推薦組合,請驗收,謝謝! 注>:該郵件為自動發送,如有問題請聯繫矽谷團隊 telan_qian@chifufund.com"
rebalance: "Dear All: 附件是今天生成的推薦組合以及調倉信號,請驗收,謝謝! 注>:該郵件為自動發送,如有問題請聯繫矽谷團隊 telan_qian@chifufund.com"
daily-monitor:
file-name: svROBO5_monitor
file-name: svROBO6_monitor
include-report:
- name: relative-range-report # 相对区间收益报告
min-date: ~
......@@ -226,11 +227,11 @@ reports: # 报告模块相关
receives:
- wenwen.tang@thizgroup.com
copies: ${MONITOR_EMAIL_COPIES}
subject: "SVROBO5-实盘版-每日监测_{today}"
subject: "SVROBO6-实盘版-每日监测_{today}"
content: "Dear All: 附件是今天生成的监测数据,請驗收,謝謝! 注>:該郵件為自動發送,如有問題請聯繫矽谷團隊 telan_qian@chifufund.com"
robo-executor: # 执行器相关
use: ${ROBO_EXECUTOR:backtest} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data: ${SYNC_DATA:off} # 是否开启同步资料数据
use: ${ROBO_EXECUTOR:real} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data: ${SYNC_DATA:on} # 是否开启同步资料数据
backtest: # 回测执行器相关
start-date: 2022-09-30 # 回测起始日期
end-date: 2023-03-01 # 回测截止日期
......@@ -239,7 +240,8 @@ robo-executor: # 执行器相关
end-step: ${BACKTEST_END_STEP:3} # 回测从哪一步执行完成后结束执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组
clean-up: true
real: # 实盘执行器
start-date: 2022-09-01 # 实盘开始时间
export: ${EXPORT_ENABLE:on} # 是否开启报告
start-date: 2023-01-01 # 实盘开始时间
......
import json
import logging
from datetime import datetime as dt
from datetime import datetime as dt, timedelta
from typing import List
from py_jftech import component, autowired, format_date
import pandas as pd
from py_jftech import component, autowired, format_date, prev_workday, is_workday
from pymysql import IntegrityError, constants
from api import PortfoliosBuilder, PortfoliosRisk, AssetPool, Navs, PortfoliosType, Datum, SolveType, SolverFactory, \
......@@ -143,3 +144,78 @@ class SignalReportor(RoboReportor):
'weight': weight
})
return result
@component(bean_name='daily-hold-report')
class DailyHoldReportor(RoboReportor):
@autowired
def __init__(self, datum: Datum = None):
self._datum = datum
@property
def report_name(self) -> str:
return '每日持仓信息'
def load_report(self, max_date=prev_workday(dt.today()), min_date=None) -> List[dict]:
# 月初调仓,实际相当于调仓信号在上月月末
first_day = max_date.replace(day=1)
prev_month = first_day - timedelta(days=1)
prev_month.replace(day=prev_month.day)
prev_month = prev_month if is_workday(prev_month) else prev_workday(prev_month)
portfolio = rmp.get_one(prev_month, type=PortfoliosType.NORMAL, risk=PortfoliosRisk.FT3)
result = {}
if portfolio:
datum_ids = list(json.loads(portfolio['portfolio']).keys())
datums = pd.DataFrame(self._datum.get_datums(type=DatumType.FUND, datum_ids=datum_ids))
datums.set_index('id', inplace=True)
result['risk'] = [portfolio['risk'] for i in datum_ids]
result['rebalance_type'] = [portfolio['type'] for i in datum_ids]
result['weight'] = [format(i, '.0%') for i in json.loads(portfolio['portfolio']).values()]
result['asset_ids'] = datum_ids
result['name'] = [datums.loc[int(i)]['chineseName'] for i in datum_ids]
result['lipper_id'] = [datums.loc[int(i)]['lipperKey'] for i in datum_ids]
result['date'] = [max_date for i in datum_ids]
result['rebalance_date'] = [portfolio['date'] for i in datum_ids]
result = pd.DataFrame(result)
result = result[
['lipper_id', 'asset_ids', 'name', 'weight', 'risk', 'date', 'rebalance_type', 'rebalance_date']]
return result.to_dict('records')
return []
@component(bean_name='daily-signal-report')
class DailySignalReportor(RoboReportor):
@autowired
def __init__(self, datum: Datum = None):
self._datum = datum
@property
def report_name(self) -> str:
return '每日调仓信号'
def load_report(self, max_date=prev_workday(dt.today()), min_date=None) -> List[dict]:
portfolio = rmp.get_one(max_date, type=PortfoliosType.NORMAL, risk=PortfoliosRisk.FT3)
result = {}
if portfolio:
datum_ids = list(json.loads(portfolio['portfolio']).keys())
datums = pd.DataFrame(self._datum.get_datums(type=DatumType.FUND, datum_ids=datum_ids))
datums.set_index('id', inplace=True)
result['risk'] = [portfolio['risk'] for i in datum_ids]
result['rebalance_type'] = [portfolio['type'] for i in datum_ids]
result['weight'] = [format(i, '.0%') for i in json.loads(portfolio['portfolio']).values()]
result['asset_ids'] = datum_ids
result['name'] = [datums.loc[int(i)]['chineseName'] for i in datum_ids]
result['lipper_id'] = [datums.loc[int(i)]['lipperKey'] for i in datum_ids]
result['date'] = [max_date for i in datum_ids]
result = pd.DataFrame(result)
result = result[['lipper_id', 'asset_ids', 'name', 'weight', 'risk', 'date', 'rebalance_type']]
return result.to_dict('records')
return []
import logging
import sys
from concurrent.futures import wait
from datetime import datetime as dt
from datetime import datetime as dt, timedelta
from typing import List
import pandas as pd
from py_jftech import (
component, autowired, get_config, filter_weekend, asynchronized,
parse_date, workday_range
parse_date, workday_range, is_workday, prev_workday, format_date
)
from api import (
......@@ -135,12 +135,15 @@ class BacktestExecutor(RoboExecutor):
@component(bean_name='real')
class RealExecutor(RoboExecutor):
@autowired
@autowired(names={'daily_export': 'daily-real-export', 'monitor_export': 'daily-monitor-export'})
def __init__(self, builder: PortfoliosBuilder = None, hold: PortfoliosHolder = None, syncs: List[DataSync] = None,
):
daily_export: RoboExportor = None, monitor_export: RoboExportor = None, pool: AssetPool = None, ):
self._builder = builder
self._pool = pool
self._hold = hold
self._syncs = syncs
self._daily_export = daily_export
self._monitor_export = monitor_export
self._config = get_config(__name__)['real']
@property
......@@ -164,7 +167,39 @@ class RealExecutor(RoboExecutor):
def include_date(self):
return [dt.combine(x, dt.min.time()) for x in self._config['include-date']]
@property
def export(self):
return self._config['export'] if 'export' in self._config else False
def start_exec(self):
if self.is_sync_data:
for sync in self._syncs:
sync.do_sync()
date = self.curt_date
if is_workday(date) or date in self.include_date:
date = prev_workday(filter_weekend(date))
for risk in PortfoliosRisk:
logger.info(f"start to build risk[{risk.name}] real for date[{format_date(date)}]".center(50, '-'))
now = dt.now()
first_day = date.replace(day=1)
prev_month = first_day - timedelta(days=1)
prev_month.replace(day=prev_month.day)
prev_month = prev_month if is_workday(prev_month) else prev_workday(prev_month)
self._pool.get_pool(prev_month)
self._builder.get_portfolios(prev_month, risk)
next_month = date.replace(day=28) + timedelta(days=4)
prev_month = next_month.replace(day=1) - timedelta(days=1)
prev_month = prev_month if is_workday(prev_month) else prev_workday(prev_month)
if date.day == prev_month.day:
self._pool.get_pool(date)
self._builder.get_portfolios(date, risk)
logger.info(
f"build risk[{risk.name}] real for date[{format_date(date)}] success, use[{(dt.now() - now).seconds}s]")
if self.export:
now = dt.now()
# 每日实盘报告
self._daily_export.export(max_date=date)
logger.info(
f'export email for date[{format_date(date)}] send success, use[{(dt.now() - now).seconds}s]')
else:
logger.info(f'today[{format_date(date)}] is a rest day, do not execute the daily real robo.')
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment