Skip to content
This repository was archived by the owner on Apr 26, 2026. It is now read-only.

Commit 420e90a

Browse files
Copilottheshadow76
andcommitted
Fix check_win: Add server ID to request ID mapping for order tracking
Co-authored-by: theshadow76 <59869868+theshadow76@users.noreply.github.com>
1 parent 91260bb commit 420e90a

2 files changed

Lines changed: 266 additions & 8 deletions

File tree

pocketoptionapi_async/client.py

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ def __init__(
9393
self._orders: Dict[str, OrderResult] = {}
9494
self._active_orders: Dict[str, OrderResult] = {}
9595
self._order_results: Dict[str, OrderResult] = {}
96+
self._server_id_to_request_id: Dict[str, str] = {} # Maps server deal IDs to client request IDs
9697
self._candles_cache: Dict[str, List[Candle]] = {}
9798
self._server_time: Optional[ServerTime] = None
9899
self._event_callbacks: Dict[str, List[Callable]] = defaultdict(list)
@@ -1035,6 +1036,13 @@ async def _on_json_data(self, data: Dict[str, Any]) -> None:
10351036
if "requestId" in data and "asset" in data and "amount" in data:
10361037
request_id = str(data["requestId"])
10371038

1039+
# Store mapping from server ID to request ID if server ID is present
1040+
if "id" in data:
1041+
server_id = str(data["id"])
1042+
self._server_id_to_request_id[server_id] = request_id
1043+
if self.enable_logging:
1044+
logger.debug(f"Mapped server ID {server_id} to request ID {request_id}")
1045+
10381046
# If this is a new order, add it to tracking
10391047
if (
10401048
request_id not in self._active_orders
@@ -1069,10 +1077,17 @@ async def _on_json_data(self, data: Dict[str, Any]) -> None:
10691077
elif "deals" in data and isinstance(data["deals"], list):
10701078
for deal in data["deals"]:
10711079
if isinstance(deal, dict) and "id" in deal:
1072-
order_id = str(deal["id"])
1073-
1074-
if order_id in self._active_orders:
1075-
active_order = self._active_orders[order_id]
1080+
server_deal_id = str(deal["id"])
1081+
1082+
# Try to find the request_id for this server deal ID
1083+
request_id = self._server_id_to_request_id.get(server_deal_id)
1084+
1085+
# If we have a mapping, use request_id to find the order
1086+
# Otherwise, fall back to trying server_deal_id directly
1087+
lookup_id = request_id if request_id else server_deal_id
1088+
1089+
if lookup_id in self._active_orders:
1090+
active_order = self._active_orders[lookup_id]
10761091
profit = float(deal.get("profit", 0))
10771092

10781093
# Determine status
@@ -1096,13 +1111,17 @@ async def _on_json_data(self, data: Dict[str, Any]) -> None:
10961111
payout=deal.get("payout"),
10971112
)
10981113

1099-
# Move from active to completed
1100-
self._order_results[order_id] = result
1101-
del self._active_orders[order_id]
1114+
# Move from active to completed - use the original order_id (request_id)
1115+
self._order_results[active_order.order_id] = result
1116+
del self._active_orders[lookup_id]
1117+
1118+
# Clean up the server ID mapping
1119+
if request_id and server_deal_id in self._server_id_to_request_id:
1120+
del self._server_id_to_request_id[server_deal_id]
11021121

11031122
if self.enable_logging:
11041123
logger.success(
1105-
f" Order {order_id} completed via JSON data: {status.value} - Profit: ${profit:.2f}"
1124+
f" Order {active_order.order_id} completed via JSON data: {status.value} - Profit: ${profit:.2f}"
11061125
)
11071126
await self._emit_event("order_closed", result)
11081127

tests/test_check_win.py

Lines changed: 239 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,239 @@
1+
"""
2+
Test script to verify the check_win functionality
3+
Tests that the server ID to request ID mapping works correctly
4+
"""
5+
6+
import asyncio
7+
from datetime import datetime, timedelta
8+
from pocketoptionapi_async import AsyncPocketOptionClient, OrderDirection
9+
from pocketoptionapi_async.models import OrderResult, OrderStatus
10+
11+
12+
async def test_check_win_id_mapping():
13+
"""Test that server deal IDs are properly mapped to client request IDs"""
14+
15+
# Create a client with a dummy SSID (we won't connect, just test internal logic)
16+
dummy_ssid = r'42["auth",{"session":"test_session","isDemo":1,"uid":12345,"platform":1}]'
17+
client = AsyncPocketOptionClient(ssid=dummy_ssid, is_demo=True, enable_logging=False)
18+
19+
print("Testing check_win ID mapping fix")
20+
print("=" * 50)
21+
22+
# Test 1: Verify the mapping dictionary exists
23+
print("\nTest 1: Verify _server_id_to_request_id mapping exists")
24+
assert hasattr(client, '_server_id_to_request_id'), "Client should have _server_id_to_request_id dict"
25+
assert isinstance(client._server_id_to_request_id, dict), "Should be a dictionary"
26+
print(" ✅ PASS: _server_id_to_request_id mapping exists")
27+
28+
# Test 2: Simulate order creation with server ID mapping
29+
print("\nTest 2: Simulate order data with both requestId and server id")
30+
31+
client_request_id = "abc-123-def-456" # Our client-generated UUID
32+
server_deal_id = "98765432" # Server-assigned ID
33+
34+
# Simulate the server response that includes both IDs
35+
order_data = {
36+
"requestId": client_request_id,
37+
"id": server_deal_id,
38+
"asset": "EURUSD_otc",
39+
"amount": 10.0,
40+
"command": 0, # CALL
41+
"time": 60
42+
}
43+
44+
# Call the handler that processes order data
45+
await client._on_json_data(order_data)
46+
47+
# Verify the order was added to active orders with request_id
48+
assert client_request_id in client._active_orders, "Order should be in active orders with request_id"
49+
print(f" ✅ PASS: Order added to active orders with request_id: {client_request_id}")
50+
51+
# Verify the server ID mapping was created
52+
assert server_deal_id in client._server_id_to_request_id, "Server ID should be mapped to request_id"
53+
assert client._server_id_to_request_id[server_deal_id] == client_request_id, "Mapping should point to request_id"
54+
print(f" ✅ PASS: Server ID {server_deal_id} mapped to request_id {client_request_id}")
55+
56+
# Test 3: Simulate deal completion using server's deal ID
57+
print("\nTest 3: Simulate deal completion with server's deal ID")
58+
59+
deal_data = {
60+
"deals": [
61+
{
62+
"id": server_deal_id, # Server uses its own ID
63+
"profit": 8.5,
64+
"payout": 85.0
65+
}
66+
]
67+
}
68+
69+
# Call the handler that processes deal completion
70+
await client._on_json_data(deal_data)
71+
72+
# Verify the order was moved from active to completed
73+
assert client_request_id not in client._active_orders, "Order should be removed from active orders"
74+
assert client_request_id in client._order_results, "Order should be in order_results with request_id"
75+
print(f" ✅ PASS: Order moved from active to completed using request_id")
76+
77+
# Verify the result data is correct
78+
result = client._order_results[client_request_id]
79+
assert result.order_id == client_request_id, "Result order_id should match request_id"
80+
assert result.profit == 8.5, f"Profit should be 8.5, got {result.profit}"
81+
assert result.status == OrderStatus.WIN, f"Status should be WIN, got {result.status}"
82+
print(f" ✅ PASS: Order result has correct profit ({result.profit}) and status ({result.status})")
83+
84+
# Verify the server ID mapping was cleaned up
85+
assert server_deal_id not in client._server_id_to_request_id, "Server ID mapping should be cleaned up"
86+
print(" ✅ PASS: Server ID mapping was cleaned up after order completion")
87+
88+
# Test 4: Test check_win function can find the completed order
89+
print("\nTest 4: Verify check_win finds the completed order")
90+
91+
check_result = await client.check_win(client_request_id, max_wait_time=1.0)
92+
93+
assert check_result is not None, "check_win should return a result"
94+
assert check_result["completed"] == True, "Order should be completed"
95+
assert check_result["result"] == "win", f"Result should be 'win', got {check_result['result']}"
96+
assert check_result["profit"] == 8.5, f"Profit should be 8.5, got {check_result['profit']}"
97+
print(f" ✅ PASS: check_win returned correct result: {check_result}")
98+
99+
# Test 5: Test check_order_result function
100+
print("\nTest 5: Verify check_order_result finds the completed order")
101+
102+
order_result = await client.check_order_result(client_request_id)
103+
104+
assert order_result is not None, "check_order_result should return a result"
105+
assert order_result.order_id == client_request_id, "Order ID should match"
106+
assert order_result.profit == 8.5, "Profit should be correct"
107+
print(f" ✅ PASS: check_order_result returned correct result")
108+
109+
print("\n" + "=" * 50)
110+
print("🎉 ALL TESTS PASSED! check_win ID mapping fix is working!")
111+
112+
return True
113+
114+
115+
async def test_check_win_loss_scenario():
116+
"""Test that loss orders are correctly handled"""
117+
118+
dummy_ssid = r'42["auth",{"session":"test_session","isDemo":1,"uid":12345,"platform":1}]'
119+
client = AsyncPocketOptionClient(ssid=dummy_ssid, is_demo=True, enable_logging=False)
120+
121+
print("\nTesting check_win with loss scenario")
122+
print("=" * 50)
123+
124+
client_request_id = "loss-order-123"
125+
server_deal_id = "88888888"
126+
127+
# Create order
128+
order_data = {
129+
"requestId": client_request_id,
130+
"id": server_deal_id,
131+
"asset": "EURUSD_otc",
132+
"amount": 10.0,
133+
"command": 1, # PUT
134+
"time": 60
135+
}
136+
await client._on_json_data(order_data)
137+
138+
# Complete order with a loss
139+
deal_data = {
140+
"deals": [
141+
{
142+
"id": server_deal_id,
143+
"profit": -10.0, # Lost the trade
144+
"payout": 0
145+
}
146+
]
147+
}
148+
await client._on_json_data(deal_data)
149+
150+
# Verify check_win returns loss
151+
check_result = await client.check_win(client_request_id, max_wait_time=1.0)
152+
153+
assert check_result["result"] == "loss", f"Result should be 'loss', got {check_result['result']}"
154+
assert check_result["profit"] == -10.0, f"Profit should be -10.0, got {check_result['profit']}"
155+
print(f" ✅ PASS: check_win correctly identifies loss: {check_result}")
156+
157+
return True
158+
159+
160+
async def test_check_win_fallback_without_mapping():
161+
"""Test that check_win still works if server ID happens to match request ID (backward compatibility)"""
162+
163+
dummy_ssid = r'42["auth",{"session":"test_session","isDemo":1,"uid":12345,"platform":1}]'
164+
client = AsyncPocketOptionClient(ssid=dummy_ssid, is_demo=True, enable_logging=False)
165+
166+
print("\nTesting check_win fallback (no mapping scenario)")
167+
print("=" * 50)
168+
169+
# Simulate a case where the order was added directly with the server ID
170+
# (e.g., if server returns request matching what we sent)
171+
order_id = "direct-order-789"
172+
173+
# Directly add to active orders (simulating order placement)
174+
order_result = OrderResult(
175+
order_id=order_id,
176+
asset="GBPUSD_otc",
177+
amount=5.0,
178+
direction=OrderDirection.CALL,
179+
duration=60,
180+
status=OrderStatus.ACTIVE,
181+
placed_at=datetime.now(),
182+
expires_at=datetime.now() + timedelta(seconds=60),
183+
)
184+
client._active_orders[order_id] = order_result
185+
186+
# Complete the order using the same ID (no mapping needed)
187+
deal_data = {
188+
"deals": [
189+
{
190+
"id": order_id, # Using the same ID
191+
"profit": 4.25,
192+
"payout": 85.0
193+
}
194+
]
195+
}
196+
await client._on_json_data(deal_data)
197+
198+
# Verify order was completed
199+
check_result = await client.check_win(order_id, max_wait_time=1.0)
200+
201+
assert check_result["result"] == "win", f"Result should be 'win', got {check_result['result']}"
202+
print(f" ✅ PASS: Fallback without mapping works correctly")
203+
204+
return True
205+
206+
207+
async def run_all_tests():
208+
"""Run all check_win tests"""
209+
all_passed = True
210+
211+
try:
212+
all_passed = await test_check_win_id_mapping() and all_passed
213+
except Exception as e:
214+
print(f"❌ FAIL: test_check_win_id_mapping - {e}")
215+
all_passed = False
216+
217+
try:
218+
all_passed = await test_check_win_loss_scenario() and all_passed
219+
except Exception as e:
220+
print(f"❌ FAIL: test_check_win_loss_scenario - {e}")
221+
all_passed = False
222+
223+
try:
224+
all_passed = await test_check_win_fallback_without_mapping() and all_passed
225+
except Exception as e:
226+
print(f"❌ FAIL: test_check_win_fallback_without_mapping - {e}")
227+
all_passed = False
228+
229+
print("\n" + "=" * 50)
230+
if all_passed:
231+
print("🎉 ALL CHECK_WIN TESTS PASSED!")
232+
else:
233+
print("❌ SOME TESTS FAILED")
234+
235+
return all_passed
236+
237+
238+
if __name__ == "__main__":
239+
asyncio.run(run_all_tests())

0 commit comments

Comments
 (0)