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
3d3e4fde
Commit
3d3e4fde
authored
Feb 26, 2023
by
jichao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
实盘完成
parent
c42d36f3
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
125 additions
and
42 deletions
+125
-42
api.py
api.py
+11
-1
asset_optimize.py
asset_pool/asset_optimize.py
+11
-1
robo_base_datum.py
basic/dao/robo_base_datum.py
+6
-1
datum.py
basic/datum.py
+41
-6
test_case.py
basic/test_case.py
+0
-1
config.yml
config.yml
+40
-15
test_case.py
portfolios/test_case.py
+1
-1
test_case.py
rebalance/test_case.py
+1
-1
robo_executor.py
robo_executor.py
+14
-15
No files found.
api.py
View file @
3d3e4fde
...
...
@@ -99,7 +99,7 @@ class Datum(ABC):
'''
@
abstractmethod
def
get_datums
(
self
,
type
:
DatumType
=
None
,
crncy
=
None
,
risk
=
None
,
datum_ids
=
None
,
ticker
=
None
):
def
get_datums
(
self
,
type
:
DatumType
=
None
,
crncy
=
None
,
risk
=
None
,
datum_ids
=
None
,
ticker
=
None
,
exclude
=
True
):
'''
获取资料信息,当id和ticker都有时,取二者并集
:param type: 资料类型
...
...
@@ -107,6 +107,7 @@ class Datum(ABC):
:param risk: 风险等级,仅对基金资料有效
:param datum_ids: 资料ID列表
:param ticker: 资料ticker列表
:param exclude: 是否排除过滤的资料,默认为True
:return: 资料信息数据
'''
pass
...
...
@@ -120,6 +121,15 @@ class Datum(ABC):
'''
pass
@
abstractmethod
def
update_change
(
self
,
date
):
'''
在指定对日期,执行资料变更,如果有变更则返回True, 否则返回False,注意,改方法非幂等,变更后无法复原
:param date: 执行变更的日期
:return: 如果有变更则返回True,否则返回False
'''
pass
class
Navs
(
ABC
):
'''
...
...
asset_pool/asset_optimize.py
View file @
3d3e4fde
...
...
@@ -43,9 +43,12 @@ class SortinoAssetOptimize(AssetOptimize, ABC):
return
pct_change
.
columns
[
sortino
.
index
[
0
]]
def
get_optimize_pool
(
self
,
day
):
opt_pool
=
rop
.
get_one
(
day
=
day
,
type
=
AssetPoolType
.
OPTIMIZE
)
if
opt_pool
is
not
None
:
return
json
.
loads
(
opt_pool
[
'asset_ids'
])
last_one
=
rop
.
get_last_one
(
day
=
day
,
type
=
AssetPoolType
.
OPTIMIZE
)
start
=
get_quarter_start
(
day
or
dt
.
today
())
if
not
last_one
or
start
>
last_one
[
'date'
]
or
self
.
has_incept_asset
(
last_one
[
'date'
]
+
timedelta
(
1
),
day
):
if
not
last_one
or
start
>
last_one
[
'date'
]
or
self
.
has_incept_asset
(
last_one
[
'date'
]
+
timedelta
(
1
),
day
)
or
self
.
has_change
(
day
)
:
pool
=
[]
min_dates
=
self
.
nav_min_dates
max_incept_date
=
sorted
([(
day
-
relativedelta
(
**
x
))
for
x
in
self
.
delta_kwargs
])[
0
]
...
...
@@ -86,6 +89,10 @@ class SortinoAssetOptimize(AssetOptimize, ABC):
'''
pass
@
abstractmethod
def
has_change
(
self
,
day
):
return
False
@
component
class
FundSortinoAssetOptimize
(
SortinoAssetOptimize
):
...
...
@@ -108,6 +115,9 @@ class FundSortinoAssetOptimize(SortinoAssetOptimize):
end_date
=
sorted
([(
end_date
-
relativedelta
(
**
x
))
for
x
in
self
.
delta_kwargs
])[
0
]
return
len
([
x
for
x
in
self
.
nav_min_dates
.
items
()
if
start_date
<=
x
[
1
]
<=
end_date
])
>
0
def
has_change
(
self
,
day
):
return
self
.
_datum
.
update_change
(
day
)
def
get_groups
(
self
):
funds
=
pd
.
DataFrame
(
self
.
_datum
.
get_datums
(
type
=
DatumType
.
FUND
))
min_dates
=
self
.
_navs
.
get_nav_start_date
()
...
...
basic/dao/robo_base_datum.py
View file @
3d3e4fde
from
py_jftech
import
read
,
where
,
to_tuple
from
py_jftech
import
read
,
where
,
to_tuple
,
write
from
api
import
DatumType
...
...
@@ -13,3 +13,8 @@ def get_base_datums(type: DatumType = None, crncy=None, risk=None, datum_ids=Non
'v_rbd_bloomberg_ticker'
:
to_tuple
(
ticker
)
}
return
f
'''select rbd_id as id, rbd_datas as datas from robo_base_datum {where(**kwargs)}'''
@
write
def
update_datum
(
id
,
datas
:
str
):
return
f
'''update robo_base_datum set rbd_datas = '{datas}' where rbd_id = {id}'''
basic/datum.py
View file @
3d3e4fde
import
json
import
os
from
datetime
import
datetime
as
dt
from
typing
import
List
import
pandas
as
pd
from
py_jftech
import
component
,
parse_date
,
get_config
,
to_tuple
,
autowired
from
py_jftech
import
component
,
parse_date
,
get_config
,
to_tuple
,
autowired
,
get_project_path
,
transaction
from
api
import
DatumType
,
Datum
,
PortfoliosRisk
,
RoboReportor
from
api
import
DatumType
,
Datum
,
PortfoliosRisk
,
RoboReportor
,
RoboExecutor
from
basic.dao
import
robo_base_datum
as
rbd
...
...
@@ -17,7 +18,27 @@ class DefaultDatum(Datum):
@
property
def
excludes
(
self
):
return
self
.
_config
[
'excludes'
]
if
'excludes'
in
self
.
_config
else
[]
excludes
=
self
.
_config
[
'excludes'
]
if
'excludes'
in
self
.
_config
else
[]
if
isinstance
(
excludes
,
dict
):
excludes
=
excludes
[
RoboExecutor
.
use_name
()]
if
RoboExecutor
.
use_name
()
in
excludes
else
[]
return
excludes
@
property
def
change_date
(
self
):
change
=
self
.
_config
[
'change'
]
if
'change'
in
self
.
_config
else
{}
return
pd
.
to_datetime
(
change
[
'date'
])
if
'date'
in
change
else
None
@
property
def
change_file
(
self
):
change
=
self
.
_config
[
'change'
]
if
'change'
in
self
.
_config
else
{}
if
'file'
not
in
change
or
change
[
'file'
]
is
None
:
return
None
file_path
:
str
=
change
[
'file'
]
if
file_path
.
startswith
(
'.'
):
return
os
.
path
.
abspath
(
os
.
path
.
join
(
os
.
path
.
dirname
(
__file__
),
file_path
))
elif
file_path
.
startswith
(
'/'
):
return
os
.
path
.
abspath
(
file_path
)
return
os
.
path
.
abspath
(
os
.
path
.
join
(
get_project_path
(),
file_path
))
def
format_datum
(
self
,
datum
):
if
DatumType
(
datum
[
'type'
])
is
DatumType
.
FUND
:
...
...
@@ -27,14 +48,14 @@ class DefaultDatum(Datum):
}
return
datum
def
get_datums
(
self
,
type
:
DatumType
=
None
,
crncy
=
None
,
risk
=
None
,
datum_ids
=
None
,
ticker
=
None
):
def
get_datums
(
self
,
type
:
DatumType
=
None
,
crncy
=
None
,
risk
=
None
,
datum_ids
=
None
,
ticker
=
None
,
exclude
=
True
):
datum_ids
=
to_tuple
(
datum_ids
)
if
ticker
:
datums
=
rbd
.
get_base_datums
(
type
=
type
,
ticker
=
ticker
)
datum_ids
=
tuple
(
set
(
datum_ids
or
[])
|
{
x
[
'id'
]
for
x
in
datums
})
result
=
rbd
.
get_base_datums
(
type
=
type
,
crncy
=
crncy
,
risk
=
risk
,
datum_ids
=
datum_ids
)
result
=
[{
**
json
.
loads
(
x
[
'datas'
]),
'id'
:
x
[
'id'
]}
for
x
in
result
]
return
[
self
.
format_datum
(
x
)
for
x
in
result
if
DatumType
(
x
[
'type'
])
is
not
DatumType
.
FUND
or
x
[
'bloombergTicker'
]
not
in
self
.
excludes
]
return
[
self
.
format_datum
(
x
)
for
x
in
result
if
not
exclude
or
x
[
'bloombergTicker'
]
not
in
self
.
excludes
]
def
get_high_risk_datums
(
self
,
risk
:
PortfoliosRisk
):
risk3
=
self
.
get_datums
(
type
=
DatumType
.
FUND
,
risk
=
3
)
...
...
@@ -47,6 +68,19 @@ class DefaultDatum(Datum):
return
risk3
+
self
.
get_datums
(
type
=
DatumType
.
FUND
,
risk
=
(
4
,
5
))
return
None
@
transaction
def
update_change
(
self
,
date
):
if
self
.
change_date
is
not
None
and
self
.
change_date
==
pd
.
to_datetime
(
date
):
if
self
.
change_file
is
not
None
:
for
fund
in
pd
.
read_excel
(
self
.
change_file
)
.
to_dict
(
'records'
):
db_data
=
rbd
.
get_base_datums
(
ticker
=
fund
[
'bloombergTicker'
])
rbd
.
update_datum
(
db_data
[
'id'
],
json
.
dumps
({
**
json
.
loads
(
db_data
[
'datas'
]),
**
fund
}))
return
True
return
False
@
component
(
bean_name
=
'funds-report'
)
class
FundReportor
(
RoboReportor
):
...
...
@@ -62,5 +96,6 @@ class FundReportor(RoboReportor):
def
load_report
(
self
,
max_date
=
dt
.
today
(),
min_date
=
None
)
->
List
[
dict
]:
datums
=
self
.
_datum
.
get_datums
(
type
=
DatumType
.
FUND
)
datums
=
pd
.
DataFrame
(
datums
)
datums
=
datums
[[
'id'
,
'ftTicker'
,
'bloombergTicker'
,
'chineseName'
,
'englishName'
,
'lipperKey'
,
'isin'
,
'currency'
,
'risk'
,
'inceptDate'
,
'category'
,
'assetType'
]]
datums
=
datums
[
[
'id'
,
'ftTicker'
,
'bloombergTicker'
,
'chineseName'
,
'englishName'
,
'lipperKey'
,
'isin'
,
'currency'
,
'risk'
,
'inceptDate'
,
'category'
,
'assetType'
]]
return
datums
.
to_dict
(
'records'
)
basic/test_case.py
View file @
3d3e4fde
...
...
@@ -2,7 +2,6 @@ import logging
import
unittest
from
typing
import
List
import
pandas
as
pd
from
py_jftech
import
autowired
,
parse_date
,
to_str
from
api
import
Navs
,
Datum
,
PortfoliosRisk
,
DataSync
,
RoboReportor
...
...
config.yml
View file @
3d3e4fde
...
...
@@ -35,6 +35,12 @@ py-jftech:
user
:
${MYSQL_USER:root}
password
:
${MYSQL_PWD:123456}
dbname
:
${MYSQL_DBNAME:jftech_robo}
database-2
:
host
:
106.14.56.221
port
:
3306
user
:
robo_user
password
:
robo2.1@20220521
dbname
:
robo_ft_fund_real
injectable
:
types
:
api.PortfoliosBuilder
:
portfolios.builder.PoemPortfoliosBuilder
...
...
@@ -48,15 +54,34 @@ basic: # 基础信息模块
sync
:
start-date
:
2007-01-01
# 同步数据开始日期
datum
:
# 资料模块
change
:
date
:
${DATUM_CHANGE_DATE}
file
:
${DATUM_CHANGE_FILE}
excludes
:
# 排除的资料彭博ticker
-
'
FKUQX
US
Equity'
-
'
FTAAUSH
LX
Equity'
-
'
FTJAPAU
LX
Equity'
-
'
TEGAUH1
LX
Equity'
-
'
TMEEAAU
LX
Equity'
-
'
TEUSAAU
LX
Equity'
-
'
FTEAUH1
LX
Equity'
-
'
TFIAAUS
LX
Equity'
backtest
:
-
'
FKUQX
US
Equity'
-
'
FTAAUSH
LX
Equity'
-
'
FTJAPAU
LX
Equity'
-
'
TEGAUH1
LX
Equity'
-
'
TMEEAAU
LX
Equity'
-
'
TEUSAAU
LX
Equity'
-
'
FTEAUH1
LX
Equity'
-
'
TFIAAUS
LX
Equity'
real
:
-
'
FGFSACU
LX
Equity'
-
'
TMEEAAU
LX
Equity'
-
'
FTEAMUH
LX
Equity'
-
'
FKUTX
US
Equity'
-
'
TEMUSGI
LX
Equity'
-
'
TEMFIAI
LX
Equity'
-
'
TEMGROA
LX
Equity'
-
'
TEMFMEA
LX
Equity'
-
'
TEMDGAA
LX
Equity'
-
'
TEMJAAU
LX
Equity'
-
'
TEMFHAC
LX
Equity'
-
'
TEMLATA
LX
Equity'
-
'
LEPEUAA
ID
Equity'
-
'
LGBOAAU
ID
Equity'
navs
:
# 净值模块
exrate
:
# 汇率,如果不开启,整个这块注释掉
-
from
:
EUR
# 需要转换的货币类型
...
...
@@ -139,7 +164,7 @@ rebalance: # 再平衡模块
signals
:
# 信号相关
crisis-signal
:
# 危机信号相关
exp-years
:
3
# 预警期时长,单位自然年,点到点计算
exp-init
:
20
08-01-01
# 设置起始危机预警开始时间,如果关闭初始预警起,注释到这一条即可
exp-init
:
20
22-03-04
# 设置起始危机预警开始时间,如果关闭初始预警起,注释到这一条即可
inversion-years
:
1
# 利率倒挂计算时长,单位自然年,点到点取值
inversion-threshold
:
0.3
# 利率倒挂触发阀值
crisis-1
:
# 危机1相关
...
...
@@ -211,7 +236,7 @@ reports: # 报告模块相关
backtest
:
# 回测导出曹策略
exist-build
:
on
# 如果报告文件存在,是否重新构建文件
save-path
:
${EXPORT_PATH:excels}
# 导出报告文件存放路径,如果以./或者../开头,则会以执行python文件为根目录,如果以/开头,则为系统绝对路径,否则,以项目目录为根目录
file-name
:
${EXPORT_FILENAME:
1323
}
file-name
:
${EXPORT_FILENAME:
real
}
include-report
:
# 需要导出的报告类型列表,下面的顺序,也代表了excel中sheet的顺序
# - funds-report # 基金资料
# - navs-report # 净值报告
...
...
@@ -224,16 +249,16 @@ reports: # 报告模块相关
-
fixed-range-report
# 固定区间收益报告
-
relative-range-report
# 相对区间收益报告
robo-executor
:
# 执行器相关
use
:
${ROBO_EXECUTOR:
backtest
}
# 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
use
:
${ROBO_EXECUTOR:
real
}
# 执行哪个执行器,优先取系统环境变量ROBO_EXECUTOR的值,默认backtest
sync-data
:
${SYNC_DATA:off}
# 是否开启同步资料数据
backtest
:
# 回测执行器相关
start-date
:
20
08-01-02
# 回测起始日期
end-date
:
202
2-11-01
# 回测截止日期
start-date
:
20
22-09-01
# 回测起始日期
end-date
:
202
3-02-20
# 回测截止日期
start-step
:
${BACKTEST_START_STEP:4}
# 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
end-step
:
${BACKTEST_END_STEP:4}
# 回测从哪一步执行完成后结束执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
clean-up
:
o
n
clean-up
:
o
ff
real
:
# 实盘执行器
start-date
:
2022-
11
-01
# 实盘开始时间
start-date
:
2022-
09
-01
# 实盘开始时间
include-date
:
-
2023-02-18
-
2023-03-25
...
...
portfolios/test_case.py
View file @
3d3e4fde
...
...
@@ -29,7 +29,7 @@ class PortfoliosTest(unittest.TestCase):
@
autowired
(
names
=
{
'hold'
:
'next-re'
})
def
test_build_hold
(
self
,
hold
:
PortfoliosHolder
=
None
):
hold
.
build_hold_portfolio
(
parse_date
(
'202
2-11-01
'
),
PortfoliosRisk
.
FT9
)
hold
.
build_hold_portfolio
(
parse_date
(
'202
3-02-23
'
),
PortfoliosRisk
.
FT9
)
@
autowired
(
names
=
{
'reportor'
:
'hold-report'
})
def
test_hold_report
(
self
,
reportor
:
RoboReportor
=
None
):
...
...
rebalance/test_case.py
View file @
3d3e4fde
import
logging
import
unittest
from
dateutil.relativedelta
import
relativedelta
from
dateutil.relativedelta
import
relativedelta
from
py_jftech
import
autowired
,
parse_date
,
to_str
,
next_workday
from
api
import
RebalanceSignal
,
PortfoliosRisk
,
RebalanceRuler
,
RoboReportor
...
...
robo_executor.py
View file @
3d3e4fde
...
...
@@ -174,18 +174,17 @@ class RealExecutor(RoboExecutor):
for
sync
in
self
.
_syncs
:
sync
.
do_sync
()
date
=
self
.
curt_date
for
date
in
pd
.
date_range
(
start
=
'2008-01-17'
,
end
=
'2008-01-20'
):
if
is_workday
(
date
)
or
date
in
self
.
include_date
:
date
=
prev_workday
(
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
()
# 因为每天都必须有NORMAL最优投组,不管用不用
self
.
_builder
.
get_portfolios
(
date
,
risk
)
self
.
_hold
.
build_hold_portfolio
(
date
,
risk
)
self
.
_ruler
.
take_next_signal
(
date
,
risk
)
# 如果当前持仓为风控投组,则还要计算风控投组,不管用不用
p_type
=
self
.
_hold
.
get_portfolio_type
(
date
,
risk
)
if
p_type
is
not
PortfoliosType
.
NORMAL
:
self
.
_builder
.
get_portfolios
(
date
,
risk
,
type
=
p_type
)
logger
.
info
(
f
"build risk[{risk.name}] real for date[{format_date(date)}] success, use[{(dt.now() - now).seconds}s]"
)
if
is_workday
(
date
)
or
date
in
self
.
include_date
:
date
=
prev_workday
(
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
()
# 因为每天都必须有NORMAL最优投组,不管用不用
self
.
_builder
.
get_portfolios
(
date
,
risk
)
self
.
_hold
.
build_hold_portfolio
(
date
,
risk
)
self
.
_ruler
.
take_next_signal
(
date
,
risk
)
# 如果当前持仓为风控投组,则还要计算风控投组,不管用不用
p_type
=
self
.
_hold
.
get_portfolio_type
(
date
,
risk
)
if
p_type
is
not
PortfoliosType
.
NORMAL
:
self
.
_builder
.
get_portfolios
(
date
,
risk
,
type
=
p_type
)
logger
.
info
(
f
"build risk[{risk.name}] real for date[{format_date(date)}] success, use[{(dt.now() - now).seconds}s]"
)
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