diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..d49d775 Binary files /dev/null and b/.DS_Store differ diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..78f3a66 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# These are supported funding model platforms + +github: theshadow76 +custom: ['https://paypal.me/shadowtechsc?country.x=CL&locale.x=es_XC]' diff --git a/.gitignore b/.gitignore index f905755..5b0c2f4 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ env -pocket.log \ No newline at end of file +pocket.log +.env \ No newline at end of file diff --git a/README.md b/README.md index 49de9f4..9817d5c 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# Pocket Option API \ No newline at end of file +# Pocket Option API + +In development! + +for a better understanding, check this [link](https://github.com/theshadow76/PocketOptionAPI/issues/4) + +# help me + +you can donate here: [paypal](https://paypal.me/shadowtechsc?country.x=CL&locale.x=es_XC) + +or my BTC adress: bc1qmvpmpdt96vmgq3s3rh3gzxf75eml52r2d7sclw + +By donating you are helping me make this project better! diff --git a/client_test.py b/client_test.py index 3badb47..d3feefd 100644 --- a/client_test.py +++ b/client_test.py @@ -2,18 +2,20 @@ import anyio from rich.pretty import pprint as print import json +from pocketoptionapi.constants import REGION -SESSION = r'42["auth",{"session":"a:4:{s:10:\"session_id\";s:32:\"da7a5a82c8f6c35a87b2ee31d4f5b3b4\";s:10:\"ip_address\";s:10:\"90.36.9.15\";s:10:\"user_agent\";s:120:\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36 OP\";s:13:\"last_activity\";i:1707667599;}3a0058a58a6df5e7b49f652f8e4f8249","isDemo":1,"uid":27658142,"platform":1}]' +SESSION = r'42["auth",{"session":"a:4:{s:10:\"session_id\";s:32:\"a1dc009a7f1f0c8267d940d0a036156f\";s:10:\"ip_address\";s:12:\"190.162.4.33\";s:10:\"user_agent\";s:120:\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 OP\";s:13:\"last_activity\";i:1709914958;}793884e7bccc89ec798c06ef1279fcf2","isDemo":0,"uid":27658142,"platform":1}]' async def websocket_client(url, pro): - while True: + for i in REGION.get_regions(REGION): + print(f"Trying {i}...") try: async with websockets.connect( - url, + i, #teoria de los issues extra_headers={ - "Origin": "https://pocket-link19.co", - # "Origin": "https://po.trade/" + #"Origin": "https://pocket-link19.co", + "Origin": "https://po.trade/" }, ) as websocket: async for message in websocket: @@ -23,7 +25,7 @@ async def websocket_client(url, pro): except Exception as e: print(e) print("Connection lost... reconnecting") - await anyio.sleep(5) + # await anyio.sleep(5) return True diff --git a/docs/todo.md b/docs/todo.md index 7b41d26..67b5a55 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -1,4 +1,6 @@ # todo ### Add login system -- Not Done \ No newline at end of file +- Not Done + +-- updated \ No newline at end of file diff --git a/pocketoptionapi/_.py b/pocketoptionapi/_.py new file mode 100644 index 0000000..e69de29 diff --git a/pocketoptionapi/__pycache__/__init__.cpython-310.pyc b/pocketoptionapi/__pycache__/__init__.cpython-310.pyc index a486141..4327073 100644 Binary files a/pocketoptionapi/__pycache__/__init__.cpython-310.pyc and b/pocketoptionapi/__pycache__/__init__.cpython-310.pyc differ diff --git a/pocketoptionapi/__pycache__/__init__.cpython-311.pyc b/pocketoptionapi/__pycache__/__init__.cpython-311.pyc index 75e6432..d6f4f31 100644 Binary files a/pocketoptionapi/__pycache__/__init__.cpython-311.pyc and b/pocketoptionapi/__pycache__/__init__.cpython-311.pyc differ diff --git a/pocketoptionapi/__pycache__/__init__.cpython-312.pyc b/pocketoptionapi/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..140c81a Binary files /dev/null and b/pocketoptionapi/__pycache__/__init__.cpython-312.pyc differ diff --git a/pocketoptionapi/__pycache__/__init__.cpython-37.pyc b/pocketoptionapi/__pycache__/__init__.cpython-37.pyc new file mode 100644 index 0000000..13a4bf9 Binary files /dev/null and b/pocketoptionapi/__pycache__/__init__.cpython-37.pyc differ diff --git a/pocketoptionapi/__pycache__/__init__.cpython-38.pyc b/pocketoptionapi/__pycache__/__init__.cpython-38.pyc new file mode 100644 index 0000000..67ab17c Binary files /dev/null and b/pocketoptionapi/__pycache__/__init__.cpython-38.pyc differ diff --git a/pocketoptionapi/__pycache__/api.cpython-310.pyc b/pocketoptionapi/__pycache__/api.cpython-310.pyc new file mode 100644 index 0000000..52860c5 Binary files /dev/null and b/pocketoptionapi/__pycache__/api.cpython-310.pyc differ diff --git a/pocketoptionapi/__pycache__/api.cpython-311.pyc b/pocketoptionapi/__pycache__/api.cpython-311.pyc new file mode 100644 index 0000000..61e04fd Binary files /dev/null and b/pocketoptionapi/__pycache__/api.cpython-311.pyc differ diff --git a/pocketoptionapi/__pycache__/api.cpython-312.pyc b/pocketoptionapi/__pycache__/api.cpython-312.pyc new file mode 100644 index 0000000..f88e8c1 Binary files /dev/null and b/pocketoptionapi/__pycache__/api.cpython-312.pyc differ diff --git a/pocketoptionapi/__pycache__/api.cpython-37.pyc b/pocketoptionapi/__pycache__/api.cpython-37.pyc new file mode 100644 index 0000000..816eeb4 Binary files /dev/null and b/pocketoptionapi/__pycache__/api.cpython-37.pyc differ diff --git a/pocketoptionapi/__pycache__/api.cpython-38.pyc b/pocketoptionapi/__pycache__/api.cpython-38.pyc new file mode 100644 index 0000000..3365c6b Binary files /dev/null and b/pocketoptionapi/__pycache__/api.cpython-38.pyc differ diff --git a/pocketoptionapi/__pycache__/api.cpython-39.pyc b/pocketoptionapi/__pycache__/api.cpython-39.pyc new file mode 100644 index 0000000..68af619 Binary files /dev/null and b/pocketoptionapi/__pycache__/api.cpython-39.pyc differ diff --git a/pocketoptionapi/__pycache__/constants.cpython-310.pyc b/pocketoptionapi/__pycache__/constants.cpython-310.pyc new file mode 100644 index 0000000..8f08772 Binary files /dev/null and b/pocketoptionapi/__pycache__/constants.cpython-310.pyc differ diff --git a/pocketoptionapi/__pycache__/constants.cpython-311.pyc b/pocketoptionapi/__pycache__/constants.cpython-311.pyc index d1c5fc9..83015a0 100644 Binary files a/pocketoptionapi/__pycache__/constants.cpython-311.pyc and b/pocketoptionapi/__pycache__/constants.cpython-311.pyc differ diff --git a/pocketoptionapi/__pycache__/constants.cpython-312.pyc b/pocketoptionapi/__pycache__/constants.cpython-312.pyc new file mode 100644 index 0000000..d0ac147 Binary files /dev/null and b/pocketoptionapi/__pycache__/constants.cpython-312.pyc differ diff --git a/pocketoptionapi/__pycache__/constants.cpython-37.pyc b/pocketoptionapi/__pycache__/constants.cpython-37.pyc new file mode 100644 index 0000000..d1c7785 Binary files /dev/null and b/pocketoptionapi/__pycache__/constants.cpython-37.pyc differ diff --git a/pocketoptionapi/__pycache__/constants.cpython-38.pyc b/pocketoptionapi/__pycache__/constants.cpython-38.pyc new file mode 100644 index 0000000..5ad48c4 Binary files /dev/null and b/pocketoptionapi/__pycache__/constants.cpython-38.pyc differ diff --git a/pocketoptionapi/__pycache__/constants.cpython-39.pyc b/pocketoptionapi/__pycache__/constants.cpython-39.pyc new file mode 100644 index 0000000..619aef0 Binary files /dev/null and b/pocketoptionapi/__pycache__/constants.cpython-39.pyc differ diff --git a/pocketoptionapi/__pycache__/expiration.cpython-310.pyc b/pocketoptionapi/__pycache__/expiration.cpython-310.pyc new file mode 100644 index 0000000..70f0d08 Binary files /dev/null and b/pocketoptionapi/__pycache__/expiration.cpython-310.pyc differ diff --git a/pocketoptionapi/__pycache__/expiration.cpython-311.pyc b/pocketoptionapi/__pycache__/expiration.cpython-311.pyc new file mode 100644 index 0000000..5204f43 Binary files /dev/null and b/pocketoptionapi/__pycache__/expiration.cpython-311.pyc differ diff --git a/pocketoptionapi/__pycache__/expiration.cpython-312.pyc b/pocketoptionapi/__pycache__/expiration.cpython-312.pyc new file mode 100644 index 0000000..eb86a1e Binary files /dev/null and b/pocketoptionapi/__pycache__/expiration.cpython-312.pyc differ diff --git a/pocketoptionapi/__pycache__/expiration.cpython-37.pyc b/pocketoptionapi/__pycache__/expiration.cpython-37.pyc new file mode 100644 index 0000000..384058d Binary files /dev/null and b/pocketoptionapi/__pycache__/expiration.cpython-37.pyc differ diff --git a/pocketoptionapi/__pycache__/expiration.cpython-38.pyc b/pocketoptionapi/__pycache__/expiration.cpython-38.pyc new file mode 100644 index 0000000..3e45a9b Binary files /dev/null and b/pocketoptionapi/__pycache__/expiration.cpython-38.pyc differ diff --git a/pocketoptionapi/__pycache__/expiration.cpython-39.pyc b/pocketoptionapi/__pycache__/expiration.cpython-39.pyc new file mode 100644 index 0000000..0d18314 Binary files /dev/null and b/pocketoptionapi/__pycache__/expiration.cpython-39.pyc differ diff --git a/pocketoptionapi/__pycache__/global_value.cpython-310.pyc b/pocketoptionapi/__pycache__/global_value.cpython-310.pyc new file mode 100644 index 0000000..f096c95 Binary files /dev/null and b/pocketoptionapi/__pycache__/global_value.cpython-310.pyc differ diff --git a/pocketoptionapi/__pycache__/global_value.cpython-311.pyc b/pocketoptionapi/__pycache__/global_value.cpython-311.pyc new file mode 100644 index 0000000..d141896 Binary files /dev/null and b/pocketoptionapi/__pycache__/global_value.cpython-311.pyc differ diff --git a/pocketoptionapi/__pycache__/global_value.cpython-312.pyc b/pocketoptionapi/__pycache__/global_value.cpython-312.pyc new file mode 100644 index 0000000..4c4ffeb Binary files /dev/null and b/pocketoptionapi/__pycache__/global_value.cpython-312.pyc differ diff --git a/pocketoptionapi/__pycache__/global_value.cpython-37.pyc b/pocketoptionapi/__pycache__/global_value.cpython-37.pyc new file mode 100644 index 0000000..268c77f Binary files /dev/null and b/pocketoptionapi/__pycache__/global_value.cpython-37.pyc differ diff --git a/pocketoptionapi/__pycache__/global_value.cpython-38.pyc b/pocketoptionapi/__pycache__/global_value.cpython-38.pyc new file mode 100644 index 0000000..fe0c4c1 Binary files /dev/null and b/pocketoptionapi/__pycache__/global_value.cpython-38.pyc differ diff --git a/pocketoptionapi/__pycache__/global_value.cpython-39.pyc b/pocketoptionapi/__pycache__/global_value.cpython-39.pyc new file mode 100644 index 0000000..7e529a8 Binary files /dev/null and b/pocketoptionapi/__pycache__/global_value.cpython-39.pyc differ diff --git a/pocketoptionapi/__pycache__/stable_api.cpython-310.pyc b/pocketoptionapi/__pycache__/stable_api.cpython-310.pyc new file mode 100644 index 0000000..1983d9a Binary files /dev/null and b/pocketoptionapi/__pycache__/stable_api.cpython-310.pyc differ diff --git a/pocketoptionapi/__pycache__/stable_api.cpython-311.pyc b/pocketoptionapi/__pycache__/stable_api.cpython-311.pyc new file mode 100644 index 0000000..c58b1f1 Binary files /dev/null and b/pocketoptionapi/__pycache__/stable_api.cpython-311.pyc differ diff --git a/pocketoptionapi/__pycache__/stable_api.cpython-312.pyc b/pocketoptionapi/__pycache__/stable_api.cpython-312.pyc new file mode 100644 index 0000000..388669b Binary files /dev/null and b/pocketoptionapi/__pycache__/stable_api.cpython-312.pyc differ diff --git a/pocketoptionapi/__pycache__/stable_api.cpython-37.pyc b/pocketoptionapi/__pycache__/stable_api.cpython-37.pyc new file mode 100644 index 0000000..ae93045 Binary files /dev/null and b/pocketoptionapi/__pycache__/stable_api.cpython-37.pyc differ diff --git a/pocketoptionapi/__pycache__/stable_api.cpython-38.pyc b/pocketoptionapi/__pycache__/stable_api.cpython-38.pyc new file mode 100644 index 0000000..aad7d75 Binary files /dev/null and b/pocketoptionapi/__pycache__/stable_api.cpython-38.pyc differ diff --git a/pocketoptionapi/__pycache__/stable_api.cpython-39.pyc b/pocketoptionapi/__pycache__/stable_api.cpython-39.pyc new file mode 100644 index 0000000..9e457df Binary files /dev/null and b/pocketoptionapi/__pycache__/stable_api.cpython-39.pyc differ diff --git a/pocketoptionapi/api.py b/pocketoptionapi/api.py new file mode 100644 index 0000000..f925d78 --- /dev/null +++ b/pocketoptionapi/api.py @@ -0,0 +1,298 @@ +"""Module for Pocket Option API.""" +import asyncio +import datetime +import time +import json +import logging +import threading +import requests +import ssl +import atexit +from collections import deque +from pocketoptionapi.ws.client import WebsocketClient +from pocketoptionapi.ws.channels.get_balances import * + +from pocketoptionapi.ws.channels.ssid import Ssid +# from pocketoptionapi.ws.channels.subscribe import * +# from pocketoptionapi.ws.channels.unsubscribe import * +# from pocketoptionapi.ws.channels.setactives import SetActives +from pocketoptionapi.ws.channels.candles import GetCandles +# from pocketoptionapi.ws.channels.buyv2 import Buyv2 +from pocketoptionapi.ws.channels.buyv3 import * +# from pocketoptionapi.ws.channels.user import * +# from pocketoptionapi.ws.channels.api_game_betinfo import Game_betinfo +# from pocketoptionapi.ws.channels.instruments import Get_instruments +# from pocketoptionapi.ws.channels.get_financial_information import GetFinancialInformation +# from pocketoptionapi.ws.channels.strike_list import Strike_list +# from pocketoptionapi.ws.channels.leaderboard import Leader_Board + +# from pocketoptionapi.ws.channels.traders_mood import Traders_mood_subscribe +# from pocketoptionapi.ws.channels.traders_mood import Traders_mood_unsubscribe +# from pocketoptionapi.ws.channels.buy_place_order_temp import Buy_place_order_temp +# from pocketoptionapi.ws.channels.get_order import Get_order +# from pocketoptionapi.ws.channels.get_deferred_orders import GetDeferredOrders +# from pocketoptionapi.ws.channels.get_positions import * + +# from pocketoptionapi.ws.channels.get_available_leverages import Get_available_leverages +# from pocketoptionapi.ws.channels.cancel_order import Cancel_order +# from pocketoptionapi.ws.channels.close_position import Close_position +# from pocketoptionapi.ws.channels.get_overnight_fee import Get_overnight_fee +# from pocketoptionapi.ws.channels.heartbeat import Heartbeat + +# from pocketoptionapi.ws.channels.digital_option import * +# from pocketoptionapi.ws.channels.api_game_getoptions import * +# from pocketoptionapi.ws.channels.sell_option import Sell_Option +# from pocketoptionapi.ws.channels.change_tpsl import Change_Tpsl +# from pocketoptionapi.ws.channels.change_auto_margin_call import ChangeAutoMarginCall + +from pocketoptionapi.ws.objects.timesync import TimeSync +# from pocketoptionapi.ws.objects.profile import Profile +from pocketoptionapi.ws.objects.candles import Candles +# from pocketoptionapi.ws.objects.listinfodata import ListInfoData +# from pocketoptionapi.ws.objects.betinfo import Game_betinfo_data +import pocketoptionapi.global_value as global_value +from pocketoptionapi.ws.channels.change_symbol import ChangeSymbol +from collections import defaultdict +from pocketoptionapi.ws.objects.time_sync import TimeSynchronizer + + +def nested_dict(n, type): + if n == 1: + return defaultdict(type) + else: + return defaultdict(lambda: nested_dict(n - 1, type)) + + +# InsecureRequestWarning: Unverified HTTPS request is being made. +# Adding certificate verification is strongly advised. +# See: https://urllib3.readthedocs.org/en/latest/security.html + + +class PocketOptionAPI(object): # pylint: disable=too-many-instance-attributes + """Class for communication with Pocket Option API.""" + + # pylint: disable=too-many-public-methods + socket_option_opened = {} + time_sync = TimeSync() + sync = TimeSynchronizer() + timesync = None + # pylint: disable=too-many-arguments + # profile = Profile() + candles = Candles() + # listinfodata = ListInfoData() + api_option_init_all_result = [] + api_option_init_all_result_v2 = [] + # for digital + underlying_list_data = None + position_changed = None + instrument_quites_generated_data = nested_dict(2, dict) + instrument_quotes_generated_raw_data = nested_dict(2, dict) + instrument_quites_generated_timestamp = nested_dict(2, dict) + strike_list = None + leaderboard_deals_client = None + # position_changed_data = nested_dict(2, dict) + # microserviceName_binary_options_name_option=nested_dict(2,dict) + order_async = None + # game_betinfo = Game_betinfo_data() + instruments = None + financial_information = None + buy_id = None + buy_order_id = None + traders_mood = {} # get hight(put) % + order_data = None + positions = None + position = None + deferred_orders = None + position_history = None + position_history_v2 = None + available_leverages = None + order_canceled = None + close_position_data = None + overnight_fee = None + # ---for real time + digital_option_placed_id = None + live_deal_data = nested_dict(3, deque) + + subscribe_commission_changed_data = nested_dict(2, dict) + real_time_candles = nested_dict(3, dict) + real_time_candles_maxdict_table = nested_dict(2, dict) + candle_generated_check = nested_dict(2, dict) + candle_generated_all_size_check = nested_dict(1, dict) + # ---for api_game_getoptions_result + api_game_getoptions_result = None + sold_options_respond = None + tpsl_changed_respond = None + auto_margin_call_changed_respond = None + top_assets_updated_data = {} + get_options_v2_data = None + # --for binary option multi buy + buy_multi_result = None + buy_multi_option = {} + # + result = None + training_balance_reset_request = None + balances_raw = None + user_profile_client = None + leaderboard_userinfo_deals_client = None + users_availability = None + history_data = None + historyNew = None + server_timestamp = None + sync_datetime = None + + # ------------------ + + def __init__(self, proxies=None): + """ + :param dict proxies: (optional) The http request proxies. + """ + self.websocket_client = None + self.websocket_thread = None + # self.wss_url = "wss://api-us-north.po.market/socket.io/?EIO=4&transport=websocket" + self.session = requests.Session() + self.session.verify = False + self.session.trust_env = False + self.proxies = proxies + # is used to determine if a buyOrder was set or failed. If + # it is None, there had been no buy order yet or just send. + # If it is false, the last failed + # If it is true, the last buy order was successful + self.buy_successful = None + self.loop = asyncio.get_event_loop() + self.websocket_client = WebsocketClient(self) + + @property + def websocket(self): + """Property to get websocket. + + :returns: The instance of :class:`WebSocket `. + """ + return self.websocket_client + + def send_websocket_request(self, name, msg, request_id="", no_force_send=True): + """Send websocket request to IQ Option server. + + :param no_force_send: + :param request_id: + :param str name: The websocket request name. + :param dict msg: The websocket request msg. + """ + + logger = logging.getLogger(__name__) + + # data = json.dumps(dict(name=name, msg=msg, request_id=request_id)) + data = f'42{json.dumps(msg)}' + + while (global_value.ssl_Mutual_exclusion or global_value.ssl_Mutual_exclusion_write) and no_force_send: + pass + global_value.ssl_Mutual_exclusion_write = True + + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # Ejecutar la corutina connect dentro del bucle de eventos del nuevo hilo + loop.run_until_complete(self.websocket.send_message(data)) + + logger.debug(data) + global_value.ssl_Mutual_exclusion_write = False + + def start_websocket(self): + global_value.websocket_is_connected = False + global_value.check_websocket_if_error = False + global_value.websocket_error_reason = None + + # Obtener o crear un nuevo bucle de eventos para este hilo + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # Ejecutar la corutina connect dentro del bucle de eventos del nuevo hilo + loop.run_until_complete(self.websocket.connect()) + loop.run_forever() + + while True: + try: + if global_value.check_websocket_if_error: + return False, global_value.websocket_error_reason + if global_value.websocket_is_connected is False: + return False, "Websocket connection closed." + elif global_value.websocket_is_connected is True: + return True, None + + except: + pass + pass + + def connect(self): + """Method for connection to Pocket Option API.""" + + global_value.ssl_Mutual_exclusion = False + global_value.ssl_Mutual_exclusion_write = False + + check_websocket, websocket_reason = self.start_websocket() + + if not check_websocket: + return check_websocket, websocket_reason + + self.time_sync.server_timestamps = None + while True: + try: + if self.time_sync.server_timestamps is not None: + break + except: + pass + return True, None + + async def close(self, error=None): + await self.websocket.on_close(error) + self.websocket_thread.join() + + def websocket_alive(self): + return self.websocket_thread.is_alive() + + @property + def get_balances(self): + """Property for get IQ Option http getprofile resource. + + :returns: The instance of :class:`Login + `. + """ + return Get_Balances(self) + + # ____________for_______binary_______option_____________ + + @property + def buyv3(self): + return Buyv3(self) + + @property + def getcandles(self): + """Property for get IQ Option websocket candles chanel. + + :returns: The instance of :class:`GetCandles + `. + """ + return GetCandles(self) + + @property + def change_symbol(self): + """Property for get Pocket Option websocket change_symbol chanel. + + :returns: The instance of :class:`ChangeSymbol + `. + """ + return ChangeSymbol(self) + + @property + def synced_datetime(self): + try: + if self.time_sync is not None: + self.sync.synchronize(self.time_sync.server_timestamp) + self.sync_datetime = self.sync.get_synced_datetime() + else: + logging.error("timesync no está establecido") + self.sync_datetime = None + except Exception as e: + logging.error(e) + self.sync_datetime = None + + return self.sync_datetime diff --git a/pocketoptionapi/backend/__pycache__/__init__.cpython-311.pyc b/pocketoptionapi/backend/__pycache__/__init__.cpython-311.pyc index a6627c1..026da60 100644 Binary files a/pocketoptionapi/backend/__pycache__/__init__.cpython-311.pyc and b/pocketoptionapi/backend/__pycache__/__init__.cpython-311.pyc differ diff --git a/pocketoptionapi/backend/ws/__pycache__/__init__.cpython-311.pyc b/pocketoptionapi/backend/ws/__pycache__/__init__.cpython-311.pyc index e6ebcc3..e913397 100644 Binary files a/pocketoptionapi/backend/ws/__pycache__/__init__.cpython-311.pyc and b/pocketoptionapi/backend/ws/__pycache__/__init__.cpython-311.pyc differ diff --git a/pocketoptionapi/backend/ws/__pycache__/client.cpython-311.pyc b/pocketoptionapi/backend/ws/__pycache__/client.cpython-311.pyc index a0f22ee..a235fbc 100644 Binary files a/pocketoptionapi/backend/ws/__pycache__/client.cpython-311.pyc and b/pocketoptionapi/backend/ws/__pycache__/client.cpython-311.pyc differ diff --git a/pocketoptionapi/backend/ws/client.py b/pocketoptionapi/backend/ws/client.py index 4a73830..b6e3547 100644 --- a/pocketoptionapi/backend/ws/client.py +++ b/pocketoptionapi/backend/ws/client.py @@ -14,8 +14,8 @@ async def websocket_client(self, url, pro): async with websockets.connect( url, extra_headers={ - "Origin": "https://pocket-link19.co", - # "Origin": "https://po.trade/" + # "Origin": "https://pocket-link19.co", + "Origin": "https://po.trade/" }, ) as websocket: async for message in websocket: diff --git a/pocketoptionapi/candles.json b/pocketoptionapi/candles.json new file mode 100644 index 0000000..4519e16 --- /dev/null +++ b/pocketoptionapi/candles.json @@ -0,0 +1,17 @@ +{ + "asset": "AUDNZD_otc", + "index": 171201484810, + "data": + [ + { + "symbol_id": 70, + "time": 1712002800, + "open": 1.08567, + "close": 1.08553, + "high": 1.08586, + "low": 1.08475, + "asset": "AUDNZD_otc" + } + ], + "period": 60 +} diff --git a/pocketoptionapi/constants.py b/pocketoptionapi/constants.py index b3deb62..58cc096 100644 --- a/pocketoptionapi/constants.py +++ b/pocketoptionapi/constants.py @@ -1,32 +1,171 @@ -import random - -class REGION: - - REGIONS = { - "SEYCHELLES": "wss://api-sc.po.market/socket.io/?EIO=4&transport=websocket", - "HONGKONG": "wss://api-hk.po.market/socket.io/?EIO=4&transport=websocket", - "SERVER1": "wss://api-spb.po.market/socket.io/?EIO=4&transport=websocket", - "FRANCE2": "wss://api-fr2.po.market/socket.io/?EIO=4&transport=websocket", - "UNITED_STATES4": "wss://api-us4.po.market/socket.io/?EIO=4&transport=websocket", - "UNITED_STATES3": "wss://api-us3.po.market/socket.io/?EIO=4&transport=websocket", - "UNITED_STATES2": "wss://api-us2.po.market/socket.io/?EIO=4&transport=websocket", - "RUSSIA": "wss://api-msk.po.market/socket.io/?EIO=4&transport=websocket", - "SERVER2": "wss://api-l.po.market/socket.io/?EIO=4&transport=websocket", - "INDIA": "wss://api-in.po.market/socket.io/?EIO=4&transport=websocket", - "FRANCE": "wss://api-fr.po.market/socket.io/?EIO=4&transport=websocket", - "FINLAND": "wss://api-fin.po.market/socket.io/?EIO=4&transport=websocket", - "SERVER3": "wss://api-c.po.market/socket.io/?EIO=4&transport=websocket", - "ASIA": "wss://api-asia.po.market/socket.io/?EIO=4&transport=websocket", - } - - - def __getattr__(self, key): - try: - return self.REGIONS[key] - except KeyError: - raise AttributeError(f"'{self.REGIONS}' object has no attribute '{key}'") - - def get_regions(self, randomize: bool = True): - if randomize: - return sorted(list(self.REGIONS.values()), key=lambda k: random.random()) - return list(self.REGIONS.values()) \ No newline at end of file +import random + +ACTIVES = { + '#AAPL': 5, + '#AAPL_otc': 170, + '#AXP': 140, + '#AXP_otc': 291, + '#BA': 8, + '#BA_otc': 292, + '#CSCO': 154, + '#CSCO_otc': 427, + '#FB': 177, + '#FB_otc': 187, + '#INTC': 180, + '#INTC_otc': 190, + '#JNJ': 144, + '#JNJ_otc': 296, + '#JPM': 20, + '#MCD': 23, + '#MCD_otc': 175, + '#MSFT': 24, + '#MSFT_otc': 176, + '#PFE': 147, + '#PFE_otc': 297, + '#TSLA': 186, + '#TSLA_otc': 196, + '#XOM': 153, + '#XOM_otc': 426, + '100GBP': 315, + '100GBP_otc': 403, + 'AEX25': 449, + 'AMZN_otc': 412, + 'AUDCAD': 36, + 'AUDCAD_otc': 67, + 'AUDCHF': 37, + 'AUDCHF_otc': 68, + 'AUDJPY': 38, + 'AUDJPY_otc': 69, + 'AUDNZD_otc': 70, + 'AUDUSD': 40, + 'AUDUSD_otc': 71, + 'AUS200': 305, + 'AUS200_otc': 306, + 'BABA': 183, + 'BABA_otc': 428, + 'BCHEUR': 450, + 'BCHGBP': 451, + 'BCHJPY': 452, + 'BTCGBP': 453, + 'BTCJPY': 454, + 'BTCUSD': 197, + 'CAC40': 455, + 'CADCHF': 41, + 'CADCHF_otc': 72, + 'CADJPY': 42, + 'CADJPY_otc': 73, + 'CHFJPY': 43, + 'CHFJPY_otc': 74, + 'CHFNOK_otc': 457, + 'CITI': 326, + 'CITI_otc': 413, + 'D30EUR': 318, + 'D30EUR_otc': 406, + 'DASH_USD': 209, + 'DJI30': 322, + 'DJI30_otc': 409, + 'DOTUSD': 458, + 'E35EUR': 314, + 'E35EUR_otc': 402, + 'E50EUR': 319, + 'E50EUR_otc': 407, + 'ETHUSD': 272, + 'EURAUD': 44, + 'EURCAD': 45, + 'EURCHF': 46, + 'EURCHF_otc': 77, + 'EURGBP': 47, + 'EURGBP_otc': 78, + 'EURHUF_otc': 460, + 'EURJPY': 48, + 'EURJPY_otc': 79, + 'EURNZD_otc': 80, + 'EURRUB_otc': 200, + 'EURUSD': 1, + 'EURUSD_otc': 66, + 'F40EUR': 316, + 'F40EUR_otc': 404, + 'FDX_otc': 414, + 'GBPAUD': 51, + 'GBPAUD_otc': 81, + 'GBPCAD': 52, + 'GBPCHF': 53, + 'GBPJPY': 54, + 'GBPJPY_otc': 84, + 'GBPUSD': 56, + 'H33HKD': 463, + 'JPN225': 317, + 'JPN225_otc': 405, + 'LNKUSD': 464, + 'NASUSD': 323, + 'NASUSD_otc': 410, + 'NFLX': 182, + 'NFLX_otc': 429, + 'NZDJPY_otc': 89, + 'NZDUSD_otc': 90, + 'SMI20': 466, + 'SP500': 321, + 'SP500_otc': 408, + 'TWITTER': 330, + 'TWITTER_otc': 415, + 'UKBrent': 50, + 'UKBrent_otc': 164, + 'USCrude': 64, + 'USCrude_otc': 165, + 'USDCAD': 61, + 'USDCAD_otc': 91, + 'USDCHF': 62, + 'USDCHF_otc': 92, + 'USDJPY': 63, + 'USDJPY_otc': 93, + 'USDRUB_otc': 199, + 'VISA_otc': 416, + 'XAGEUR': 103, + 'XAGUSD': 65, + 'XAGUSD_otc': 167, + 'XAUEUR': 102, + 'XAUUSD': 2, + 'XAUUSD_otc': 169, + 'XNGUSD': 311, + 'XNGUSD_otc': 399, + 'XPDUSD': 313, + 'XPDUSD_otc': 401, + 'XPTUSD': 312, + 'XPTUSD_otc': 400, +} + + +class REGION: + REGIONS = { + "DEMO": "wss://demo-api-eu.po.market/socket.io/?EIO=4&transport=websocket", + "DEMO_2": "wss://try-demo-eu.po.market/socket.io/?EIO=4&transport=websocket", + "EUROPA": "wss://api-eu.po.market/socket.io/?EIO=4&transport=websocket", + "SEYCHELLES": "wss://api-sc.po.market/socket.io/?EIO=4&transport=websocket", + "HONGKONG": "wss://api-hk.po.market/socket.io/?EIO=4&transport=websocket", + "SERVER1": "wss://api-spb.po.market/socket.io/?EIO=4&transport=websocket", + "FRANCE2": "wss://api-fr2.po.market/socket.io/?EIO=4&transport=websocket", + "UNITED_STATES4": "wss://api-us4.po.market/socket.io/?EIO=4&transport=websocket", + "UNITED_STATES3": "wss://api-us3.po.market/socket.io/?EIO=4&transport=websocket", + "UNITED_STATES2": "wss://api-us2.po.market/socket.io/?EIO=4&transport=websocket", + + "UNITED_STATES": "wss://api-us-north.po.market/socket.io/?EIO=4&transport=websocket", + "RUSSIA": "wss://api-msk.po.market/socket.io/?EIO=4&transport=websocket", + "SERVER2": "wss://api-l.po.market/socket.io/?EIO=4&transport=websocket", + "INDIA": "wss://api-in.po.market/socket.io/?EIO=4&transport=websocket", + "FRANCE": "wss://api-fr.po.market/socket.io/?EIO=4&transport=websocket", + "FINLAND": "wss://api-fin.po.market/socket.io/?EIO=4&transport=websocket", + "SERVER3": "wss://api-c.po.market/socket.io/?EIO=4&transport=websocket", + "ASIA": "wss://api-asia.po.market/socket.io/?EIO=4&transport=websocket", + "SERVER4": "wss://api-us-south.po.market/socket.io/?EIO=4&transport=websocket" + } + + def __getattr__(self, key): + try: + return self.REGIONS[key] + except KeyError: + raise AttributeError(f"'{self.REGIONS}' object has no attribute '{key}'") + + def get_regions(self, randomize: bool = True): + if randomize: + return sorted(list(self.REGIONS.values()), key=lambda k: random.random()) + return list(self.REGIONS.values()) diff --git a/pocketoptionapi/expiration.py b/pocketoptionapi/expiration.py new file mode 100644 index 0000000..88927dd --- /dev/null +++ b/pocketoptionapi/expiration.py @@ -0,0 +1,80 @@ +import time +from datetime import datetime, timedelta + +# https://docs.python.org/3/library/datetime.html If optional argument tz is None or not specified, the timestamp is +# converted to the platform's local date and time, and the returned datetime object is naive. time.mktime( +# dt.timetuple()) + + +from datetime import datetime, timedelta +import time + + +def date_to_timestamp(date): + """Convierte un objeto datetime a timestamp.""" + return int(date.timestamp()) + + +def get_expiration_time(timestamp, duration): + """ + Calcula el tiempo de expiración más cercano basado en un timestamp dado y una duración. + El tiempo de expiración siempre terminará en el segundo:30 del minuto. + + :param timestamp: El timestamp inicial para el cálculo. + :param duration: La duración deseada en minutos. + """ + # Convertir el timestamp dado a un objeto datetime + now_date = datetime.fromtimestamp(timestamp) + + # Ajustar los segundos a: 30 si no lo están ya, de lo contrario, pasar al siguiente: 30 + if now_date.second < 30: + exp_date = now_date.replace(second=30, microsecond=0) + else: + exp_date = (now_date + timedelta(minutes=1)).replace(second=30, microsecond=0) + + # Calcular el tiempo de expiración teniendo en cuenta la duración + if duration > 1: + # Si la duración es más de un minuto, calcular el tiempo final agregando la duración + # menos un minuto, ya que ya hemos ajustado para terminar en: 30 segundos. + exp_date += timedelta(minutes=duration - 1) + + # Sumar dos horas al tiempo de expiración + exp_date += timedelta(hours=2) + # Convertir el tiempo de expiración a timestamp + expiration_timestamp = date_to_timestamp(exp_date) + + return expiration_timestamp + + +def get_remaning_time(timestamp): + now_date = datetime.fromtimestamp(timestamp) + exp_date = now_date.replace(second=0, microsecond=0) + if (int(date_to_timestamp(exp_date+timedelta(minutes=1)))-timestamp) > 30: + exp_date = exp_date+timedelta(minutes=1) + + else: + exp_date = exp_date+timedelta(minutes=2) + exp = [] + for _ in range(5): + exp.append(date_to_timestamp(exp_date)) + exp_date = exp_date+timedelta(minutes=1) + idx = 11 + index = 0 + now_date = datetime.fromtimestamp(timestamp) + exp_date = now_date.replace(second=0, microsecond=0) + while index < idx: + if int(exp_date.strftime("%M")) % 15 == 0 and (int(date_to_timestamp(exp_date))-int(timestamp)) > 60*5: + exp.append(date_to_timestamp(exp_date)) + index = index+1 + exp_date = exp_date+timedelta(minutes=1) + + remaning = [] + + for idx, t in enumerate(exp): + if idx >= 5: + dr = 15*(idx-4) + else: + dr = idx+1 + remaning.append((dr, int(t)-int(time.time()))) + + return remaning \ No newline at end of file diff --git a/pocketoptionapi/global_value.py b/pocketoptionapi/global_value.py new file mode 100644 index 0000000..356be65 --- /dev/null +++ b/pocketoptionapi/global_value.py @@ -0,0 +1,23 @@ +# python +websocket_is_connected = False +# try fix ssl.SSLEOFError: EOF occurred in violation of protocol (_ssl.c:2361) +ssl_Mutual_exclusion = False # mutex read write +# if false websocket can sent self.websocket.send(data) +# else can not sent self.websocket.send(data) +ssl_Mutual_exclusion_write = False # if thread write + +SSID = None + +check_websocket_if_error = False +websocket_error_reason = None + +balance_id = None +balance = None +balance_type = None +balance_updated = None +result = None +order_data = {} +order_open = [] +order_closed = [] +stat = [] +DEMO = None diff --git a/pocketoptionapi/indicators.py b/pocketoptionapi/indicators.py new file mode 100644 index 0000000..e69de29 diff --git a/pocketoptionapi/prueba_temp.py b/pocketoptionapi/prueba_temp.py new file mode 100644 index 0000000..1141261 --- /dev/null +++ b/pocketoptionapi/prueba_temp.py @@ -0,0 +1,9 @@ +import pandas as pd + + +df_1 = pd.read_csv('datos_completos_AUDNZD_otc.csv') +df_2 = pd.read_csv('datos_completos_AUDNZD_otc_2.csv') + +df_full = pd.concat([df_1, df_2], axis=0) +print(df_full.shape) +df_full.to_csv('datos_full_AUDNZD_otc.csv', index=False) diff --git a/pocketoptionapi/stable_api.py b/pocketoptionapi/stable_api.py new file mode 100644 index 0000000..336432b --- /dev/null +++ b/pocketoptionapi/stable_api.py @@ -0,0 +1,408 @@ +# This is a sample Python script. +import asyncio +import threading +import sys +from tzlocal import get_localzone +import json +from pocketoptionapi.api import PocketOptionAPI +import pocketoptionapi.constants as OP_code +# import pocketoptionapi.country_id as Country +# import threading +import time +import logging +import operator +import pocketoptionapi.global_value as global_value +from collections import defaultdict +from collections import deque +# from pocketoptionapi.expiration import get_expiration_time, get_remaning_time +import pandas as pd + +# Obtener la zona horaria local del sistema como una cadena en el formato IANA +local_zone_name = get_localzone() + + +def nested_dict(n, type): + if n == 1: + return defaultdict(type) + else: + return defaultdict(lambda: nested_dict(n - 1, type)) + + +def get_balance(): + # balances_raw = self.get_balances() + return global_value.balance + + +class PocketOption: + __version__ = "1.0.0" + + def __init__(self, ssid,demo): + self.size = [1, 5, 10, 15, 30, 60, 120, 300, 600, 900, 1800, + 3600, 7200, 14400, 28800, 43200, 86400, 604800, 2592000] + global_value.SSID = ssid + global_value.DEMO = demo + self.suspend = 0.5 + self.thread = None + self.subscribe_candle = [] + self.subscribe_candle_all_size = [] + self.subscribe_mood = [] + # for digit + self.get_digital_spot_profit_after_sale_data = nested_dict(2, int) + self.get_realtime_strike_list_temp_data = {} + self.get_realtime_strike_list_temp_expiration = 0 + self.SESSION_HEADER = { + "User-Agent": r"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) " + r"Chrome/66.0.3359.139 Safari/537.36"} + self.SESSION_COOKIE = {} + self.api = PocketOptionAPI() + self.loop = asyncio.get_event_loop() + + # + + # --start + # self.connect() + # this auto function delay too long + + # -------------------------------------------------------------------------- + + def get_server_timestamp(self): + return self.api.time_sync.server_timestamp + def Stop(self): + sys.exit() + + def get_server_datetime(self): + return self.api.time_sync.server_datetime + + def set_session(self, header, cookie): + self.SESSION_HEADER = header + self.SESSION_COOKIE = cookie + + def get_async_order(self, buy_order_id): + # name': 'position-changed', 'microserviceName': "portfolio"/"digital-options" + if self.api.order_async["deals"][0]["id"] == buy_order_id: + return self.api.order_async["deals"][0] + else: + return None + + def get_async_order_id(self, buy_order_id): + return self.api.order_async["deals"][0][buy_order_id] + + def start_async(self): + asyncio.run(self.api.connect()) + def disconnect(self): + """Gracefully close the WebSocket connection and clean up.""" + try: + # Close the WebSocket connection + if global_value.websocket_is_connected: + asyncio.run(self.api.close()) # Use the close method from the PocketOptionAPI class + print("WebSocket connection closed successfully.") + else: + print("WebSocket was not connected.") + + # Cancel any running asyncio tasks + if self.loop is not None: + for task in asyncio.all_tasks(self.loop): + task.cancel() + + # If you were using a custom event loop, stop and close it + if not self.loop.is_closed(): + self.loop.stop() + self.loop.close() + print("Event loop stopped and closed successfully.") + + # Clean up the WebSocket thread if it's still running + if self.api.websocket_thread is not None and self.api.websocket_thread.is_alive(): + self.api.websocket_thread.join() + print("WebSocket thread joined successfully.") + + except Exception as e: + print(f"Error during disconnect: {e}") + + def connect(self): + """ + Método síncrono para establecer la conexión. + Utiliza internamente el bucle de eventos de asyncio para ejecutar la coroutine de conexión. + """ + try: + # Iniciar el hilo que manejará la conexión WebSocket + websocket_thread = threading.Thread(target=self.api.connect, daemon=True) + websocket_thread.start() + + except Exception as e: + print(f"Error al conectar: {e}") + return False + return True + + def GetPayout(self, pair): + data = self.api.GetPayoutData() + data = json.loads(data) + data2 = None + for i in data: + if i[1] == pair: + data2 = i + + return data2[5] + + @staticmethod + def check_connect(): + # True/False + if global_value.websocket_is_connected == 0: + return False + elif global_value.websocket_is_connected is None: + return False + else: + return True + + # wait for timestamp getting + + # self.update_ACTIVES_OPCODE() + @staticmethod + def get_balance(): + if global_value.balance_updated: + return global_value.balance + else: + return None + @staticmethod + def check_open(): + #print(global_value.order_open) + return global_value.order_open + @staticmethod + def check_order_closed(ido): + + while ido not in global_value.order_closed : + time.sleep(0.1) + + for pack in global_value.stat : + if pack[0] == ido : + print('Order Closed',pack[1]) + + #print(global_value.order_closed) + return pack[0] + + + def buy(self, amount, active, action, expirations): + self.api.buy_multi_option = {} + self.api.buy_successful = None + req_id = "buy" + + try: + if req_id not in self.api.buy_multi_option: + self.api.buy_multi_option[req_id] = {"id": None} + else: + self.api.buy_multi_option[req_id]["id"] = None + except Exception as e: + logging.error(f"Error initializing buy_multi_option: {e}") + return False, None + + global_value.order_data = None + global_value.result = None + + + + self.api.buyv3(amount, active, action, expirations, req_id) + + start_t = time.time() + while True: + if global_value.result is not None and global_value.order_data is not None: + break + if time.time() - start_t >= 5: + if isinstance(global_value.order_data, dict) and "error" in global_value.order_data: + logging.error(global_value.order_data["error"]) + else: + logging.error("Unknown error occurred during buy operation") + return False, None + time.sleep(0.1) # Sleep for a short period to prevent busy-waiting + + return global_value.result, global_value.order_data.get("id", None) + + def check_win(self, id_number): + """Return amount of deals and win/lose status.""" + + start_t = time.time() + order_info = None + + while True: + try: + order_info = self.get_async_order(id_number) + if order_info and "id" in order_info and order_info["id"] is not None: + break + except: + pass + # except Exception as e: + # logging.error(f"Error retrieving order info: {e}") + + if time.time() - start_t >= 120: + logging.error("Timeout: Could not retrieve order info in time.") + return None, "unknown" + + time.sleep(0.1) # Sleep for a short period to prevent busy-waiting + + if order_info and "profit" in order_info: + status = "win" if order_info["profit"] > 0 else "lose" + return order_info["profit"], status + else: + logging.error("Invalid order info retrieved.") + return None, "unknown" + + @staticmethod + def last_time(timestamp, period): + # Divide por 60 para convertir a minutos, usa int() para truncar al entero más cercano (redondear hacia abajo), + # y luego multiplica por 60 para volver a convertir a segundos. + timestamp_redondeado = (timestamp // period) * period + return int(timestamp_redondeado) + + def get_candles(self, active, period, start_time=None, count=6000, count_request=1): + """ + Realiza múltiples peticiones para obtener datos históricos de velas y los procesa. + Devuelve un Dataframe ordenado de menor a mayor por la columna 'time'. + + :param active: El activo para el cual obtener las velas. + :param period: El intervalo de tiempo de cada vela en segundos. + :param count: El número de segundos a obtener en cada petición, max: 9000 = 150 datos de 1 min. + :param start_time: El tiempo final para la última vela. + :param count_request: El número de peticiones para obtener más datos históricos. + """ + try: + print("In try") + if start_time is None: + time_sync = self.get_server_timestamp() + time_red = self.last_time(time_sync, period) + else: + time_red = start_time + time_sync = self.get_server_timestamp() + + all_candles = [] + + for _ in range(count_request): + self.api.history_data = None + print("In FOr Loop") + + while True: + logging.info("Entered WHileloop in GetCandles") + print("In WHile loop") + try: + # Enviar la petición de velas + print("Before get candles") + self.api.getcandles(active, 30, count, time_red) + print("AFter get candles") + + # Esperar hasta que history_data no sea None + for i in range(1, 100): + if self.api.history_data is None: + print(f"SLeeping, attempt: {i} / 100") + time.sleep(0.1) + if i == 99: + break + + if self.api.history_data is not None: + print("In break") + all_candles.extend(self.api.history_data) + break + + except Exception as e: + logging.error(e) + # Puedes agregar lógica de reconexión aquí si es necesario + #self.api.connect() + + # Ordenar all_candles por 'index' para asegurar que estén en el orden correcto + all_candles = sorted(all_candles, key=lambda x: x["time"]) + + # Asegurarse de que se han recibido velas antes de actualizar time_red + if all_candles: + # Usar el tiempo de la última vela recibida para la próxima petición + time_red = all_candles[0]["time"] + + # Crear un DataFrame con todas las velas obtenidas + df_candles = pd.DataFrame(all_candles) + + # Ordenar por la columna 'time' de menor a mayor + df_candles = df_candles.sort_values(by='time').reset_index(drop=True) + df_candles['time'] = pd.to_datetime(df_candles['time'], unit='s') + df_candles.set_index('time', inplace=True) + df_candles.index = df_candles.index.floor('1s') + + # Resamplear los datos en intervalos de 30 segundos y calcular open, high, low, close + df_resampled = df_candles['price'].resample(f'{period}s').ohlc() + + # Resetear el índice para que 'time' vuelva a ser una columna + df_resampled.reset_index(inplace=True) + + print("FINISHED!!!") + + return df_resampled + except: + print("In except") + return None + + @staticmethod + def process_data_history(data, period): + """ + Este método toma datos históricos, los convierte en un DataFrame de pandas, redondea los tiempos al minuto más cercano, + y calcula los valores OHLC (Open, High, Low, Close) para cada minuto. Luego, convierte el resultado en un diccionario + y lo devuelve. + + :param dict data: Datos históricos que incluyen marcas de tiempo y precios. + :param int period: Periodo en minutos + :return: Un diccionario que contiene los valores OHLC agrupados por minutos redondeados. + """ + # Crear DataFrame + df = pd.DataFrame(data['history'], columns=['timestamp', 'price']) + # Convertir a datetime y redondear al minuto + df['datetime'] = pd.to_datetime(df['timestamp'], unit='s', utc=True) + # df['datetime'] = df['datetime'].dt.tz_convert(str(local_zone_name)) + df['minute_rounded'] = df['datetime'].dt.floor(f'{period / 60}min') + + # Calcular OHLC + ohlcv = df.groupby('minute_rounded').agg( + open=('price', 'first'), + high=('price', 'max'), + low=('price', 'min'), + close=('price', 'last') + ).reset_index() + + ohlcv['time'] = ohlcv['minute_rounded'].apply(lambda x: int(x.timestamp())) + ohlcv = ohlcv.drop(columns='minute_rounded') + + ohlcv = ohlcv.iloc[:-1] + + ohlcv_dict = ohlcv.to_dict(orient='records') + + return ohlcv_dict + + @staticmethod + def process_candle(candle_data, period): + """ + Resumen: Este método estático de Python, denominado `process_candle`, toma datos de velas financieras y un período de tiempo específico como entrada. + Realiza varias operaciones de limpieza y organización de datos utilizando pandas, incluyendo la ordenación por tiempo, eliminación de duplicados, + y reindexación. Además, verifica si las diferencias de tiempo entre las entradas consecutivas son iguales al período especificado y retorna tanto el DataFrame procesado + como un booleano indicando si todas las diferencias son iguales al período dado. Este método es útil para preparar y verificar la consistencia de los datos de velas financieras + para análisis posteriores. + + Procesa los datos de las velas recibidos como entrada. + Convierte los datos de entrada en un DataFrame de pandas, los ordena por tiempo de forma ascendente, + elimina duplicados basados en la columna 'time', y reinicia el índice del DataFrame. + Adicionalmente, verifica si las diferencias de tiempo entre las filas consecutivas son iguales al período especificado, + asumiendo que el período está dado en segundos, e imprime si todas las diferencias son de 60 segundos. + :param list candle_data: Datos de las velas a procesar. + :param int period: El período de tiempo entre las velas, usado para la verificación de diferencias de tiempo. + :return: DataFrame procesado con los datos de las velas. + """ + # Convierte los datos en un DataFrame y los añade al DataFrame final + data_df = pd.DataFrame(candle_data) + # datos_completos = pd.concat([datos_completos, data_df], ignore_index=True) + # Procesa los datos obtenidos + data_df.sort_values(by='time', ascending=True, inplace=True) + data_df.drop_duplicates(subset='time', keep="first", inplace=True) + data_df.reset_index(drop=True, inplace=True) + data_df.ffill(inplace=True) + #data_df.drop(columns='symbol_id', inplace=True) + # Verificación opcional: Comprueba si las diferencias son todas de 60 segundos (excepto el primer valor NaN) + diferencias = data_df['time'].diff() + diff = (diferencias[1:] == period).all() + return data_df, diff + + def change_symbol(self, active, period): + return self.api.change_symbol(active, period) + + def sync_datetime(self): + return self.api.synced_datetime diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-310.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-310.pyc new file mode 100644 index 0000000..38cc8ac Binary files /dev/null and b/pocketoptionapi/ws/__pycache__/client.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-311.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-311.pyc new file mode 100644 index 0000000..c231de3 Binary files /dev/null and b/pocketoptionapi/ws/__pycache__/client.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-312.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-312.pyc new file mode 100644 index 0000000..8394e57 Binary files /dev/null and b/pocketoptionapi/ws/__pycache__/client.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-37.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-37.pyc new file mode 100644 index 0000000..b841419 Binary files /dev/null and b/pocketoptionapi/ws/__pycache__/client.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-38.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-38.pyc new file mode 100644 index 0000000..9049a49 Binary files /dev/null and b/pocketoptionapi/ws/__pycache__/client.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-39.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-39.pyc new file mode 100644 index 0000000..448012b Binary files /dev/null and b/pocketoptionapi/ws/__pycache__/client.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/chanels/__pycache__/base.cpython-39.pyc b/pocketoptionapi/ws/chanels/__pycache__/base.cpython-39.pyc new file mode 100644 index 0000000..946781e Binary files /dev/null and b/pocketoptionapi/ws/chanels/__pycache__/base.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/chanels/__pycache__/buyv3.cpython-39.pyc b/pocketoptionapi/ws/chanels/__pycache__/buyv3.cpython-39.pyc new file mode 100644 index 0000000..85ecc42 Binary files /dev/null and b/pocketoptionapi/ws/chanels/__pycache__/buyv3.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/chanels/__pycache__/candles.cpython-39.pyc b/pocketoptionapi/ws/chanels/__pycache__/candles.cpython-39.pyc new file mode 100644 index 0000000..e553ea8 Binary files /dev/null and b/pocketoptionapi/ws/chanels/__pycache__/candles.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/chanels/__pycache__/get_balances.cpython-39.pyc b/pocketoptionapi/ws/chanels/__pycache__/get_balances.cpython-39.pyc new file mode 100644 index 0000000..1803a0b Binary files /dev/null and b/pocketoptionapi/ws/chanels/__pycache__/get_balances.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/chanels/__pycache__/ssid.cpython-39.pyc b/pocketoptionapi/ws/chanels/__pycache__/ssid.cpython-39.pyc new file mode 100644 index 0000000..930d8f8 Binary files /dev/null and b/pocketoptionapi/ws/chanels/__pycache__/ssid.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/chanels/base.py b/pocketoptionapi/ws/chanels/base.py new file mode 100644 index 0000000..2733f90 --- /dev/null +++ b/pocketoptionapi/ws/chanels/base.py @@ -0,0 +1,26 @@ +"""Module for base Pocket Option base websocket chanel.""" + + +class Base(object): + """Class for base Pocket Option websocket chanel.""" + + # pylint: disable=too-few-public-methods + + def __init__(self, api): + """ + :param api: The instance of :class:`IQOptionAPI + `. + """ + self.api = api + + def send_websocket_request(self, name, msg, request_id=""): + """Send request to Pocket Option server websocket. + + :param request_id: + :param str name: The websocket chanel name. + :param list msg: The websocket chanel msg. + + :returns: The instance of :class:`requests.Response`. + """ + + return self.api.send_websocket_request(name, msg, request_id) diff --git a/pocketoptionapi/ws/chanels/buyv3.py b/pocketoptionapi/ws/chanels/buyv3.py new file mode 100644 index 0000000..f39fa99 --- /dev/null +++ b/pocketoptionapi/ws/chanels/buyv3.py @@ -0,0 +1,61 @@ +import datetime +import json +import time +from pocketoptionapi.ws.chanels.base import Base +import logging +import pocketoptionapi.global_value as global_value +from pocketoptionapi.expiration import get_expiration_time + + +class Buyv3(Base): + name = "sendMessage" + + def __call__(self, amount, active, direction, duration, request_id): + + # thank Darth-Carrotpie's code + # https://github.com/Lu-Yi-Hsun/iqoptionapi/issues/6 + exp = get_expiration_time(int(self.api.timesync.server_timestamps), duration) + """if idx < 5: + option = 3 # "turbo" + else: + option = 1 # "binary""" + # Construir el diccionario + data_dict = { + "asset": active, + "amount": amount, + "action": direction, + "isDemo": 1, + "requestId": request_id, + "optionType": 100, + "time": duration + } + + message = ["openOrder", data_dict] + + self.send_websocket_request(self.name, message, str(request_id)) + + +class Buyv3_by_raw_expired(Base): + name = "sendMessage" + + def __call__(self, price, active, direction, option, expired, request_id): + + # thank Darth-Carrotpie's code + # https://github.com/Lu-Yi-Hsun/iqoptionapi/issues/6 + + if option == "turbo": + option_id = 3 # "turbo" + elif option == "binary": + option_id = 1 # "binary" + data = { + "body": {"price": price, + "active_id": active, + "expired": int(expired), + "direction": direction.lower(), + "option_type_id": option_id, + "user_balance_id": int(global_value.balance_id) + }, + "name": "binary-options.open-option", + "version": "1.0" + } + self.send_websocket_request(self.name, data, str(request_id)) diff --git a/pocketoptionapi/ws/chanels/candles.py b/pocketoptionapi/ws/chanels/candles.py new file mode 100644 index 0000000..62dcc76 --- /dev/null +++ b/pocketoptionapi/ws/chanels/candles.py @@ -0,0 +1,42 @@ +"""Module for Pocket option candles websocket chanel.""" + +from pocketoptionapi.ws.chanels.base import Base +import time +import random + + +def index_num(): + # El número mínimo sería 100000000000 (12 dígitos) + minimo = 5000 + # El número máximo sería 999999999999 (12 dígitos) + maximo = 10000 - 1 + # Generar y retornar un número aleatorio dentro del rango + return random.randint(minimo, maximo) + + +class GetCandles(Base): + """Class for Pocket option candles websocket chanel.""" + # pylint: disable=too-few-public-methods + + name = "sendMessage" + + def __call__(self, active_id, interval, count, end_time): + """Method to send message to candles websocket chanel. + + :param active_id: The active/asset identifier. + :param interval: The candle duration (timeframe for the candles). + :param count: The number of candles you want to have + """ + + # {"asset": "AUDNZD_otc", "index": 171201484810, "time": 1712002800, "offset": 9000, "period": 60}] + data = { + "asset": str(active_id), + "index": end_time, + "time": end_time, + "offset": count, # number of candles + "period": interval, # time size sample:if interval set 1 mean get time 0~1 candle + } + + data = ["loadHistoryPeriod", data] + + self.send_websocket_request(self.name, data) diff --git a/pocketoptionapi/ws/chanels/get_balances.py b/pocketoptionapi/ws/chanels/get_balances.py new file mode 100644 index 0000000..97659bb --- /dev/null +++ b/pocketoptionapi/ws/chanels/get_balances.py @@ -0,0 +1,18 @@ +from pocketoptionapi.ws.chanels.base import Base +import time + + +class Get_Balances(Base): + name = "sendMessage" + + def __call__(self): + """ + :param options_ids: list or int + """ + + data = {"name": "get-balances", + "version": "1.0" + } + print("get_balances in get_balances.py") + + self.send_websocket_request(self.name, data) diff --git a/pocketoptionapi/ws/chanels/ssid.py b/pocketoptionapi/ws/chanels/ssid.py new file mode 100644 index 0000000..62582ae --- /dev/null +++ b/pocketoptionapi/ws/chanels/ssid.py @@ -0,0 +1,17 @@ +"""Module for Pocket Option API ssid websocket chanel.""" + +from pocketoptionapi.ws.chanels.base import Base + + +class Ssid(Base): + """Class for Pocket Option API ssid websocket chanel.""" + # pylint: disable=too-few-public-methods + + name = "ssid" + + def __call__(self, ssid): + """Method to send message to ssid websocket chanel. + + :param ssid: The session identifier. + """ + self.send_websocket_request(self.name, ssid) diff --git a/pocketoptionapi/ws/channels/__pycache__/base.cpython-310.pyc b/pocketoptionapi/ws/channels/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000..f254efb Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/base.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/base.cpython-311.pyc b/pocketoptionapi/ws/channels/__pycache__/base.cpython-311.pyc new file mode 100644 index 0000000..8a9c9c1 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/base.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/base.cpython-312.pyc b/pocketoptionapi/ws/channels/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..7dd9986 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/base.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/base.cpython-37.pyc b/pocketoptionapi/ws/channels/__pycache__/base.cpython-37.pyc new file mode 100644 index 0000000..3c4fbb0 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/base.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/base.cpython-38.pyc b/pocketoptionapi/ws/channels/__pycache__/base.cpython-38.pyc new file mode 100644 index 0000000..0379593 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/base.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/base.cpython-39.pyc b/pocketoptionapi/ws/channels/__pycache__/base.cpython-39.pyc new file mode 100644 index 0000000..6e0aec9 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/base.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-310.pyc b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-310.pyc new file mode 100644 index 0000000..c5ff517 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-311.pyc b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-311.pyc new file mode 100644 index 0000000..5edc979 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-312.pyc b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-312.pyc new file mode 100644 index 0000000..5c4e350 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-37.pyc b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-37.pyc new file mode 100644 index 0000000..46d6cd2 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-38.pyc b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-38.pyc new file mode 100644 index 0000000..79cc305 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-39.pyc b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-39.pyc new file mode 100644 index 0000000..24ec963 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/buyv3.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/candles.cpython-310.pyc b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-310.pyc new file mode 100644 index 0000000..777acb4 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/candles.cpython-311.pyc b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-311.pyc new file mode 100644 index 0000000..2e0d25b Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/candles.cpython-312.pyc b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-312.pyc new file mode 100644 index 0000000..41eebcf Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/candles.cpython-37.pyc b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-37.pyc new file mode 100644 index 0000000..6e03805 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/candles.cpython-38.pyc b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-38.pyc new file mode 100644 index 0000000..4a4d37f Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/candles.cpython-39.pyc b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-39.pyc new file mode 100644 index 0000000..3e01754 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/candles.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-310.pyc b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-310.pyc new file mode 100644 index 0000000..34a0144 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-311.pyc b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-311.pyc new file mode 100644 index 0000000..8ed93bb Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-312.pyc b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-312.pyc new file mode 100644 index 0000000..2753898 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-37.pyc b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-37.pyc new file mode 100644 index 0000000..fd305e0 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-38.pyc b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-38.pyc new file mode 100644 index 0000000..027db20 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-39.pyc b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-39.pyc new file mode 100644 index 0000000..395b0e1 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/change_symbol.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-310.pyc b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-310.pyc new file mode 100644 index 0000000..66e1c60 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-311.pyc b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-311.pyc new file mode 100644 index 0000000..f5380dd Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-312.pyc b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-312.pyc new file mode 100644 index 0000000..53705f2 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-37.pyc b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-37.pyc new file mode 100644 index 0000000..c78d9d3 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-38.pyc b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-38.pyc new file mode 100644 index 0000000..3e1fd5a Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-39.pyc b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-39.pyc new file mode 100644 index 0000000..47bd32f Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/get_balances.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-310.pyc b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-310.pyc new file mode 100644 index 0000000..0b94b8c Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-311.pyc b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-311.pyc new file mode 100644 index 0000000..160e8f2 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-312.pyc b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-312.pyc new file mode 100644 index 0000000..4d88ce0 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-37.pyc b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-37.pyc new file mode 100644 index 0000000..c7fb444 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-38.pyc b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-38.pyc new file mode 100644 index 0000000..30c5b8b Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-39.pyc b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-39.pyc new file mode 100644 index 0000000..33542f7 Binary files /dev/null and b/pocketoptionapi/ws/channels/__pycache__/ssid.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/channels/base.py b/pocketoptionapi/ws/channels/base.py new file mode 100644 index 0000000..5a93fb1 --- /dev/null +++ b/pocketoptionapi/ws/channels/base.py @@ -0,0 +1,26 @@ +"""Module for base Pocket Option base websocket chanel.""" + + +class Base(object): + """Class for base Pocket Option websocket chanel.""" + + # pylint: disable=too-few-public-methods + + def __init__(self, api): + """ + :param api: The instance of :class:`PocketOptionAPI + `. + """ + self.api = api + + def send_websocket_request(self, name, msg, request_id=""): + """Send request to Pocket Option server websocket. + + :param request_id: + :param str name: The websocket chanel name. + :param list msg: The websocket chanel msg. + + :returns: The instance of :class:`requests.Response`. + """ + + return self.api.send_websocket_request(name, msg, request_id) diff --git a/pocketoptionapi/ws/channels/buyv3.py b/pocketoptionapi/ws/channels/buyv3.py new file mode 100644 index 0000000..406ecf4 --- /dev/null +++ b/pocketoptionapi/ws/channels/buyv3.py @@ -0,0 +1,61 @@ +import datetime +import json +import time +from pocketoptionapi.ws.channels.base import Base +import logging +import pocketoptionapi.global_value as global_value +from pocketoptionapi.expiration import get_expiration_time + + +class Buyv3(Base): + name = "sendMessage" + + def __call__(self, amount, active, direction, duration, request_id): + + # thank Darth-Carrotpie's code + # https://github.com/Lu-Yi-Hsun/iqoptionapi/issues/6 + # exp = get_expiration_time(int(self.api.timesync.server_timestamps), duration) + """if idx < 5: + option = 3 # "turbo" + else: + option = 1 # "binary""" + # Construir el diccionario + data_dict = { + "asset": active, + "amount": amount, + "action": direction, + "isDemo": 1, + "requestId": request_id, + "optionType": 100, + "time": duration + } + + message = ["openOrder", data_dict] + + self.send_websocket_request(self.name, message, str(request_id)) + + +class Buyv3_by_raw_expired(Base): + name = "sendMessage" + + def __call__(self, price, active, direction, option, expired, request_id): + + # thank Darth-Carrotpie's code + # https://github.com/Lu-Yi-Hsun/iqoptionapi/issues/6 + + if option == "turbo": + option_id = 3 # "turbo" + elif option == "binary": + option_id = 1 # "binary" + data = { + "body": {"price": price, + "active_id": active, + "expired": int(expired), + "direction": direction.lower(), + "option_type_id": option_id, + "user_balance_id": int(global_value.balance_id) + }, + "name": "binary-options.open-option", + "version": "1.0" + } + self.send_websocket_request(self.name, data, str(request_id)) diff --git a/pocketoptionapi/ws/channels/candles.py b/pocketoptionapi/ws/channels/candles.py new file mode 100644 index 0000000..806ca4f --- /dev/null +++ b/pocketoptionapi/ws/channels/candles.py @@ -0,0 +1,42 @@ +"""Module for Pocket option candles websocket chanel.""" + +from pocketoptionapi.ws.channels.base import Base +import time +import random + + +def index_num(): + # El número mínimo sería 100000000000 (12 dígitos) + minimo = 5000 + # El número máximo sería 999999999999 (12 dígitos) + maximo = 10000 - 1 + # Generar y retornar un número aleatorio dentro del rango + return random.randint(minimo, maximo) + + +class GetCandles(Base): + """Class for Pocket option candles websocket chanel.""" + # pylint: disable=too-few-public-methods + + name = "sendMessage" + + def __call__(self, active_id, interval, count, end_time): + """Method to send message to candles websocket chanel. + + :param active_id: The active/asset identifier. + :param interval: The candle duration (timeframe for the candles). + :param count: The number of candles you want to have + """ + + # {"asset": "AUDNZD_otc", "index": 171201484810, "time": 1712002800, "offset": 9000, "period": 60}] + data = { + "asset": str(active_id), + "index": end_time, + "offset": count, # number of candles + "period": interval, + "time": end_time, # time size sample:if interval set 1 mean get time 0~1 candle + } + + data = ["loadHistoryPeriod", data] + + self.send_websocket_request(self.name, data) diff --git a/pocketoptionapi/ws/channels/change_symbol.py b/pocketoptionapi/ws/channels/change_symbol.py new file mode 100644 index 0000000..9f6ff97 --- /dev/null +++ b/pocketoptionapi/ws/channels/change_symbol.py @@ -0,0 +1,25 @@ +"""Module for PocketOption change symbol websocket chanel.""" + +from pocketoptionapi.ws.channels.base import Base +import time +import random + + +class ChangeSymbol(Base): + """Class for Pocket option change symbol websocket chanel.""" + # pylint: disable=too-few-public-methods + + name = "sendMessage" + + def __call__(self, active_id, interval): + """Method to send message to candles websocket chanel. + + :param active_id: The active/asset identifier. + :param interval: The candle duration (timeframe for the candles). + """ + + data_stream = ["changeSymbol", { + "asset": active_id, + "period": interval}] + + self.send_websocket_request(self.name, data_stream) diff --git a/pocketoptionapi/ws/channels/get_balances.py b/pocketoptionapi/ws/channels/get_balances.py new file mode 100644 index 0000000..377b90c --- /dev/null +++ b/pocketoptionapi/ws/channels/get_balances.py @@ -0,0 +1,18 @@ +from pocketoptionapi.ws.channels.base import Base +import time + + +class Get_Balances(Base): + name = "sendMessage" + + def __call__(self): + """ + :param options_ids: list or int + """ + + data = {"name": "get-balances", + "version": "1.0" + } + print("get_balances in get_balances.py") + + self.send_websocket_request(self.name, data) diff --git a/pocketoptionapi/ws/channels/ssid.py b/pocketoptionapi/ws/channels/ssid.py new file mode 100644 index 0000000..777d934 --- /dev/null +++ b/pocketoptionapi/ws/channels/ssid.py @@ -0,0 +1,17 @@ +"""Module for Pocket Option API ssid websocket chanel.""" + +from pocketoptionapi.ws.channels.base import Base + + +class Ssid(Base): + """Class for Pocket Option API ssid websocket chanel.""" + # pylint: disable=too-few-public-methods + + name = "ssid" + + def __call__(self, ssid): + """Method to send message to ssid websocket chanel. + + :param ssid: The session identifier. + """ + self.send_websocket_request(self.name, ssid) diff --git a/pocketoptionapi/ws/client.py b/pocketoptionapi/ws/client.py new file mode 100644 index 0000000..a570c56 --- /dev/null +++ b/pocketoptionapi/ws/client.py @@ -0,0 +1,271 @@ +import asyncio +from datetime import datetime, timedelta, timezone + +import websockets +import json +import logging +import ssl + +# Suponiendo la existencia de estos módulos basados en tu código original +import pocketoptionapi.constants as OP_code +import pocketoptionapi.global_value as global_value +from pocketoptionapi.constants import REGION +from pocketoptionapi.ws.objects.timesync import TimeSync +from pocketoptionapi.ws.objects.time_sync import TimeSynchronizer + +logger = logging.getLogger(__name__) + +timesync = TimeSync() +sync = TimeSynchronizer() + + +async def on_open(): # pylint: disable=unused-argument + """Method to process websocket open.""" + print("CONNECTED SUCCESSFUL") + logger.debug("Websocket client connected.") + global_value.websocket_is_connected = True + + +async def send_ping(ws): + while global_value.websocket_is_connected is False: + await asyncio.sleep(0.1) + pass + while True: + await asyncio.sleep(20) + await ws.send('42["ps"]') + + +async def process_message(message): + try: + data = json.loads(message) + print(f"Received message: {data}") + + # Procesa el mensaje dependiendo del tipo + if isinstance(data, dict) and 'uid' in data: + uid = data['uid'] + print(f"UID: {uid}") + elif isinstance(data, list) and len(data) > 0: + event_type = data[0] + event_data = data[1] + print(f"Event type: {event_type}, Event data: {event_data}") + # Aquí puedes añadir más lógica para manejar diferentes tipos de eventos + + except json.JSONDecodeError as e: + print(f"JSON decode error: {e}") + except KeyError as e: + print(f"Key error: {e}") + except Exception as e: + print(f"Error processing message: {e}") + + +class WebsocketClient(object): + def __init__(self, api) -> None: + """ + Inicializa el cliente WebSocket. + + :param api: Instancia de la clase PocketOptionApi + """ + + self.updateHistoryNew = None + self.updateStream = None + self.history_data_ready = None + self.successCloseOrder = False + self.api = api + self.message = None + self.url = None + self.ssid = global_value.SSID + self.websocket = None + self.region = REGION() + self.loop = asyncio.get_event_loop() + self.wait_second_message = False + self._updateClosedDeals = False + + async def websocket_listener(self, ws): + try: + async for message in ws: + await self.on_message(message) + except Exception as e: + logging.warning(f"Error occurred: {e}") + + async def connect(self): + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + try: + await self.api.close() + except: + pass + + while not global_value.websocket_is_connected: + for url in self.region.get_regions(True): + print(url) + try: + async with websockets.connect( + url, + ssl=ssl_context, + extra_headers={"Origin": "https://pocketoption.com", "Cache-Control": "no-cache"}, + user_agent_header="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, " + "like Gecko) Chrome/124.0.0.0 Safari/537.36" + ) as ws: + + # print("Connected a: ", url) + self.websocket = ws + self.url = url + global_value.websocket_is_connected = True + + # Crear y ejecutar tareas + # process_message_task = asyncio.create_task(process_message(self.message)) + on_message_task = asyncio.create_task(self.websocket_listener(ws)) + sender_task = asyncio.create_task(self.send_message(self.message)) + ping_task = asyncio.create_task(send_ping(ws)) + + await asyncio.gather(on_message_task, sender_task, ping_task) + + except websockets.ConnectionClosed as e: + global_value.websocket_is_connected = False + await self.on_close(e) + logger.warning("Trying another server") + + except Exception as e: + global_value.websocket_is_connected = False + await self.on_error(e) + + await asyncio.sleep(1) # Esperar antes de intentar reconectar + + return True + + async def send_message(self, message): + while global_value.websocket_is_connected is False: + await asyncio.sleep(0.1) + + self.message = message + + if global_value.websocket_is_connected and message is not None: + try: + await self.websocket.send(message) + except Exception as e: + logger.warning(f"Error sending message: {e}") + elif message is not None: + logger.warning("WebSocket not connected") + + @staticmethod + def dict_queue_add(self, dict, maxdict, key1, key2, key3, value): + if key3 in dict[key1][key2]: + dict[key1][key2][key3] = value + else: + while True: + try: + dic_size = len(dict[key1][key2]) + except: + dic_size = 0 + if dic_size < maxdict: + dict[key1][key2][key3] = value + break + else: + # del mini key + del dict[key1][key2][sorted(dict[key1][key2].keys(), reverse=False)[0]] + + async def on_message(self, message): # pylint: disable=unused-argument + """Method to process websocket messages.""" + # global_value.ssl_Mutual_exclusion = True + logger.debug(message) + + if type(message) is bytes: + message = message.decode('utf-8') + message = json.loads(message) + + # print(message, type(message)) + if "balance" in message: + if "uid" in message: + global_value.balance_id = message["uid"] + global_value.balance = message["balance"] + global_value.balance_type = message["isDemo"] + + elif "requestId" in message and message["requestId"] == 'buy': + global_value.order_data = message + + elif self.wait_second_message and isinstance(message, list): + self.wait_second_message = False # Restablecer para futuros mensajes + self._updateClosedDeals = False # Restablecer el estado + + elif isinstance(message, dict) and self.successCloseOrder: + self.api.order_async = message + self.successCloseOrder = False # Restablecer para futuros mensajes + + elif self.history_data_ready and isinstance(message, dict): + self.history_data_ready = False + self.api.history_data = message["data"] + + elif self.updateStream and isinstance(message, list): + self.updateStream = False + self.api.time_sync.server_timestamp = message[0][1] + + elif self.updateHistoryNew and isinstance(message, dict): + self.updateHistoryNew = False + self.api.historyNew = message + + return + + else: + pass + # print(message) + + if message.startswith('0') and "sid" in message: + await self.websocket.send("40") + + elif message == "2": + await self.websocket.send("3") + + elif "40" and "sid" in message: + await self.websocket.send(self.ssid) + + elif message.startswith('451-['): + json_part = message.split("-", 1)[1] # Eliminar el prefijo numérico y el guion para obtener el JSON válido + + # Convertir la parte JSON a un objeto Python + message = json.loads(json_part) + + if message[0] == "successauth": + await on_open() + + elif message[0] == "successupdateBalance": + global_value.balance_updated = True + elif message[0] == "successopenOrder": + global_value.result = True + + # Si es el primer mensaje de interés + elif message[0] == "updateClosedDeals": + # Establecemos que hemos recibido el primer mensaje de interés + self._updateClosedDeals = True + self.wait_second_message = True # Establecemos que esperamos el segundo mensaje de interés + await self.websocket.send('42["changeSymbol",{"asset":"AUDNZD_otc","period":60}]') + + elif message[0] == "successcloseOrder": + self.successCloseOrder = True + self.wait_second_message = True # Establecemos que esperamos el segundo mensaje de interés + + elif message[0] == "loadHistoryPeriod": + self.history_data_ready = True + + elif message[0] == "updateStream": + self.updateStream = True + + elif message[0] == "updateHistoryNew": + self.updateHistoryNew = True + # self.api.historyNew = None + + elif message.startswith("42") and "NotAuthorized" in message: + logging.error("User not Authorized: Please Change SSID for one valid") + global_value.ssl_Mutual_exclusion = False + await self.websocket.close() + + async def on_error(self, error): # pylint: disable=unused-argument + logger.error(error) + global_value.websocket_error_reason = str(error) + global_value.check_websocket_if_error = True + + async def on_close(self, error): # pylint: disable=unused-argument + # logger.debug("Websocket connection closed.") + # logger.warning(f"Websocket connection closed. Reason: {error}") + global_value.websocket_is_connected = False diff --git a/pocketoptionapi/ws/objects/__pycache__/base.cpython-310.pyc b/pocketoptionapi/ws/objects/__pycache__/base.cpython-310.pyc new file mode 100644 index 0000000..277920a Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/base.cpython-311.pyc b/pocketoptionapi/ws/objects/__pycache__/base.cpython-311.pyc new file mode 100644 index 0000000..0503f2b Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/base.cpython-312.pyc b/pocketoptionapi/ws/objects/__pycache__/base.cpython-312.pyc new file mode 100644 index 0000000..77965a6 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/base.cpython-37.pyc b/pocketoptionapi/ws/objects/__pycache__/base.cpython-37.pyc new file mode 100644 index 0000000..dbb7ac8 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/base.cpython-38.pyc b/pocketoptionapi/ws/objects/__pycache__/base.cpython-38.pyc new file mode 100644 index 0000000..6ce24aa Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/base.cpython-39.pyc b/pocketoptionapi/ws/objects/__pycache__/base.cpython-39.pyc new file mode 100644 index 0000000..cd49bfb Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/candles.cpython-310.pyc b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-310.pyc new file mode 100644 index 0000000..ed21b27 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/candles.cpython-311.pyc b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-311.pyc new file mode 100644 index 0000000..1a6608a Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/candles.cpython-312.pyc b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-312.pyc new file mode 100644 index 0000000..92bacfb Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/candles.cpython-37.pyc b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-37.pyc new file mode 100644 index 0000000..ef7c9cf Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/candles.cpython-38.pyc b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-38.pyc new file mode 100644 index 0000000..2c911e6 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/candles.cpython-39.pyc b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-39.pyc new file mode 100644 index 0000000..44d0c72 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-310.pyc b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-310.pyc new file mode 100644 index 0000000..c7b67a4 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-311.pyc b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-311.pyc new file mode 100644 index 0000000..6aa4d02 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-312.pyc b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-312.pyc new file mode 100644 index 0000000..e4739d5 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-37.pyc b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-37.pyc new file mode 100644 index 0000000..bb26b23 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-38.pyc b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-38.pyc new file mode 100644 index 0000000..5f4a5cf Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/time_sync.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-310.pyc b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-310.pyc new file mode 100644 index 0000000..1d5ad67 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-310.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-311.pyc b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-311.pyc new file mode 100644 index 0000000..0f1d11f Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-311.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-312.pyc b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-312.pyc new file mode 100644 index 0000000..ee8bb76 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-312.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-37.pyc b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-37.pyc new file mode 100644 index 0000000..02aa6eb Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-37.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-38.pyc b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-38.pyc new file mode 100644 index 0000000..b011954 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-38.pyc differ diff --git a/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-39.pyc b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-39.pyc new file mode 100644 index 0000000..c89d48d Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/timesync.cpython-39.pyc differ diff --git a/pocketoptionapi/ws/objects/base.py b/pocketoptionapi/ws/objects/base.py new file mode 100644 index 0000000..60c6d5d --- /dev/null +++ b/pocketoptionapi/ws/objects/base.py @@ -0,0 +1,17 @@ +"""Module for Pocket Option Base websocket object.""" + + +class Base(object): + """Class for Pocket Option Base websocket object.""" + # pylint: disable=too-few-public-methods + + def __init__(self): + self.__name = None + + @property + def name(self): + """Property to get websocket object name. + + :returns: The name of websocket object. + """ + return self.__name diff --git a/pocketoptionapi/ws/objects/candles.py b/pocketoptionapi/ws/objects/candles.py new file mode 100644 index 0000000..cc022e8 --- /dev/null +++ b/pocketoptionapi/ws/objects/candles.py @@ -0,0 +1,113 @@ +"""Module for Pocket Option Candles websocket object.""" + +from pocketoptionapi.ws.objects.base import Base + + +class Candle(object): + """Class for Pocket Option candle.""" + + def __init__(self, candle_data): + """ + :param candle_data: The list of candles data. + """ + self.__candle_data = candle_data + + @property + def candle_time(self): + """Property to get candle time. + + :returns: The candle time. + """ + return self.__candle_data[0] + + @property + def candle_open(self): + """Property to get candle open value. + + :returns: The candle open value. + """ + return self.__candle_data[1] + + @property + def candle_close(self): + """Property to get candle close value. + + :returns: The candle close value. + """ + return self.__candle_data[2] + + @property + def candle_high(self): + """Property to get candle high value. + + :returns: The candle high value. + """ + return self.__candle_data[3] + + @property + def candle_low(self): + """Property to get candle low value. + + :returns: The candle low value. + """ + return self.__candle_data[4] + + @property + def candle_type(self): # pylint: disable=inconsistent-return-statements + """Property to get candle type value. + + :returns: The candle type value. + """ + if self.candle_open < self.candle_close: + return "green" + elif self.candle_open > self.candle_close: + return "red" + + +class Candles(Base): + """Class for Pocket Option Candles websocket object.""" + + def __init__(self): + super(Candles, self).__init__() + self.__name = "candles" + self.__candles_data = None + + @property + def candles_data(self): + """Property to get candles data. + + :returns: The list of candles data. + """ + return self.__candles_data + + @candles_data.setter + def candles_data(self, candles_data): + """Method to set candles data.""" + self.__candles_data = candles_data + + @property + def first_candle(self): + """Method to get first candle. + + :returns: The instance of :class:`Candle + `. + """ + return Candle(self.candles_data[0]) + + @property + def second_candle(self): + """Method to get second candle. + + :returns: The instance of :class:`Candle + `. + """ + return Candle(self.candles_data[1]) + + @property + def current_candle(self): + """Method to get current candle. + + :returns: The instance of :class:`Candle + `. + """ + return Candle(self.candles_data[-1]) diff --git a/pocketoptionapi/ws/objects/time_sync.py b/pocketoptionapi/ws/objects/time_sync.py new file mode 100644 index 0000000..e5d7995 --- /dev/null +++ b/pocketoptionapi/ws/objects/time_sync.py @@ -0,0 +1,70 @@ +import logging +import time +from datetime import datetime, timedelta, timezone + + +class TimeSynchronizer: + def __init__(self): + self.server_time_reference = None + self.local_time_reference = None + self.timezone_offset = timedelta(seconds=self._get_local_timezone_offset()) + + @staticmethod + def _get_local_timezone_offset(): + """ + Obtiene el desplazamiento de la zona horaria local en segundos. + + :return: Desplazamiento de la zona horaria local en segundos. + """ + local_time = datetime.now() + utc_time = datetime.utcnow() + offset = (local_time - utc_time).total_seconds() + return offset + + def synchronize(self, server_time): + """ + Sincroniza el tiempo local con el tiempo del servidor. + + :param server_time: Tiempo del servidor en segundos (puede ser un timestamp). + """ + + self.server_time_reference = server_time + self.local_time_reference = time.time() + + def get_synced_time(self): + """ + Obtiene el tiempo sincronizado basado en el tiempo actual del sistema. + + :return: Tiempo sincronizado en segundos. + """ + if self.server_time_reference is None or self.local_time_reference is None: + raise ValueError("El tiempo no ha sido sincronizado aún.") + + # Calcula la diferencia de tiempo desde la última sincronización + elapsed_time = time.time() - self.local_time_reference + # Calcula el tiempo sincronizado + synced_time = self.server_time_reference + elapsed_time + return synced_time + + def get_synced_datetime(self): + """ + Convierte el tiempo sincronizado a un objeto datetime ajustado a la zona horaria local. + + :return: Tiempo sincronizado como un objeto datetime. + """ + synced_time_seconds = self.get_synced_time() + # Redondear los segundos + rounded_time_seconds = round(synced_time_seconds) + # Convertir a datetime en UTC + synced_datetime_utc = datetime.fromtimestamp(rounded_time_seconds, tz=timezone.utc) + # Ajustar el tiempo sincronizado a la zona horaria local + synced_datetime_local = synced_datetime_utc + self.timezone_offset + return synced_datetime_local + + def update_sync(self, new_server_time): + """ + Actualiza la sincronización con un nuevo tiempo del servidor. + + :param new_server_time: Nuevo tiempo del servidor en segundos. + """ + self.synchronize(new_server_time) diff --git a/pocketoptionapi/ws/objects/timesync.py b/pocketoptionapi/ws/objects/timesync.py new file mode 100644 index 0000000..34bed24 --- /dev/null +++ b/pocketoptionapi/ws/objects/timesync.py @@ -0,0 +1,71 @@ +"""Module for Pocket Option TimeSync websocket object.""" + +import time +import datetime + +from pocketoptionapi.ws.objects.base import Base + + +class TimeSync(Base): + """Class for Pocket Option TimeSync websocket object.""" + + def __init__(self): + super(TimeSync, self).__init__() + self.__name = "timeSync" + self.__server_timestamp = time.time() + self.__expiration_time = 1 + + @property + def server_timestamp(self): + """Property to get server timestamp. + + :returns: The server timestamp. + """ + return self.__server_timestamp + + @server_timestamp.setter + def server_timestamp(self, timestamp): + """Method to set server timestamp.""" + self.__server_timestamp = timestamp + + @property + def server_datetime(self): + """Property to get server datetime. + + :returns: The server datetime. + """ + return datetime.datetime.fromtimestamp(self.server_timestamp) + + @property + def expiration_time(self): + """Property to get expiration time. + + :returns: The expiration time. + """ + return self.__expiration_time + + @expiration_time.setter + def expiration_time(self, minutes): + """Method to set expiration time + + :param int minutes: The expiration time in minutes. + """ + self.__expiration_time = minutes + + @property + def expiration_datetime(self): + """Property to get expiration datetime. + + :returns: The expiration datetime. + """ + return self.server_datetime + datetime.timedelta(minutes=self.expiration_time) + + @property + def expiration_timestamp(self): + """Property to get expiration timestamp. + + :returns: The expiration timestamp. + """ + return time.mktime(self.expiration_datetime.timetuple()) + + diff --git a/test.py b/test.py index fb0f44c..3be0cc8 100644 --- a/test.py +++ b/test.py @@ -1,12 +1,21 @@ -from pocketoptionapi.backend.ws.client import WebSocketClient -import anyio - -data = r'42["auth",{"session":"a:4:{s:10:\"session_id\";s:32:\"d8fa39dd2f2f58e34e8640fd61f054c2\";s:10:\"ip_address\";s:14:\"90.36.9.15\";s:10:\"user_agent\";s:101:\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\";s:13:\"last_activity\";i:1707850603;}9f383935faff5a86bc1658bbde8c61e7","isDemo":1,"uid":27658142,"platform":3}]' - -async def main(): - url = "wss://api-l.po.market/socket.io/?EIO=4&transport=websocket" - wb = WebSocketClient(session=data) - await wb.websocket_client(url=url, pro=wb.pro) - -if __name__ == '__main__': - anyio.run(main) \ No newline at end of file +import random +import time +import dotenv +from pocketoptionapi.stable_api import PocketOption +import logging +import os +logging.basicConfig(level=logging.DEBUG,format='%(asctime)s %(message)s') + +dotenv.DotEnv() + +ssid = (r'42["auth",{"session":"vtftn12e6f5f5008moitsd6skl","isDemo":1,"uid":27658142,"platform":1}]') #os.getenv("SSID") +print(ssid) +api = PocketOption(ssid) + +if __name__ == "__main__": + api.connect() + time.sleep(5) + + print(api.check_connect(), "check connect") + + print(api.get_balance()) diff --git a/test/client_test_1.py b/test/client_test_1.py new file mode 100644 index 0000000..d7f5620 --- /dev/null +++ b/test/client_test_1.py @@ -0,0 +1,62 @@ +import websockets +import anyio +from rich.pretty import pprint as print +import json + +SESSION = r'42["auth",{"session":"a:4:{s:10:\"session_id\";s:32:\"c53eec05c6f8a8be2d134d4fd55266f8\";s:10:\"ip_address\";s:14:\"46.138.176.190\";s:10:\"user_agent\";s:101:\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\";s:13:\"last_activity\";i:1707850603;}9f383935faff5a86bc1658bbde8c61e7","isDemo":1,"uid":72038016,"platform":3}]' + + +async def websocket_client(url, pro): + while True: + try: + async with websockets.connect( + url, + extra_headers={ + "Origin": "https://pocket-link19.co", + #"Origin": "https://po.trade/" + }, + ) as websocket: + async for message in websocket: + await pro(message, websocket, url) + except KeyboardInterrupt: + exit() + except Exception as e: + print(e) + print("Connection lost... reconnecting") + await anyio.sleep(5) + return True + + +async def pro(message, websocket, url): + # if byte data + if type(message) == type(b""): + # cut 100 first symbols of byte date to prevent spam + print(str(message)[:100]) + return + else: + print(message) + + # Code to make order + # data = r'42["openOrder",{"asset":"#AXP_otc","amount":1,"action":"call","isDemo":1,"requestId":14680035,"optionType":100,"time":20}]' + # await websocket.send(data) + + if message.startswith('0{"sid":"'): + print(f"{url.split('/')[2]} got 0 sid send 40 ") + await websocket.send("40") + elif message == "2": + # ping-pong thing + print(f"{url.split('/')[2]} got 2 send 3") + await websocket.send("3") + + if message.startswith('40{"sid":"'): + print(f"{url.split('/')[2]} got 40 sid send session") + await websocket.send(SESSION) + + +async def main(): + url = "wss://api-l.po.market/socket.io/?EIO=4&transport=websocket" + await websocket_client(url, pro) + + +if __name__ == "__main__": + anyio.run(main) \ No newline at end of file