/force_trader_close
This commit is contained in:
parent
694e5a95d1
commit
bb3fb692df
|
|
@ -3,7 +3,7 @@ next:
|
||||||
. Now the trader supports multiple safety orders at the same time.
|
. Now the trader supports multiple safety orders at the same time.
|
||||||
. Removed forcing orders when importing a trader. Maybe it will be reinstated at a later date.
|
. Removed forcing orders when importing a trader. Maybe it will be reinstated at a later date.
|
||||||
. Removed endpoint /reload_safety_orders.
|
. Removed endpoint /reload_safety_orders.
|
||||||
. New endpoints: /mod_concurrent_safety orders and /mod_boosted_concurrent_safety_orders.
|
. New endpoints: /mod_concurrent_safety orders, /mod_boosted_concurrent_safety_orders and /force_trader_close.
|
||||||
|
|
||||||
2025.08.19:
|
2025.08.19:
|
||||||
. Improved log trimming.
|
. Improved log trimming.
|
||||||
|
|
|
||||||
52
main.py
52
main.py
|
|
@ -18,7 +18,7 @@ import exchange_wrapper
|
||||||
import trader
|
import trader
|
||||||
|
|
||||||
|
|
||||||
version = "2025.09.01"
|
version = "2025.09.02"
|
||||||
|
|
||||||
'''
|
'''
|
||||||
Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
Color definitions. If you want to change them, check the reference at https://en.wikipedia.org/wiki/ANSI_escape_code#Colors
|
||||||
|
|
@ -1135,6 +1135,30 @@ def toggle_autoswitch():
|
||||||
return jsonify({'Error': 'Halp'})
|
return jsonify({'Error': 'Halp'})
|
||||||
|
|
||||||
|
|
||||||
|
@base_api.route("/force_trader_close", methods=['POST'])
|
||||||
|
def force_trader_close():
|
||||||
|
'''
|
||||||
|
POST request
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base: str
|
||||||
|
quote: str
|
||||||
|
'''
|
||||||
|
|
||||||
|
if not "X-API-KEY" in request.headers or not request.headers.get("X-API-KEY") in valid_keys:
|
||||||
|
return jsonify({'Error': 'API key invalid'}), 401
|
||||||
|
try:
|
||||||
|
if request.json is None:
|
||||||
|
return jsonify({'Error': 'request.json is None'})
|
||||||
|
data = request.json
|
||||||
|
base = data["base"]
|
||||||
|
quote = data["quote"]
|
||||||
|
return unwrapped_force_trader_close(base,quote)
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
return jsonify({'Error': 'Halp'})
|
||||||
|
|
||||||
|
|
||||||
@base_api.route("/toggle_liquidate_after_switch", methods=['POST'])
|
@base_api.route("/toggle_liquidate_after_switch", methods=['POST'])
|
||||||
def toggle_liquidate_after_switch():
|
def toggle_liquidate_after_switch():
|
||||||
'''
|
'''
|
||||||
|
|
@ -2168,6 +2192,32 @@ def unwrapped_toggle_cleanup(base,quote):
|
||||||
return jsonify({"Error": "Task failed successfully"})
|
return jsonify({"Error": "Task failed successfully"})
|
||||||
|
|
||||||
|
|
||||||
|
def unwrapped_force_trader_close(base,quote):
|
||||||
|
'''
|
||||||
|
Forces a trader to close the position.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
base (str): The base currency of the pair to close
|
||||||
|
quote (str): The quote currency of the pair to close
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
jsonify: A jsonified dictionary detailing the outcome of the operation.
|
||||||
|
'''
|
||||||
|
|
||||||
|
try:
|
||||||
|
symbol = f"{base}/{quote}"
|
||||||
|
for instance in running_traders:
|
||||||
|
if symbol==instance.status.get_pair():
|
||||||
|
outcome = instance.force_close()
|
||||||
|
if outcome==0:
|
||||||
|
return jsonify({"Success": "Trader closed position successfully"})
|
||||||
|
return jsonify({"Error": "Error while forcing trader to close position"})
|
||||||
|
return jsonify({"Error": "Trader not found"})
|
||||||
|
except Exception as e:
|
||||||
|
broker.logger.log_this(f"Exception while forcing trader to close position: {e}",1,symbol)
|
||||||
|
return jsonify({"Error": "Halp"})
|
||||||
|
|
||||||
|
|
||||||
def unwrapped_toggle_autoswitch(base,quote):
|
def unwrapped_toggle_autoswitch(base,quote):
|
||||||
'''
|
'''
|
||||||
Signals a trader to enable or disable autoswitch.
|
Signals a trader to enable or disable autoswitch.
|
||||||
|
|
|
||||||
39
trader.py
39
trader.py
|
|
@ -702,6 +702,39 @@ class trader:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
def force_close(self):
|
||||||
|
'''
|
||||||
|
This method forces the closing of a deal. It replaces the take profit order with a market order.
|
||||||
|
A simpler way of doing it would be to edit the price of the take profit order,
|
||||||
|
but KuCoin only supports order editing on high frequency orders.
|
||||||
|
'''
|
||||||
|
|
||||||
|
self.pause = True
|
||||||
|
self.status.set_pause_reason("force_close - order handling")
|
||||||
|
|
||||||
|
#Close the take profit order
|
||||||
|
self.broker.cancel_order(self.status.get_take_profit_order()["id"],self.status.get_pair())
|
||||||
|
|
||||||
|
#Send the market order
|
||||||
|
amount = self.status.get_take_profit_order()["amount"]
|
||||||
|
market_order = self.broker.new_market_order(self.status.get_pair(),amount,"sell",amount_in_base=True)
|
||||||
|
|
||||||
|
#Wait for it to be filled
|
||||||
|
tries = self.broker.get_retries()
|
||||||
|
while True:
|
||||||
|
order = self.broker.get_order(market_order["id"],self.status.get_pair())
|
||||||
|
if order["status"]=="closed":
|
||||||
|
break
|
||||||
|
tries-=1
|
||||||
|
if tries==0:
|
||||||
|
self.broker.logger.log_this("Forced market order not filling.",1,self.status.get_pair())
|
||||||
|
self.quit = True
|
||||||
|
return 1
|
||||||
|
|
||||||
|
#Call take profit routine
|
||||||
|
return self.take_profit_routine(order)
|
||||||
|
|
||||||
|
|
||||||
def take_profit_routine(self, filled_order: dict) -> int:
|
def take_profit_routine(self, filled_order: dict) -> int:
|
||||||
'''
|
'''
|
||||||
When profit is reached, this method is called to handle the profit calculations, the closing of orders,
|
When profit is reached, this method is called to handle the profit calculations, the closing of orders,
|
||||||
|
|
@ -715,7 +748,6 @@ class trader:
|
||||||
self.deals_timestamps.append(time.time())
|
self.deals_timestamps.append(time.time())
|
||||||
self.deals_timestamps = self.deals_timestamps[-self.config.get_boosted_deals_range():]
|
self.deals_timestamps = self.deals_timestamps[-self.config.get_boosted_deals_range():]
|
||||||
|
|
||||||
|
|
||||||
#Let's do some type checking first
|
#Let's do some type checking first
|
||||||
if self.status.get_take_profit_order() is None:
|
if self.status.get_take_profit_order() is None:
|
||||||
self.status.set_pause_reason(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.status.get_pair()} | TP order is None"))
|
self.status.set_pause_reason(time.strftime(f"[%Y/%m/%d %H:%M:%S] | {self.status.get_pair()} | TP order is None"))
|
||||||
|
|
@ -731,6 +763,7 @@ class trader:
|
||||||
#Cancel all the safety orders ASAP
|
#Cancel all the safety orders ASAP
|
||||||
for order in self.status.get_safety_orders():
|
for order in self.status.get_safety_orders():
|
||||||
self.broker.cancel_order(order["id"],self.status.get_pair())
|
self.broker.cancel_order(order["id"],self.status.get_pair())
|
||||||
|
|
||||||
#Check if some safety orders were filled
|
#Check if some safety orders were filled
|
||||||
for order in self.status.get_safety_orders():
|
for order in self.status.get_safety_orders():
|
||||||
closed_order = self.broker.get_order(order["id"],self.status.get_pair())
|
closed_order = self.broker.get_order(order["id"],self.status.get_pair())
|
||||||
|
|
@ -738,6 +771,7 @@ class trader:
|
||||||
#If this order wasn't filled, it is safe to assume that no order coming after this one was.
|
#If this order wasn't filled, it is safe to assume that no order coming after this one was.
|
||||||
break
|
break
|
||||||
#Sum the filled amounts
|
#Sum the filled amounts
|
||||||
|
#Better than this, the total filled and total cost can be used to send a sell market order of the partial filled amount, and add that to the profit.
|
||||||
self.broker.logger.log_this(f"Old safety order is partially filled, ID: {closed_order['id']}, {closed_order['filled']}/{closed_order['amount']} {self.base} filled",1,self.status.get_pair())
|
self.broker.logger.log_this(f"Old safety order is partially filled, ID: {closed_order['id']}, {closed_order['filled']}/{closed_order['amount']} {self.base} filled",1,self.status.get_pair())
|
||||||
self.status.set_base_bought(self.status.get_base_bought() + closed_order["filled"] - self.parse_fees(closed_order)[0])
|
self.status.set_base_bought(self.status.get_base_bought() + closed_order["filled"] - self.parse_fees(closed_order)[0])
|
||||||
self.status.set_quote_spent(self.status.get_quote_spent() + closed_order["cost"])
|
self.status.set_quote_spent(self.status.get_quote_spent() + closed_order["cost"])
|
||||||
|
|
@ -749,8 +783,7 @@ class trader:
|
||||||
break
|
break
|
||||||
#Now we can clear the safety order list
|
#Now we can clear the safety order list
|
||||||
self.status.set_safety_orders([])
|
self.status.set_safety_orders([])
|
||||||
|
|
||||||
|
|
||||||
if not self.broker.check_for_duplicate_profit_in_db(filled_order):
|
if not self.broker.check_for_duplicate_profit_in_db(filled_order):
|
||||||
self.status.set_pause_reason("calculating profit")
|
self.status.set_pause_reason("calculating profit")
|
||||||
# Calculate the profit
|
# Calculate the profit
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ TRADERS
|
||||||
68) toggle_check_old_long_price 69) switch_quote_currency
|
68) toggle_check_old_long_price 69) switch_quote_currency
|
||||||
70) view_old_long 71) switch_price 72) reload_trader_config
|
70) view_old_long 71) switch_price 72) reload_trader_config
|
||||||
73) toggle_liquidate_after_switch 74) base_add_calculation
|
73) toggle_liquidate_after_switch 74) base_add_calculation
|
||||||
75) mod_concurrent_safety_orders
|
75) mod_concurrent_safety_orders 76) force_trader_close
|
||||||
|
|
||||||
98) Change broker 99) Exit
|
98) Change broker 99) Exit
|
||||||
'''
|
'''
|
||||||
|
|
@ -880,4 +880,18 @@ if __name__=="__main__":
|
||||||
"quote": quote,
|
"quote": quote,
|
||||||
"amount": new_amount}
|
"amount": new_amount}
|
||||||
print(json.loads(requests.post(url, headers=headers, json=parameters).content))
|
print(json.loads(requests.post(url, headers=headers, json=parameters).content))
|
||||||
input("Press ENTER to continue ")
|
input("Press ENTER to continue ")
|
||||||
|
|
||||||
|
elif command==76:
|
||||||
|
print("force_trader_close forces a trader to close the current position")
|
||||||
|
trading_pair = input("Input trader in the format BASE/QUOTE: ").upper()
|
||||||
|
if not validate_pair(trading_pair):
|
||||||
|
print("The input is invalid")
|
||||||
|
break
|
||||||
|
if input("Proceed? (Y/n) ") in ["Y","y",""]:
|
||||||
|
url = f"{base_url}{port}/force_trader_close"
|
||||||
|
base,quote = trading_pair.split("/")
|
||||||
|
parameters = {"base": base,
|
||||||
|
"quote": quote}
|
||||||
|
print(json.loads(requests.post(url, headers=headers, json=parameters).content))
|
||||||
|
input("Press ENTER to continue ")
|
||||||
Loading…
Reference in New Issue