mirror of
https://github.com/microsoft/qlib.git
synced 2026-07-11 23:06:58 +08:00
new twap (more even)
This commit is contained in:
@@ -34,11 +34,16 @@ class TWAPStrategy(BaseStrategy):
|
|||||||
|
|
||||||
super(TWAPStrategy, self).reset(outer_trade_decision=outer_trade_decision, **kwargs)
|
super(TWAPStrategy, self).reset(outer_trade_decision=outer_trade_decision, **kwargs)
|
||||||
if outer_trade_decision is not None:
|
if outer_trade_decision is not None:
|
||||||
self.trade_amount = {}
|
self.trade_amount_remain = {}
|
||||||
for order in outer_trade_decision.get_decision():
|
for order in outer_trade_decision.get_decision():
|
||||||
self.trade_amount[order.stock_id] = order.amount
|
self.trade_amount_remain[order.stock_id] = order.amount
|
||||||
|
|
||||||
def generate_trade_decision(self, execute_result=None):
|
def generate_trade_decision(self, execute_result=None):
|
||||||
|
# NOTE: corner cases!!!
|
||||||
|
# - If using upperbound round, please don't sell the amount which should in next step
|
||||||
|
# - the coordinate of the amount between steps is hard to be dealed between steps in the same level. It
|
||||||
|
# is easier to be dealed in upper steps
|
||||||
|
|
||||||
# strategy is not available. Give an empty decision
|
# strategy is not available. Give an empty decision
|
||||||
if len(self.outer_trade_decision.get_decision()) == 0:
|
if len(self.outer_trade_decision.get_decision()) == 0:
|
||||||
return TradeDecisionWO(order_list=[], strategy=self)
|
return TradeDecisionWO(order_list=[], strategy=self)
|
||||||
@@ -53,16 +58,28 @@ class TWAPStrategy(BaseStrategy):
|
|||||||
# It is not time to start trading or trading has ended.
|
# It is not time to start trading or trading has ended.
|
||||||
return TradeDecisionWO(order_list=[], strategy=self)
|
return TradeDecisionWO(order_list=[], strategy=self)
|
||||||
|
|
||||||
rel_trade_step = trade_step - start_idx # trade_step relative to start_idx
|
rel_trade_step = trade_step - start_idx # trade_step relative to start_idx (number of steps has already passed)
|
||||||
|
|
||||||
# update the order amount
|
# update the order amount
|
||||||
if execute_result is not None:
|
if execute_result is not None:
|
||||||
for order, _, _, _ in execute_result:
|
for order, _, _, _ in execute_result:
|
||||||
self.trade_amount[order.stock_id] -= order.deal_amount
|
self.trade_amount_remain[order.stock_id] -= order.deal_amount
|
||||||
|
|
||||||
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)
|
||||||
order_list = []
|
order_list = []
|
||||||
for order in self.outer_trade_decision.get_decision():
|
for order in self.outer_trade_decision.get_decision():
|
||||||
|
# the expected trade amount after current step
|
||||||
|
amount_expect = order.amount / trade_len * (rel_trade_step + 1)
|
||||||
|
|
||||||
|
# remain amount
|
||||||
|
amount_remain = self.trade_amount_remain[order.stock_id]
|
||||||
|
|
||||||
|
# the amount has already been finished now.
|
||||||
|
amount_finished = order.amount - amount_remain
|
||||||
|
|
||||||
|
# the expected amount of current step
|
||||||
|
amount_delta = amount_expect - amount_finished
|
||||||
|
|
||||||
# Don't peek the future information
|
# Don't peek the future information
|
||||||
# if not self.trade_exchange.is_stock_tradable(
|
# if not self.trade_exchange.is_stock_tradable(
|
||||||
# stock_id=order.stock_id, start_time=trade_start_time, end_time=trade_end_time
|
# stock_id=order.stock_id, start_time=trade_start_time, end_time=trade_end_time
|
||||||
@@ -71,38 +88,26 @@ class TWAPStrategy(BaseStrategy):
|
|||||||
_amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(
|
_amount_trade_unit = self.trade_exchange.get_amount_of_trade_unit(
|
||||||
stock_id=order.stock_id, start_time=order.start_time, end_time=order.end_time
|
stock_id=order.stock_id, start_time=order.start_time, end_time=order.end_time
|
||||||
)
|
)
|
||||||
_order_amount = None
|
|
||||||
# considering trade unit
|
# round the amount_delta by trade_unit and clip by remain
|
||||||
|
# NOTE: this could be more than expected.
|
||||||
if _amount_trade_unit is None:
|
if _amount_trade_unit is None:
|
||||||
# divide the order into equal parts, and trade one part
|
# divide the order into equal parts, and trade one part
|
||||||
_order_amount = self.trade_amount[order.stock_id] / (trade_len - rel_trade_step)
|
amount_delta_target = amount_delta
|
||||||
# without considering trade unit
|
|
||||||
else:
|
else:
|
||||||
# divide the order into equal parts, and trade one part
|
amount_delta_target = min(
|
||||||
# calculate the total count of trade units to trade
|
np.round(amount_delta / _amount_trade_unit) * _amount_trade_unit, amount_remain
|
||||||
trade_unit_cnt = int(self.trade_amount[order.stock_id] // _amount_trade_unit)
|
|
||||||
# calculate the amount of one part, ceil the amount
|
|
||||||
# floor((trade_unit_cnt + trade_len - rel_trade_step) / (trade_len - rel_trade_step + 1)) == ceil(trade_unit_cnt / (trade_len - rel_trade_step + 1))
|
|
||||||
_order_amount = (
|
|
||||||
(trade_unit_cnt + trade_len - rel_trade_step - 1)
|
|
||||||
// (trade_len - rel_trade_step)
|
|
||||||
* _amount_trade_unit
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if order.direction == order.SELL:
|
# handle last step to make sure all positions have gone
|
||||||
# sell all amount at last
|
# necessity: the last step can't be rounded to the a unit (e.g. reminder < 0.5 unit)
|
||||||
if self.trade_amount[order.stock_id] > 1e-5 and (
|
if rel_trade_step == trade_len - 1:
|
||||||
_order_amount < 1e-5 or rel_trade_step == trade_len - 1
|
amount_delta_target = amount_remain
|
||||||
):
|
|
||||||
_order_amount = self.trade_amount[order.stock_id]
|
|
||||||
|
|
||||||
_order_amount = min(_order_amount, self.trade_amount[order.stock_id])
|
|
||||||
|
|
||||||
if _order_amount > 1e-5:
|
|
||||||
|
|
||||||
|
if amount_delta_target > 1e-5:
|
||||||
_order = Order(
|
_order = Order(
|
||||||
stock_id=order.stock_id,
|
stock_id=order.stock_id,
|
||||||
amount=_order_amount,
|
amount=amount_delta_target,
|
||||||
start_time=trade_start_time,
|
start_time=trade_start_time,
|
||||||
end_time=trade_end_time,
|
end_time=trade_end_time,
|
||||||
direction=order.direction, # 1 for buy
|
direction=order.direction, # 1 for buy
|
||||||
|
|||||||
Reference in New Issue
Block a user