firstrade 0.0.20__py3-none-any.whl → 0.0.30__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
firstrade/order.py CHANGED
@@ -1,15 +1,20 @@
1
1
  from enum import Enum
2
2
 
3
- from bs4 import BeautifulSoup
4
-
5
3
  from firstrade import urls
6
4
  from firstrade.account import FTSession
7
5
 
8
6
 
9
7
  class PriceType(str, Enum):
10
8
  """
11
- This is an :class: 'enum.Enum'
12
- that contains the valid price types for an order.
9
+ Enum for valid price types in an order.
10
+
11
+ Attributes:
12
+ MARKET (str): Market order, executed at the current market price.
13
+ LIMIT (str): Limit order, executed at a specified price or better.
14
+ STOP (str): Stop order, becomes a market order once a specified price is reached.
15
+ STOP_LIMIT (str): Stop-limit order, becomes a limit order once a specified price is reached.
16
+ TRAILING_STOP_DOLLAR (str): Trailing stop order with a specified dollar amount.
17
+ TRAILING_STOP_PERCENT (str): Trailing stop order with a specified percentage.
13
18
  """
14
19
 
15
20
  LIMIT = "2"
@@ -22,8 +27,14 @@ class PriceType(str, Enum):
22
27
 
23
28
  class Duration(str, Enum):
24
29
  """
25
- This is an :class:'~enum.Enum'
26
- that contains the valid durations for an order.
30
+ Enum for valid order durations.
31
+
32
+ Attributes:
33
+ DAY (str): Day order.
34
+ GT90 (str): Good till 90 days order.
35
+ PRE_MARKET (str): Pre-market order.
36
+ AFTER_MARKET (str): After-market order.
37
+ DAY_EXT (str): Day extended order.
27
38
  """
28
39
 
29
40
  DAY = "0"
@@ -35,220 +46,202 @@ class Duration(str, Enum):
35
46
 
36
47
  class OrderType(str, Enum):
37
48
  """
38
- This is an :class:'~enum.Enum'
39
- that contains the valid order types for an order.
49
+ Enum for valid order types.
50
+
51
+ Attributes:
52
+ BUY (str): Buy order.
53
+ SELL (str): Sell order.
54
+ SELL_SHORT (str): Sell short order.
55
+ BUY_TO_COVER (str): Buy to cover order.
56
+ BUY_OPTION (str): Buy option order.
57
+ SELL_OPTION (str): Sell option order.
40
58
  """
41
59
 
42
60
  BUY = "B"
43
61
  SELL = "S"
44
62
  SELL_SHORT = "SS"
45
63
  BUY_TO_COVER = "BC"
64
+ BUY_OPTION = "BO"
65
+ SELL_OPTION = "SO"
66
+
67
+
68
+ class OrderInstructions(str, Enum):
69
+ """
70
+ Enum for valid order instructions.
71
+
72
+ Attributes:
73
+ AON (str): All or none.
74
+ OPG (str): At the Open.
75
+ CLO (str): At the Close.
76
+ """
77
+
78
+ AON = "1"
79
+ OPG = "4"
80
+ CLO = "5"
81
+
82
+
83
+ class OptionType(str, Enum):
84
+ """
85
+ Enum for valid option types.
86
+
87
+ Attributes:
88
+ CALL (str): Call option.
89
+ PUT (str): Put option.
90
+ """
91
+
92
+ CALL = "C"
93
+ PUT = "P"
46
94
 
47
95
 
48
96
  class Order:
49
97
  """
50
- This class contains information about an order.
51
- It also contains a method to place an order.
98
+ Represents an order with methods to place it.
99
+
100
+ Attributes:
101
+ ft_session (FTSession): The session object for placing orders.
52
102
  """
53
103
 
54
104
  def __init__(self, ft_session: FTSession):
55
105
  self.ft_session = ft_session
56
- self.order_confirmation = {}
57
106
 
58
107
  def place_order(
59
108
  self,
60
- account,
61
- symbol,
109
+ account: str,
110
+ symbol: str,
62
111
  price_type: PriceType,
63
112
  order_type: OrderType,
64
- quantity,
65
113
  duration: Duration,
66
- price=0.00,
67
- dry_run=True,
68
- notional=False,
114
+ quantity: int = 0,
115
+ price: float = 0.00,
116
+ stop_price: float = None,
117
+ dry_run: bool = True,
118
+ notional: bool = False,
119
+ order_instruction: OrderInstructions = "0",
69
120
  ):
70
121
  """
71
122
  Builds and places an order.
72
- :attr: 'order_confirmation`
73
- contains the order confirmation data after order placement.
74
123
 
75
124
  Args:
76
- account (str): Account number of the account to place the order in.
77
- symbol (str): Ticker to place the order for.
78
- order_type (PriceType): Price Type i.e. LIMIT, MARKET, STOP, etc.
79
- quantity (float): The number of shares to buy.
80
- duration (Duration): Duration of the order i.e. DAY, GT90, etc.
81
- price (float, optional): The price to buy the shares at. Defaults to 0.00.
82
- dry_run (bool, optional): Whether you want the order to be placed or not.
83
- Defaults to True.
125
+ account (str): The account number to place the order in.
126
+ symbol (str): The ticker symbol for the order.
127
+ price_type (PriceType): The price type for the order (e.g., LIMIT, MARKET, STOP).
128
+ order_type (OrderType): The type of order (e.g., BUY, SELL).
129
+ duration (Duration): The duration of the order (e.g., DAY, GT90).
130
+ quantity (int, optional): The number of shares to buy or sell. Defaults to 0.
131
+ price (float, optional): The price at which to buy or sell the shares. Defaults to 0.00.
132
+ stop_price (float, optional): The stop price for stop orders. Defaults to None.
133
+ dry_run (bool, optional): If True, the order will not be placed but will be built and validated. Defaults to True.
134
+ notional (bool, optional): If True, the order will be placed based on a notional dollar amount rather than share quantity. Defaults to False.
135
+ order_instruction (OrderInstructions, optional): Additional order instructions (e.g., AON, OPG). Defaults to "0".
136
+
137
+ Raises:
138
+ ValueError: If AON orders are not limit orders or if AON orders have a quantity of 100 shares or less.
139
+ PreviewOrderError: If the order preview fails.
140
+ PlaceOrderError: If the order placement fails.
84
141
 
85
142
  Returns:
86
- Order:order_confirmation: Dictionary containing the order confirmation data.
143
+ dict: A dictionary containing the order confirmation data.
87
144
  """
88
145
 
89
- if price_type == PriceType.MARKET:
146
+ if price_type == PriceType.MARKET and not notional:
90
147
  price = ""
91
-
148
+ if order_instruction == OrderInstructions.AON and price_type != PriceType.LIMIT:
149
+ raise ValueError("AON orders must be a limit order.")
150
+ if order_instruction == OrderInstructions.AON and quantity <= 100:
151
+ raise ValueError("AON orders must be greater than 100 shares.")
152
+
92
153
  data = {
93
- "submiturl": "/cgi-bin/orderbar",
94
- "orderbar_clordid": "",
95
- "orderbar_accountid": "",
96
- "notional": "yes" if notional else "",
97
- "stockorderpage": "yes",
98
- "submitOrders": "",
99
- "previewOrders": "1",
100
- "lotMethod": "1",
101
- "accountType": "1",
102
- "quoteprice": "",
103
- "viewederror": "",
104
- "stocksubmittedcompanyname1": "",
105
- "accountId": account,
106
- "transactionType": order_type,
107
- "quantity": quantity,
108
154
  "symbol": symbol,
109
- "priceType": price_type,
110
- "limitPrice": price,
155
+ "transaction": order_type,
156
+ "shares": quantity,
111
157
  "duration": duration,
112
- "qualifier": "0",
113
- "cond_symbol0_0": "",
114
- "cond_type0_0": "2",
115
- "cond_compare_type0_0": "2",
116
- "cond_compare_value0_0": "",
117
- "cond_and_or0": "1",
118
- "cond_symbol0_1": "",
119
- "cond_type0_1": "2",
120
- "cond_compare_type0_1": "2",
121
- "cond_compare_value0_1": "",
158
+ "preview": "true",
159
+ "instructions": order_instruction,
160
+ "account": account,
161
+ "price_type": price_type,
162
+ "limit_price": "0",
122
163
  }
164
+ if notional:
165
+ data["dollar_amount"] = price
166
+ del data["shares"]
167
+ if price_type in [PriceType.LIMIT, PriceType.STOP_LIMIT]:
168
+ data["limit_price"] = price
169
+ if price_type in [PriceType.STOP, PriceType.STOP_LIMIT]:
170
+ data["stop_price"] = stop_price
171
+ response = self.ft_session.post(url=urls.order(), data=data)
172
+ if response.status_code != 200 or response.json()["error"] != "":
173
+ return response.json()
174
+ preview_data = response.json()
175
+ if dry_run:
176
+ return preview_data
177
+ data["preview"] = "false"
178
+ data["stage"] = "P"
179
+ response = self.ft_session.post(url=urls.order(), data=data)
180
+ return response.json()
181
+
182
+ def place_option_order(
183
+ self,
184
+ account: str,
185
+ option_symbol: str,
186
+ price_type: PriceType,
187
+ order_type: OrderType,
188
+ contracts: int,
189
+ duration: Duration,
190
+ stop_price: float = None,
191
+ price: float = 0.00,
192
+ dry_run: bool = True,
193
+ order_instruction: OrderInstructions = "0",
194
+ ):
195
+ """
196
+ Builds and places an option order.
123
197
 
124
- order_data = BeautifulSoup(
125
- self.ft_session.post(
126
- url=urls.orderbar(), headers=urls.session_headers(), data=data
127
- ).text,
128
- "xml",
129
- )
130
- order_confirmation = {}
131
- cdata = order_data.find("actiondata").string
132
- cdata_soup = BeautifulSoup(cdata, "html.parser")
133
- span = (
134
- cdata_soup.find("div", class_="msg_bg")
135
- .find("div", class_="yellow box")
136
- .find("div", class_="error_msg")
137
- .find("div", class_="outbox")
138
- .find("div", class_="inbox")
139
- .find("span")
140
- )
141
- if span:
142
- order_warning = span.text.strip()
143
- order_confirmation["warning"] = order_warning
144
- data["viewederror"] = "1"
145
- if not dry_run:
146
- data["previewOrders"] = ""
147
- data["submitOrders"] = "1"
148
- order_data = BeautifulSoup(
149
- self.ft_session.post(
150
- url=urls.orderbar(), headers=urls.session_headers(), data=data
151
- ).text,
152
- "xml",
153
- )
154
-
155
- order_success = order_data.find("success").text.strip()
156
- order_confirmation["success"] = order_success
157
- action_data = order_data.find("actiondata").text.strip()
158
- if order_success != "No":
159
- # Extract the table data
160
- table_start = action_data.find("<table")
161
- table_end = action_data.find("</table>") + len("</table>")
162
- table_data = action_data[table_start:table_end]
163
- table_data = BeautifulSoup(table_data, "xml")
164
- titles = table_data.find_all("th")
165
- data = table_data.find_all("td")
166
- for i, title in enumerate(titles):
167
- order_confirmation[f"{title.get_text()}"] = data[i].get_text()
168
- if not dry_run:
169
- start_index = action_data.find(
170
- "Your order reference number is: "
171
- ) + len("Your order reference number is: ")
172
- end_index = action_data.find("</div>", start_index)
173
- order_number = action_data[start_index:end_index]
174
- else:
175
- start_index = action_data.find('id="') + len('id="')
176
- end_index = action_data.find('" style=', start_index)
177
- order_number = action_data[start_index:end_index]
178
- order_confirmation["orderid"] = order_number
179
- else:
180
- order_confirmation["actiondata"] = action_data
181
- order_confirmation["errcode"] = order_data.find("errcode").text.strip()
182
- self.order_confirmation = order_confirmation
183
-
184
-
185
- def get_orders(ft_session, account):
186
- """
187
- Retrieves existing order data for a given account.
188
-
189
- Args:
190
- ft_session (FTSession): The session object used for making HTTP requests to Firstrade.
191
- account (str): Account number of the account to retrieve orders for.
192
-
193
- Returns:
194
- list: A list of dictionaries, each containing details about an order.
195
- """
198
+ Args:
199
+ account (str): The account number to place the order in.
200
+ option_symbol (str): The option ticker symbol for the order.
201
+ price_type (PriceType): The price type for the order (e.g., LIMIT, MARKET, STOP).
202
+ order_type (OrderType): The type of order (e.g., BUY, SELL).
203
+ contracts (int): The number of option contracts to buy or sell.
204
+ duration (Duration): The duration of the order (e.g., DAY, GT90).
205
+ stop_price (float, optional): The stop price for stop orders. Defaults to None.
206
+ price (float, optional): The price at which to buy or sell the option contracts. Defaults to 0.00.
207
+ dry_run (bool, optional): If True, the order will not be placed but will be built and validated. Defaults to True.
208
+ order_instruction (OrderInstructions, optional): Additional order instructions (e.g., AON, OPG). Defaults to "0".
209
+
210
+ Raises:
211
+ ValueError: If AON orders are not limit orders or if AON orders have a quantity of 100 contracts or less.
212
+ PreviewOrderError: If there is an error during the preview of the order.
213
+ PlaceOrderError: If there is an error during the placement of the order.
196
214
 
197
- # Data dictionary to send with the request
198
- data = {
199
- "accountId": account,
200
- }
201
-
202
- # Post request to retrieve the order data
203
- response = ft_session.post(
204
- url=urls.order_list(), headers=urls.session_headers(), data=data
205
- ).text
206
-
207
- # Parse the response using BeautifulSoup
208
- soup = BeautifulSoup(response, "html.parser")
209
-
210
- # Find the table containing orders
211
- table = soup.find("table", class_="tablesorter")
212
- if not table:
213
- return []
214
-
215
- rows = table.find_all("tr")[1:] # skip the header row
216
-
217
- orders = []
218
- for row in rows:
219
- try:
220
- cells = row.find_all("td")
221
- tooltip_content = row.find("a", {"class": "info"}).get("onmouseover")
222
- tooltip_soup = BeautifulSoup(
223
- tooltip_content.split("tooltip.show(")[1].strip("');"), "html.parser"
224
- )
225
- order_ref = tooltip_soup.find(text=lambda text: "Order Ref" in text)
226
- order_ref_number = order_ref.split("#: ")[1] if order_ref else None
227
- status = cells[8]
228
- # print(status)
229
- sub_status = status.find("strong")
230
- # print(sub_status)
231
- sub_status = sub_status.get_text(strip=True)
232
- # print(sub_status)
233
- status = (
234
- status.find("strong").get_text(strip=True)
235
- if status.find("strong")
236
- else status.get_text(strip=True)
237
- )
238
- order = {
239
- "Date/Time": cells[0].get_text(strip=True),
240
- "Reference": order_ref_number,
241
- "Transaction": cells[1].get_text(strip=True),
242
- "Quantity": int(cells[2].get_text(strip=True)),
243
- "Symbol": cells[3].get_text(strip=True),
244
- "Type": cells[4].get_text(strip=True),
245
- "Price": float(cells[5].get_text(strip=True)),
246
- "Duration": cells[6].get_text(strip=True),
247
- "Instr.": cells[7].get_text(strip=True),
248
- "Status": status,
249
- }
250
- orders.append(order)
251
- except Exception as e:
252
- print(f"Error parsing order: {e}")
253
-
254
- return orders
215
+ Returns:
216
+ dict: A dictionary containing the order confirmation data.
217
+ """
218
+
219
+ if order_instruction == OrderInstructions.AON and price_type != PriceType.LIMIT:
220
+ raise ValueError("AON orders must be a limit order.")
221
+ if order_instruction == OrderInstructions.AON and contracts <= 100:
222
+ raise ValueError("AON orders must be greater than 100 shares.")
223
+
224
+
225
+ data = {
226
+ "duration": duration,
227
+ "instructions": order_instruction,
228
+ "transaction": order_type,
229
+ "contracts": contracts,
230
+ "symbol": option_symbol,
231
+ "preview": "true",
232
+ "account": account,
233
+ "price_type": price_type,
234
+ }
235
+ if price_type in [PriceType.LIMIT, PriceType.STOP_LIMIT]:
236
+ data["limit_price"] = price
237
+ if price_type in [PriceType.STOP, PriceType.STOP_LIMIT]:
238
+ data["stop_price"] = stop_price
239
+
240
+ response = self.ft_session.post(url=urls.option_order(), data=data)
241
+ if response.status_code != 200 or response.json()["error"] != "":
242
+ return response.json()
243
+ if dry_run:
244
+ return response.json()
245
+ data["preview"] = "false"
246
+ response = self.ft_session.post(url=urls.option_order(), data=data)
247
+ return response.json()
firstrade/symbols.py CHANGED
@@ -1,77 +1,172 @@
1
- from bs4 import BeautifulSoup
2
-
3
1
  from firstrade import urls
4
2
  from firstrade.account import FTSession
3
+ from firstrade.exceptions import QuoteRequestError, QuoteResponseError
5
4
 
6
5
 
7
6
  class SymbolQuote:
8
7
  """
9
- Dataclass containing quote information for a symbol.
8
+ Data class representing a stock quote for a given symbol.
10
9
 
11
10
  Attributes:
12
- ft_session (FTSession):
13
- The session object used for making HTTP requests to Firstrade.
11
+ ft_session (FTSession): The session object used for making HTTP requests to Firstrade.
14
12
  symbol (str): The symbol for which the quote information is retrieved.
15
- exchange (str): The exchange where the symbol is traded.
16
- bid (float): The bid price for the symbol.
17
- ask (float): The ask price for the symbol.
13
+ sec_type (str): The security type of the symbol.
14
+ tick (str): The tick size of the symbol.
15
+ bid (int): The bid price for the symbol.
16
+ bid_size (int): The size of the bid.
17
+ ask (int): The ask price for the symbol.
18
+ ask_size (int): The size of the ask.
18
19
  last (float): The last traded price for the symbol.
19
20
  change (float): The change in price for the symbol.
20
21
  high (float): The highest price for the symbol during the trading day.
21
22
  low (float): The lowest price for the symbol during the trading day.
23
+ bid_mmid (str): The market maker ID for the bid.
24
+ ask_mmid (str): The market maker ID for the ask.
25
+ last_mmid (str): The market maker ID for the last trade.
26
+ last_size (int): The size of the last trade.
27
+ change_color (str): The color indicating the change in price.
22
28
  volume (str): The volume of shares traded for the symbol.
29
+ today_close (float): The closing price for the symbol today.
30
+ open (str): The opening price for the symbol.
31
+ quote_time (str): The time of the quote.
32
+ last_trade_time (str): The time of the last trade.
23
33
  company_name (str): The name of the company associated with the symbol.
24
- real_time (bool): If the quote is real-time or not
25
- fractional (bool): If the stock can be traded fractionally, or not
34
+ exchange (str): The exchange where the symbol is traded.
35
+ has_option (bool): Indicates if the symbol has options.
36
+ is_etf (bool): Indicates if the symbol is an ETF.
37
+ is_fractional (bool): Indicates if the stock can be traded fractionally.
38
+ realtime (str): Indicates if the quote is real-time.
39
+ nls (str): Nasdaq last sale.
40
+ shares (int): The number of shares.
26
41
  """
27
42
 
28
- def __init__(self, ft_session: FTSession, symbol: str):
43
+ def __init__(self, ft_session: FTSession, account: str, symbol: str):
29
44
  """
30
45
  Initializes a new instance of the SymbolQuote class.
31
46
 
47
+ Args:
48
+ ft_session (FTSession): The session object used for making HTTP requests to Firstrade.
49
+ account (str): The account number for which the quote information is retrieved.
50
+ symbol (str): The symbol for which the quote information is retrieved.
51
+
52
+ Raises:
53
+ QuoteRequestError: If the quote request fails with a non-200 status code.
54
+ QuoteResponseError: If the quote response contains an error message.
55
+ """
56
+ self.ft_session = ft_session
57
+ response = self.ft_session.get(url=urls.quote(account, symbol))
58
+ if response.status_code != 200:
59
+ raise QuoteRequestError(response.status_code)
60
+ if response.json().get("error", "") != "":
61
+ raise QuoteResponseError(symbol, response.json()["error"])
62
+ self.symbol = response.json()["result"]["symbol"]
63
+ self.sec_type = response.json()["result"]["sec_type"]
64
+ self.tick = response.json()["result"]["tick"]
65
+ self.bid = response.json()["result"]["bid"]
66
+ self.bid_size = response.json()["result"]["bid_size"]
67
+ self.ask = response.json()["result"]["ask"]
68
+ self.ask_size = response.json()["result"]["ask_size"]
69
+ self.last = response.json()["result"]["last"]
70
+ self.change = response.json()["result"]["change"]
71
+ self.high = response.json()["result"]["high"]
72
+ self.low = response.json()["result"]["low"]
73
+ self.bid_mmid = response.json()["result"]["bid_mmid:"]
74
+ self.ask_mmid = response.json()["result"]["ask_mmid:"]
75
+ self.last_mmid = response.json()["result"]["last_mmid:"]
76
+ self.last_size = response.json()["result"]["last_size"]
77
+ self.change_color = response.json()["result"]["change_color"]
78
+ self.volume = response.json()["result"]["vol"]
79
+ self.today_close = response.json()["result"]["today_close"]
80
+ self.open = response.json()["result"]["open"]
81
+ self.quote_time = response.json()["result"]["quote_time"]
82
+ self.last_trade_time = response.json()["result"]["last_trade_time"]
83
+ self.company_name = response.json()["result"]["company_name"]
84
+ self.exchange = response.json()["result"]["exchange"]
85
+ self.has_option = response.json()["result"]["has_option"]
86
+ self.is_etf = bool(response.json()["result"]["is_etf"])
87
+ self.is_fractional = bool(response.json()["result"]["is_fractional"])
88
+ self.realtime = response.json()["result"]["realtime"]
89
+ self.nls = response.json()["result"]["nls"]
90
+ self.shares = response.json()["result"]["shares"]
91
+
92
+
93
+ class OptionQuote:
94
+ """
95
+ Data class representing an option quote for a given symbol.
96
+
97
+ Attributes:
98
+ ft_session (FTSession): The session object used for making HTTP requests to Firstrade.
99
+ symbol (str): The symbol for which the option quote information is retrieved.
100
+ option_dates (dict): A dict of expiration dates for options on the given symbol.
101
+ """
102
+
103
+ def __init__(self, ft_session: FTSession, symbol: str):
104
+ """
105
+ Initializes a new instance of the OptionQuote class.
106
+
32
107
  Args:
33
108
  ft_session (FTSession):
34
109
  The session object used for making HTTP requests to Firstrade.
35
- symbol (str): The symbol for which the quote information is retrieved.
110
+ symbol (str): The symbol for which the option quote information is retrieved.
36
111
  """
37
112
  self.ft_session = ft_session
38
113
  self.symbol = symbol
39
- symbol_data = self.ft_session.get(
40
- url=urls.quote(self.symbol), headers=urls.session_headers()
41
- )
42
- soup = BeautifulSoup(symbol_data.text, "xml")
43
- quote = soup.find("quote")
44
- self.symbol = quote.find("symbol").text
45
- self.underlying_symbol = quote.find("underlying_symbol").text
46
- self.tick = quote.find("tick").text
47
- self.exchange = quote.find("exchange").text
48
- self.bid = float(quote.find("bid").text.replace(",", ""))
49
- self.ask = float(quote.find("ask").text.replace(",", ""))
50
- self.last = float(quote.find("last").text.replace(",", ""))
51
- temp_store = quote.find("bidsize").text.replace(",", "")
52
- self.bid_size = int(temp_store) if temp_store.isdigit() else 0
53
- temp_store = quote.find("asksize").text.replace(",", "")
54
- self.ask_size = int(temp_store) if temp_store.isdigit() else 0
55
- temp_store = quote.find("lastsize").text.replace(",", "")
56
- self.last_size = int(temp_store) if temp_store.isdigit() else 0
57
- self.bid_mmid = quote.find("bidmmid").text
58
- self.ask_mmid = quote.find("askmmid").text
59
- self.last_mmid = quote.find("lastmmid").text
60
- self.change = float(quote.find("change").text.replace(",", ""))
61
- if quote.find("high").text == "N/A":
62
- self.high = None
63
- else:
64
- self.high = float(quote.find("high").text.replace(",", ""))
65
- if quote.find("low").text == "N/A":
66
- self.low = "None"
67
- else:
68
- self.low = float(quote.find("low").text.replace(",", ""))
69
- self.change_color = quote.find("changecolor").text
70
- self.volume = quote.find("vol").text
71
- self.bidxask = quote.find("bidxask").text
72
- self.quote_time = quote.find("quotetime").text
73
- self.last_trade_time = quote.find("lasttradetime").text
74
- self.real_time = quote.find("realtime").text == "T"
75
- self.fractional = quote.find("fractional").text == "T"
76
- self.err_code = quote.find("errcode").text
77
- self.company_name = quote.find("companyname").text
114
+ self.option_dates = self.get_option_dates(symbol)
115
+
116
+ def get_option_dates(self, symbol: str):
117
+ """
118
+ Retrieves the expiration dates for options on a given symbol.
119
+
120
+ Args:
121
+ symbol (str): The symbol for which the expiration dates are retrieved.
122
+
123
+ Returns:
124
+ dict: A dict of expiration dates and other information for options on the given symbol.
125
+
126
+ Raises:
127
+ QuoteRequestError: If the request for option dates fails with a non-200 status code.
128
+ QuoteResponseError: If the response for option dates contains an error message.
129
+ """
130
+ response = self.ft_session.get(url=urls.option_dates(symbol))
131
+ return response.json()
132
+
133
+ def get_option_quote(self, symbol: str, date: str):
134
+ """
135
+ Retrieves the quote for a given option symbol.
136
+
137
+ Args:
138
+ symbol (str): The symbol for which the quote is retrieved.
139
+
140
+ Returns:
141
+ dict: A dictionary containing the quote and other information for the given option symbol.
142
+
143
+ Raises:
144
+ QuoteRequestError: If the request for the option quote fails with a non-200 status code.
145
+ QuoteResponseError: If the response for the option quote contains an error message.
146
+ """
147
+ response = self.ft_session.get(url=urls.option_quotes(symbol, date))
148
+ return response.json()
149
+
150
+ def get_greek_options(self, symbol: str, exp_date: str):
151
+ """
152
+ Retrieves the greeks for options on a given symbol.
153
+
154
+ Args:
155
+ symbol (str): The symbol for which the greeks are retrieved.
156
+ exp_date (str): The expiration date of the options.
157
+
158
+ Returns:
159
+ dict: A dictionary containing the greeks for the options on the given symbol.
160
+
161
+ Raises:
162
+ QuoteRequestError: If the request for the greeks fails with a non-200 status code.
163
+ QuoteResponseError: If the response for the greeks contains an error message.
164
+ """
165
+ data = {
166
+ "type": "chain",
167
+ "chains_range": "A",
168
+ "root_symbol": symbol,
169
+ "exp_date": exp_date,
170
+ }
171
+ response = self.ft_session.post(url=urls.greek_options(), data=data)
172
+ return response.json()