mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-04 03:21:00 +08:00
support parallel HF trading
This commit is contained in:
@@ -242,6 +242,7 @@ class Exchange:
|
||||
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)
|
||||
# NOTE: order will be changed in this function
|
||||
trade_val, trade_cost = self._calc_trade_info_by_order(
|
||||
order, trade_account.current if trade_account else position
|
||||
)
|
||||
@@ -256,16 +257,6 @@ class Exchange:
|
||||
|
||||
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):
|
||||
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
|
||||
|
||||
**NOTE**: Order will be changed in this function
|
||||
|
||||
:param order:
|
||||
:param position: Position
|
||||
:return: trade_val, trade_cost
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import copy
|
||||
import warnings
|
||||
import pandas as pd
|
||||
from typing import Union
|
||||
from typing import List, Union
|
||||
|
||||
from qlib.backtest.report import Indicator
|
||||
|
||||
@@ -317,6 +317,15 @@ class NestedExecutor(BaseExecutor):
|
||||
class SimulatorExecutor(BaseExecutor):
|
||||
"""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__(
|
||||
self,
|
||||
time_per_step: str,
|
||||
@@ -328,6 +337,7 @@ class SimulatorExecutor(BaseExecutor):
|
||||
track_data: bool = False,
|
||||
trade_exchange: Exchange = None,
|
||||
common_infra: CommonInfrastructure = None,
|
||||
trade_type: str = TT_PARAL,
|
||||
**kwargs,
|
||||
):
|
||||
"""
|
||||
@@ -336,6 +346,8 @@ class SimulatorExecutor(BaseExecutor):
|
||||
trade_exchange : Exchange
|
||||
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
|
||||
trade_type: str
|
||||
please refer to the doc of `TT_SERIAL` & `TT_PARAL`
|
||||
"""
|
||||
super(SimulatorExecutor, self).__init__(
|
||||
time_per_step=time_per_step,
|
||||
@@ -351,6 +363,8 @@ class SimulatorExecutor(BaseExecutor):
|
||||
if trade_exchange is not None:
|
||||
self.trade_exchange = trade_exchange
|
||||
|
||||
self.trade_type = trade_type
|
||||
|
||||
def reset_common_infra(self, common_infra):
|
||||
"""
|
||||
reset infrastructure for trading
|
||||
@@ -360,14 +374,45 @@ class SimulatorExecutor(BaseExecutor):
|
||||
if common_infra.has("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):
|
||||
|
||||
trade_step = self.trade_calendar.get_trade_step()
|
||||
trade_start_time, trade_end_time = self.trade_calendar.get_step_time(trade_step)
|
||||
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:
|
||||
# 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(
|
||||
order, trade_account=self.trade_account
|
||||
)
|
||||
@@ -404,6 +449,7 @@ class SimulatorExecutor(BaseExecutor):
|
||||
# do nothing
|
||||
pass
|
||||
|
||||
# Account will not be changed in this function
|
||||
self.trade_account.update_bar_end(
|
||||
trade_start_time,
|
||||
trade_end_time,
|
||||
|
||||
@@ -718,8 +718,11 @@ class FileOrderStrategy(BaseStrategy):
|
||||
----------
|
||||
file : Union[IO, str, Path]
|
||||
this parameters will specify the info of expected orders
|
||||
|
||||
Here is an example of the content
|
||||
|
||||
1) Amount (**adjusted**) based strategy
|
||||
|
||||
datetime,instrument,amount,direction
|
||||
20200102, SH600519, 1000, sell
|
||||
20200103, SH600519, 1000, buy
|
||||
|
||||
Reference in New Issue
Block a user