1
0
mirror of https://github.com/microsoft/qlib.git synced 2026-07-17 17:34:35 +08:00

Merge pull request #66 from microsoft/support-non-fullfill-rate

support non-full-fill rate of executor
This commit is contained in:
Pengrong Zhu
2020-11-10 20:52:10 +08:00
committed by GitHub
4 changed files with 25 additions and 31 deletions

View File

@@ -49,42 +49,38 @@ class Account:
return self.current.position["cash"] return self.current.position["cash"]
def update_state_from_order(self, order, trade_val, cost, trade_price): def update_state_from_order(self, order, trade_val, cost, trade_price):
# update cash
if order.direction == Order.SELL: # 0 for sell
self.current.position["cash"] += trade_val - cost
elif order.direction == Order.BUY: # 1 for buy
self.current.position["cash"] -= trade_val + cost
else:
raise NotImplementedError("{} ".format(order.direction))
# update turnover # update turnover
self.to += trade_val self.to += trade_val
# update cost # update cost
self.ct += cost self.ct += cost
# update return # update return
# update self.rtn from order # update self.rtn from order
trade_amount = trade_val / trade_price
if order.direction == Order.SELL: # 0 for sell if order.direction == Order.SELL: # 0 for sell
# when sell stock, get profit from price change # when sell stock, get profit from price change
profit = trade_val - self.current.get_stock_price(order.stock_id) * order.deal_amount profit = trade_val - self.current.get_stock_price(order.stock_id) * trade_amount
self.rtn += profit # note here do not consider cost self.rtn += profit # note here do not consider cost
elif order.direction == Order.BUY: # 1 for buy elif order.direction == Order.BUY: # 1 for buy
# when buy stock, we get return for the rtn computing method # when buy stock, we get return for the rtn computing method
# profit in buy order is to make self.rtn is consistent with self.earning at the end of date # profit in buy order is to make self.rtn is consistent with self.earning at the end of date
profit = self.current.get_stock_price(order.stock_id) * order.deal_amount - trade_val profit = self.current.get_stock_price(order.stock_id) * trade_amount - trade_val
self.rtn += profit self.rtn += profit
def update_order(self, order, trade_val, cost, trade_price): def update_order(self, order, trade_val, cost, trade_price):
# if stock is sold out, no stock price information in Position, then we should update account first, then update current position # if stock is sold out, no stock price information in Position, then we should update account first, then update current position
# if stock is bought, there is no stock in current position, update current, then update account # if stock is bought, there is no stock in current position, update current, then update account
# The cost will be substracted from the cash at last. So the trading logic can ignore the cost calculation
trade_amount = trade_val / trade_price
if order.direction == Order.SELL: if order.direction == Order.SELL:
# sell stock # sell stock
self.update_state_from_order(order, trade_val, cost, trade_price) self.update_state_from_order(order, trade_val, cost, trade_price)
# update current position # update current position
# for may sell all of stock_id # for may sell all of stock_id
self.current.update_order(order, trade_price) self.current.update_order(order, trade_val, cost, trade_price)
else: else:
# buy stock # buy stock
# deal order, then update state # deal order, then update state
self.current.update_order(order, trade_price) self.current.update_order(order, trade_val, cost, trade_price)
self.update_state_from_order(order, trade_val, cost, trade_price) self.update_state_from_order(order, trade_val, cost, trade_price)
def update_daily_end(self, today, trader): def update_daily_end(self, today, trader):

View File

@@ -208,14 +208,9 @@ class Exchange:
# If the order can only be deal 0 trade_val. Nothing to be updated # If the order can only be deal 0 trade_val. Nothing to be updated
# Otherwise, it will result some stock with 0 amount in the position # Otherwise, it will result some stock with 0 amount in the position
if trade_account: if trade_account:
trade_account.update_order( trade_account.update_order(order=order, trade_val=trade_val, cost=trade_cost, trade_price=trade_price)
order=order,
trade_val=trade_val,
cost=trade_cost,
trade_price=trade_price,
)
elif position: elif position:
position.update_order(order, trade_price) position.update_order(order=order, trade_val=trade_val, cost=trade_cost, trade_price=trade_price)
return trade_val, trade_cost, trade_price return trade_val, trade_cost, trade_price

View File

@@ -43,38 +43,44 @@ class Position:
self.position[stock_id]["price"] = price self.position[stock_id]["price"] = price
self.position[stock_id]["weight"] = 0 # update the weight in the end of the trade date self.position[stock_id]["weight"] = 0 # update the weight in the end of the trade date
def buy_stock(self, stock_id, amount, price): def buy_stock(self, stock_id, trade_val, cost, trade_price):
trade_amount = trade_val / trade_price
if stock_id not in self.position: if stock_id not in self.position:
self.init_stock(stock_id=stock_id, amount=amount, price=price) self.init_stock(stock_id=stock_id, amount=trade_amount, price=trade_price)
else: else:
# exist, add amount # exist, add amount
self.position[stock_id]["amount"] += amount self.position[stock_id]["amount"] += trade_amount
def sell_stock(self, stock_id, amount): self.position["cash"] -= trade_val + cost
def sell_stock(self, stock_id, trade_val, cost, trade_price):
trade_amount = trade_val / trade_price
if stock_id not in self.position: if stock_id not in self.position:
raise KeyError("{} not in current position".format(stock_id)) raise KeyError("{} not in current position".format(stock_id))
else: else:
# decrease the amount of stock # decrease the amount of stock
self.position[stock_id]["amount"] -= amount self.position[stock_id]["amount"] -= trade_amount
# check if to delete # check if to delete
if self.position[stock_id]["amount"] < -1e-5: if self.position[stock_id]["amount"] < -1e-5:
raise ValueError( raise ValueError(
"only have {} {}, require {}".format(self.position[stock_id]["amount"], stock_id, amount) "only have {} {}, require {}".format(self.position[stock_id]["amount"], stock_id, trade_amount)
) )
elif abs(self.position[stock_id]["amount"]) <= 1e-5: elif abs(self.position[stock_id]["amount"]) <= 1e-5:
self.del_stock(stock_id) self.del_stock(stock_id)
self.position["cash"] += trade_val - cost
def del_stock(self, stock_id): def del_stock(self, stock_id):
del self.position[stock_id] del self.position[stock_id]
def update_order(self, order, trade_price): def update_order(self, order, trade_val, cost, trade_price):
# handle order, order is a order class, defined in exchange.py # handle order, order is a order class, defined in exchange.py
if order.direction == Order.BUY: if order.direction == Order.BUY:
# BUY # BUY
self.buy_stock(stock_id=order.stock_id, amount=order.deal_amount, price=trade_price) self.buy_stock(order.stock_id, trade_val, cost, trade_price)
elif order.direction == Order.SELL: elif order.direction == Order.SELL:
# SELL # SELL
self.sell_stock(stock_id=order.stock_id, amount=order.deal_amount) self.sell_stock(order.stock_id, trade_val, cost, trade_price)
else: else:
raise NotImplementedError("do not suppotr order direction {}".format(order.direction)) raise NotImplementedError("do not suppotr order direction {}".format(order.direction))

View File

@@ -238,7 +238,6 @@ class TopkDropoutStrategy(BaseStrategy, ListAdjustTimer):
sell_order_list = [] sell_order_list = []
buy_order_list = [] buy_order_list = []
# load score # load score
cash = current_temp.get_cash()
current_stock_list = current_temp.get_stock_list() current_stock_list = current_temp.get_stock_list()
last = score_series.reindex(current_stock_list).sort_values(ascending=False).index last = score_series.reindex(current_stock_list).sort_values(ascending=False).index
today = ( today = (
@@ -275,8 +274,6 @@ class TopkDropoutStrategy(BaseStrategy, ListAdjustTimer):
if trade_exchange.check_order(sell_order): if trade_exchange.check_order(sell_order):
sell_order_list.append(sell_order) sell_order_list.append(sell_order)
trade_val, trade_cost, trade_price = trade_exchange.deal_order(sell_order, position=current_temp) trade_val, trade_cost, trade_price = trade_exchange.deal_order(sell_order, position=current_temp)
# update cash
cash += trade_val - trade_cost
# sold # sold
del self.stock_count[code] del self.stock_count[code]
else: else:
@@ -292,7 +289,7 @@ class TopkDropoutStrategy(BaseStrategy, ListAdjustTimer):
# buy new stock # buy new stock
# note the current has been changed # note the current has been changed
current_stock_list = current_temp.get_stock_list() current_stock_list = current_temp.get_stock_list()
value = cash * self.risk_degree / len(buy) if len(buy) > 0 else 0 value = current_temp.get_cash() * self.risk_degree / len(buy) if len(buy) > 0 else 0
# open_cost should be considered in the real trading environment, while the backtest in evaluate.py does not consider it # open_cost should be considered in the real trading environment, while the backtest in evaluate.py does not consider it
# as the aim of demo is to accomplish same strategy as evaluate.py, so comment out this line # as the aim of demo is to accomplish same strategy as evaluate.py, so comment out this line