Skip to content

Commit 5100a5a

Browse files
committed
support parallel HF trading
1 parent a401f1e commit 5100a5a

File tree

3 files changed

+55
-13
lines changed

3 files changed

+55
-13
lines changed

qlib/backtest/exchange.py

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,7 @@ def deal_order(self, order, trade_account=None, position=None):
242242
raise ValueError("trade_account and position can only choose one")
243243

244244
trade_price = self.get_deal_price(order.stock_id, order.start_time, order.end_time)
245+
# NOTE: order will be changed in this function
245246
trade_val, trade_cost = self._calc_trade_info_by_order(
246247
order, trade_account.current if trade_account else position
247248
)
@@ -256,16 +257,6 @@ def deal_order(self, order, trade_account=None, position=None):
256257

257258
return trade_val, trade_cost, trade_price
258259

259-
def create_order(self, code, amount, start_time, end_time, direction) -> Order:
260-
return Order(
261-
stock_id=code,
262-
amount=amount,
263-
start_time=start_time,
264-
end_time=end_time,
265-
direction=direction,
266-
factor=self.get_factor(code, start_time, end_time),
267-
)
268-
269260
def get_quote_info(self, stock_id, start_time, end_time):
270261
return resam_ts_data(self.quote[stock_id], start_time, end_time, method="last").iloc[0]
271262

@@ -471,6 +462,8 @@ def _calc_trade_info_by_order(self, order, position):
471462
"""
472463
Calculation of trade info
473464
465+
**NOTE**: Order will be changed in this function
466+
474467
:param order:
475468
:param position: Position
476469
:return: trade_val, trade_cost

qlib/backtest/executor.py

Lines changed: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import copy
22
import warnings
33
import pandas as pd
4-
from typing import Union
4+
from typing import List, Union
55

66
from qlib.backtest.report import Indicator
77

@@ -317,6 +317,15 @@ def get_all_executors(self):
317317
class SimulatorExecutor(BaseExecutor):
318318
"""Executor that simulate the true market"""
319319

320+
# available trade_types
321+
TT_SERIAL = "serial"
322+
## The orders will be executed serially in a sequence
323+
# In each trading step, it is possible that users sell instruments first and use the money to buy new instruments
324+
TT_PARAL = "parallel"
325+
## The orders will be executed parallelly
326+
# In each trading step, if users try to sell instruments first and buy new instruments with money, failure will
327+
# occure
328+
320329
def __init__(
321330
self,
322331
time_per_step: str,
@@ -328,6 +337,7 @@ def __init__(
328337
track_data: bool = False,
329338
trade_exchange: Exchange = None,
330339
common_infra: CommonInfrastructure = None,
340+
trade_type: str = TT_PARAL,
331341
**kwargs,
332342
):
333343
"""
@@ -336,6 +346,8 @@ def __init__(
336346
trade_exchange : Exchange
337347
exchange that provides market info, used to deal order and generate report
338348
- If `trade_exchange` is None, self.trade_exchange will be set with common_infra
349+
trade_type: str
350+
please refer to the doc of `TT_SERIAL` & `TT_PARAL`
339351
"""
340352
super(SimulatorExecutor, self).__init__(
341353
time_per_step=time_per_step,
@@ -351,6 +363,8 @@ def __init__(
351363
if trade_exchange is not None:
352364
self.trade_exchange = trade_exchange
353365

366+
self.trade_type = trade_type
367+
354368
def reset_common_infra(self, common_infra):
355369
"""
356370
reset infrastructure for trading
@@ -360,14 +374,45 @@ def reset_common_infra(self, common_infra):
360374
if common_infra.has("trade_exchange"):
361375
self.trade_exchange = common_infra.get("trade_exchange")
362376

377+
def _get_order_iterator(self, trade_decision: BaseTradeDecision) -> List[Order]:
378+
"""
379+
380+
Parameters
381+
----------
382+
trade_decision : BaseTradeDecision
383+
the trade decision given by the strategy
384+
385+
Returns
386+
-------
387+
List[Order]:
388+
get a list orders according to `self.trade_type`
389+
"""
390+
orders = trade_decision.get_decision()
391+
392+
if self.trade_type == self.TT_SERIAL:
393+
# Orders will be traded in a parallel way
394+
order_it = orders
395+
elif self.trade_type == self.TT_PARAL:
396+
# NOTE: !!!!!!!
397+
# Assumption: there will not be orders in different trading direction in a single step of a strategy !!!!
398+
# The parallel trading failure will be caused only by the confliction of money
399+
# Therefore, make the buying go first will make sure the confliction happen.
400+
# It equals to parallel trading after sorting the order by direction
401+
order_it = sorted(orders, key=lambda order: -order.direction)
402+
else:
403+
raise NotImplementedError(f"This type of input is not supported")
404+
return order_it
405+
363406
def execute(self, trade_decision: BaseTradeDecision):
364407

365408
trade_step = self.trade_calendar.get_trade_step()
366409
trade_start_time, trade_end_time = self.trade_calendar.get_step_time(trade_step)
367410
execute_result = []
368-
for order in trade_decision.get_decision():
411+
412+
for order in self._get_order_iterator(trade_decision):
369413
if self.trade_exchange.check_order(order) is True:
370-
# execute the order
414+
# execute the order.
415+
# NOTE: The trade_account will be changed in this function
371416
trade_val, trade_cost, trade_price = self.trade_exchange.deal_order(
372417
order, trade_account=self.trade_account
373418
)
@@ -404,6 +449,7 @@ def execute(self, trade_decision: BaseTradeDecision):
404449
# do nothing
405450
pass
406451

452+
# Account will not be changed in this function
407453
self.trade_account.update_bar_end(
408454
trade_start_time,
409455
trade_end_time,

qlib/contrib/strategy/rule_strategy.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -718,8 +718,11 @@ def __init__(self, file: Union[IO, str, Path], index_range: Tuple[int, int] = No
718718
----------
719719
file : Union[IO, str, Path]
720720
this parameters will specify the info of expected orders
721+
721722
Here is an example of the content
722723
724+
1) Amount (**adjusted**) based strategy
725+
723726
datetime,instrument,amount,direction
724727
20200102, SH600519, 1000, sell
725728
20200103, SH600519, 1000, buy

0 commit comments

Comments
 (0)