Skip to content

Commit

Permalink
Merge pull request #739 from ricequant/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Zhou-JiaJun committed Oct 12, 2022
2 parents 254ea01 + 88b1ae0 commit 847607a
Show file tree
Hide file tree
Showing 21 changed files with 63 additions and 40 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.rst
Expand Up @@ -2,6 +2,12 @@
CHANGELOG
==================

4.12.0
==================
- 调整分析指标中部分指标的显示格式为百分比
- 将分析指标中的超额收益波动率从日度改为年化
- 入金支持延迟到账

4.11.3
==================
- 分析指标新增胜率和盈亏比,调整了作图中指标的布局
Expand Down
8 changes: 4 additions & 4 deletions rqalpha/apis/api_base.py
Expand Up @@ -865,17 +865,17 @@ def symbol(order_book_id, sep=", "):
EXECUTION_PHASE.SCHEDULED,
EXECUTION_PHASE.GLOBAL
)
def deposit(account_type, amount):
# type: (str, float) -> None
def deposit(account_type: str, amount: float, receiving_days: int = 0):
"""
入金(增加账户资金)
:param account_type: 账户类型
:param amount: 增加金额
:param amount: 入金金额
:param receiving_days: 入金到账天数,0 表示立刻到账,1 表示资金在下一个交易日盘前到账
:return: None
"""
env = Environment.get_instance()
return env.portfolio.deposit_withdraw(account_type, amount)
return env.portfolio.deposit_withdraw(account_type, amount, receiving_days)


@export_as_api
Expand Down
8 changes: 5 additions & 3 deletions rqalpha/mod/rqalpha_mod_sys_analyser/mod.py
Expand Up @@ -323,7 +323,7 @@ def tear_down(self, code, exception=None):
'tracking_error': risk.annual_tracking_error,
'sortino': risk.sortino,
'volatility': risk.annual_volatility,
'excess_volatility': risk.excess_volatility,
'excess_volatility': risk.excess_annual_volatility,
'excess_annual_volatility': risk.excess_annual_volatility,
'max_drawdown': risk.max_drawdown,
'excess_max_drawdown': risk.excess_max_drawdown,
Expand Down Expand Up @@ -368,7 +368,8 @@ def tear_down(self, code, exception=None):
df = pd.DataFrame(self._total_portfolios)
df['date'] = pd.to_datetime(df['date'])
total_portfolios = df.set_index('date').sort_index()
weekly_nav = df.resample("W", on="date").last().set_index("date").unit_net_value.dropna()
df.index = df['date']
weekly_nav = df.resample("W").last().set_index("date").unit_net_value.dropna()
weekly_returns = (weekly_nav / weekly_nav.shift(1).fillna(1)).fillna(0) - 1

# 最长回撤持续期
Expand Down Expand Up @@ -403,7 +404,8 @@ def tear_down(self, code, exception=None):
df = pd.DataFrame(self._total_benchmark_portfolios)
df['date'] = pd.to_datetime(df['date'])
benchmark_portfolios = df.set_index('date').sort_index()
weekly_b_nav = df.resample("W", on="date").last().set_index("date").unit_net_value.dropna()
df.index = df['date']
weekly_b_nav = df.resample("W").last().set_index("date").unit_net_value.dropna()
weekly_b_returns = (weekly_b_nav / weekly_b_nav.shift(1).fillna(1)).fillna(0) - 1
result_dict['benchmark_portfolio'] = benchmark_portfolios
# 超额收益最长回撤持续期
Expand Down
18 changes: 9 additions & 9 deletions rqalpha/mod/rqalpha_mod_sys_analyser/plot/consts.py
Expand Up @@ -79,16 +79,16 @@
], [
IndicatorInfo("benchmark_total_returns", _("BenchmarkReturns"), BLUE, "{0:.3%}", 11),
IndicatorInfo("benchmark_annualized_returns", _("BenchmarkAnnual"), BLUE, "{0:.3%}", 11),
IndicatorInfo("volatility", _("Volatility"), BLACK, "{0:.4}", 11),
IndicatorInfo("tracking_error", _("TrackingError"), BLACK, "{0:.4}", 11),
IndicatorInfo("volatility", _("Volatility"), BLACK, "{0:.3%}", 11),
IndicatorInfo("tracking_error", _("TrackingError"), BLACK, "{0:.3%}", 11),
IndicatorInfo("downside_risk", _("DownsideRisk"), BLACK, "{0:.4}", 11),
IndicatorInfo("information_ratio", _("InformationRatio"), BLACK, "{0:.4}", 11),
], [
IndicatorInfo("excess_cum_returns", _("ExcessCumReturns"), BLACK, "{0:.3%}", 11),
IndicatorInfo("win_rate", _("WinRate"), BLACK, "{0:.4}", 11),
IndicatorInfo("weekly_win_rate", _("WeeklyWinRate"), BLACK, "{0:.4}", 11),
IndicatorInfo("win_rate", _("WinRate"), BLACK, "{0:.3%}", 11),
IndicatorInfo("weekly_win_rate", _("WeeklyWinRate"), BLACK, "{0:.3%}", 11),
IndicatorInfo("profit_loss_rate", _("ProfitLossRate"), BLACK, "{0:.4}", 11),
IndicatorInfo("max_drawdown", _("MaxDrawDown"), BLACK, "{0:.4}", 11),
IndicatorInfo("max_drawdown", _("MaxDrawDown"), BLACK, "{0:.3%}", 11),
IndicatorInfo("max_dd_ddd", _("MaxDD/MaxDDD"), BLACK, "{}", 6),
]]

Expand All @@ -97,15 +97,15 @@
IndicatorInfo("weekly_beta", _("WeeklyBeta"), BLACK, "{0:.4}", 11),
IndicatorInfo("weekly_sharpe", _("WeeklySharpe"), BLACK, "{0:.4}", 11),
IndicatorInfo("weekly_information_ratio", _("WeeklyInfoRatio"), BLACK, "{0:.4}", 11),
IndicatorInfo("weekly_tracking_error", _("WeeklyTrackingError"), BLACK, "{0:.4}", 11),
IndicatorInfo("weekly_max_drawdown", _("WeeklyMaxDrawdown"), BLACK, "{0:.4}", 11),
IndicatorInfo("weekly_tracking_error", _("WeeklyTrackingError"), BLACK, "{0:.3%}", 11),
IndicatorInfo("weekly_max_drawdown", _("WeeklyMaxDrawdown"), BLACK, "{0:.3%}", 11),
]

EXCESS_INDICATORS = [
IndicatorInfo("excess_returns", _("ExcessReturns"), BLACK, "{0:.3%}", 11),
IndicatorInfo("excess_annual_returns", _("ExcessAnnual"), BLACK, "{0:.3%}", 11),
IndicatorInfo("excess_sharpe", _("ExcessSharpe"), BLACK, "{0:.4}", 11),
IndicatorInfo("excess_volatility", _("ExcessVolatility"), BLACK, "{0:.4}", 11),
IndicatorInfo("excess_max_drawdown", _("ExcessMaxDD"), BLACK, "{0:.4}", 11),
IndicatorInfo("excess_volatility", _("ExcessVolatility"), BLACK, "{0:.3%}", 11),
IndicatorInfo("excess_max_drawdown", _("ExcessMaxDD"), BLACK, "{0:.3%}", 11),
IndicatorInfo("excess_max_dd_ddd", _("ExcessMaxDD/ExcessMaxDDD"), BLACK, "{}", 6),
]
3 changes: 1 addition & 2 deletions rqalpha/mod/rqalpha_mod_sys_analyser/plot/utils.py
Expand Up @@ -74,8 +74,7 @@ def max_ddd(arr: array, index: DatetimeIndex) -> IndexRange:


def weekly_returns(portfolio: DataFrame) -> Series:
return portfolio.unit_net_value.reset_index().resample(
"W", on="date").last().set_index("date").unit_net_value.dropna() - 1
return portfolio.unit_net_value.resample("W").last().dropna() - 1


def trading_dates_index(trades: DataFrame, position_effect, index: DatetimeIndex):
Expand Down
6 changes: 3 additions & 3 deletions rqalpha/portfolio/__init__.py
Expand Up @@ -270,16 +270,16 @@ def cash_liabilities(self):
def _pre_before_trading(self, _):
self._static_unit_net_value = self.unit_net_value

def deposit_withdraw(self, account_type, amount):
def deposit_withdraw(self, account_type, amount, receiving_days=0):
"""出入金"""
# 入金 现金增加,份额增加,总权益增加,单位净值不变
# 出金 现金减少,份额减少,总权益减少,单位净值不变
if account_type not in self._accounts:
raise ValueError(_("invalid account type {}, choose in {}".format(account_type, list(self._accounts.keys()))))
unit_net_value = self.unit_net_value
self._accounts[account_type].deposit_withdraw(amount)
self._accounts[account_type].deposit_withdraw(amount, receiving_days)
_units = self.total_value / unit_net_value
user_log.info(_("Cash add {}. units {} become to {}".format(amount, self._units ,_units)))
user_log.info(_("Cash add {}. units {} become to {}".format(amount, self._units, _units)))
self._units = _units

def finance_repay(self, amount, account_type):
Expand Down
44 changes: 27 additions & 17 deletions rqalpha/portfolio/account.py
Expand Up @@ -16,6 +16,7 @@
# 详细的授权流程,请联系 public@ricequant.com 获取。

from itertools import chain
from datetime import date
from typing import Callable, Dict, Iterable, List, Optional, Union, Tuple

import six
Expand Down Expand Up @@ -60,10 +61,12 @@ def __init__(
):
self._type = account_type
self._total_cash = total_cash # 包含保证金的总资金
self._env = Environment.get_instance()

self._positions: Dict[str, Dict[POSITION_DIRECTION, Position]] = {}
self._backward_trade_set = set()
self._frozen_cash = 0
self._pending_deposit_withdraw: List[Tuple[date, float]] = []

self._cash_liabilities = 0 # 现金负债

Expand Down Expand Up @@ -91,7 +94,7 @@ def __repr__(self):
)

def register_event(self):
event_bus = Environment.get_instance().event_bus
event_bus = self._env.event_bus
event_bus.add_listener(
EVENT.TRADE, lambda e: self.apply_trade(e.trade, e.order) if e.account == self else None
)
Expand Down Expand Up @@ -276,12 +279,14 @@ def equity(self):
return sum(p.equity for p in self._iter_pos())

@property
def total_value(self):
# type: () -> float
def total_value(self) -> float:
"""
账户总权益
"""
return self._total_cash + self.equity - self.cash_liabilities - self.cash_liabilities_interest
total_value = self._total_cash + self.equity - self.cash_liabilities - self.cash_liabilities_interest
if self._pending_deposit_withdraw:
total_value += sum(amount for _, amount in self._pending_deposit_withdraw)
return total_value

@property
def total_cash(self):
Expand Down Expand Up @@ -312,7 +317,11 @@ def _on_before_trading(self, _):
if all(p.quantity == 0 and p.equity == 0 for p in six.itervalues(positions)):
del self._positions[order_book_id]

trading_date = Environment.get_instance().trading_dt.date()
trading_date = self._env.trading_dt.date()
while self._pending_deposit_withdraw and self._pending_deposit_withdraw[0][0] <= trading_date:
_, amount = self._pending_deposit_withdraw.pop(0)
self._total_cash += amount

for position in self._iter_pos():
self._total_cash += position.before_trading(trading_date)

Expand All @@ -321,7 +330,7 @@ def _on_before_trading(self, _):
self._cash_liabilities += self.cash_liabilities_interest

def _on_settlement(self, event):
trading_date = Environment.get_instance().trading_dt.date()
trading_date = self._env.trading_dt.date()

for order_book_id, positions in list(self._positions.items()):
for position in six.itervalues(positions):
Expand All @@ -335,7 +344,7 @@ def _on_settlement(self, event):
self._total_cash -= fee

# 如果 total_value <= 0 则认为已爆仓,清空仓位,资金归0
forced_liquidation = Environment.get_instance().config.base.forced_liquidation
forced_liquidation = self._env.config.base.forced_liquidation
if self.total_value <= 0 and forced_liquidation:
if self._positions:
user_system_log.warn(_("Trigger Forced Liquidation, current total_value is 0"))
Expand Down Expand Up @@ -395,7 +404,6 @@ def _get_or_create_pos(
init_price : Optional[float] = None
) -> Position:
if order_book_id not in self._positions:
env = Environment.get_instance()
if direction == POSITION_DIRECTION.LONG:
long_quantity, short_quantity = init_quantity, 0
else:
Expand All @@ -405,7 +413,7 @@ def _get_or_create_pos(
POSITION_DIRECTION.SHORT: Position(order_book_id, POSITION_DIRECTION.SHORT, short_quantity, init_price)
})
if not init_price:
last_price = env.get_last_price(order_book_id)
last_price = self._env.get_last_price(order_book_id)
for p in positions.values():
p.update_last_price(last_price)
if hasattr(positions[direction], "margin") and hasattr(self.__class__, "_margin"):
Expand All @@ -426,21 +434,19 @@ def _on_tick(self, event):
position.update_last_price(tick.last)

def _on_bar(self, _):
env = Environment.get_instance()
for order_book_id, positions in self._positions.items():
price = env.get_last_price(order_book_id)
price = self._env.get_last_price(order_book_id)
if price == price:
for position in six.itervalues(positions):
position.update_last_price(price)

def _frozen_cash_of_order(self, order):
env = Environment.get_instance()
if order.position_effect == POSITION_EFFECT.OPEN:
instrument = env.data_proxy.instrument(order.order_book_id)
instrument = self._env.data_proxy.instrument(order.order_book_id)
order_cost = instrument.calc_cash_occupation(order.frozen_price, order.quantity, order.position_direction)
else:
order_cost = 0
return order_cost + env.get_order_transaction_cost(order)
return order_cost + self._env.get_order_transaction_cost(order)

def _management_fee(self):
# type: () -> float
Expand Down Expand Up @@ -478,12 +484,16 @@ def management_fees(self):
"""该账户的管理费用总计"""
return self._management_fees

def deposit_withdraw(self, amount):
# type: (float) -> None
def deposit_withdraw(self, amount: float, receiving_days: int = 0):
"""出入金"""
if (amount < 0) and (self.cash < amount * -1):
raise ValueError(_('insufficient cash, current {}, target withdrawal {}').format(self._total_cash, amount))
self._total_cash += amount
if receiving_days >= 1:
receiving_date = self._env.data_proxy.get_next_trading_date(self._env.trading_dt.date(), n=receiving_days)
self._pending_deposit_withdraw.append((receiving_date, amount))
self._pending_deposit_withdraw.sort(key=lambda i: i[0])
else:
self._total_cash += amount

def finance_repay(self, amount):
""" 融资还款 """
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Expand Up @@ -5,7 +5,7 @@

[metadata]
name = rqalpha
version = 4.11.3
version = 4.12.0

[versioneer]
VCS = git
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -27,7 +27,7 @@
'simplejson',
'PyYAML',
'tabulate',
'rqrisk >=1.0.0',
'rqrisk >=1.0.3',
'h5py',
'matplotlib >=2.2.0',
"openpyxl"
Expand Down
6 changes: 6 additions & 0 deletions tests/api_tests/test_api_base.py
Expand Up @@ -482,4 +482,10 @@ def handle_bar(context, bar_dict):
else:
assert False, "未报出当前账户可取出金额不足异常"

deposit("STOCK", 10000, 3)
context.cash = context.portfolio.accounts["STOCK"].cash

elif context.counter == 9:
assert int(context.portfolio.accounts["STOCK"].cash) == int(context.cash) + 10000

return locals()
Binary file modified tests/outs/test_f_buy_and_hold.pkl
Binary file not shown.
Binary file modified tests/outs/test_f_macd.pkl
Binary file not shown.
Binary file modified tests/outs/test_f_macd_signal.pkl
Binary file not shown.
Binary file modified tests/outs/test_f_mean_reverting.pkl
Binary file not shown.
Binary file modified tests/outs/test_s_buy_and_hold.pkl
Binary file not shown.
Binary file modified tests/outs/test_s_dual_thrust.pkl
Binary file not shown.
Binary file modified tests/outs/test_s_scheduler.pkl
Binary file not shown.
Binary file modified tests/outs/test_s_tick_size.pkl
Binary file not shown.
Binary file modified tests/outs/test_s_turtle.pkl
Binary file not shown.
Binary file modified tests/outs/test_s_turtle_signal.pkl
Binary file not shown.
Binary file modified tests/outs/test_sf_buy_and_hold.pkl
Binary file not shown.

0 comments on commit 847607a

Please sign in to comment.