Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Sign in
Toggle navigation
R
robo-dividend
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
wenwen.tang
robo-dividend
Commits
1a14d64c
Commit
1a14d64c
authored
Aug 07, 2023
by
wenwen.tang
😕
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
update
parent
06a587a5
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
143 additions
and
114 deletions
+143
-114
api.py
api.py
+0
-8
config-svrobo6.yml
config-svrobo6.yml
+4
-3
builder.py
portfolios/builder.py
+13
-45
robo_hold_portfolios.py
portfolios/dao/robo_hold_portfolios.py
+2
-1
robo_mpt_portfolios.py
portfolios/dao/robo_mpt_portfolios.py
+11
-0
holder.py
portfolios/holder.py
+78
-36
requirements.txt
requirements.txt
+2
-0
robo_executor.py
robo_executor.py
+9
-21
__init__.py
web/__init__.py
+0
-0
robo_controller.py
web/robo_controller.py
+24
-0
No files found.
api.py
View file @
1a14d64c
...
...
@@ -297,14 +297,6 @@ class PortfoliosBuilder(ABC):
'''
pass
@
abstractmethod
def
get_all_portfolios
(
self
,
risk
:
PortfoliosRisk
=
None
):
"""
查询所有优选基金
@param risk:
"""
pass
class
Solver
(
ABC
):
'''
...
...
config-svrobo6.yml
View file @
1a14d64c
...
...
@@ -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:o
n
}
# 是否开启同步资料数据
sync-data
:
${SYNC_DATA:o
ff
}
# 是否开启同步资料数据
backtest
:
# 回测执行器相关
start-date
:
2022-09-30
# 回测起始日期
end-date
:
2023-0
3-01
# 回测截止日期
end-date
:
2023-0
7-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
:
202
3-01
-01
# 实盘开始时间
start-date
:
202
2-09
-01
# 实盘开始时间
include-date
:
[]
...
...
portfolios/builder.py
View file @
1a14d64c
...
...
@@ -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'
)
...
...
portfolios/dao/robo_hold_portfolios.py
View file @
1a14d64c
...
...
@@ -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)}
...
...
portfolios/dao/robo_mpt_portfolios.py
View file @
1a14d64c
...
...
@@ -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
portfolios/holder.py
View file @
1a14d64c
This diff is collapsed.
Click to expand it.
requirements.txt
View file @
1a14d64c
...
...
@@ -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
robo_executor.py
View file @
1a14d64c
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_
fir
st_business_day
(
start_date
,
end_date
):
def
get_
la
st_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
][
'
fir
st_business_day'
])
result
.
append
(
df
.
iloc
[
i
][
'
la
st_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_
fir
st_business_day
(
self
.
start_date
,
self
.
end_date
)
workdays
=
self
.
get_
la
st_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_
fir
st_business_day
(
self
.
start_date
,
self
.
end_date
)])
self
.
get_
la
st_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
:
...
...
web/__init__.py
0 → 100644
View file @
1a14d64c
web/robo_controller.py
0 → 100644
View file @
1a14d64c
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
)
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment