firstrade 0.0.19__py3-none-any.whl → 0.0.21__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
@@ -45,6 +45,17 @@ class OrderType(str, Enum):
45
45
  BUY_TO_COVER = "BC"
46
46
 
47
47
 
48
+ class OrderInstructions(str, Enum):
49
+ """
50
+ This is an :class:'~enum.Enum'
51
+ that contains the valid instructions for an order.
52
+ """
53
+
54
+ AON = "1"
55
+ OPG = "4"
56
+ CLO = "5"
57
+
58
+
48
59
  class Order:
49
60
  """
50
61
  This class contains information about an order.
@@ -66,6 +77,7 @@ class Order:
66
77
  price=0.00,
67
78
  dry_run=True,
68
79
  notional=False,
80
+ order_instruction: OrderInstructions = None,
69
81
  ):
70
82
  """
71
83
  Builds and places an order.
@@ -86,13 +98,12 @@ class Order:
86
98
  Order:order_confirmation: Dictionary containing the order confirmation data.
87
99
  """
88
100
 
89
- if dry_run:
90
- previewOrders = "1"
91
- else:
92
- previewOrders = ""
93
-
94
101
  if price_type == PriceType.MARKET:
95
102
  price = ""
103
+ if order_instruction == OrderInstructions.AON and price_type != PriceType.LIMIT:
104
+ raise ValueError("AON orders must be a limit order.")
105
+ if order_instruction == OrderInstructions.AON and quantity <= 100:
106
+ raise ValueError("AON orders must be greater than 100 shares.")
96
107
 
97
108
  data = {
98
109
  "submiturl": "/cgi-bin/orderbar",
@@ -100,8 +111,8 @@ class Order:
100
111
  "orderbar_accountid": "",
101
112
  "notional": "yes" if notional else "",
102
113
  "stockorderpage": "yes",
103
- "submitOrders": "1",
104
- "previewOrders": previewOrders,
114
+ "submitOrders": "",
115
+ "previewOrders": "1",
105
116
  "lotMethod": "1",
106
117
  "accountType": "1",
107
118
  "quoteprice": "",
@@ -114,7 +125,7 @@ class Order:
114
125
  "priceType": price_type,
115
126
  "limitPrice": price,
116
127
  "duration": duration,
117
- "qualifier": "0",
128
+ "qualifier": "0" if order_instruction is None else order_instruction,
118
129
  "cond_symbol0_0": "",
119
130
  "cond_type0_0": "2",
120
131
  "cond_compare_type0_0": "2",
@@ -133,6 +144,30 @@ class Order:
133
144
  "xml",
134
145
  )
135
146
  order_confirmation = {}
147
+ cdata = order_data.find("actiondata").string
148
+ cdata_soup = BeautifulSoup(cdata, "html.parser")
149
+ span = (
150
+ cdata_soup.find("div", class_="msg_bg")
151
+ .find("div", class_="yellow box")
152
+ .find("div", class_="error_msg")
153
+ .find("div", class_="outbox")
154
+ .find("div", class_="inbox")
155
+ .find("span")
156
+ )
157
+ if span:
158
+ order_warning = span.text.strip()
159
+ order_confirmation["warning"] = order_warning
160
+ data["viewederror"] = "1"
161
+ if not dry_run:
162
+ data["previewOrders"] = ""
163
+ data["submitOrders"] = "1"
164
+ order_data = BeautifulSoup(
165
+ self.ft_session.post(
166
+ url=urls.orderbar(), headers=urls.session_headers(), data=data
167
+ ).text,
168
+ "xml",
169
+ )
170
+
136
171
  order_success = order_data.find("success").text.strip()
137
172
  order_confirmation["success"] = order_success
138
173
  action_data = order_data.find("actiondata").text.strip()
@@ -177,48 +212,56 @@ def get_orders(ft_session, account):
177
212
 
178
213
  # Data dictionary to send with the request
179
214
  data = {
180
- 'accountId': account,
215
+ "accountId": account,
181
216
  }
182
217
 
183
218
  # Post request to retrieve the order data
184
- response = ft_session.post(url=urls.order_list(), headers=urls.session_headers(), data=data).text
219
+ response = ft_session.post(
220
+ url=urls.order_list(), headers=urls.session_headers(), data=data
221
+ ).text
185
222
 
186
223
  # Parse the response using BeautifulSoup
187
224
  soup = BeautifulSoup(response, "html.parser")
188
225
 
189
226
  # Find the table containing orders
190
- table = soup.find('table', class_='tablesorter')
227
+ table = soup.find("table", class_="tablesorter")
191
228
  if not table:
192
229
  return []
193
230
 
194
- rows = table.find_all('tr')[1:] # skip the header row
231
+ rows = table.find_all("tr")[1:] # skip the header row
195
232
 
196
233
  orders = []
197
234
  for row in rows:
198
235
  try:
199
- cells = row.find_all('td')
200
- tooltip_content = row.find('a', {'class': 'info'}).get('onmouseover')
201
- tooltip_soup = BeautifulSoup(tooltip_content.split('tooltip.show(')[1].strip("');"), 'html.parser')
202
- order_ref = tooltip_soup.find(text=lambda text: 'Order Ref' in text)
203
- order_ref_number = order_ref.split('#: ')[1] if order_ref else None
236
+ cells = row.find_all("td")
237
+ tooltip_content = row.find("a", {"class": "info"}).get("onmouseover")
238
+ tooltip_soup = BeautifulSoup(
239
+ tooltip_content.split("tooltip.show(")[1].strip("');"), "html.parser"
240
+ )
241
+ order_ref = tooltip_soup.find(text=lambda text: "Order Ref" in text)
242
+ order_ref_number = order_ref.split("#: ")[1] if order_ref else None
204
243
  status = cells[8]
205
244
  # print(status)
206
- sub_status = status.find('strong')
245
+ sub_status = status.find("strong")
207
246
  # print(sub_status)
208
247
  sub_status = sub_status.get_text(strip=True)
209
248
  # print(sub_status)
210
- status = status.find('strong').get_text(strip=True) if status.find('strong') else status.get_text(strip=True)
249
+ status = (
250
+ status.find("strong").get_text(strip=True)
251
+ if status.find("strong")
252
+ else status.get_text(strip=True)
253
+ )
211
254
  order = {
212
- 'Date/Time': cells[0].get_text(strip=True),
213
- 'Reference': order_ref_number,
214
- 'Transaction': cells[1].get_text(strip=True),
215
- 'Quantity': int(cells[2].get_text(strip=True)),
216
- 'Symbol': cells[3].get_text(strip=True),
217
- 'Type': cells[4].get_text(strip=True),
218
- 'Price': float(cells[5].get_text(strip=True)),
219
- 'Duration': cells[6].get_text(strip=True),
220
- 'Instr.': cells[7].get_text(strip=True),
221
- 'Status': status,
255
+ "Date/Time": cells[0].get_text(strip=True),
256
+ "Reference": order_ref_number,
257
+ "Transaction": cells[1].get_text(strip=True),
258
+ "Quantity": int(cells[2].get_text(strip=True)),
259
+ "Symbol": cells[3].get_text(strip=True),
260
+ "Type": cells[4].get_text(strip=True),
261
+ "Price": float(cells[5].get_text(strip=True)),
262
+ "Duration": cells[6].get_text(strip=True),
263
+ "Instr.": cells[7].get_text(strip=True),
264
+ "Status": status,
222
265
  }
223
266
  orders.append(order)
224
267
  except Exception as e:
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: firstrade
3
- Version: 0.0.19
3
+ Version: 0.0.21
4
4
  Summary: An unofficial API for Firstrade
5
5
  Home-page: https://github.com/MaxxRK/firstrade-api
6
- Download-URL: https://github.com/MaxxRK/firstrade-api/archive/refs/tags/0019.tar.gz
6
+ Download-URL: https://github.com/MaxxRK/firstrade-api/archive/refs/tags/0021.tar.gz
7
7
  Author: MaxxRK
8
8
  Author-email: maxxrk@pm.me
9
9
  License: MIT
@@ -0,0 +1,10 @@
1
+ firstrade/__init__.py,sha256=fNiWYgSTjElY1MNv0Ug-sVLMTR2z_Ngri_FY7Pekdrw,95
2
+ firstrade/account.py,sha256=xDgTMXDy2UWUNi4kboXAKKBrbmZGpXLJ11MCHlMvzB0,9218
3
+ firstrade/order.py,sha256=bvqO06BzrtWQNP8PAWbPR3Ypnu9LEAwq4BWn1-joScY,9105
4
+ firstrade/symbols.py,sha256=tZD7jexvyvh1rTaOdAKv_vyZWzpjffvl_nPwBe1QaiA,3633
5
+ firstrade/urls.py,sha256=OrfXGDsNpA2rTm4o55KAQzpeigG_pxufWyTDBlbhJYQ,1248
6
+ firstrade-0.0.21.dist-info/LICENSE,sha256=wPEQjDqm5zMBmEcZp219Labmq_YIjhudpZiUzyVKaFA,1057
7
+ firstrade-0.0.21.dist-info/METADATA,sha256=Krhj7olyyZ4ylGtU7yRZ7_F7h9FgIzM5MX13Ipi7Q6k,2399
8
+ firstrade-0.0.21.dist-info/WHEEL,sha256=Wyh-_nZ0DJYolHNn1_hMa4lM7uDedD_RGVwbmTjyItk,91
9
+ firstrade-0.0.21.dist-info/top_level.txt,sha256=tdA8v-KDxU1u4VV6soiNWGBlni4ojv_t_j2wFn5nZcs,10
10
+ firstrade-0.0.21.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (70.1.0)
2
+ Generator: setuptools (71.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,10 +0,0 @@
1
- firstrade/__init__.py,sha256=fNiWYgSTjElY1MNv0Ug-sVLMTR2z_Ngri_FY7Pekdrw,95
2
- firstrade/account.py,sha256=xDgTMXDy2UWUNi4kboXAKKBrbmZGpXLJ11MCHlMvzB0,9218
3
- firstrade/order.py,sha256=BK2OmIOr_LO4SrNRVWjkydaoc_4fLkl6Rr7_LCRTyLE,7635
4
- firstrade/symbols.py,sha256=tZD7jexvyvh1rTaOdAKv_vyZWzpjffvl_nPwBe1QaiA,3633
5
- firstrade/urls.py,sha256=OrfXGDsNpA2rTm4o55KAQzpeigG_pxufWyTDBlbhJYQ,1248
6
- firstrade-0.0.19.dist-info/LICENSE,sha256=wPEQjDqm5zMBmEcZp219Labmq_YIjhudpZiUzyVKaFA,1057
7
- firstrade-0.0.19.dist-info/METADATA,sha256=apZ7RN0uTgzdVp90m3LeQyZcs34zBtWtUTFmxK7akFA,2399
8
- firstrade-0.0.19.dist-info/WHEEL,sha256=cpQTJ5IWu9CdaPViMhC9YzF8gZuS5-vlfoFihTBC86A,91
9
- firstrade-0.0.19.dist-info/top_level.txt,sha256=tdA8v-KDxU1u4VV6soiNWGBlni4ojv_t_j2wFn5nZcs,10
10
- firstrade-0.0.19.dist-info/RECORD,,