Skip to content

Commit

Permalink
pr update
Browse files Browse the repository at this point in the history
  • Loading branch information
Lin-Dongzhao committed Apr 3, 2024
1 parent 61ae558 commit a6e9e64
Show file tree
Hide file tree
Showing 12 changed files with 45 additions and 79 deletions.
3 changes: 1 addition & 2 deletions rqalpha/apis/api_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,8 +171,7 @@ def submit_order(id_or_ins, amount, side, price=None, position_effect=None):
market_price = env.get_last_price(order_book_id)
if not is_valid_price(market_price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, reason=reason))
env.order_creation_faild(order_book_id, reason)
return

amount = int(amount)
Expand Down
11 changes: 9 additions & 2 deletions rqalpha/environment.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,16 @@
from datetime import datetime
from typing import Optional, Dict, List
from itertools import chain
from typing import TYPE_CHECKING

import rqalpha
from rqalpha.core.events import EventBus, Event, EVENT
from rqalpha.const import INSTRUMENT_TYPE
from rqalpha.utils.logger import system_log, user_log, user_system_log
from rqalpha.core.global_var import GlobalVars
from rqalpha.utils.i18n import gettext as _
if TYPE_CHECKING:
from rqalpha.model.order import Order


class Environment(object):
Expand Down Expand Up @@ -130,6 +133,10 @@ def can_cancel_order(self, order):
if not v.can_cancel_order(order, account):
return False
return True

def order_creation_faild(self, order_book_id, reason):
user_system_log.warn(reason)
self.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, reason=reason))

def get_universe(self):
return self._universe.get()
Expand Down Expand Up @@ -182,15 +189,15 @@ def update_time(self, calendar_dt, trading_dt):
self.calendar_dt = calendar_dt
self.trading_dt = trading_dt

def can_submit_order(self, order):
def can_submit_order(self, order: 'Order') -> bool:
# forward compatible
instrument_type = self.data_proxy.instrument(order.order_book_id).type
account = self.portfolio.get_account(order.order_book_id)
for v in self._get_frontend_validators(instrument_type):
try:
reason = v.validate_submission(order, account)
if reason:
self.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order.order_book_id, reason=reason))
self.order_creation_faild(order_book_id=order.order_book_id, reason=reason)
return False
except NotImplementedError:
# 避免由于某些 mod 版本未更新,Validator method 未修改
Expand Down
8 changes: 5 additions & 3 deletions rqalpha/interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@

import abc
from datetime import datetime, date
from typing import Any, Union, Optional, Iterable, Dict, List, Sequence
from typing import Any, Union, Optional, Iterable, Dict, List, Sequence, TYPE_CHECKING
if TYPE_CHECKING:
from rqalpha.portfolio.account import Account

import numpy
from six import with_metaclass
Expand Down Expand Up @@ -650,7 +652,7 @@ class AbstractFrontendValidator(with_metaclass(abc.ABCMeta)):
扩展模块可以通过 env.add_frontend_validator 添加自定义的前端风控逻辑
"""
@abc.abstractmethod
def validate_submission(self, order, account=None):
def validate_submission(self, order: Order, account: Optional['Account'] = None) -> Optional[str]:
"""
进行下单前的验证,若通过则返回 None
Expand All @@ -659,7 +661,7 @@ def validate_submission(self, order, account=None):
raise NotImplementedError

@abc.abstractmethod
def validate_cancellation(self, order, account=None):
def validate_cancellation(self, order: Order, account: Optional['Account'] = None) -> Optional[str]:
"""
进行撤销订单前的验证,若通过则返回 None
Expand Down
12 changes: 4 additions & 8 deletions rqalpha/mod/rqalpha_mod_sys_accounts/api/api_future.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,7 @@ def _submit_order(id_or_ins, amount, side, position_effect, style):
reason = _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}").format(
order_book_id=order_book_id
)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, order=None, reason=reason))
env.order_creation_faild(order_book_id=order_book_id, reason=reason)
return None
if isinstance(style, LimitOrder) and np.isnan(style.get_limit_price()):
raise RQInvalidArgument(_(u"Limit order price should not be nan."))
Expand All @@ -66,8 +65,7 @@ def _submit_order(id_or_ins, amount, side, position_effect, style):
price = env.get_last_price(order_book_id)
if not is_valid_price(price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, order=None, reason=reason))
env.order_creation_faild(order_book_id=order_book_id, reason=reason)
return

orders = []
Expand All @@ -80,8 +78,7 @@ def _submit_order(id_or_ins, amount, side, position_effect, style):
"Order Creation Failed: "
"close today amount {amount} is larger than today closable quantity {quantity}").format(
amount=amount, quantity=position.today_closable)
user_system_log.warning(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, order=None, reason=reason))
env.order_creation_faild(order_book_id=order_book_id, reason=reason)
return []
orders.append(Order.__from_create__(
order_book_id, amount, side, style, POSITION_EFFECT.CLOSE_TODAY
Expand All @@ -91,8 +88,7 @@ def _submit_order(id_or_ins, amount, side, position_effect, style):
if amount > quantity:
reason = _(u"Order Creation Failed: close amount {amount} is larger than position quantity {quantity}").format(
amount=amount, quantity=quantity)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, order=None, reason=reason))
env.order_creation_faild(order_book_id=order_book_id, reason=reason)
return []
if amount > old_quantity:
if old_quantity != 0:
Expand Down
50 changes: 13 additions & 37 deletions rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,7 @@
from rqalpha.core.execution_context import ExecutionContext
from rqalpha.core.events import Event, EVENT
from rqalpha.environment import Environment
from rqalpha.mod.rqalpha_mod_sys_risk.validators.cash_validator import \
is_cash_enough
from rqalpha.mod.rqalpha_mod_sys_risk.validators.cash_validator import validate_cash
from rqalpha.model.instrument import IndustryCode as industry_code
from rqalpha.model.instrument import IndustryCodeItem, Instrument
from rqalpha.model.instrument import SectorCode as sector_code
Expand Down Expand Up @@ -102,8 +101,7 @@ def _submit_order(ins, amount, side, position_effect, style, current_quantity, a
price = env.data_proxy.get_last_price(ins.order_book_id)
if not is_valid_price(price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=ins.order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
env.order_creation_faild(order_book_id=ins.order_book_id, reason=reason)
return

if (side == SIDE.BUY and current_quantity != -amount) or (side == SIDE.SELL and current_quantity != abs(amount)):
Expand All @@ -112,13 +110,12 @@ def _submit_order(ins, amount, side, position_effect, style, current_quantity, a

if amount == 0:
reason = _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}").format(order_book_id=ins.order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
env.order_creation_faild(order_book_id=ins.order_book_id, reason=reason)
return
order = Order.__from_create__(ins.order_book_id, abs(amount), side, style, position_effect)
if side == SIDE.BUY and auto_switch_order_value:
account, position, ins = _get_account_position_ins(ins)
if not is_cash_enough(env, order, account.cash):
if validate_cash(env, order, account.cash):
user_system_log.warn(_(
"insufficient cash, use all remaining cash({}) to create order"
).format(account.cash))
Expand All @@ -131,7 +128,7 @@ def _order_shares(ins, amount, style, quantity, auto_switch_order_value):
return _submit_order(ins, amount, side, position_effect, style, quantity, auto_switch_order_value)


def _order_value(account, position, ins, cash_amount, style):
def _order_value(account, position, ins, cash_amount, style, zero_amount_as_exception=True):
env = Environment.get_instance()
if cash_amount > 0:
cash_amount = min(cash_amount, account.cash)
Expand All @@ -141,8 +138,7 @@ def _order_value(account, position, ins, cash_amount, style):
price = env.data_proxy.get_last_price(ins.order_book_id)
if not is_valid_price(price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=ins.order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
env.order_creation_faild(order_book_id=ins.order_book_id, reason=reason)
return

amount = int(Decimal(cash_amount) / Decimal(price))
Expand All @@ -157,9 +153,9 @@ def _order_value(account, position, ins, cash_amount, style):
break
amount -= round_lot
else:
reason = _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}").format(order_book_id=ins.order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
if zero_amount_as_exception:
reason = _(u"Order Creation Failed: 0 order quantity, order_book_id={order_book_id}").format(order_book_id=ins.order_book_id)
env.order_creation_faild(order_book_id=ins.order_book_id, reason=reason)
return

if amount < 0:
Expand Down Expand Up @@ -192,46 +188,28 @@ def stock_order_percent(id_or_ins, percent, price_or_style=None, price=None, sty

@order_target_value.register(INST_TYPE_IN_STOCK_ACCOUNT)
def stock_order_target_value(id_or_ins, cash_amount, price_or_style=None, price=None, style=None):
env = Environment.get_instance()
account, position, ins = _get_account_position_ins(id_or_ins)
open_style, close_style = calc_open_close_style(price, style, price_or_style)
if cash_amount == 0:
return _submit_order(
ins, position.closable, SIDE.SELL, POSITION_EFFECT.CLOSE, close_style, position.quantity, False
)
_delta = cash_amount - position.market_value
price = env.data_proxy.get_last_price(ins.order_book_id)
if not is_valid_price(price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=ins.order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
amount = int(Decimal(_delta) / Decimal(price))
if _round_order_quantity(ins, amount) == 0:
return
_style = open_style if _delta > 0 else close_style
return _order_value(account, position, ins, _delta, _style)
return _order_value(account, position, ins, _delta, _style, zero_amount_as_exception=False)


@order_target_percent.register(INST_TYPE_IN_STOCK_ACCOUNT)
def stock_order_target_percent(id_or_ins, percent, price_or_style=None, price=None, style=None):
env = Environment.get_instance()
account, position, ins = _get_account_position_ins(id_or_ins)
open_style, close_style = calc_open_close_style(price, style, price_or_style)
if percent == 0:
return _submit_order(
ins, position.closable, SIDE.SELL, POSITION_EFFECT.CLOSE, close_style, position.quantity, False
)
_delta = account.total_value * percent - position.market_value
price = env.data_proxy.get_last_price(ins.order_book_id)
if not is_valid_price(price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=ins.order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
amount = int(Decimal(_delta) / Decimal(price))
if _round_order_quantity(ins, amount) == 0:
return
_style = open_style if _delta > 0 else close_style
return _order_value(account, position, ins, _delta, _style)
return _order_value(account, position, ins, _delta, _style, zero_amount_as_exception=False)


@order.register(INST_TYPE_IN_STOCK_ACCOUNT)
Expand Down Expand Up @@ -363,8 +341,7 @@ def order_target_portfolio(
last_price = env.data_proxy.get_last_price(order_book_id)
if not is_valid_price(last_price):
reason = _(u"Order Creation Failed: [{order_book_id}] No market data").format(order_book_id=order_book_id)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=ins.order_book_id, reason=reason))
env.order_creation_faild(order_book_id=order_book_id, reason=reason)
continue

price_or_style = price_or_styles.get(ins.order_book_id)
Expand Down Expand Up @@ -403,8 +380,7 @@ def order_target_portfolio(
reason = _("Adjust position of {id_or_ins} Failed: Invalid close/open price {close_price}/{open_price}").format(
id_or_ins=order_book_id, close_price=close_price, open_price=open_price
)
user_system_log.warn(reason)
env.event_bus.publish_event(Event(EVENT.ORDER_CREATION_REJECT, order_book_id=order_book_id, reason=reason))
env.order_creation_faild(order_book_id=order_book_id, reason=reason)
continue

delta_quantity = (account_value * target_percent / close_price) - current_quantities.get(order_book_id, 0)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ def validate_submission(self, order: Order, account: Optional[Account] = None) -
return None
else:
reason = "Order Creation Failed: margin stock pool not contains {}.".format(order.order_book_id)
user_system_log.warn(reason)
return reason

def validate_cancellation(self, order: Order, account: Optional[Account] = None) -> Optional[str]:
Expand Down
2 changes: 0 additions & 2 deletions rqalpha/mod/rqalpha_mod_sys_accounts/position_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ def validate_submission(self, order: Order, account: Optional[Account] = None) -
quantity=order.quantity,
closable=position.today_closable,
)
user_system_log.warn(reason)
return reason
if order.position_effect == POSITION_EFFECT.CLOSE and order.quantity > position.closable:
reason = _(
Expand All @@ -54,6 +53,5 @@ def validate_submission(self, order: Order, account: Optional[Account] = None) -
quantity=order.quantity,
closable=position.closable,
)
user_system_log.warn(reason)
return reason
return None
1 change: 0 additions & 1 deletion rqalpha/mod/rqalpha_mod_sys_accounts/validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ class MarginInstrumentValidator(AbstractFrontendValidator):
def validate_submission(self, order: Order, account: Optional[Account] = None) -> Optional[str]:
if account.cash_liabilities > 0:
reason = "Order Creation Failed: cash liabilities > 0, {} not support submit order".format(order.order_book_id)
user_system_log.warn(reason)
return reason
else:
return None
Expand Down
29 changes: 13 additions & 16 deletions rqalpha/mod/rqalpha_mod_sys_risk/validators/cash_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,26 +20,23 @@
from rqalpha.const import POSITION_EFFECT
from rqalpha.model.order import Order
from rqalpha.portfolio.account import Account
from rqalpha.utils.logger import user_system_log
from rqalpha.environment import Environment

from rqalpha.utils.i18n import gettext as _


def is_cash_enough(env, order, cash, warn=False):
def validate_cash(env: Environment, order: Order, cash: float) -> Optional[str]:
instrument = env.data_proxy.instrument(order.order_book_id)
cost_money = instrument.calc_cash_occupation(order.frozen_price, order.quantity, order.position_direction, order.trading_datetime.date())
cost_money += env.get_order_transaction_cost(order)
if cost_money <= cash:
return True
if warn:
reason = _("Order Creation Failed: not enough money to buy {order_book_id}, needs {cost_money:.2f},"
" cash {cash:.2f}").format(
order_book_id=order.order_book_id,
cost_money=cost_money,
cash=cash)
user_system_log.warn(reason)
return reason
return False
return None
reason = _("Order Creation Failed: not enough money to buy {order_book_id}, needs {cost_money:.2f},"
" cash {cash:.2f}").format(
order_book_id=order.order_book_id,
cost_money=cost_money,
cash=cash)
return reason


class CashValidator(AbstractFrontendValidator):
Expand All @@ -52,8 +49,8 @@ def validate_cancellation(self, order: Order, account: Optional[Account] = None)
def validate_submission(self, order: Order, account: Optional[Account] = None) -> Optional[str]:
if (account is None) or (order.position_effect != POSITION_EFFECT.OPEN):
return None
reason = is_cash_enough(self._env, order, account.cash, warn=True)
if reason is True:
return None
else:
reason = validate_cash(self._env, order, account.cash)
if reason:
return reason
else:
return None
Original file line number Diff line number Diff line change
Expand Up @@ -34,15 +34,13 @@ def validate_submission(self, order: Order, account: Optional[Account] = None) -
if instrument.type != INSTRUMENT_TYPE.INDX and not instrument.listing_at(self._env.trading_dt):
reason = _(u"Order Creation Failed: {order_book_id} is not listing!").format(
order_book_id=order.order_book_id)
user_system_log.warn(reason)
return reason

if instrument.type == 'CS' and self._env.data_proxy.is_suspended(order.order_book_id, self._env.trading_dt):
reason = _(u"Order Creation Failed: security {order_book_id} is suspended on {date}").format(
order_book_id=order.order_book_id,
date=self._env.trading_dt.date()
)
user_system_log.warn(reason)
return reason

return None
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ def validate_submission(self, order: Order, account: Optional[Account] = None) -
limit_price=order.price,
limit_up=limit_up
)
user_system_log.warn(reason)
return reason

limit_down = round(self._env.price_board.get_limit_down(order.order_book_id), 4)
Expand All @@ -54,7 +53,6 @@ def validate_submission(self, order: Order, account: Optional[Account] = None) -
limit_price=order.price,
limit_down=limit_down
)
user_system_log.warn(reason)
return reason
return None

Expand Down
Loading

0 comments on commit a6e9e64

Please sign in to comment.