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
3dc3238f
Commit
3dc3238f
authored
Dec 07, 2022
by
jichao
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
导出模块完毕
parent
61860c1c
Hide whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
100 additions
and
64 deletions
+100
-64
api.py
api.py
+27
-21
robo_eco_datas.py
basic/dao/robo_eco_datas.py
+6
-4
datum.py
basic/datum.py
+6
-12
navs.py
basic/navs.py
+20
-5
sync.py
basic/sync.py
+6
-3
config.yml
config.yml
+2
-2
solver.py
portfolios/solver.py
+1
-1
robo_rebalance_signal.py
rebalance/dao/robo_rebalance_signal.py
+1
-1
crisis_signal.py
rebalance/signals/crisis_signal.py
+7
-7
test_case.py
rebalance/test_case.py
+24
-8
No files found.
api.py
View file @
3dc3238f
...
...
@@ -98,26 +98,14 @@ class Datum(ABC):
@
abstractmethod
def
get_datums
(
self
,
type
:
DatumType
=
None
,
crncy
=
None
,
risk
=
None
,
datum_ids
=
None
,
ticker
=
None
):
pass
@
abstractmethod
def
get_fund_datums
(
self
,
crncy
=
None
,
risk
=
None
,
fund_ids
=
None
):
'''
获取基金资料数据
:param crncy: 货币类型
:param risk: 风险等级,e.g: 1-5
:param fund_ids: 基金ID列表
:return:基金资料信息
'''
pass
@
abstractmethod
def
get_index_datums
(
self
,
ticker
=
None
,
index_ids
=
None
):
'''
获取指标资料数据
:param ticker: 指标的彭博ticker
:param index_ids: 指标id列表
:return: 即表资料信息
获取资料信息,当id和ticker都有时,取二者并集
:param type: 资料类型
:param crncy: 货币类型,仅对基金资料有效
:param risk: 风险等级,仅对基金资料有效
:param datum_ids: 资料ID列表
:param ticker: 资料ticker列表
:return: 资料信息数据
'''
pass
...
...
@@ -181,11 +169,29 @@ class Navs(ABC):
pass
@
abstractmethod
def
get_eco_values
(
self
,
datum_ids
=
None
,
min_date
=
None
,
max_date
=
None
,
ticker
=
None
):
def
get_eco_values
(
self
,
datum_ids
=
None
,
min_date
=
None
,
max_date
=
None
,
ticker
=
None
,
by_release_date
=
False
):
'''
获取经济指标数据,若同时给出ID,和ticker,则取二者并集
:param datum_ids: 经济指标id
:param min_date: 起始日期
:param max_date: 截止日期
:param ticker: 经济指标ticker
:param by_release_date: 如果为True,则使用公告日期查询,否则使用抓取日期
:return: 经济指标的值,包括查询日期,指标和公告日期
'''
pass
@
abstractmethod
def
get_last_eco_values
(
self
,
max_date
,
datum_id
=
None
,
ticker
=
None
,
count
=
1
):
def
get_last_eco_values
(
self
,
max_date
,
datum_id
=
None
,
ticker
=
None
,
count
=
1
,
by_release_date
=
False
):
'''
获取指定资料或ticker,指定日期之前最后count个指标数据,当指定datum_id后,ticker参数无效
:param max_date: 指定日期
:param datum_id: 指标id,只能指定一个
:param ticker: 指标ticker,只能指定一个,当指标id有值后,该参数无效
:param count: 指定要返回数据的个数
:param by_release_date: 如果为True,则使用公告日期查询,否则使用抓取日期
:return: 如果存在,则返回指定日期最后count个指标项(查询日期,指标,公告日期),否则返回None
'''
pass
...
...
basic/dao/robo_eco_datas.py
View file @
3dc3238f
...
...
@@ -29,8 +29,9 @@ def get_list(eco_ids=None, min_date=None, max_date=None):
@
read
(
one
=
True
)
def
get_last_one
(
eco_id
,
max_date
=
None
):
sql
=
f
"red_date <= '{format_date(max_date)}'"
if
max_date
else
None
def
get_last_one
(
eco_id
,
max_date
=
None
,
by_release_date
=
False
):
date_field
=
'red_release_date'
if
by_release_date
else
'red_date'
sql
=
f
"{date_field} <= '{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_eco_datas
{where(sql, red_eco_id=eco_id)} order by red_date desc limit 1
...
...
@@ -38,8 +39,9 @@ def get_last_one(eco_id, max_date=None):
@
read
def
get_last
(
eco_id
,
max_date
=
None
,
count
=
1
):
sql
=
f
"red_date <= '{format_date(max_date)}'"
if
max_date
else
None
def
get_last
(
eco_id
,
max_date
=
None
,
count
=
1
,
by_release_date
=
False
):
date_field
=
'red_release_date'
if
by_release_date
else
'red_date'
sql
=
f
"{date_field} <= '{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_eco_datas
{where(sql, red_eco_id=eco_id)} order by red_date desc limit {count}
...
...
basic/datum.py
View file @
3dc3238f
import
json
from
py_jftech
import
component
,
parse_date
,
get_config
from
py_jftech
import
component
,
parse_date
,
get_config
,
to_tuple
from
api
import
DatumType
,
Datum
,
PortfoliosRisk
from
basic.dao
import
robo_base_datum
as
rbd
...
...
@@ -25,20 +25,14 @@ class DefaultDatum(Datum):
return
datum
def
get_datums
(
self
,
type
:
DatumType
=
None
,
crncy
=
None
,
risk
=
None
,
datum_ids
=
None
,
ticker
=
None
):
result
=
rbd
.
get_base_datums
(
type
=
type
,
crncy
=
crncy
,
risk
=
risk
,
datum_ids
=
datum_ids
,
ticker
=
ticker
)
datum_ids
=
to_tuple
(
datum_ids
)
if
ticker
:
datums
=
rbd
.
get_base_datums
(
type
=
type
,
ticker
=
ticker
)
datum_ids
=
tuple
(
set
(
list
(
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
]
def
get_fund_datums
(
self
,
crncy
=
None
,
risk
=
None
,
fund_ids
=
None
):
result
=
rbd
.
get_base_datums
(
type
=
DatumType
.
FUND
,
crncy
=
crncy
,
risk
=
risk
,
datum_ids
=
fund_ids
)
result
=
[{
**
json
.
loads
(
x
[
'datas'
]),
'id'
:
x
[
'id'
]}
for
x
in
result
]
return
[{
**
x
,
'inceptDate'
:
parse_date
(
x
[
'inceptDate'
])}
for
x
in
result
if
x
[
'bloombergTicker'
]
not
in
self
.
excludes
]
def
get_index_datums
(
self
,
ticker
=
None
,
index_ids
=
None
):
result
=
rbd
.
get_base_datums
(
type
=
DatumType
.
INDEX
,
ticker
=
ticker
,
datum_ids
=
index_ids
)
return
[{
**
json
.
loads
(
x
[
'datas'
]),
'id'
:
x
[
'id'
]}
for
x
in
result
]
def
get_high_risk_datums
(
self
,
risk
:
PortfoliosRisk
):
risk3
=
self
.
get_datums
(
type
=
DatumType
.
FUND
,
risk
=
3
)
if
risk
is
PortfoliosRisk
.
FT3
:
...
...
basic/navs.py
View file @
3dc3238f
...
...
@@ -2,7 +2,7 @@ import pandas as pd
from
py_jftech
import
get_config
,
component
,
autowired
,
to_tuple
from
api
import
Navs
,
Datum
,
DatumType
from
basic.dao
import
robo_exrate
as
re
,
robo_fund_navs
as
rfn
,
robo_index_datas
as
rid
from
basic.dao
import
robo_exrate
as
re
,
robo_fund_navs
as
rfn
,
robo_index_datas
as
rid
,
robo_eco_datas
as
red
@
component
...
...
@@ -66,8 +66,23 @@ class DefaultNavs(Navs):
'close'
:
x
[
'close'
]
}
for
x
in
last
]
if
last
else
None
def
get_eco_values
(
self
,
datum_ids
=
None
,
min_date
=
None
,
max_date
=
None
,
ticker
=
None
):
pass
def
get_eco_values
(
self
,
datum_ids
=
None
,
min_date
=
None
,
max_date
=
None
,
ticker
=
None
,
by_release_date
=
False
):
datum_ids
=
to_tuple
(
datum_ids
)
if
ticker
:
datums
=
self
.
_datum
.
get_datums
(
type
=
DatumType
.
INDEX
,
ticker
=
ticker
)
datum_ids
=
tuple
(
set
(
list
(
datum_ids
or
[])
|
{
x
[
'id'
]
for
x
in
datums
}))
return
red
.
get_list
(
eco_ids
=
datum_ids
,
min_date
=
min_date
,
max_date
=
max_date
,
by_release_date
=
by_release_date
)
def
get_last_eco_values
(
self
,
max_date
,
datum_id
=
None
,
ticker
=
None
,
count
=
1
,
by_release_date
=
False
):
if
not
datum_id
:
assert
ticker
,
"get last eco close, datum_id and ticker give at least one"
datum
=
self
.
_datum
.
get_datums
(
type
=
DatumType
.
ECO
,
ticker
=
ticker
)
datum_id
=
datum
[
0
][
'id'
]
if
datum
else
None
assert
datum_id
,
"get last eco close, datum id is not found"
assert
max_date
,
"get last eco close, start_date is not found"
if
count
==
1
:
return
red
.
get_last_one
(
eco_id
=
datum_id
,
max_date
=
max_date
,
by_release_date
=
by_release_date
)
else
:
return
red
.
get_last
(
eco_id
=
datum_id
,
max_date
=
max_date
,
count
=
count
,
by_release_date
=
by_release_date
)
def
get_last_eco_values
(
self
,
max_date
,
datum_id
=
None
,
ticker
=
None
,
count
=
1
):
pass
basic/sync.py
View file @
3dc3238f
...
...
@@ -91,7 +91,8 @@ class IndexSync(JDCDataSync):
'pb'
:
x
[
'pbRatio'
]
if
'pbRatio'
in
x
else
None
,
'volume'
:
x
[
'volume'
]
if
'volume'
in
x
else
None
,
}
for
x
in
datas
if
is_workday
(
dt
.
fromtimestamp
(
x
[
'date'
]
/
1000
))
and
'close'
in
x
]
rid
.
batch_insert
(
save_datas
)
if
save_datas
:
rid
.
batch_insert
(
save_datas
)
@
component
(
bean_name
=
'eco-sync'
)
...
...
@@ -115,7 +116,8 @@ class EcoSync(JDCDataSync):
'indicator'
:
x
[
'close'
],
'release_date'
:
dt
.
fromtimestamp
(
x
[
'releaseDate'
]
/
1000
),
}
for
x
in
datas
if
'releaseDate'
in
x
]
red
.
batch_insert
(
save_datas
)
if
save_datas
:
red
.
batch_insert
(
save_datas
)
@
component
(
bean_name
=
'navs-sync'
)
...
...
@@ -160,4 +162,5 @@ class FundNavSync(JDCDataSync):
'div_p'
:
x
[
'postDividend'
]
if
'postDividend'
in
x
else
0
,
'nav_cal'
:
x
[
'calibrateValue'
]
}
for
x
in
datas
if
is_workday
(
dt
.
fromtimestamp
(
x
[
'date'
]
/
1000
))]
rfn
.
batch_insert
(
save_navs
)
if
save_navs
:
rfn
.
batch_insert
(
save_navs
)
config.yml
View file @
3dc3238f
...
...
@@ -212,8 +212,8 @@ robo-executor: # 执行器相关
backtest
:
# 回测执行器相关
start-date
:
2008-01-02
# 回测起始日期
end-date
:
2022-11-01
# 回测截止日期
start-step
:
3
# 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
clean-up
:
o
ff
start-step
:
4
# 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
clean-up
:
o
n
real
:
# 实盘执行器
start-date
:
2022-11-01
# 实盘开始时间
...
...
portfolios/solver.py
View file @
3dc3238f
...
...
@@ -210,7 +210,7 @@ class DefaultSolver(Solver):
def
reset_navs
(
self
,
day
):
asset_ids
=
self
.
_assets
.
get_pool
(
day
)
asset_risk
=
self
.
get_config
(
'navs.risk'
)
datum
=
self
.
_datum
.
get_datums
(
type
=
DatumType
.
FUND
,
fund
_ids
=
asset_ids
,
risk
=
asset_risk
)
datum
=
self
.
_datum
.
get_datums
(
type
=
DatumType
.
FUND
,
datum
_ids
=
asset_ids
,
risk
=
asset_risk
)
exclude
=
self
.
get_config
(
'navs.exclude-asset-type'
)
or
[]
asset_ids
=
list
(
set
(
asset_ids
)
&
set
([
x
[
'id'
]
for
x
in
datum
if
x
[
'assetType'
]
not
in
exclude
]))
...
...
rebalance/dao/robo_rebalance_signal.py
View file @
3dc3238f
...
...
@@ -48,7 +48,7 @@ def get_one(type: SignalType, risk: PortfoliosRisk, date):
def
get_first_after
(
type
:
SignalType
,
risk
:
PortfoliosRisk
,
min_date
,
effective
=
None
):
return
f
'''
select {','.join([f"{x[0]} as {x[1]}" for x in __COLUMNS__.items()])} from robo_rebalance_signal
{where(f"rrs_date >=
{format_date(min_date)}
", rrs_type=type, rrs_risk=risk, rrs_effective=effective)} order by rrs_date limit 1
{where(f"rrs_date >=
'{format_date(min_date)}'
", rrs_type=type, rrs_risk=risk, rrs_effective=effective)} order by rrs_date limit 1
'''
...
...
rebalance/signals/crisis_signal.py
View file @
3dc3238f
...
...
@@ -109,14 +109,14 @@ class CrisisTwoSignal(CrisisSignal, BaseRebalanceSignal):
if
not
crisis_two
:
ng_date
=
day
-
relativedelta
(
years
=
self
.
negative_growth_years
)
ten_today
=
self
.
_navs
.
get_last_index_close
(
max_date
=
day
,
ticker
=
'USGG10YR Index'
)
cpi_today
=
self
.
_navs
.
get_last_
index_close
(
max_date
=
day
,
ticker
=
'CPI YOY Index'
)
cpi_today
=
self
.
_navs
.
get_last_
eco_values
(
max_date
=
day
,
ticker
=
'CPI YOY Index'
,
by_release_date
=
True
)
ten_before
=
self
.
_navs
.
get_last_index_close
(
max_date
=
ng_date
,
ticker
=
'USGG10YR Index'
)
cpi_before
=
self
.
_navs
.
get_last_
index_close
(
max_date
=
ng_date
,
ticker
=
'CPI YOY Index'
)
before
=
ten_before
[
'close'
]
-
cpi_before
[
'
close
'
]
today
=
ten_today
[
'close'
]
-
cpi_today
[
'
close
'
]
cpi_before
=
self
.
_navs
.
get_last_
eco_values
(
max_date
=
ng_date
,
ticker
=
'CPI YOY Index'
,
by_release_date
=
True
)
before
=
ten_before
[
'close'
]
-
cpi_before
[
'
indicator
'
]
today
=
ten_today
[
'close'
]
-
cpi_today
[
'
indicator
'
]
fed_today
=
self
.
_navs
.
get_last_
index_close
(
max_date
=
day
,
ticker
=
'FDTR Index'
)
fed_before
=
self
.
_navs
.
get_last_
index_close
(
max_date
=
day
-
relativedelta
(
months
=
self
.
fed_months
),
ticker
=
'FDTR Index'
)
fed_today
=
self
.
_navs
.
get_last_
eco_values
(
max_date
=
day
,
ticker
=
'FDTR Index'
,
by_release_date
=
True
)
fed_before
=
self
.
_navs
.
get_last_
eco_values
(
max_date
=
day
-
relativedelta
(
months
=
self
.
fed_months
),
ticker
=
'FDTR Index'
,
by_release_date
=
True
)
return
today
<=
before
and
fed_today
[
'
close'
]
-
fed_before
[
'close
'
]
<
self
.
fed_threshold
return
today
<=
before
and
fed_today
[
'
indicator'
]
-
fed_before
[
'indicator
'
]
<
self
.
fed_threshold
return
False
rebalance/test_case.py
View file @
3dc3238f
import
logging
import
unittest
from
dateutil.relativedelta
import
relativedelta
from
py_jftech
import
autowired
,
parse_date
,
to_str
from
py_jftech
import
autowired
,
parse_date
,
to_str
,
next_workday
from
api
import
RebalanceSignal
,
PortfoliosRisk
,
RebalanceRuler
,
RoboReportor
logger
=
logging
.
getLogger
(
__name__
)
class
RebalanceTest
(
unittest
.
TestCase
):
logger
=
logging
.
getLogger
(
__name__
)
class
RebalanceTest
(
unittest
.
TestCase
):
@
autowired
(
names
=
{
'builder'
:
'crisis_one'
})
def
test_crisis_one
(
self
,
builder
:
RebalanceSignal
=
None
):
signal
=
builder
.
get_signal
(
parse_date
(
'2022-10-13'
),
PortfoliosRisk
.
FT9
)
self
.
logger
.
info
(
signal
)
start
=
parse_date
(
'2018-07-06'
)
end
=
start
+
relativedelta
(
years
=
3
)
while
start
<
end
:
signal
=
builder
.
is_trigger
(
start
,
PortfoliosRisk
.
FT9
)
if
signal
:
logger
.
info
(
start
)
start
=
next_workday
(
start
)
@
autowired
(
names
=
{
'builder'
:
'crisis_two'
})
def
test_crisis_two
(
self
,
builder
:
RebalanceSignal
=
None
):
start
=
parse_date
(
'2018-07-06'
)
end
=
start
+
relativedelta
(
years
=
3
)
while
start
<
end
:
signal
=
builder
.
is_trigger
(
start
,
PortfoliosRisk
.
FT9
)
if
signal
:
logger
.
info
(
start
)
start
=
next_workday
(
start
)
@
autowired
(
names
=
{
'builder'
:
'market-right'
})
def
test_market_right
(
self
,
builder
:
RebalanceSignal
=
None
):
signal
=
builder
.
get_signal
(
parse_date
(
'2022-10-13'
),
PortfoliosRisk
.
FT9
)
self
.
logger
.
info
(
signal
)
logger
.
info
(
signal
)
@
autowired
(
names
=
{
'builder'
:
'curve-drift'
})
def
test_curve_drift
(
self
,
builder
:
RebalanceSignal
=
None
):
signal
=
builder
.
get_signal
(
parse_date
(
'2022-11-07'
),
PortfoliosRisk
.
FT3
)
self
.
logger
.
info
(
signal
)
logger
.
info
(
signal
)
@
autowired
(
names
=
{
'builder'
:
'high-buy'
})
def
test_high_buy
(
self
,
builder
:
RebalanceSignal
=
None
):
...
...
@@ -36,7 +52,7 @@ class RebalanceTest(unittest.TestCase):
@
autowired
(
names
=
{
'reportor'
:
'signal-report'
})
def
test_signal_report
(
self
,
reportor
:
RoboReportor
=
None
):
result
=
reportor
.
load_report
()
self
.
logger
.
info
(
to_str
(
result
,
show_line
=
10
))
logger
.
info
(
to_str
(
result
,
show_line
=
10
))
if
__name__
==
'__main__'
:
...
...
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