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 0000000..42a9c1a 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/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 0000000..3b7ca3e Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/base.cpython-39.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..063c346 Binary files /dev/null and b/pocketoptionapi/ws/objects/__pycache__/candles.cpython-39.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..38798ce 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/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