1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-14 00:06:58 +08:00

support parallel HF trading

This commit is contained in:
Young
2021-07-03 08:46:09 +00:00
parent a401f1eafe
commit ef7fe8aa75
3 changed files with 55 additions and 13 deletions

View File

@@ -242,6 +242,7 @@ class Exchange:
raise ValueError("trade_account and position can only choose one") raise ValueError("trade_account and position can only choose one")
trade_price = self.get_deal_price(order.stock_id, order.start_time, order.end_time) trade_price = self.get_deal_price(order.stock_id, order.start_time, order.end_time)
# NOTE: order will be changed in this function
trade_val, trade_cost = self._calc_trade_info_by_order( trade_val, trade_cost = self._calc_trade_info_by_order(
order, trade_account.current if trade_account else position order, trade_account.current if trade_account else position
) )
@@ -256,16 +257,6 @@ class Exchange:
return trade_val, trade_cost, trade_price return trade_val, trade_cost, trade_price
def create_order(self, code, amount, start_time, end_time, direction) -> Order:
return Order(
stock_id=code,
amount=amount,
start_time=start_time,
end_time=end_time,
direction=direction,
factor=self.get_factor(code, start_time, end_time),
)
def get_quote_info(self, stock_id, start_time, end_time): def get_quote_info(self, stock_id, start_time, end_time):
return resam_ts_data(self.quote[stock_id], start_time, end_time, method="last").iloc[0] return resam_ts_data(self.quote[stock_id], start_time, end_time, method="last").iloc[0]
@@ -471,6 +462,8 @@ class Exchange:
""" """
Calculation of trade info Calculation of trade info
**NOTE**: Order will be changed in this function
:param order: :param order:
:param position: Position :param position: Position
:return: trade_val, trade_cost :return: trade_val, trade_cost

View File

@@ -1,7 +1,7 @@
import copy import copy
import warnings import warnings
import pandas as pd import pandas as pd
from typing import Union from typing import List, Union
from qlib.backtest.report import Indicator from qlib.backtest.report import Indicator
@@ -317,6 +317,15 @@ class NestedExecutor(BaseExecutor):
class SimulatorExecutor(BaseExecutor): class SimulatorExecutor(BaseExecutor):
"""Executor that simulate the true market""" """Executor that simulate the true market"""
# available trade_types
TT_SERIAL = "serial"
## The orders will be executed serially in a sequence
# In each trading step, it is possible that users sell instruments first and use the money to buy new instruments
TT_PARAL = "parallel"
## The orders will be executed parallelly
# In each trading step, if users try to sell instruments first and buy new instruments with money, failure will
# occur
def __init__( def __init__(
self, self,
time_per_step: str, time_per_step: str,
@@ -328,6 +337,7 @@ class SimulatorExecutor(BaseExecutor):
track_data: bool = False, track_data: bool = False,
trade_exchange: Exchange = None, trade_exchange: Exchange = None,
common_infra: CommonInfrastructure = None, common_infra: CommonInfrastructure = None,
trade_type: str = TT_PARAL,
**kwargs, **kwargs,
): ):
""" """
@@ -336,6 +346,8 @@ class SimulatorExecutor(BaseExecutor):
trade_exchange : Exchange trade_exchange : Exchange
exchange that provides market info, used to deal order and generate report exchange that provides market info, used to deal order and generate report
- If `trade_exchange` is None, self.trade_exchange will be set with common_infra - If `trade_exchange` is None, self.trade_exchange will be set with common_infra
trade_type: str
please refer to the doc of `TT_SERIAL` & `TT_PARAL`
""" """
super(SimulatorExecutor, self).__init__( super(SimulatorExecutor, self).__init__(
time_per_step=time_per_step, time_per_step=time_per_step,
@@ -351,6 +363,8 @@ class SimulatorExecutor(BaseExecutor):
if trade_exchange is not None: if trade_exchange is not None:
self.trade_exchange = trade_exchange self.trade_exchange = trade_exchange
self.trade_type = trade_type
def reset_common_infra(self, common_infra): def reset_common_infra(self, common_infra):
""" """
reset infrastructure for trading reset infrastructure for trading
@@ -360,14 +374,45 @@ class SimulatorExecutor(BaseExecutor):
if common_infra.has("trade_exchange"): if common_infra.has("trade_exchange"):
self.trade_exchange = common_infra.get("trade_exchange") self.trade_exchange = common_infra.get("trade_exchange")
def _get_order_iterator(self, trade_decision: BaseTradeDecision) -> List[Order]:
"""
Parameters
----------
trade_decision : BaseTradeDecision
the trade decision given by the strategy
Returns
-------
List[Order]:
get a list orders according to `self.trade_type`
"""
orders = trade_decision.get_decision()
if self.trade_type == self.TT_SERIAL:
# Orders will be traded in a parallel way
order_it = orders
elif self.trade_type == self.TT_PARAL:
# NOTE: !!!!!!!
# Assumption: there will not be orders in different trading direction in a single step of a strategy !!!!
# The parallel trading failure will be caused only by the confliction of money
# Therefore, make the buying go first will make sure the confliction happen.
# It equals to parallel trading after sorting the order by direction
order_it = sorted(orders, key=lambda order: -order.direction)
else:
raise NotImplementedError(f"This type of input is not supported")
return order_it
def execute(self, trade_decision: BaseTradeDecision): def execute(self, trade_decision: BaseTradeDecision):
trade_step = self.trade_calendar.get_trade_step() trade_step = self.trade_calendar.get_trade_step()
trade_start_time, trade_end_time = self.trade_calendar.get_step_time(trade_step) trade_start_time, trade_end_time = self.trade_calendar.get_step_time(trade_step)
execute_result = [] execute_result = []
for order in trade_decision.get_decision():
for order in self._get_order_iterator(trade_decision):
if self.trade_exchange.check_order(order) is True: if self.trade_exchange.check_order(order) is True:
# execute the order # execute the order.
# NOTE: The trade_account will be changed in this function
trade_val, trade_cost, trade_price = self.trade_exchange.deal_order( trade_val, trade_cost, trade_price = self.trade_exchange.deal_order(
order, trade_account=self.trade_account order, trade_account=self.trade_account
) )
@@ -404,6 +449,7 @@ class SimulatorExecutor(BaseExecutor):
# do nothing # do nothing
pass pass
# Account will not be changed in this function
self.trade_account.update_bar_end( self.trade_account.update_bar_end(
trade_start_time, trade_start_time,
trade_end_time, trade_end_time,

View File

@@ -718,8 +718,11 @@ class FileOrderStrategy(BaseStrategy):
---------- ----------
file : Union[IO, str, Path] file : Union[IO, str, Path]
this parameters will specify the info of expected orders this parameters will specify the info of expected orders
Here is an example of the content Here is an example of the content
1) Amount (**adjusted**) based strategy
datetime,instrument,amount,direction datetime,instrument,amount,direction
20200102, SH600519, 1000, sell 20200102, SH600519, 1000, sell
20200103, SH600519, 1000, buy 20200103, SH600519, 1000, buy