Commit 3dc3238f authored by jichao's avatar jichao

导出模块完毕

parent 61860c1c
...@@ -98,26 +98,14 @@ class Datum(ABC): ...@@ -98,26 +98,14 @@ class Datum(ABC):
@abstractmethod @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):
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):
''' '''
获取指标资料数据 获取资料信息,当id和ticker都有时,取二者并集
:param ticker: 指标的彭博ticker :param type: 资料类型
:param index_ids: 指标id列表 :param crncy: 货币类型,仅对基金资料有效
:return: 即表资料信息 :param risk: 风险等级,仅对基金资料有效
:param datum_ids: 资料ID列表
:param ticker: 资料ticker列表
:return: 资料信息数据
''' '''
pass pass
...@@ -181,11 +169,29 @@ class Navs(ABC): ...@@ -181,11 +169,29 @@ class Navs(ABC):
pass pass
@abstractmethod @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 pass
@abstractmethod @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 pass
......
...@@ -29,8 +29,9 @@ def get_list(eco_ids=None, min_date=None, max_date=None): ...@@ -29,8 +29,9 @@ def get_list(eco_ids=None, min_date=None, max_date=None):
@read(one=True) @read(one=True)
def get_last_one(eco_id, max_date=None): def get_last_one(eco_id, max_date=None, by_release_date=False):
sql = f"red_date <= '{format_date(max_date)}'" if max_date else None 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''' return f'''
select {','.join([f"{x[0]} as {x[1]}" for x in __COLUMNS__.items()])} from robo_eco_datas 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 {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): ...@@ -38,8 +39,9 @@ def get_last_one(eco_id, max_date=None):
@read @read
def get_last(eco_id, max_date=None, count=1): def get_last(eco_id, max_date=None, count=1, by_release_date=False):
sql = f"red_date <= '{format_date(max_date)}'" if max_date else None 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''' return f'''
select {','.join([f"{x[0]} as {x[1]}" for x in __COLUMNS__.items()])} from robo_eco_datas 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} {where(sql, red_eco_id=eco_id)} order by red_date desc limit {count}
......
import json 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 api import DatumType, Datum, PortfoliosRisk
from basic.dao import robo_base_datum as rbd from basic.dao import robo_base_datum as rbd
...@@ -25,20 +25,14 @@ class DefaultDatum(Datum): ...@@ -25,20 +25,14 @@ class DefaultDatum(Datum):
return 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):
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] 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 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): def get_high_risk_datums(self, risk: PortfoliosRisk):
risk3 = self.get_datums(type=DatumType.FUND, risk=3) risk3 = self.get_datums(type=DatumType.FUND, risk=3)
if risk is PortfoliosRisk.FT3: if risk is PortfoliosRisk.FT3:
......
...@@ -2,7 +2,7 @@ import pandas as pd ...@@ -2,7 +2,7 @@ import pandas as pd
from py_jftech import get_config, component, autowired, to_tuple from py_jftech import get_config, component, autowired, to_tuple
from api import Navs, Datum, DatumType 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 @component
...@@ -66,8 +66,23 @@ class DefaultNavs(Navs): ...@@ -66,8 +66,23 @@ class DefaultNavs(Navs):
'close': x['close'] 'close': x['close']
} for x in last] if last else None } for x in last] if last else None
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):
pass 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
...@@ -91,6 +91,7 @@ class IndexSync(JDCDataSync): ...@@ -91,6 +91,7 @@ class IndexSync(JDCDataSync):
'pb': x['pbRatio'] if 'pbRatio' in x else None, 'pb': x['pbRatio'] if 'pbRatio' in x else None,
'volume': x['volume'] if 'volume' 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] } for x in datas if is_workday(dt.fromtimestamp(x['date'] / 1000)) and 'close' in x]
if save_datas:
rid.batch_insert(save_datas) rid.batch_insert(save_datas)
...@@ -115,6 +116,7 @@ class EcoSync(JDCDataSync): ...@@ -115,6 +116,7 @@ class EcoSync(JDCDataSync):
'indicator': x['close'], 'indicator': x['close'],
'release_date': dt.fromtimestamp(x['releaseDate'] / 1000), 'release_date': dt.fromtimestamp(x['releaseDate'] / 1000),
} for x in datas if 'releaseDate' in x] } for x in datas if 'releaseDate' in x]
if save_datas:
red.batch_insert(save_datas) red.batch_insert(save_datas)
...@@ -160,4 +162,5 @@ class FundNavSync(JDCDataSync): ...@@ -160,4 +162,5 @@ class FundNavSync(JDCDataSync):
'div_p': x['postDividend'] if 'postDividend' in x else 0, 'div_p': x['postDividend'] if 'postDividend' in x else 0,
'nav_cal': x['calibrateValue'] 'nav_cal': x['calibrateValue']
} for x in datas if is_workday(dt.fromtimestamp(x['date'] / 1000))] } for x in datas if is_workday(dt.fromtimestamp(x['date'] / 1000))]
if save_navs:
rfn.batch_insert(save_navs) rfn.batch_insert(save_navs)
...@@ -212,8 +212,8 @@ robo-executor: # 执行器相关 ...@@ -212,8 +212,8 @@ robo-executor: # 执行器相关
backtest: # 回测执行器相关 backtest: # 回测执行器相关
start-date: 2008-01-02 # 回测起始日期 start-date: 2008-01-02 # 回测起始日期
end-date: 2022-11-01 # 回测截止日期 end-date: 2022-11-01 # 回测截止日期
start-step: 3 # 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组 start-step: 4 # 回测从哪一步开始执行 1:计算资产ewma;2:计算资产池;3:计算最优投组:4:计算再平衡信号以及持仓投组
clean-up: off clean-up: on
real: # 实盘执行器 real: # 实盘执行器
start-date: 2022-11-01 # 实盘开始时间 start-date: 2022-11-01 # 实盘开始时间
......
...@@ -210,7 +210,7 @@ class DefaultSolver(Solver): ...@@ -210,7 +210,7 @@ class DefaultSolver(Solver):
def reset_navs(self, day): def reset_navs(self, day):
asset_ids = self._assets.get_pool(day) asset_ids = self._assets.get_pool(day)
asset_risk = self.get_config('navs.risk') 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 [] 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])) asset_ids = list(set(asset_ids) & set([x['id'] for x in datum if x['assetType'] not in exclude]))
......
...@@ -48,7 +48,7 @@ def get_one(type: SignalType, risk: PortfoliosRisk, date): ...@@ -48,7 +48,7 @@ def get_one(type: SignalType, risk: PortfoliosRisk, date):
def get_first_after(type: SignalType, risk: PortfoliosRisk, min_date, effective=None): def get_first_after(type: SignalType, risk: PortfoliosRisk, min_date, effective=None):
return f''' return f'''
select {','.join([f"{x[0]} as {x[1]}" for x in __COLUMNS__.items()])} from robo_rebalance_signal 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
''' '''
......
...@@ -109,14 +109,14 @@ class CrisisTwoSignal(CrisisSignal, BaseRebalanceSignal): ...@@ -109,14 +109,14 @@ class CrisisTwoSignal(CrisisSignal, BaseRebalanceSignal):
if not crisis_two: if not crisis_two:
ng_date = day - relativedelta(years=self.negative_growth_years) ng_date = day - relativedelta(years=self.negative_growth_years)
ten_today = self._navs.get_last_index_close(max_date=day, ticker='USGG10YR Index') 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') 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') 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['close'] before = ten_before['close'] - cpi_before['indicator']
today = ten_today['close'] - cpi_today['close'] today = ten_today['close'] - cpi_today['indicator']
fed_today = self._navs.get_last_index_close(max_date=day, 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_index_close(max_date=day - relativedelta(months=self.fed_months), ticker='FDTR Index') 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 return False
import logging import logging
import unittest 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 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'}) @autowired(names={'builder': 'crisis_one'})
def test_crisis_one(self, builder: RebalanceSignal = None): def test_crisis_one(self, builder: RebalanceSignal = None):
signal = builder.get_signal(parse_date('2022-10-13'), PortfoliosRisk.FT9) start = parse_date('2018-07-06')
self.logger.info(signal) 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'}) @autowired(names={'builder': 'market-right'})
def test_market_right(self, builder: RebalanceSignal = None): def test_market_right(self, builder: RebalanceSignal = None):
signal = builder.get_signal(parse_date('2022-10-13'), PortfoliosRisk.FT9) signal = builder.get_signal(parse_date('2022-10-13'), PortfoliosRisk.FT9)
self.logger.info(signal) logger.info(signal)
@autowired(names={'builder': 'curve-drift'}) @autowired(names={'builder': 'curve-drift'})
def test_curve_drift(self, builder: RebalanceSignal = None): def test_curve_drift(self, builder: RebalanceSignal = None):
signal = builder.get_signal(parse_date('2022-11-07'), PortfoliosRisk.FT3) signal = builder.get_signal(parse_date('2022-11-07'), PortfoliosRisk.FT3)
self.logger.info(signal) logger.info(signal)
@autowired(names={'builder': 'high-buy'}) @autowired(names={'builder': 'high-buy'})
def test_high_buy(self, builder: RebalanceSignal = None): def test_high_buy(self, builder: RebalanceSignal = None):
...@@ -36,7 +52,7 @@ class RebalanceTest(unittest.TestCase): ...@@ -36,7 +52,7 @@ class RebalanceTest(unittest.TestCase):
@autowired(names={'reportor': 'signal-report'}) @autowired(names={'reportor': 'signal-report'})
def test_signal_report(self, reportor: RoboReportor = None): def test_signal_report(self, reportor: RoboReportor = None):
result = reportor.load_report() result = reportor.load_report()
self.logger.info(to_str(result, show_line=10)) logger.info(to_str(result, show_line=10))
if __name__ == '__main__': if __name__ == '__main__':
......
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