yfrake.client.client.ClientSingleton.get()   A
last analyzed

Complexity

Conditions 3

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 14
nop 3
dl 0
loc 24
rs 9.7
c 0
b 0
f 0
1
# ==================================================================================== #
2
#    client.py - This file is part of the YFrake package.                              #
3
# ------------------------------------------------------------------------------------ #
4
#                                                                                      #
5
#    MIT License                                                                       #
6
#                                                                                      #
7
#    Copyright (c) 2022 Mattias Aabmets                                                #
8
#                                                                                      #
9
#    Permission is hereby granted, free of charge, to any person obtaining a copy      #
10
#    of this software and associated documentation files (the "Software"), to deal     #
11
#    in the Software without restriction, including without limitation the rights      #
12
#    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell         #
13
#    copies of the Software, and to permit persons to whom the Software is             #
14
#    furnished to do so, subject to the following conditions:                          #
15
#                                                                                      #
16
#    The above copyright notice and this permission notice shall be included in all    #
17
#    copies or substantial portions of the Software.                                   #
18
#                                                                                      #
19
#    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR        #
20
#    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,          #
21
#    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE       #
22
#    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER            #
23
#    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,     #
24
#    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE     #
25
#    SOFTWARE.                                                                         #
26
#                                                                                      #
27
# ==================================================================================== #
28
from ..cache.cache import CacheSingleton
29
from .return_types import ClientResponse
30
from .return_types import AsyncResults
31
from .return_types import ThreadResults
32
from .validators import validate_request
33
from .decorator import Decorator
34
from . import endpoints
35
from . import session
36
import asyncio
37
38
39
# ==================================================================================== #
40
class ClientSingleton(Decorator):
41
    _err_sess_not_open = 'Session has not been opened! (YFrake)'
42
    _cache = CacheSingleton()
43
    __instance__ = None
44
45
    # Singleton pattern
46
    # ------------------------------------------------------------------------------------ #
47
    def __new__(cls):
48
        if not cls.__instance__:
49
            cls.__instance__ = super(ClientSingleton, cls).__new__(cls)
50
        return cls.__instance__
51
52
    # ------------------------------------------------------------------------------------ #
53
    @classmethod
54
    async def _wrapper(cls, endpoint: str, params: dict, resp: ClientResponse) -> ClientResponse:
55
        result = cls._cache.get(endpoint, params)
56
        if not result:
57
            attr = 'get_' + endpoint
58
            func = getattr(endpoints, attr)
59
            result = await func(endpoint, dict(params))
60
            cls._cache.set(endpoint, params, result)
61
        setattr(resp, '_endpoint', result['endpoint'])
62
        setattr(resp, '_error', result['error'])
63
        setattr(resp, '_data', result['data'])
64
        getattr(resp, '_event').set()
65
        return resp
66
67
    # ------------------------------------------------------------------------------------ #
68
    @classmethod
69
    def get(cls, endpoint: str = '', **kwargs) -> ClientResponse:
70
        """
71
        This method schedules a single task to fetch
72
        market data from the Yahoo Finance API servers.
73
        Returns immediately with the pending
74
        ClientResponse object.
75
        """
76
        if not session.is_locked():
77
            raise RuntimeError(cls._err_sess_not_open)
78
79
        validate_request(endpoint, kwargs)
80
        resp = ClientResponse(cls._async_mode)
81
82
        if cls._async_mode:
83
            future = asyncio.create_task(
84
                cls._wrapper(endpoint, kwargs, resp))
85
        else:
86
            loop = session.get_event_loop()
87
            future = asyncio.run_coroutine_threadsafe(
88
                cls._wrapper(endpoint, kwargs, resp), loop)
89
90
        setattr(resp, '_future', future)
91
        return resp
92
93
    # ------------------------------------------------------------------------------------ #
94
    @classmethod
95
    def batch_get(cls, queries: list) -> AsyncResults | ThreadResults:
96
        """
97
        This helper method schedules multiple tasks to fetch
98
        market data from the Yahoo Finance API servers.
99
        Returns immediately with either the pending
100
        AsyncResults or ThreadResults collection.
101
        """
102
        requests = dict()
103
        for query in queries:
104
            resp = cls.get(**query)
105
            fut = resp.future
106
            requests[fut] = resp
107
108
        return {
109
            True: AsyncResults,
110
            False: ThreadResults
111
        }[cls._async_mode](requests)
112
113
    # ------------------------------------------------------------------------------------ #
114
    @classmethod
115
    def get_all(cls, symbol: str) -> AsyncResults | ThreadResults:
116
        """
117
        This helper method obtains all the available data about a symbol.
118
        Returns immediately with either the pending
119
        AsyncResults or ThreadResults collection.
120
        """
121
        queries = [
122
            dict(endpoint='historical_prices', symbol=symbol, interval='1d', range='max', events=True, extHours=True),
123
            dict(endpoint='shares_outstanding', symbol=symbol, startDate=946728000, endDate=3162293065),
124
            dict(endpoint='options', symbol=symbol, getAllData=True),
125
            dict(endpoint='balance_statements', symbol=symbol),
126
            dict(endpoint='calendar_events', symbol=symbol),
127
            dict(endpoint='cashflow_statements', symbol=symbol),
128
            dict(endpoint='company_overview', symbol=symbol),
129
            dict(endpoint='detailed_summary', symbol=symbol),
130
            dict(endpoint='earnings', symbol=symbol),
131
            dict(endpoint='earnings_history', symbol=symbol),
132
            dict(endpoint='earnings_trend', symbol=symbol),
133
            dict(endpoint='esg_chart', symbol=symbol),
134
            dict(endpoint='esg_scores', symbol=symbol),
135
            dict(endpoint='financials', symbol=symbol),
136
            dict(endpoint='fund_ownership', symbol=symbol),
137
            dict(endpoint='income_statements', symbol=symbol),
138
            dict(endpoint='insider_holders', symbol=symbol),
139
            dict(endpoint='insider_transactions', symbol=symbol),
140
            dict(endpoint='insights', symbol=symbol),
141
            dict(endpoint='institution_ownership', symbol=symbol),
142
            dict(endpoint='key_statistics', symbol=symbol),
143
            dict(endpoint='major_holders', symbol=symbol),
144
            dict(endpoint='news', symbol=symbol),
145
            dict(endpoint='price_overview', symbol=symbol),
146
            dict(endpoint='purchase_activity', symbol=symbol),
147
            dict(endpoint='quote_type', symbol=symbol),
148
            dict(endpoint='quotes_overview', symbols=symbol),
149
            dict(endpoint='ratings_history', symbol=symbol),
150
            dict(endpoint='recommendation_trend', symbol=symbol),
151
            dict(endpoint='recommendations', symbol=symbol),
152
            dict(endpoint='sec_filings', symbol=symbol),
153
            dict(endpoint='validate_symbols', symbols=symbol)
154
        ]
155
        return cls.batch_get(queries)
156