Skip to content

Commit

Permalink
Merge pull request #650 from ricequant/develop
Browse files Browse the repository at this point in the history
Develop
  • Loading branch information
Cuizi7 committed Aug 11, 2021
2 parents b2b00e1 + 1fa73db commit 43dad6f
Show file tree
Hide file tree
Showing 41 changed files with 111 additions and 134 deletions.
4 changes: 2 additions & 2 deletions rqalpha/apis/api_rqdatac.py
Original file line number Diff line number Diff line change
Expand Up @@ -1108,14 +1108,14 @@ def get_pit_financials_ex(order_book_ids, fields, count, statements='latest'):
end_quarter = str(y) + 'q' + str(q)

delta_year = count // 4
start_q = q - count % 4
start_q = q - count % 4 + 1
if start_q <= 0:
start_q = 4
delta_year += 1
start_quarter = str(y - delta_year) + 'q' + str(start_q)

result = rqdatac.get_pit_financials_ex(fields=fields, start_quarter=start_quarter, end_quarter=end_quarter,
order_book_ids=order_book_ids, statements=statements, market='cn')
order_book_ids=order_book_ids, statements=statements, market='cn', date=dt)
return result


Expand Down
3 changes: 1 addition & 2 deletions rqalpha/cmds/run.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,11 @@
@click.option('-e', '--end-date', 'base__end_date', type=Date())
@click.option('-mm', '--margin-multiplier', 'base__margin_multiplier', type=click.FLOAT)
@click.option('-a', '--account', 'base__accounts', nargs=2, multiple=True,
help="set account type with starting cash")
help="set account type with starting cash, eg: -a stock 1000000 -a future 1000000")
@click.option('--position', 'base__init_positions', type=click.STRING, help="set init position")
@click.option('-fq', '--frequency', 'base__frequency', type=click.Choice(['1d', '1m', 'tick']))
@click.option('-rt', '--run-type', 'base__run_type', type=click.Choice(['b', 'p', 'r']), default="b")
@click.option('-rp', '--round-price', 'base__round_price', is_flag=True)
@click.option('-mk', '--market', 'base__market', type=click.Choice(['cn', 'hk']), default=None)
@click.option('--source-code', 'base__source_code')
@click.option('--rqdatac', '--rqdatac-uri', 'base__rqdatac_uri', default=None,
help='rqdatac uri, eg user:password or or license:xxxxxxx or tcp://user:password@ip:port')
Expand Down
2 changes: 0 additions & 2 deletions rqalpha/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ base:
# 如果想设置使用某个账户,只需要增加对应的初始资金即可
stock: ~
future: ~
# 交易市场,如 cn 中国市场,hk 香港市场
market: cn
# 设置初始仓位
init_positions: {}
# 根据价格最小变动单位调整发单价格
Expand Down
13 changes: 9 additions & 4 deletions rqalpha/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,10 +189,15 @@ class DAYS_CNT(object):
TRADING_DAYS_A_YEAR = 252


# noinspection PyPep8Naming
class MARKET(CustomEnum):
CN = "CN"
HK = "HK"
class EXCHANGE(CustomEnum):
XSHE = "XSHE"
XSHG = "XSHG"
SHFE = "SHFE"
INE = "INE"
DCE = "DCE"
CZCE = "CZCE"
CFFEX = "CFFEX"
SGEX = "SGEX"


# noinspection PyPep8Naming
Expand Down
4 changes: 3 additions & 1 deletion rqalpha/data/base_data_source/data_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,9 @@ def get_bar(self, instrument, dt, frequency):
def get_open_auction_bar(self, instrument, dt):
# type: (Instrument, Union[datetime, date]) -> Dict
day_bar = self.get_bar(instrument, dt, "1d")
return {k: day_bar[k] for k in self.OPEN_AUCTION_BAR_FIELDS}
bar = {k: day_bar[k] for k in self.OPEN_AUCTION_BAR_FIELDS}
bar["last"] = bar["open"]
return bar

def get_settle_price(self, instrument, date):
bar = self.get_bar(instrument, date, '1d')
Expand Down
7 changes: 6 additions & 1 deletion rqalpha/data/data_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,12 @@ def get_last_price(self, order_book_id):

def all_instruments(self, types, dt=None):
# type: (List[INSTRUMENT_TYPE], Optional[datetime]) -> List[Instrument]
return [i for i in self._data_source.get_instruments(types=types) if dt is None or i.listing_at(dt)]
li = []
for i in self._data_source.get_instruments(types=types):
if dt is None or i.listing_at(dt):
li.append(i)
return li
# return [i for i in self._data_source.get_instruments(types=types) if dt is None or i.listing_at(dt)]

def instruments(self, sym_or_ids):
# type: (StrOrIter) -> Union[None, Instrument, List[Instrument]]
Expand Down
4 changes: 4 additions & 0 deletions rqalpha/data/trading_dates_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ def get_trading_dt(self, calendar_dt):
return datetime.datetime.combine(trading_date, calendar_dt.time())

def get_future_trading_date(self, dt):
# type: (datetime.datetime) -> pd.Timestamp
return self._get_future_trading_date(dt.replace(minute=0, second=0, microsecond=0))

def get_n_trading_dates_until(self, dt, n, trading_calendar_type=None):
Expand All @@ -98,6 +99,9 @@ def count_trading_dates(self, start_date, end_date, trading_calendar_type=None):

@lru_cache(512)
def _get_future_trading_date(self, dt):
# 获取指定时间所在的期货交易日
# 认为晚八点后为第二个交易日,认为晚八点至次日凌晨四点为夜盘
# 非交易日抛出 RuntimeError
dt1 = dt - datetime.timedelta(hours=4)
td = pd.Timestamp(dt1.date())
trading_dates = self.get_trading_calendar(TRADING_CALENDAR_TYPE.EXCHANGE)
Expand Down
7 changes: 6 additions & 1 deletion rqalpha/mod/rqalpha_mod_sys_accounts/api/api_stock.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,12 @@

def _get_account_position_ins(id_or_ins):
ins = assure_instrument(id_or_ins)
account = Environment.get_instance().portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK]
try:
account = Environment.get_instance().portfolio.accounts[DEFAULT_ACCOUNT_TYPE.STOCK]
except KeyError:
raise KeyError(_(
u"order_book_id: {order_book_id} needs stock account, please set and try again!"
).format(order_book_id=ins.order_book_id))
position = account.get_position(ins.order_book_id, POSITION_DIRECTION.LONG)
return account, position, ins

Expand Down
14 changes: 9 additions & 5 deletions rqalpha/mod/rqalpha_mod_sys_accounts/position_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ def get_state(self):
def before_trading(self, trading_date):
# type: (date) -> float
delta_cash = super(StockPosition, self).before_trading(trading_date)
if self.quantity == 0:
if self.quantity == 0 and not self._dividend_receivable:
return delta_cash
if self.direction != POSITION_DIRECTION.LONG:
raise RuntimeError("direction of stock position {} is not supposed to be short".format(self._order_book_id))
Expand Down Expand Up @@ -186,10 +186,14 @@ def _handle_dividend_payable(self, trading_date):
self._dividend_receivable = None
if self.dividend_reinvestment:
last_price = self.last_price
self.apply_trade(Trade.__from_create__(
None, last_price, dividend_value / last_price, SIDE.BUY, POSITION_EFFECT.OPEN, self._order_book_id
))
return 0
amount = int(Decimal(dividend_value) / Decimal(last_price))
round_lot = self._instrument.round_lot
amount = int(Decimal(amount) / Decimal(round_lot)) * round_lot
if amount > 0:
self.apply_trade(Trade.__from_create__(
None, last_price, amount, SIDE.BUY, POSITION_EFFECT.OPEN, self._order_book_id
))
return dividend_value - amount * last_price
else:
return dividend_value

Expand Down
11 changes: 7 additions & 4 deletions rqalpha/mod/rqalpha_mod_sys_analyser/mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ def _collect_daily(self, _):

for order_book_id, pos in pos_dict.items():
self._positions[account_type].append(self._to_position_record(
date, order_book_id, pos[POSITION_DIRECTION.LONG], pos[POSITION_DIRECTION.SHORT]
date, order_book_id, pos.get(POSITION_DIRECTION.LONG), pos.get(POSITION_DIRECTION.SHORT)
))

def _symbol(self, order_book_id):
Expand Down Expand Up @@ -242,9 +242,12 @@ def _to_position_record(self, date, order_book_id, long, short):
for field in ['quantity', 'last_price', 'avg_price', 'market_value']:
data[field] = self._safe_convert(getattr(long, field, None))
else:
for field in ['margin', 'contract_multiplier', 'last_price']:
data[field] = self._safe_convert(getattr(long, field))
for direction_prefix, pos in (("buy", long), ("sell", short)):
position = long or short
if position:
for field in ['margin', 'contract_multiplier', 'last_price']:
data[field] = self._safe_convert(getattr(position, field))
direction_pos_iter = ((pos.direction, pos) for pos in (long, short) if pos)
for direction_prefix, pos in direction_pos_iter:
data[direction_prefix + "_pnl"] = self._safe_convert(getattr(pos, "pnl", None))
data[direction_prefix + "_margin"] = self._safe_convert(pos.margin)
data[direction_prefix + "_quantity"] = self._safe_convert(pos.quantity)
Expand Down
7 changes: 4 additions & 3 deletions rqalpha/mod/rqalpha_mod_sys_simulation/simulation_broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,9 +76,10 @@ def register_matcher(self, instrument_type, matcher):

def get_open_orders(self, order_book_id=None):
if order_book_id is None:
return [order for account, order in self._open_orders]
return [order for account, order in chain(self._open_orders, self._open_auction_orders)]
else:
return [order for account, order in self._open_orders if order.order_book_id == order_book_id]
return [order for account, order in chain(self._open_orders, self._open_auction_orders) if
order.order_book_id == order_book_id]

def get_state(self):
return jsonpickle.dumps({
Expand Down Expand Up @@ -175,4 +176,4 @@ def _match(self, order_book_id=None):
def _check_subscribe(self, order):
if self._env.config.base.frequency == "tick" and order.order_book_id not in self._env.get_universe():
raise RuntimeError(_("{order_book_id} should be subscribed when frequency is tick.").format(
order_book_id=order.order_book_id))
order_book_id=order.order_book_id))
28 changes: 7 additions & 21 deletions rqalpha/mod/rqalpha_mod_sys_simulation/simulation_event_source.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
from rqalpha.core.events import Event, EVENT
from rqalpha.utils.exception import patch_user_exc
from rqalpha.utils.datetime_func import convert_int_to_datetime
from rqalpha.const import DEFAULT_ACCOUNT_TYPE, MARKET
from rqalpha.const import DEFAULT_ACCOUNT_TYPE
from rqalpha.utils.i18n import gettext as _


Expand All @@ -34,14 +34,8 @@ def __init__(self, env):
self._universe_changed = False
self._env.event_bus.add_listener(EVENT.POST_UNIVERSE_CHANGED, self._on_universe_changed)

if self._config.base.market == MARKET.CN:
self._get_day_bar_dt = lambda date: date.replace(hour=15, minute=0)
self._get_after_trading_dt = lambda date: date.replace(hour=15, minute=30)
elif self._config.base.market == MARKET.HK:
self._get_day_bar_dt = lambda date: date.replace(hour=16, minute=6)
self._get_after_trading_dt = lambda date: date.replace(hour=16, minute=30)
else:
raise NotImplementedError(_("Unsupported market {}".format(self._config.base.market)))
self._get_day_bar_dt = lambda date: date.replace(hour=15, minute=0)
self._get_after_trading_dt = lambda date: date.replace(hour=15, minute=30)

def _on_universe_changed(self, _):
self._universe_changed = True
Expand All @@ -58,18 +52,10 @@ def _get_universe(self):
def _get_stock_trading_minutes(self, trading_date):
trading_minutes = set()

if self._env.config.base.market == MARKET.CN:
current_dt = datetime.combine(trading_date, time(9, 31))
am_end_dt = current_dt.replace(hour=11, minute=30)
pm_start_dt = current_dt.replace(hour=13, minute=1)
pm_end_dt = current_dt.replace(hour=15, minute=0)
elif self._env.config.base.market == MARKET.HK:
current_dt = datetime.combine(trading_date, time(9, 31))
am_end_dt = current_dt.replace(hour=12, minute=0)
pm_start_dt = current_dt.replace(hour=13, minute=1)
pm_end_dt = current_dt.replace(hour=16, minute=0)
else:
raise NotImplementedError(_("Unsupported market {}".format(self._env.config.base.market)))
current_dt = datetime.combine(trading_date, time(9, 31))
am_end_dt = current_dt.replace(hour=11, minute=30)
pm_start_dt = current_dt.replace(hour=13, minute=1)
pm_end_dt = current_dt.replace(hour=15, minute=0)

delta_minute = timedelta(minutes=1)
while current_dt <= am_end_dt:
Expand Down
2 changes: 0 additions & 2 deletions rqalpha/mod/rqalpha_mod_sys_transaction_cost/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ RQAlpha 交易税费 Mod,实现了不同市场不同交易标的的税费计
{
# A股最小手续费
"cn_stock_min_commission": 5,
# 港股最小手续费
"hk_stock_min_commission": 50,
# 设置手续费乘数,默认为1
"commission_multiplier": 1,
}
Expand Down
12 changes: 0 additions & 12 deletions rqalpha/mod/rqalpha_mod_sys_transaction_cost/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@
__config__ = {
# A股最小手续费
"cn_stock_min_commission": 5,
# 港股最小手续费
"hk_stock_min_commission": 50,
# 设置手续费乘数,默认为1
"commission_multiplier": 1,
# 印花税乘数,默认为1
Expand All @@ -46,16 +44,6 @@
)
)


cli.commands['run'].params.append(
click.Option(
('-hksmc', '--hk-stock-min-commission', cli_prefix + 'hk_stock_min_commission'),
type=click.FLOAT,
help="[sys_simulation] set minimum commission in Hong Kong stock trades."
)
)


# [deprecated]
cli.commands['run'].params.append(
click.Option(
Expand Down
23 changes: 0 additions & 23 deletions rqalpha/mod/rqalpha_mod_sys_transaction_cost/deciders.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,29 +87,6 @@ def _get_tax(self, order_book_id, side, cost_money):
return cost_money * self.tax_rate * self.tax_multiplier if side == SIDE.SELL else 0


class HKStockTransactionCostDecider(StockTransactionCostDecider):
def __init__(self, commission_multiplier, min_commission, tax_multiplier):
super(HKStockTransactionCostDecider, self).__init__(0.0005, commission_multiplier, min_commission)
self.tax_rate = 0.0011
self.tax_multiplier = tax_multiplier

def _get_tax(self, order_book_id, _, cost_money):
"""
港交所收费项目繁多,按照如下逻辑计算税费:
1. 税费比例为 0.11%,不足 1 元按 1 元记,四舍五入保留两位小数(包括印花税、交易征费、交易系统使用费)。
2,五元固定费用(包括卖方收取的转手纸印花税、买方收取的过户费用)。
"""
instrument = Environment.get_instance().get_instrument(order_book_id)
if instrument.type != 'CS':
return 0
tax = cost_money * self.tax_rate * self.tax_multiplier
if tax < 1:
tax = 1
else:
tax = round(tax, 2)
return tax + 5


class CNFutureTransactionCostDecider(AbstractTransactionCostDecider):
def __init__(self, commission_multiplier):
self.commission_multiplier = commission_multiplier
Expand Down
30 changes: 11 additions & 19 deletions rqalpha/mod/rqalpha_mod_sys_transaction_cost/mod.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,12 @@
# 详细的授权流程,请联系 public@ricequant.com 获取。

from rqalpha.interface import AbstractMod
from rqalpha.const import MARKET, INSTRUMENT_TYPE
from rqalpha.const import INSTRUMENT_TYPE
from rqalpha.utils.exception import patch_user_exc
from rqalpha.utils import INST_TYPE_IN_STOCK_ACCOUNT
from rqalpha.utils.i18n import gettext as _

from .deciders import CNStockTransactionCostDecider, CNFutureTransactionCostDecider, HKStockTransactionCostDecider
from .deciders import CNStockTransactionCostDecider, CNFutureTransactionCostDecider


class TransactionCostMod(AbstractMod):
Expand All @@ -30,25 +30,17 @@ def start_up(self, env, mod_config):
raise patch_user_exc(ValueError(_(u"invalid commission multiplier or tax multiplier"
u" value: value range is [0, +∞)")))

if env.config.base.market == MARKET.CN:
for instrument_type in INST_TYPE_IN_STOCK_ACCOUNT:
if instrument_type == INSTRUMENT_TYPE.PUBLIC_FUND:
continue
env.set_transaction_cost_decider(instrument_type, CNStockTransactionCostDecider(
mod_config.commission_multiplier, mod_config.cn_stock_min_commission,
mod_config.tax_multiplier
))

env.set_transaction_cost_decider(INSTRUMENT_TYPE.FUTURE, CNFutureTransactionCostDecider(
mod_config.commission_multiplier
for instrument_type in INST_TYPE_IN_STOCK_ACCOUNT:
if instrument_type == INSTRUMENT_TYPE.PUBLIC_FUND:
continue
env.set_transaction_cost_decider(instrument_type, CNStockTransactionCostDecider(
mod_config.commission_multiplier, mod_config.cn_stock_min_commission,
mod_config.tax_multiplier
))

elif env.config.base.market == MARKET.HK:
for instrument_type in INST_TYPE_IN_STOCK_ACCOUNT:
env.set_transaction_cost_decider(instrument_type, HKStockTransactionCostDecider(
mod_config.commission_multiplier, mod_config.hk_stock_min_commission,
mod_config.tax_multiplier
))
env.set_transaction_cost_decider(INSTRUMENT_TYPE.FUTURE, CNFutureTransactionCostDecider(
mod_config.commission_multiplier
))

def tear_down(self, code, exception=None):
pass
6 changes: 6 additions & 0 deletions rqalpha/model/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@
# 未经米筐科技授权,任何个人不得出于任何商业目的使用本软件(包括但不限于向第三方提供、销售、出租、出借、转让本软件、本软件的衍生产品、引用或借鉴了本软件功能或源代码的产品或服务),任何法人或其他组织不得出于任何目的使用本软件,否则米筐科技有权追究相应的知识产权侵权责任。
# 在此前提下,对本软件的使用同样需要遵守 Apache 2.0 许可,Apache 2.0 许可与本许可冲突之处,以本许可为准。
# 详细的授权流程,请联系 public@ricequant.com 获取。

from .order import Order
from .trade import Trade
from .instrument import Instrument
from .bar import BarMap, BarObject, PartialBarObject
from .tick import TickObject
7 changes: 2 additions & 5 deletions rqalpha/model/bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
class PartialBarObject(metaclass=PropertyReprMeta):
# 用于 open_auction
__repr_properties__ = (
"order_book_id", "datetime", "open", "limit_up", "limit_down"
"order_book_id", "datetime", "open", "limit_up", "limit_down", "last"
)

def __init__(self, instrument, data, dt=None):
Expand Down Expand Up @@ -112,10 +112,7 @@ def last(self):
"""
[float] 当前最新价
"""
try:
return self._data["last"]
except KeyError:
return self.open
return self._data["last"]

@cached_property
def volume(self):
Expand Down
Loading

0 comments on commit 43dad6f

Please sign in to comment.