Commit 1a14d64c authored by wenwen.tang's avatar wenwen.tang 😕

update

parent 06a587a5
......@@ -297,14 +297,6 @@ class PortfoliosBuilder(ABC):
'''
pass
@abstractmethod
def get_all_portfolios(self, risk: PortfoliosRisk = None):
"""
查询所有优选基金
@param risk:
"""
pass
class Solver(ABC):
'''
......
......@@ -82,6 +82,7 @@ portfolios: # 投组模块
min-interval-days: 10 # 两次实际调仓最小间隔期,单位交易日
dividend-rate: 0.09 #设定年化配息率
dividend-date: 15 #配息日,每月15号
dividend-adjust-day: [1,4,7,10] #每年的首个季度调整配息
warehouse-frequency: 1 #每隔1个月调一次仓
redeem-list: [ 'TEUSAAU LX Equity', 'LIGTRAA ID Equity', 'TEMFHAC LX Equity', 'LUSHUAA ID Equity' ] #从持仓中的低风险资产“直接”按序赎回
solver: # 解算器相关
......@@ -231,17 +232,17 @@ reports: # 报告模块相关
content: "Dear All: 附件是今天生成的监测数据,請驗收,謝謝! 注>:該郵件為自動發送,如有問題請聯繫矽谷團隊 telan_qian@chifufund.com"
robo-executor: # 执行器相关
use: ${ROBO_EXECUTOR:real} # 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data: ${SYNC_DATA:on} # 是否开启同步资料数据
sync-data: ${SYNC_DATA:off} # 是否开启同步资料数据
backtest: # 回测执行器相关
start-date: 2022-09-30 # 回测起始日期
end-date: 2023-03-01 # 回测截止日期
end-date: 2023-07-03 # 回测截止日期
sealing-period: 10 #调仓封闭期
start-step: ${BACKTEST_START_STEP:3} # 回测从哪一步开始执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组
end-step: ${BACKTEST_END_STEP:3} # 回测从哪一步执行完成后结束执行 1:计算资产池;2:计算最优投组:3:计算再平衡信号以及持仓投组
clean-up: true
real: # 实盘执行器
export: ${EXPORT_ENABLE:on} # 是否开启报告
start-date: 2023-01-01 # 实盘开始时间
start-date: 2022-09-01 # 实盘开始时间
include-date: []
......
......@@ -4,13 +4,12 @@ from datetime import datetime as dt, timedelta
from typing import List
import pandas as pd
from py_jftech import component, autowired, format_date, prev_workday, is_workday
from py_jftech import component, autowired, format_date, prev_workday, is_workday, get_config
from pymysql import IntegrityError, constants
from api import PortfoliosBuilder, PortfoliosRisk, AssetPool, Navs, PortfoliosType, Datum, SolveType, SolverFactory, \
RoboReportor, DatumType
from portfolios.dao import robo_mpt_portfolios as rmp
from portfolios.dao.robo_mpt_portfolios import get_list
logger = logging.getLogger(__name__)
......@@ -27,7 +26,18 @@ class MptPortfoliosBuilder(PortfoliosBuilder):
def get_portfolios(self, day, risk: PortfoliosRisk, type: PortfoliosType = PortfoliosType.NORMAL):
try:
portfolio = rmp.get_one(day, type, risk)
# 若记录为空则,将传入日期作为初始日期,进行build
portfolio = rmp.get_last_one(day, type, risk)
if portfolio:
frequency = get_config('portfolios')['holder']['warehouse-frequency']
date = pd.to_datetime(day.replace(day=1)) + pd.DateOffset(months=frequency)
date = date - timedelta(days=1)
# 指定周期末的工作日
date = date if is_workday(date) else prev_workday(date)
if date == day:
portfolio = None
elif portfolio['date'] != day:
return None
if not portfolio:
result = self.build_portfolio(day, type)
for build_risk, datas in result.items():
......@@ -87,9 +97,6 @@ class MptPortfoliosBuilder(PortfoliosBuilder):
def clear(self, day=None, risk: PortfoliosRisk = None):
rmp.delete(min_date=day, risk=risk)
def get_all_portfolios(self, risk: PortfoliosRisk = None):
return get_list(risk=risk)
@component(bean_name='poem')
class PoemPortfoliosBuilder(MptPortfoliosBuilder):
......@@ -146,45 +153,6 @@ class SignalReportor(RoboReportor):
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'] = [datums.loc[int(i)]['ftTicker'] for i in 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')
......
......@@ -14,6 +14,7 @@ __COLUMNS__ = {
'rhp_nav': 'nav',
'rhp_fund_av': 'fund_av',
'rhp_fund_div': 'fund_div',
'rhp_div_forecast': 'div_forecast',
'rhp_asset_nav': 'asset_nav',
'rhp_port_div': 'port_div',
'v_nav_div_acc': 'acc_av',
......@@ -40,7 +41,7 @@ def get_one(day, risk: PortfoliosRisk):
@read(one=True)
def get_last_one(risk: PortfoliosRisk = None, max_date=None, rebalance: bool = None, signal_id=None):
sql = "rhp_date <= '{format_date(max_date)}'" if max_date else None
sql = f"rhp_date <= '{format_date(max_date)}'" if max_date else None
return f'''
select {','.join([f'{x[0]} as {x[1]}' for x in __COLUMNS__.items()])} from robo_hold_portfolios
{where(sql, rhp_risk=risk, rhp_rrs_id=signal_id, rhp_rebalance=rebalance)}
......
......@@ -51,3 +51,14 @@ def get_list(max_date=None, min_date=None, type: PortfoliosType = None, risk: Po
{where(*sqls, rmp_risk=risk, rmp_type=type)}
order by rmp_date
'''
@read(one=True)
def get_last_one(date=None, type: PortfoliosType = None, risk: PortfoliosRisk = None):
sqls = []
if date:
sqls.append(f"rmp_date <= '{format_date(date)}'")
return f'''
select {','.join([f"{x[0]} as {x[1]}" for x in __COLUMNS__.items()])} from robo_mpt_portfolios
{where(*sqls, rmp_risk=risk, rmp_type=type)}
order by rmp_date desc limit 1
'''
\ No newline at end of file
This diff is collapsed.
......@@ -20,3 +20,5 @@ requests==2.28.1
scipy==1.9.3
six==1.16.0
urllib3==1.26.12
fastapi==0.100.0
uvicorn==0.23.1
\ No newline at end of file
import logging
import sys
from concurrent.futures import wait
from datetime import datetime as dt, timedelta
from datetime import datetime as dt
from typing import List
import pandas as pd
......@@ -34,19 +34,17 @@ class BacktestExecutor(RoboExecutor):
self._config = get_config(__name__)['backtest']
@staticmethod
def get_first_business_day(start_date, end_date):
def get_last_business_day(start_date, end_date):
# 生成日期范围并转换为DataFrame
dates = pd.date_range(start_date, end_date, freq='MS')
dates = dates.insert(0, start_date)
df = pd.DataFrame({'dates': dates})
# 提取每个月的第一个工作日
df['first_business_day'] = df['dates'].apply(
lambda x: pd.date_range(start=x, end=x + pd.offsets.MonthEnd(0), freq='B')[0]
df['last_business_day'] = df['dates'].map(
lambda date: pd.date_range(start=date, periods=1, freq='BM')[-1]
)
# 每隔n个月提取第一个工作日
result = []
for i in range(0, len(df), get_config('portfolios')['holder']['warehouse-frequency']):
result.append(df.iloc[i]['first_business_day'])
result.append(df.iloc[i]['last_business_day'])
delta = workday_range(result[0], result[1])
period = get_config(__name__)['backtest']['sealing-period']
if len(delta) <= period:
......@@ -98,7 +96,7 @@ class BacktestExecutor(RoboExecutor):
if self.start_step.within(BacktestStep.ASSET_POOL) and self.end_step.without(BacktestStep.ASSET_POOL):
logger.info("start to build asset pool".center(50, '-'))
now = dt.now()
workdays = self.get_first_business_day(self.start_date, self.end_date)
workdays = self.get_last_business_day(self.start_date, self.end_date)
for date in workdays:
self._pool.get_pool(date)
logger.info(f"build asset pool success, use[{(dt.now() - now).seconds}s]")
......@@ -107,7 +105,7 @@ class BacktestExecutor(RoboExecutor):
logger.info("start to build normal portfolios".center(50, '-'))
now = dt.now()
wait([self.async_build_portfolios(day, risk) for risk in PortfoliosRisk for day in
self.get_first_business_day(self.start_date, self.end_date)])
self.get_last_business_day(self.start_date, self.end_date)])
logger.info(f"build normal portfolios success, use[{(dt.now() - now).seconds}s]")
if self.start_step.within(BacktestStep.HOLD_PORTFOLIO) and self.end_step.without(BacktestStep.HOLD_PORTFOLIO):
logger.info("start to build hold portfolios".center(50, '-'))
......@@ -181,18 +179,8 @@ class RealExecutor(RoboExecutor):
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)
# 更新持仓
self._hold.build_hold_portfolio(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:
......
import uvicorn
from fastapi import FastAPI
app = FastAPI()
REC_GID = 'E3886FBA-123B-7890-123E-123456BEEED'
@app.get("/recommend")
async def root():
rec_list = []
portfolios = {'recomm_guid': REC_GID}
data = {'recomm_guid': REC_GID}
data['data_date'] = '2019-09-20'
data['funds'] = [
{'weight': '5', 'fund_id': '0152'}
]
portfolios['data'] = data
rec_list.append(portfolios)
return rec_list
if __name__ == "__main__":
uvicorn.run("robo_controller:app", reload=True, port=8080)
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