babaTEEpe commited on
Commit
eb0c442
·
verified ·
1 Parent(s): 099bcf0

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +79 -3
app.py CHANGED
@@ -861,13 +861,89 @@ async def webhook(request: Request):
861
  res = place_market_order(ssl_sock, account_id, symbol_id, ProtoOATradeSide.SELL, current_trade_volume)
862
  execution_msg = f"Opened SELL position for {ticker}. Order Result: {res.get('status')} {res.get('message', '')}"
863
 
864
- elif action in ["STOP-TRADE", "CLOSE"]:
865
- # Close all active positions on this symbol
866
  if len(symbol_positions) > 0:
867
  for p in symbol_positions:
868
  side = ProtoOATradeSide.SELL if p.tradeData.tradeSide == 1 else ProtoOATradeSide.BUY
869
  place_market_order(ssl_sock, account_id, symbol_id, side, p.tradeData.volume, position_id=p.positionId)
870
- execution_msg = f"Closed {len(symbol_positions)} open positions on {ticker}."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
871
  else:
872
  execution_msg = f"No open positions on {ticker} to close."
873
 
 
861
  res = place_market_order(ssl_sock, account_id, symbol_id, ProtoOATradeSide.SELL, current_trade_volume)
862
  execution_msg = f"Opened SELL position for {ticker}. Order Result: {res.get('status')} {res.get('message', '')}"
863
 
864
+ elif action == "CLOSE":
865
+ # CLOSE = force-close ALL positions on this symbol (manual override)
866
  if len(symbol_positions) > 0:
867
  for p in symbol_positions:
868
  side = ProtoOATradeSide.SELL if p.tradeData.tradeSide == 1 else ProtoOATradeSide.BUY
869
  place_market_order(ssl_sock, account_id, symbol_id, side, p.tradeData.volume, position_id=p.positionId)
870
+ execution_msg = f"CLOSE: Force-closed {len(symbol_positions)} positions on {ticker}."
871
+ else:
872
+ execution_msg = f"No open positions on {ticker} to close."
873
+
874
+ elif action == "STOP-TRADE":
875
+ # STOP-TRADE = only close PROFITABLE positions, leave losing ones open
876
+ if len(symbol_positions) > 0:
877
+ # Get current spot price (bid/ask) for this symbol
878
+ current_bid = None
879
+ current_ask = None
880
+ try:
881
+ spot_req = ProtoOASubscribeSpotsReq()
882
+ spot_req.ctidTraderAccountId = int(account_id)
883
+ spot_req.symbolId.append(int(symbol_id))
884
+ send_proto_message(ssl_sock, spot_req, 2127)
885
+
886
+ # Read responses until we get a spot event
887
+ for _ in range(10):
888
+ proto_msg = receive_proto_message(ssl_sock)
889
+ if not proto_msg:
890
+ break
891
+ if proto_msg.payloadType == 2131: # ProtoOASpotEvent
892
+ spot = ProtoOASpotEvent()
893
+ spot.ParseFromString(proto_msg.payload)
894
+ if hasattr(spot, 'bid') and spot.bid:
895
+ current_bid = spot.bid / 100000.0 # Convert from protocol price
896
+ if hasattr(spot, 'ask') and spot.ask:
897
+ current_ask = spot.ask / 100000.0
898
+ if current_bid and current_ask:
899
+ break
900
+ elif proto_msg.payloadType == 2128: # Subscribe confirmation
901
+ continue
902
+
903
+ # Unsubscribe from spots
904
+ try:
905
+ unsub_req = ProtoOAUnsubscribeSpotsReq()
906
+ unsub_req.ctidTraderAccountId = int(account_id)
907
+ unsub_req.symbolId.append(int(symbol_id))
908
+ send_proto_message(ssl_sock, unsub_req, 2129)
909
+ receive_proto_message(ssl_sock) # Consume the response
910
+ except Exception:
911
+ pass
912
+ except Exception as e:
913
+ logger.warning(f"Could not fetch spot price: {e}")
914
+
915
+ closed_count = 0
916
+ skipped_count = 0
917
+
918
+ for p in symbol_positions:
919
+ entry_price = p.price # Entry price of the position
920
+ is_buy = p.tradeData.tradeSide == 1
921
+
922
+ # Determine if position is profitable
923
+ in_profit = False
924
+ if current_bid and current_ask:
925
+ if is_buy:
926
+ # BUY position profits when current bid > entry price
927
+ in_profit = current_bid > entry_price
928
+ else:
929
+ # SELL position profits when entry price > current ask
930
+ in_profit = entry_price > current_ask
931
+ else:
932
+ # Fallback: use the swap field as a rough P&L indicator
933
+ # If we couldn't get spot prices, check if swap is positive
934
+ in_profit = getattr(p, 'swap', 0) > 0
935
+ logger.warning(f"No spot price available, using swap as fallback for position {p.positionId}")
936
+
937
+ if in_profit:
938
+ side = ProtoOATradeSide.SELL if is_buy else ProtoOATradeSide.BUY
939
+ logger.info(f"Closing PROFITABLE {'BUY' if is_buy else 'SELL'} position {p.positionId} (entry: {entry_price})")
940
+ place_market_order(ssl_sock, account_id, symbol_id, side, p.tradeData.volume, position_id=p.positionId)
941
+ closed_count += 1
942
+ else:
943
+ logger.info(f"KEEPING losing {'BUY' if is_buy else 'SELL'} position {p.positionId} (entry: {entry_price})")
944
+ skipped_count += 1
945
+
946
+ execution_msg = f"STOP-TRADE: Closed {closed_count} profitable position(s), kept {skipped_count} losing position(s) on {ticker}."
947
  else:
948
  execution_msg = f"No open positions on {ticker} to close."
949