@ -567,7 +567,7 @@ class Exchange:
rate : float , params : Dict = { } ) - > Dict [ str , Any ] :
order_id = f ' dry_run_ { side } _ { datetime . now ( ) . timestamp ( ) } '
_amount = self . amount_to_precision ( pair , amount )
dry_order = {
dry_order : Dict [ str , Any ] = {
' id ' : order_id ,
' symbol ' : pair ,
' price ' : rate ,
@ -583,26 +583,94 @@ class Exchange:
' fee ' : None ,
' info ' : { }
}
self . _store_dry_order ( dry_order , pair )
if dry_order [ " type " ] in [ " stop_loss_limit " , " stop-loss-limit " ] :
dry_order [ " info " ] = { " stopPrice " : dry_order [ " price " ] }
if dry_order [ " type " ] == " market " :
# Update market order pricing
average = self . get_dry_market_fill_price ( pair , side , amount , rate )
dry_order . update ( {
' average ' : average ,
' cost ' : dry_order [ ' amount ' ] * average ,
} )
dry_order = self . add_dry_order_fee ( pair , dry_order )
dry_order = self . check_dry_limit_order_filled ( dry_order )
self . _dry_run_open_orders [ dry_order [ " id " ] ] = dry_order
# Copy order and close it - so the returned order is open unless it's a market order
return dry_order
def _store_dry_order ( self , dry_order : Dict , pair : str ) - > None :
closed_order = dry_order . copy ( )
if closed_order [ ' type ' ] in [ " market " , " limit " ] :
closed_order . update ( {
' status ' : ' closed ' ,
' filled ' : closed_order [ ' amount ' ] ,
' remaining ' : 0 ,
' fee ' : {
' currency ' : self . get_pair_quote_currency ( pair ) ,
' cost ' : dry_order [ ' cost ' ] * self . get_fee ( pair ) ,
' rate ' : self . get_fee ( pair )
}
} )
if closed_order [ " type " ] in [ " stop_loss_limit " , " stop-loss-limit " ] :
closed_order [ " info " ] . update ( { " stopPrice " : closed_order [ " price " ] } )
self . _dry_run_open_orders [ closed_order [ " id " ] ] = closed_order
def add_dry_order_fee ( self , pair : str , dry_order : Dict [ str , Any ] ) - > Dict [ str , Any ] :
dry_order . update ( {
' fee ' : {
' currency ' : self . get_pair_quote_currency ( pair ) ,
' cost ' : dry_order [ ' cost ' ] * self . get_fee ( pair ) ,
' rate ' : self . get_fee ( pair )
}
} )
return dry_order
def get_dry_market_fill_price ( self , pair : str , side : str , amount : float , rate : float ) - > float :
"""
Get the market order fill price based on orderbook interpolation
"""
if self . exchange_has ( ' fetchL2OrderBook ' ) :
ob = self . fetch_l2_order_book ( pair , 20 )
ob_type = ' asks ' if side == ' buy ' else ' bids '
remaining_amount = amount
filled_amount = 0
for book_entry in ob [ ob_type ] :
book_entry_price = book_entry [ 0 ]
book_entry_coin_volume = book_entry [ 1 ]
if remaining_amount > 0 :
if remaining_amount < book_entry_coin_volume :
filled_amount + = remaining_amount * book_entry_price
else :
filled_amount + = book_entry_coin_volume * book_entry_price
remaining_amount - = book_entry_coin_volume
else :
break
else :
# If remaining_amount wasn't consumed completely (break was not called)
filled_amount + = remaining_amount * book_entry_price
forecast_avg_filled_price = filled_amount / amount
return self . price_to_precision ( pair , forecast_avg_filled_price )
return rate
def _is_dry_limit_order_filled ( self , pair : str , side : str , limit : float ) - > bool :
if not self . exchange_has ( ' fetchL2OrderBook ' ) :
return True
ob = self . fetch_l2_order_book ( pair , 1 )
if side == ' buy ' :
price = ob [ ' asks ' ] [ 0 ] [ 0 ]
logger . debug ( f " { pair } checking dry buy-order: price= { price } , limit= { limit } " )
if limit > = price :
return True
else :
price = ob [ ' bids ' ] [ 0 ] [ 0 ]
logger . debug ( f " { pair } checking dry sell-order: price= { price } , limit= { limit } " )
if limit < = price :
return True
return False
def check_dry_limit_order_filled ( self , order : Dict [ str , Any ] ) - > Dict [ str , Any ] :
"""
Check dry - run limit order fill and update fee ( if it filled ) .
"""
if order [ ' status ' ] != " closed " and order [ ' type ' ] in [ " limit " ] :
pair = order [ ' symbol ' ]
if self . _is_dry_limit_order_filled ( pair , order [ ' side ' ] , order [ ' price ' ] ) :
order . update ( {
' status ' : ' closed ' ,
' filled ' : order [ ' amount ' ] ,
' remaining ' : 0 ,
} )
self . add_dry_order_fee ( pair , order )
return order
def fetch_dry_run_order ( self , order_id ) - > Dict [ str , Any ] :
"""
@ -611,6 +679,7 @@ class Exchange:
"""
try :
order = self . _dry_run_open_orders [ order_id ]
order = self . check_dry_limit_order_filled ( order )
return order
except KeyError as e :
# Gracefully handle errors with dry-run orders.