-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathfcs_api.py
More file actions
194 lines (157 loc) · 4.98 KB
/
Copy pathfcs_api.py
File metadata and controls
194 lines (157 loc) · 4.98 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
"""
FCS API - REST API Client
Python client for Forex, Cryptocurrency, and Stock market data
@package FcsApi
@author FCS API <support@fcsapi.com>
@link https://fcsapi.com
"""
import requests
from typing import Dict, Optional, Any, Union
from .fcs_config import FcsConfig
class FcsApi:
"""
FCS API REST Client
Main client class for accessing Forex, Crypto, and Stock market data.
"""
BASE_URL = 'https://api-v4.fcsapi.com/'
def __init__(self, config: Union[str, FcsConfig, None] = None):
"""
Constructor
Args:
config: API key string, FcsConfig object, or None to use default config
"""
self._last_response: Dict = {}
# Configure authentication
if isinstance(config, FcsConfig):
self.config = config
elif isinstance(config, str):
# Backward compatible: accept string API key
self.config = FcsConfig.with_access_key(config)
else:
# Use default config (key from FcsConfig)
self.config = FcsConfig()
# Lazy-loaded modules
self._forex = None
self._crypto = None
self._stock = None
@property
def forex(self):
"""Get Forex API module (lazy loading)"""
if self._forex is None:
from .fcs_forex import FcsForex
self._forex = FcsForex(self)
return self._forex
@property
def crypto(self):
"""Get Crypto API module (lazy loading)"""
if self._crypto is None:
from .fcs_crypto import FcsCrypto
self._crypto = FcsCrypto(self)
return self._crypto
@property
def stock(self):
"""Get Stock API module (lazy loading)"""
if self._stock is None:
from .fcs_stock import FcsStock
self._stock = FcsStock(self)
return self._stock
def set_timeout(self, seconds: int) -> 'FcsApi':
"""
Set request timeout
Args:
seconds: Timeout in seconds
Returns:
self for method chaining
"""
self.config.timeout = seconds
return self
def get_config(self) -> FcsConfig:
"""
Get config
Returns:
FcsConfig instance
"""
return self.config
def generate_token(self) -> Dict[str, Any]:
"""
Generate token for frontend use
Only works when auth_method is 'token'
Returns:
dict: {'_token': str, '_expiry': int, '_public_key': str}
"""
return self.config.generate_token()
def request(self, endpoint: str, params: Optional[Dict] = None) -> Optional[Dict]:
"""
Make API request (POST with form data)
Args:
endpoint: API endpoint
params: Request parameters
Returns:
Response data or None on error
"""
if params is None:
params = {}
# Add authentication parameters
auth_params = self.config.get_auth_params()
all_params = {**params, **auth_params}
url = self.BASE_URL + endpoint
try:
response = requests.post(
url,
data=all_params,
headers={
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json',
'Accept-Encoding': 'gzip, deflate'
},
timeout=(self.config.connect_timeout, self.config.timeout)
)
data = response.json()
self._last_response = data
return data
except requests.exceptions.RequestException as e:
self._last_response = {
'status': False,
'code': 0,
'msg': f'Request Error: {str(e)}',
'response': None
}
return None
except ValueError as e:
self._last_response = {
'status': False,
'code': 0,
'msg': f'Invalid JSON response: {str(e)}',
'response': None
}
return None
def get_last_response(self) -> Dict:
"""
Get last response
Returns:
Last response dictionary
"""
return self._last_response
def get_response_data(self) -> Any:
"""
Get response data only
Returns:
Response data or None
"""
return self._last_response.get('response')
def is_success(self) -> bool:
"""
Check if last request was successful
Returns:
True if successful, False otherwise
"""
return self._last_response.get('status') is True
def get_error(self) -> Optional[str]:
"""
Get error message from last response
Returns:
Error message or None if successful
"""
if self.is_success():
return None
return self._last_response.get('msg', 'Unknown error')