From 97749c9bd257281a2dddef9b2e87a6ac0c1cdc89 Mon Sep 17 00:00:00 2001 From: chema2411 <130813519+chema2411@users.noreply.github.com> Date: Thu, 11 Apr 2024 11:12:49 -0400 Subject: [PATCH] Add files via upload --- pocketoptionapi/_.py | 0 pocketoptionapi/api.py | 274 ++++++++++++++++++ pocketoptionapi/candles.json | 17 ++ pocketoptionapi/constants.py | 200 +++++++++++-- pocketoptionapi/expiration.py | 80 +++++ pocketoptionapi/global_value.py | 19 ++ pocketoptionapi/stable_api.py | 271 +++++++++++++++++ .../ws/__pycache__/client.cpython-39.pyc | Bin 0 -> 5741 bytes .../chanels/__pycache__/base.cpython-39.pyc | Bin 0 -> 1129 bytes .../chanels/__pycache__/buyv3.cpython-39.pyc | Bin 0 -> 1648 bytes .../__pycache__/candles.cpython-39.pyc | Bin 0 -> 1288 bytes .../__pycache__/get_balances.cpython-39.pyc | Bin 0 -> 750 bytes .../chanels/__pycache__/ssid.cpython-39.pyc | Bin 0 -> 786 bytes pocketoptionapi/ws/chanels/base.py | 26 ++ pocketoptionapi/ws/chanels/buyv3.py | 61 ++++ pocketoptionapi/ws/chanels/candles.py | 42 +++ pocketoptionapi/ws/chanels/get_balances.py | 18 ++ pocketoptionapi/ws/chanels/ssid.py | 17 ++ pocketoptionapi/ws/client.py | 230 +++++++++++++++ .../objects/__pycache__/base.cpython-39.pyc | Bin 0 -> 789 bytes .../__pycache__/candles.cpython-39.pyc | Bin 0 -> 3779 bytes .../__pycache__/timesync.cpython-39.pyc | Bin 0 -> 2485 bytes pocketoptionapi/ws/objects/base.py | 17 ++ pocketoptionapi/ws/objects/candles.py | 113 ++++++++ pocketoptionapi/ws/objects/timesync.py | 71 +++++ test.py | 43 ++- 26 files changed, 1455 insertions(+), 44 deletions(-) create mode 100644 pocketoptionapi/_.py create mode 100644 pocketoptionapi/api.py create mode 100644 pocketoptionapi/candles.json create mode 100644 pocketoptionapi/expiration.py create mode 100644 pocketoptionapi/global_value.py create mode 100644 pocketoptionapi/stable_api.py create mode 100644 pocketoptionapi/ws/__pycache__/client.cpython-39.pyc create mode 100644 pocketoptionapi/ws/chanels/__pycache__/base.cpython-39.pyc create mode 100644 pocketoptionapi/ws/chanels/__pycache__/buyv3.cpython-39.pyc create mode 100644 pocketoptionapi/ws/chanels/__pycache__/candles.cpython-39.pyc create mode 100644 pocketoptionapi/ws/chanels/__pycache__/get_balances.cpython-39.pyc create mode 100644 pocketoptionapi/ws/chanels/__pycache__/ssid.cpython-39.pyc create mode 100644 pocketoptionapi/ws/chanels/base.py create mode 100644 pocketoptionapi/ws/chanels/buyv3.py create mode 100644 pocketoptionapi/ws/chanels/candles.py create mode 100644 pocketoptionapi/ws/chanels/get_balances.py create mode 100644 pocketoptionapi/ws/chanels/ssid.py create mode 100644 pocketoptionapi/ws/client.py create mode 100644 pocketoptionapi/ws/objects/__pycache__/base.cpython-39.pyc create mode 100644 pocketoptionapi/ws/objects/__pycache__/candles.cpython-39.pyc create mode 100644 pocketoptionapi/ws/objects/__pycache__/timesync.cpython-39.pyc create mode 100644 pocketoptionapi/ws/objects/base.py create mode 100644 pocketoptionapi/ws/objects/candles.py create mode 100644 pocketoptionapi/ws/objects/timesync.py diff --git a/pocketoptionapi/_.py b/pocketoptionapi/_.py new file mode 100644 index 0000000..e69de29 diff --git a/pocketoptionapi/api.py b/pocketoptionapi/api.py new file mode 100644 index 0000000..cb0d725 --- /dev/null +++ b/pocketoptionapi/api.py @@ -0,0 +1,274 @@ +"""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.chanels.get_balances import * + +from pocketoptionapi.ws.chanels.ssid import Ssid +# from pocketoptionapi.ws.chanels.subscribe import * +# from pocketoptionapi.ws.chanels.unsubscribe import * +# from pocketoptionapi.ws.chanels.setactives import SetActives +from pocketoptionapi.ws.chanels.candles import GetCandles +# from pocketoptionapi.ws.chanels.buyv2 import Buyv2 +from pocketoptionapi.ws.chanels.buyv3 import * +# from pocketoptionapi.ws.chanels.user import * +# from pocketoptionapi.ws.chanels.api_game_betinfo import Game_betinfo +# from pocketoptionapi.ws.chanels.instruments import Get_instruments +# from pocketoptionapi.ws.chanels.get_financial_information import GetFinancialInformation +# from pocketoptionapi.ws.chanels.strike_list import Strike_list +# from pocketoptionapi.ws.chanels.leaderboard import Leader_Board + +# from pocketoptionapi.ws.chanels.traders_mood import Traders_mood_subscribe +# from pocketoptionapi.ws.chanels.traders_mood import Traders_mood_unsubscribe +# from pocketoptionapi.ws.chanels.buy_place_order_temp import Buy_place_order_temp +# from pocketoptionapi.ws.chanels.get_order import Get_order +# from pocketoptionapi.ws.chanels.get_deferred_orders import GetDeferredOrders +# from pocketoptionapi.ws.chanels.get_positions import * + +# from pocketoptionapi.ws.chanels.get_available_leverages import Get_available_leverages +# from pocketoptionapi.ws.chanels.cancel_order import Cancel_order +# from pocketoptionapi.ws.chanels.close_position import Close_position +# from pocketoptionapi.ws.chanels.get_overnight_fee import Get_overnight_fee +# from pocketoptionapi.ws.chanels.heartbeat import Heartbeat + +# from pocketoptionapi.ws.chanels.digital_option import * +# from pocketoptionapi.ws.chanels.api_game_getoptions import * +# from pocketoptionapi.ws.chanels.sell_option import Sell_Option +# from pocketoptionapi.ws.chanels.change_tpsl import Change_Tpsl +# from pocketoptionapi.ws.chanels.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 collections import defaultdict + + +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 = {} + timesync = TimeSync() + # 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 + + # ------------------ + + 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 + + # self.websocket_client.send_message(data) + 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 + + try: + self.close() + except: + pass + + check_websocket, websocket_reason = self.start_websocket() + + if not check_websocket: + return check_websocket, websocket_reason + + self.timesync.server_timestamps = None + while True: + try: + if self.timesync.server_timestamps is not None: + break + except: + pass + return True, None + + def close(self): + self.websocket.close() + 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) 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..88cd61c 100644 --- a/pocketoptionapi/constants.py +++ b/pocketoptionapi/constants.py @@ -1,32 +1,168 @@ -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 = { + # "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..f5e1c7c --- /dev/null +++ b/pocketoptionapi/global_value.py @@ -0,0 +1,19 @@ +# 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 = {} diff --git a/pocketoptionapi/stable_api.py b/pocketoptionapi/stable_api.py new file mode 100644 index 0000000..214b184 --- /dev/null +++ b/pocketoptionapi/stable_api.py @@ -0,0 +1,271 @@ +# This is a sample Python script. +import asyncio +import threading + +from tzlocal import get_localzone + +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__ = "6.8.9.1" + + def __init__(self, ssid): + 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 + 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.timesync.server_timestamp + + def get_server_datetime(self): + return self.api.timesync.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 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 + + @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 + + def buy(self, amount, ACTIVES, ACTION, expirations): + self.api.buy_multi_option = {} + self.api.buy_successful = None + req_id = "buy" + try: + self.api.buy_multi_option[req_id]["id"] = None + except: + pass + global_value.order_data = None + global_value.result = None + self.api.buyv3( + amount, ACTIVES, ACTION, expirations, req_id) + start_t = time.time() + while global_value.result is None or global_value.order_data is None: + if time.time() - start_t >= 5: + logging.error(global_value.order_data["error"]) + return False, None + + return global_value.result, global_value.order_data["id"] + + def check_win(self, id_number): + """Return amount of deals and win/lose status.""" + + while True: + try: + order_info = self.get_async_order(id_number) + if order_info["id"] is not None: + break + except: + pass + + # Determina el estado de win/lose basado en el profit. + status = "win" if order_info["profit"] > 0 else "lose" + return order_info["profit"], status + + @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, ACTIVES, period, end_time, count=9000, 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 ACTIVES: 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. + :param end_time: El tiempo final para la última vela. + :param count_request: El número de peticiones para obtener más datos históricos. + """ + + time_sync = self.get_server_timestamp() - period + time_red = self.last_time(time_sync, period) + print(time_red) + data_temp = [] + self.api.candles = data_temp + + if self.api.historyNew is not None: + data_temp.extend(self.process_data_history(self.api.historyNew, period)) + + for i in range(count_request): + self.api.history_data = None + while True: + try: + self.api.getcandles(ACTIVES, period, count, time_red) + while self.check_connect and self.api.history_data is None: + pass + if self.api.history_data is not None: + data_temp.extend(self.api.history_data) + break + + except: + logging.error('**error** get_candles need reconnect') + # self.connect() + + # Ajusta el tiempo de inicio para la próxima petición + time_red -= count + + return self.api.candles + + def process_data_history(self, 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}T') + + # 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_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.fillna(method='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 + diff --git a/pocketoptionapi/ws/__pycache__/client.cpython-39.pyc b/pocketoptionapi/ws/__pycache__/client.cpython-39.pyc new file mode 100644 index 0000000000000000000000000000000000000000..42a9c1a9ee42af347c1c2927e771f2ba1a4e307f GIT binary patch literal 5741 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 ZNw0u+fqDC{Tf(scuTylgZa$wc{1^MsuNwdW literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..946781e1c531fcc9ae4b1995cb209f929e839390 GIT binary patch literal 1129 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. + """ + 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/client.py b/pocketoptionapi/ws/client.py new file mode 100644 index 0000000..92067c9 --- /dev/null +++ b/pocketoptionapi/ws/client.py @@ -0,0 +1,230 @@ +import asyncio +from datetime import datetime, timedelta, timezone + +import websocket +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 + +logger = logging.getLogger(__name__) + +timesync = TimeSync() + + +async def on_open(): # pylint: disable=unused-argument + """Method to process websocket open.""" + print("CONECTADO CON EXITO") + logger = logging.getLogger(__name__) + logger.debug("Websocket client connected.") + global_value.websocket_is_connected = True + + +async def send_pin(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"]') + + +class WebsocketClient(object): + def __init__(self, api) -> None: + """ + Inicializa el cliente WebSocket. + + + :param ssid: El ID de sesión para la autenticación. + :param url: La URL del WebSocket a la que conectarse. + """ + + 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.esperar_segundo_mensaje = False + self.recibido_updateClosedDeals = False + + async def websocket_listener(self, ws): + async for message in ws: + await self.on_message(message) + + async def connect(self): + ssl_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT) + ssl_context.check_hostname = False + ssl_context.verify_mode = ssl.CERT_NONE + + while global_value.websocket_is_connected is False: + for url in self.region.get_regions(True): + print(url) + try: + async with websockets.connect(url, extra_headers={"Origin": "https://m.pocketoption.com"}) as ws: + + print("Conectado a: ", url) + self.websocket = ws + self.url = url + on_message_task = asyncio.create_task(self.websocket_listener(ws)) + sender_task = asyncio.create_task(send_pin(ws)) + message_task = asyncio.create_task(self.send_message(self.message)) + await asyncio.gather(on_message_task, sender_task, message_task) + except websockets.ConnectionClosed as e: + await self.on_close(e) + except Exception as e: + await self.on_error(e) + + return True + + async def send_message(self, message): + while global_value.websocket_is_connected is False: + await asyncio.sleep(0.1) + pass + + self.message = message + + if global_value.websocket_is_connected: + + if message is not None: + await self.websocket.send(message) + + else: + logging.warning("WebSocked 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 = logging.getLogger(__name__) + logger.debug(message) + + # message = json.loads(str(message)) + + if type(message) is bytes: + # Paso 1: Decodificar los bytes a una cadena de texto (string) + message = message.decode('utf-8') + message = json.loads(message) + + # print(message, type(message)) # [:1000000]) + if "balance" 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 + + # Supongamos que este es el segundo mensaje de interés basado en tu lógica + elif self.esperar_segundo_mensaje and isinstance(message, list): + self.esperar_segundo_mensaje = False # Restablecer para futuros mensajes + self.recibido_updateClosedDeals = False # Restablecer el estado + + elif self.esperar_segundo_mensaje and isinstance(message, dict) and self.successcloseOrder: + self.api.order_async = message + self.successcloseOrder = False # Restablecer para futuros mensajes + self.esperar_segundo_mensaje = False # Restablecer el estado + + 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.timesync.server_timestamp = message[0][1] + # print("server_timestamp asignado:", timesync.server_timestamp) + + elif self.updateHistoryNew and isinstance(message, dict): + self.updateHistoryNew = False + self.api.historyNew = message + + return + + else: + pass + # print(message) + + if message.startswith('0{"sid":"'): + # print(f"{self.url.split('/')[2]} got 0 sid send 40") + await self.websocket.send("40") + elif message == "2": + # print(f"{self.url.split('/')[2]} got 2 send 3") + await self.websocket.send("3") + + elif message.startswith('40{"sid":"'): + # print(f"{self.url.split('/')[2]} got 40 sid send session") + await self.websocket.send(self.ssid) + + elif message.startswith('451-['): + # Eliminar el prefijo numérico y el guion para obtener el JSON válido + json_part = message.split("-", 1)[1] + + # 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.recibido_updateClosedDeals = True + self.esperar_segundo_mensaje = 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.esperar_segundo_mensaje = 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 + + async def on_error(self, error): # pylint: disable=unused-argument + """Method to process websocket errors.""" + logger = logging.getLogger(__name__) + 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 + """Method to process websocket close.""" + logger = logging.getLogger(__name__) + logger.debug("Websocket connection closed.") + global_value.websocket_is_connected = False 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 0000000000000000000000000000000000000000..3b7ca3ed30069b594bf5622c03b38e9b0742a261 GIT binary patch literal 789 zcma)4v2NQi5G5sAR+M<}$e~LXbvJ?_ZBlfqV<4Rb0asb16DPK*kTL=b+2T*h;$PC5 zz3AMjca$5XX@?wehr1(n_uiw5!60F1KYk|DN6y$26)#O-af0sNVNfh%Uzp-5*f14n z{t#w?3NP59Xg+*3YBkq#YOEZa@)p2{oG1%l8Dn!Jx$M<8nU zJOS#C@THI=r2uULu<*YM<_7?`t75)8=>sTJ0@ywcjH?|NOR~IJN77r`X-Ne7KONnO ixd`Jh;J!OL4DA6n+o%)UH~%-IjgLAs$281i7vXRI*uBjF literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..063c3461acbc861d8a44551bedf718751d9967c6 GIT binary patch literal 3779 zcmc&%TW{k;6rOP$$IYef?sf|+E;Toyyaa)ekfO3I6mb#SP^pVRR)y<&CT+S-oSku~ z8uhJ}_67J2@YsLB@9-;6{R_OnIWxB7wDrBa3#bQF|5mfmxG>o`}QD%g6Xt)L?UlUeg<~d;|ubfp}6Z%zVLEqvA^sB74L)!NA zqsI}Ogxu*z!r6|3r#yDPAIF1epc)N<82XUTI7}t2=4w zj(y<|9sLTA`LW-1o*Z%~9LU&-`ssjlaHNy3>RnKG&C`B)USVNpR?k;kJm7X}PN-q66~zc(6NGfrwOqeCQv`T^$s;tKTR)j+_J7A^KZRJQ(s$J%`;D zJf4V=)D+Fl$@mobA%Xk$f{H5I2wgWdu#$`zeDw-E(B50qyQ%m3tsV|90N{rb0Dx-b z{Njg`|AkSUnSr^?!3ZR}3<@rNr5p+lF97Ao5-33!Nq##lb5nCL7=9I*8^JVF;?>%X z0-AgCfl=cNz$uH&;o#u#c4&$-b1;~M%E4rt441xNz;b1i;qU@bc1j`>M#r~9Qka+n z!J|?Rq@G3vR(?>xac4dzFj@}dU08PthaD0P7V0?sBT0Qb#UXpY_vKN{rPOxuD4ss4S{Kgl`rH4CGhn z4n87unObzZnxZLJLCon}*(y8;Kogyoob62bOC<2cw**i?+#xq^^EBN`h4MReN2$0q zUCRIzX;|QD^V(w`A4Uv?T;@Wa!srQ1>~I8?Fgxg*Vsl0*KHGt+y{X)|0~C39eq)tW z@Dg#RuT{U!zKxfN{y@MbO5-TKMGQtV_D2E7%T`y#xVxt*$T`PEvN?E`Uy3@%vXfql zIw?yXz2xEE?2e{xA{T}_g935g90%oOdWHKbM_!x7p*UedK7O0$1Mbq%h`l^Q;2#DN zZGH~a9T~Eg2?(d$ifKU2XayFGc5Ek3;85~C;SO)TB)lb@OFL+B>xc~8#b?h_K97Yz zPNjdCM$d)zf6VkGo%zR;L>!&ZBL7UBsA45OnTkjcU&H6f!GPw@ccBsy&YF*1PSgt! z1OYe%p#=6TG6sO$e(42{XccfSY2XTX+U72TGqVrV)MpoF=$Ixk_=>@s8uB>Z9!R!}= z+1j~st_etBTIgL)0EK-Jc)B#?YdmJq&Rc4p2`TZg++%Dqn`p*@9ah0*Y!)UWcW__+ zLF2U&@^>K_zE?U-vixCVQM|||GDOcAg&XjW67d+u4r3AWXY7Xj{lnA!(cyV?8p`BQ zrsz&JD*Rr!{o8CZ%8a*D69ys7&(x^!yc(HrLr@H}bLj$s9yH}aN9Z&(NJ`81bDzM`rOy4|0~2{LD#S84!d2yeDJ9q=8@d4yB(17-BokG18D^ zw^Cfwt0+R?bZ%v`++|Yjqm4{fl-?q2qJM|Y@*u;dkpx*N!^EG=w4Y37b1lkrSlIvy z)%vhbpac0Hpsqr%UG85Ey!)W@$&%fgH?RPiz`F+mCbFhexcjCyr+?7+1t?d!|6v`C8x(K1 z0p_wUQ}4>YCT05l@*n)`w0RQ)G?Z%#I1>H~NbV-jaaH0aQJ6r_vsi3of3HERKJx1Z z{YS@e6c+yBCe1+~YZewL#_}#>iTQm1xyRUV^Dr(~+KlluV$2kY+{?b+fkt!GJNf!~3$}{T1Fvrv5pWM?I@&{{!rs BZ2 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/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..b71d8d3 100644 --- a/test.py +++ b/test.py @@ -1,12 +1,31 @@ -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 + +from pocketoptionapi.stable_api import PocketOption + +ssid = (r'') +api = PocketOption(ssid) + + +def direction(): + # Selecciona aleatoriamente entre 'call' y 'put' + return random.choice(['call', 'put']) + + +if __name__ == "__main__": + api.connect() + time.sleep(2) + + print(api.check_connect(), "check connect") + + data_candles = api.get_candles("AUDNZD_otc", 60, time.time(), count_request=1) + + data, diff = api.process_candle(data_candles, 60) + print(data) + print(diff) + data.to_csv('datos_AUDNZD_otc_test.csv', index=False) + while api.check_connect(): + print(api.get_server_timestamp(), "server datetime") + time.sleep(1) + + # Cierra la conexión con la API