Commit bf40eee2 authored by 纪超's avatar 纪超

添加信号模块

parent 4fa60d2f
...@@ -367,7 +367,7 @@ class DriftSolver(ABC): ...@@ -367,7 +367,7 @@ class DriftSolver(ABC):
pass pass
class SignalBuilder(ABC): class RebalanceSignal(ABC):
''' '''
控制信号,发起是否调仓服务 控制信号,发起是否调仓服务
''' '''
...@@ -386,17 +386,26 @@ class SignalBuilder(ABC): ...@@ -386,17 +386,26 @@ class SignalBuilder(ABC):
pass pass
class RebalanceBuilder(ABC): class RebalanceRuler(ABC):
''' '''
再平衡构建器 再平衡信号分配器,根据既定的规则,再众多信号中,选出进行再平衡的信号
''' '''
@abstractmethod @abstractmethod
def get_rebalance(self, day, risk: PortfoliosRisk): def take_next_signal(self, day, risk: PortfoliosRisk):
''' '''
获取指定日期,指定风险等级的再平衡数据 取出指定日期,指定风险等级的再平衡信号数据,注意取出消费后,无法退回,非幂等函数
:param day: 指定日期 :param day: 指定日期
:param risk: 指定风险等级 :param risk: 指定风险等级
:return: 再平衡数据 :return: 如果存在,则返回取出的再平衡信号信息,否则返回None
'''
pass
@abstractmethod
def cancel_signal(self, sign_id):
'''
取消信号ID为已消费状态,即设置信号为未消费状态
:param sign_id: 信号ID
:return 取消成功则返回True, 否则返回False
''' '''
pass pass
...@@ -76,6 +76,7 @@ asset-pool: ...@@ -76,6 +76,7 @@ asset-pool:
portfolios: portfolios:
holder: holder:
init-nav: 100 init-nav: 100
min-interval-days: 10
solver: solver:
tol: 1E-10 tol: 1E-10
navs: navs:
...@@ -119,7 +120,7 @@ rebalance: ...@@ -119,7 +120,7 @@ rebalance:
init-factor: 0.000000002 init-factor: 0.000000002
high-weight: high-weight:
coef: 0.2 coef: 0.2
builder: ruler:
disable-period: #自然日 disable-period: #自然日
normal: 10 normal: 10
crisis_1: 15 crisis_1: 15
......
from framework import component, autowired from framework import component, autowired
from api import PortfoliosHolder, PortfoliosRisk, RebalanceBuilder from api import PortfoliosHolder, PortfoliosRisk, RebalanceRuler
from portfolios.dao import robo_hold_portfolios as rhp from portfolios.dao import robo_hold_portfolios as rhp
import json import json
...@@ -8,7 +8,7 @@ import json ...@@ -8,7 +8,7 @@ import json
class NextReblanceHolder(PortfoliosHolder): class NextReblanceHolder(PortfoliosHolder):
@autowired @autowired
def __init__(self, rebalance: RebalanceBuilder): def __init__(self, rebalance: RebalanceRuler):
self._rebalance = rebalance self._rebalance = rebalance
def get_portfolios_weight(self, day, risk: PortfoliosRisk): def get_portfolios_weight(self, day, risk: PortfoliosRisk):
......
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from api import SignalBuilder, PortfoliosBuilder, PortfoliosRisk from api import RebalanceSignal, PortfoliosBuilder, PortfoliosRisk
from framework import autowired from framework import autowired
from rebalance.dao import robo_rebalance_signal as rrs from rebalance.dao import robo_rebalance_signal as rrs
class BaseSignalBuilder(SignalBuilder, ABC): class BaseRebalanceSignal(RebalanceSignal, ABC):
@autowired @autowired
def __init__(self, builder: PortfoliosBuilder = None): def __init__(self, builder: PortfoliosBuilder = None):
......
from framework import component, autowired, get_config from framework import component, autowired, get_config
from api import RebalanceBuilder, PortfoliosRisk, SignalBuilder, SignalType, PortfoliosType from api import RebalanceRuler, PortfoliosRisk, RebalanceSignal, SignalType, PortfoliosType
from typing import List from typing import List
from rebalance.dao import robo_rebalance_signal as rrs from rebalance.dao import robo_rebalance_signal as rrs
@component @component
class LevelRebalanceBuilder(RebalanceBuilder): class LevelRebalanceRuler(RebalanceRuler):
'''
定义:
1.定义所有调仓类型为非NORMAL类型的信号为清仓信号
2.定义所有调仓类型为NORMAL类型的信号为加仓信号
3.定义持久信号为上次选用调仓的信号时间到当前时间内,该信号都有效
4.定义临时信号为仅当天有效
规则:
1.所有清仓信号为持久信号,所有加仓信号为临时信号
2.对于持久信号规则如下:
2.1 上一次选用信号到当前时间内,是否有持久信号
2.2 如果有,则看级别是否高于上一次选用信号
2.3 如果高于,则输出该信号
3.如果没有持久信号,则从临时信号中根据级别排序找出第一个,作为输出信号
'''
@autowired @autowired
def __init__(self, signals: List[SignalBuilder] = None): def __init__(self, signals: List[RebalanceSignal] = None):
self._signals = signals self._signals = signals
self._config = get_config(__name__) self._config = get_config(__name__)
...@@ -17,7 +31,7 @@ class LevelRebalanceBuilder(RebalanceBuilder): ...@@ -17,7 +31,7 @@ class LevelRebalanceBuilder(RebalanceBuilder):
result = self._config['disable-period'] result = self._config['disable-period']
return {PortfoliosType(x[0]): x[1] for x in result.items()} return {PortfoliosType(x[0]): x[1] for x in result.items()}
def get_rebalance(self, day, risk: PortfoliosRisk): def take_next_signal(self, day, risk: PortfoliosRisk):
last_re = rrs.get_last_one(max_date=day, risk=risk, effective=True) last_re = rrs.get_last_one(max_date=day, risk=risk, effective=True)
if last_re and last_re['date'] == day: if last_re and last_re['date'] == day:
return last_re return last_re
...@@ -31,4 +45,5 @@ class LevelRebalanceBuilder(RebalanceBuilder): ...@@ -31,4 +45,5 @@ class LevelRebalanceBuilder(RebalanceBuilder):
return use_signal return use_signal
return None return None
def cancel_signal(self, sign_id):
pass
...@@ -5,11 +5,11 @@ from dateutil.relativedelta import relativedelta ...@@ -5,11 +5,11 @@ from dateutil.relativedelta import relativedelta
from api import PortfoliosRisk, SignalType, Navs from api import PortfoliosRisk, SignalType, Navs
from framework import get_config, autowired, component from framework import get_config, autowired, component
from rebalance.base_signal import BaseSignalBuilder from rebalance.base_signal import BaseRebalanceSignal
from rebalance.dao import robo_rebalance_signal as rrs from rebalance.dao import robo_rebalance_signal as rrs
class CrisisSignal(BaseSignalBuilder, ABC): class CrisisSignal(BaseRebalanceSignal, ABC):
@autowired @autowired
def __init__(self, navs: Navs = None): def __init__(self, navs: Navs = None):
...@@ -58,7 +58,7 @@ class CrisisSignal(BaseSignalBuilder, ABC): ...@@ -58,7 +58,7 @@ class CrisisSignal(BaseSignalBuilder, ABC):
@component(bean_name='crisis_one') @component(bean_name='crisis_one')
class CrisisOneSignal(CrisisSignal, BaseSignalBuilder): class CrisisOneSignal(CrisisSignal, BaseRebalanceSignal):
@property @property
def consecut_days(self): def consecut_days(self):
...@@ -84,7 +84,7 @@ class CrisisOneSignal(CrisisSignal, BaseSignalBuilder): ...@@ -84,7 +84,7 @@ class CrisisOneSignal(CrisisSignal, BaseSignalBuilder):
@component(bean_name='crisis_two') @component(bean_name='crisis_two')
class CrisisTwoSignal(CrisisSignal, BaseSignalBuilder): class CrisisTwoSignal(CrisisSignal, BaseRebalanceSignal):
@property @property
def negative_growth_years(self): def negative_growth_years(self):
......
from api import PortfoliosRisk, SignalType, Datum, PortfoliosHolder, DriftSolver from api import PortfoliosRisk, SignalType, Datum, PortfoliosHolder, DriftSolver
from framework import component, autowired, get_config from framework import component, autowired, get_config
from rebalance.base_signal import BaseSignalBuilder from rebalance.base_signal import BaseRebalanceSignal
from rebalance.dao import robo_rebalance_signal as rrs from rebalance.dao import robo_rebalance_signal as rrs
@component(bean_name='curve-drift') @component(bean_name='curve-drift')
class CurveDrift(BaseSignalBuilder): class CurveDrift(BaseRebalanceSignal):
@autowired(names={'solver': 'date-curve'}) @autowired(names={'solver': 'date-curve'})
def __init__(self, datum: Datum = None, hold: PortfoliosHolder = None, solver: DriftSolver = None): def __init__(self, datum: Datum = None, hold: PortfoliosHolder = None, solver: DriftSolver = None):
......
...@@ -2,12 +2,12 @@ import pandas as pd ...@@ -2,12 +2,12 @@ import pandas as pd
from api import PortfoliosBuilder, SignalType, PortfoliosRisk, Datum, DriftSolver from api import PortfoliosBuilder, SignalType, PortfoliosRisk, Datum, DriftSolver
from framework import component, autowired, get_config, filter_weekend, next_workday, is_workday from framework import component, autowired, get_config, filter_weekend, next_workday, is_workday
from rebalance.base_signal import BaseSignalBuilder from rebalance.base_signal import BaseRebalanceSignal
from rebalance.dao import robo_weight_drift as rwd, robo_rebalance_signal as rrs from rebalance.dao import robo_weight_drift as rwd, robo_rebalance_signal as rrs
@component(bean_name='high-buy') @component(bean_name='high-buy')
class HighBuySignal(BaseSignalBuilder): class HighBuySignal(BaseRebalanceSignal):
@autowired(names={'solver': 'high-weight'}) @autowired(names={'solver': 'high-weight'})
def __init__(self, solver: DriftSolver = None): def __init__(self, solver: DriftSolver = None):
......
from api import PortfoliosRisk, SignalType from api import PortfoliosRisk, SignalType
from framework import component from framework import component
from rebalance.base_signal import BaseSignalBuilder from rebalance.base_signal import BaseRebalanceSignal
from rebalance.dao import robo_rebalance_signal as rrs from rebalance.dao import robo_rebalance_signal as rrs
@component(bean_name='init') @component(bean_name='init')
class InitSignalBuilder(BaseSignalBuilder): class InitSignalBuilder(BaseRebalanceSignal):
@property @property
def signal_type(self) -> SignalType: def signal_type(self) -> SignalType:
......
...@@ -3,12 +3,12 @@ from scipy.stats import norm ...@@ -3,12 +3,12 @@ from scipy.stats import norm
from api import SignalType, PortfoliosRisk, Navs from api import SignalType, PortfoliosRisk, Navs
from framework import component, autowired, get_config from framework import component, autowired, get_config
from rebalance.base_signal import BaseSignalBuilder from rebalance.base_signal import BaseRebalanceSignal
from rebalance.dao import robo_rebalance_signal as rrs from rebalance.dao import robo_rebalance_signal as rrs
@component(bean_name='market-right') @component(bean_name='market-right')
class MarketRight(BaseSignalBuilder): class MarketRight(BaseRebalanceSignal):
@autowired @autowired
def __init__(self, navs: Navs = None): def __init__(self, navs: Navs = None):
......
import unittest import unittest
from api import SignalBuilder, PortfoliosRisk, RebalanceBuilder from api import RebalanceSignal, PortfoliosRisk, RebalanceRuler
from framework import autowired, parse_date, get_logger from framework import autowired, parse_date, get_logger
...@@ -9,27 +9,27 @@ class RebalanceTest(unittest.TestCase): ...@@ -9,27 +9,27 @@ class RebalanceTest(unittest.TestCase):
logger = get_logger(__name__) logger = get_logger(__name__)
@autowired(names={'builder': 'crisis_one'}) @autowired(names={'builder': 'crisis_one'})
def test_crisis_one(self, builder: SignalBuilder = None): def test_crisis_one(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) self.logger.info(signal)
@autowired(names={'builder': 'market-right'}) @autowired(names={'builder': 'market-right'})
def test_market_right(self, builder: SignalBuilder = 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) self.logger.info(signal)
@autowired(names={'builder': 'curve-drift'}) @autowired(names={'builder': 'curve-drift'})
def test_curve_drift(self, builder: SignalBuilder = 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) self.logger.info(signal)
@autowired(names={'builder': 'high-buy'}) @autowired(names={'builder': 'high-buy'})
def test_high_buy(self, builder: SignalBuilder = None): def test_high_buy(self, builder: RebalanceSignal = None):
builder.get_signal(parse_date('2022-09-10'), PortfoliosRisk.FT3) builder.get_signal(parse_date('2022-09-10'), PortfoliosRisk.FT3)
@autowired @autowired
def test_rebalance_builder(self, builder: RebalanceBuilder = None): def test_rebalance_builder(self, builder: RebalanceRuler = None):
builder.get_rebalance(parse_date('2022-09-01'), PortfoliosRisk.FT3) builder.take_next_signal(parse_date('2022-09-01'), PortfoliosRisk.FT3)
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