Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dev/plot #890

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions requirements_dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@ python-dateutil >=2.5.3
six >=1.10.0
logbook >=1.0.0
click >=7.0
jsonpickle ==0.9.4
simplejson >=3.10.0
dill ==0.2.5
PyYAML >=3.12
tabulate
rqrisk >=0.0.9
h5py
matplotlib >=1.5.1 ; python_version >= '3.6'
matplotlib >=1.5.1,<=3.0.3 ; python_version == '3.5'

# pip install jsonpickle==0.9.4
# pip install dill==0.2.5
# pip install rqrisk
# mamba install matplotlib
# pip install . -e
2 changes: 1 addition & 1 deletion rqalpha/apis/api_abstract.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def order_percent(id_or_ins, percent, price_or_style=None, price=None, style=Non
*common_rules
)
@instype_singledispatch
def order_target_value(id_or_ins, cash_amount, price_or_style=None, price=None, style=None):
def order_target_value(id_or_ins, cash_amount, price_or_style=None, price=None, style=None, allow_fraction_lot=False):
# type: (Union[str, Instrument], float, TUPLE_PRICE_OR_STYLE_TYPE, Optional[float], Optional[OrderStyle]) -> Optional[Order]
"""
买入/卖出并且自动调整该证券的仓位到一个目标价值。
Expand Down
9 changes: 1 addition & 8 deletions rqalpha/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -301,14 +301,7 @@ def set_loggers(config):
from rqalpha.utils.logger import init_logger
from rqalpha.utils import logger
extra_config = config.extra

init_logger()

for log in [system_log, user_system_log]:
log.level = getattr(logbook, config.extra.log_level.upper(), logbook.NOTSET)

user_log.level = logbook.DEBUG

init_logger(level=extra_config.log_level, log_to_file = extra_config.log_to_file, log_to_console=extra_config.log_to_console, file_name = extra_config.log_file)
for logger_name, level in extra_config.logger:
getattr(logger, logger_name).level = getattr(logbook, level.upper())

Expand Down
28 changes: 17 additions & 11 deletions rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
export_as_api(sector_code, name='sector_code')

KSH_MIN_AMOUNT = 200
ROUND_LOT_PRECISION = 1e-10



def _get_account_position_ins(id_or_ins):
Expand All @@ -70,12 +72,14 @@ def _get_account_position_ins(id_or_ins):
return account, position, ins


def _round_order_quantity(ins, quantity) -> int:
def _round_order_quantity(ins, quantity, allow_fraction_lot = False) -> int:
if ins.type == "CS" and ins.board_type == "KSH":
# KSH can buy(sell) 201, 202 shares
return 0 if abs(quantity) < KSH_MIN_AMOUNT else int(quantity)
else:
round_lot = ins.round_lot
if allow_fraction_lot:
round_lot = ROUND_LOT_PRECISION
try:
return int(Decimal(quantity) / Decimal(round_lot)) * round_lot
except ValueError:
Expand All @@ -94,7 +98,7 @@ def _get_order_style_price(order_book_id, style):
raise RuntimeError(f"no support {style} order style")


def _submit_order(ins, amount, side, position_effect, style, current_quantity, auto_switch_order_value, zero_amount_as_exception=True):
def _submit_order(ins, amount, side, position_effect, style, current_quantity, auto_switch_order_value, zero_amount_as_exception=True, allow_fraction_lot = False):
env = Environment.get_instance()
if isinstance(style, LimitOrder) and np.isnan(style.get_limit_price()):
raise RQInvalidArgument(_(u"Limit order price should not be nan."))
Expand All @@ -106,7 +110,7 @@ def _submit_order(ins, amount, side, position_effect, style, current_quantity, a

if (side == SIDE.BUY and current_quantity != -amount) or (side == SIDE.SELL and current_quantity != abs(amount)):
# 在融券回测中,需要用买单作为平空,对于此种情况下出现的碎股,亦允许一次性申报卖出
amount = _round_order_quantity(ins, amount)
amount = _round_order_quantity(ins, amount, allow_fraction_lot)

if amount == 0:
if zero_amount_as_exception:
Expand All @@ -124,12 +128,12 @@ def _submit_order(ins, amount, side, position_effect, style, current_quantity, a
return env.submit_order(order)


def _order_shares(ins, amount, style, quantity, auto_switch_order_value, zero_amount_as_exception=True):
def _order_shares(ins, amount, style, quantity, auto_switch_order_value, zero_amount_as_exception=True, allow_fraction_lot = False):
side, position_effect = (SIDE.BUY, POSITION_EFFECT.OPEN) if amount > 0 else (SIDE.SELL, POSITION_EFFECT.CLOSE)
return _submit_order(ins, amount, side, position_effect, style, quantity, auto_switch_order_value, zero_amount_as_exception)
return _submit_order(ins, amount, side, position_effect, style, quantity, auto_switch_order_value, zero_amount_as_exception, allow_fraction_lot)


def _order_value(account, position, ins, cash_amount, style, zero_amount_as_exception=True):
def _order_value(account, position, ins, cash_amount, style, zero_amount_as_exception=True, allow_fraction_lot = False):
env = Environment.get_instance()
if cash_amount > 0:
cash_amount = min(cash_amount, account.cash)
Expand All @@ -144,8 +148,11 @@ def _order_value(account, position, ins, cash_amount, style, zero_amount_as_exce

amount = int(Decimal(cash_amount) / Decimal(price))
round_lot = int(ins.round_lot)
if allow_fraction_lot:
amount = float(Decimal(cash_amount) / Decimal(price))
round_lot = ROUND_LOT_PRECISION
if cash_amount > 0:
amount = _round_order_quantity(ins, amount)
amount = _round_order_quantity(ins, amount, allow_fraction_lot = allow_fraction_lot)
while amount > 0:
expected_transaction_cost = env.get_order_transaction_cost(Order.__from_create__(
ins.order_book_id, amount, SIDE.BUY, LimitOrder(price), POSITION_EFFECT.OPEN
Expand All @@ -158,11 +165,10 @@ def _order_value(account, position, ins, cash_amount, style, zero_amount_as_exce
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_failed(order_book_id=ins.order_book_id, reason=reason)
return

if amount < 0:
amount = max(amount, -position.closable)

return _order_shares(ins, amount, style, position.quantity, auto_switch_order_value=False, zero_amount_as_exception=zero_amount_as_exception)
return _order_shares(ins, amount, style, position.quantity, auto_switch_order_value=False, zero_amount_as_exception=zero_amount_as_exception, allow_fraction_lot = allow_fraction_lot)


@order_shares.register(INST_TYPE_IN_STOCK_ACCOUNT)
Expand All @@ -188,7 +194,7 @@ 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):
def stock_order_target_value(id_or_ins, cash_amount, price_or_style=None, price=None, style=None, allow_fraction_lot=False):
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:
Expand All @@ -197,7 +203,7 @@ def stock_order_target_value(id_or_ins, cash_amount, price_or_style=None, price=
)
_delta = cash_amount - position.market_value
_style = open_style if _delta > 0 else close_style
return _order_value(account, position, ins, _delta, _style, zero_amount_as_exception=False)
return _order_value(account, position, ins, _delta, _style, zero_amount_as_exception=False, allow_fraction_lot = allow_fraction_lot)


@order_target_percent.register(INST_TYPE_IN_STOCK_ACCOUNT)
Expand Down
5 changes: 3 additions & 2 deletions rqalpha/mod/rqalpha_mod_sys_analyser/plot/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,14 +170,13 @@ def _plot(title: str, sub_plots: List[SubPlot], strategy_name):
img_height = sum(s.height for s in sub_plots)
water_mark = WaterMark(IMG_WIDTH, img_height, strategy_name)
fig = pyplot.figure(title, figsize=(IMG_WIDTH, img_height), dpi=water_mark.dpi, clear=True)
water_mark.plot(fig)

gs = gridspec.GridSpec(img_height, 8, figure=fig)
last_height = 0
for p in sub_plots:
p.plot(pyplot.subplot(gs[last_height:last_height + p.height, :p.right_pad]))
last_height += p.height

pyplot.tight_layout()
return fig

Expand Down Expand Up @@ -253,3 +252,5 @@ def plot_result(

if show:
pyplot.show()
else:
pyplot.close()
55 changes: 38 additions & 17 deletions rqalpha/utils/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
# 详细的授权流程,请联系 [email protected] 获取。
import logbook
from logbook import Logger, StderrHandler

import logging
logbook.set_datetime_format("local")


Expand All @@ -32,37 +32,58 @@

DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.%f"

def configure_logger(
logger,
level=logging.DEBUG,
log_to_file=False,
log_to_console=True,
file_name='app_log.log'
):
# Create a logger
logger.setLevel(level)
# Avoid adding handlers multiple times
if not logger.handlers:
# Create formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# Create handlers based on configuration
if log_to_file:
file_handler = logging.FileHandler(file_name)
file_handler.setLevel(level)
file_handler.setFormatter(formatter)
logger.addHandler(file_handler)

if log_to_console:
console_handler = logging.StreamHandler()
console_handler.setLevel(level)
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

return logger


def user_log_processor(record):
from rqalpha.environment import Environment
time = Environment.get_instance().calendar_dt
if time is not None:
record.time = time


user_log_group = logbook.LoggerGroup(processor=user_log_processor)


# loggers
# 用户代码logger日志
user_log = Logger("user_log")
user_log = logging.getLogger("user_log")
# 给用户看的系统日志
user_system_log = Logger("user_system_log")

user_log_group.add_logger(user_log)
user_log_group.add_logger(user_system_log)

user_system_log = logging.getLogger("user_system_log")
# 系统日志
system_log = Logger("system_log")
system_log = logging.getLogger("system_log")

original_print = print


def init_logger():
system_log.handlers = [StderrHandler(bubble=True)]
user_log.handlers = [StderrHandler(bubble=True)]
user_system_log.handlers = [StderrHandler(bubble=True)]

def init_logger(level, log_to_file=False, log_to_console=True, file_name='app_log.log'):
_level = logging._nameToLevel[level.upper()]
configure_logger(logger=system_log, level = _level, log_to_file=log_to_file, log_to_console = log_to_console, file_name = file_name)
configure_logger(logger=user_log, level = _level, log_to_file=log_to_file, log_to_console = log_to_console, file_name = file_name)
configure_logger(logger=user_system_log, level = _level, log_to_file=log_to_file, log_to_console = log_to_console, file_name = file_name)

def user_print(*args, **kwargs):
sep = kwargs.get("sep", " ")
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@

setup(
name='rqalpha',
version=versioneer.get_version(),
# remove .dirty in version
version=versioneer.get_version() if ".dirty" not in versioneer.get_version() else versioneer.get_version().split('.dirty')[0],
cmdclass=versioneer.get_cmdclass(),
description='Ricequant Algorithm Trading System',
packages=find_packages(exclude=[]),
Expand Down