firstrade 0.0.30__py3-none-any.whl → 0.0.33__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/account.py +18 -3
- firstrade/exceptions.py +17 -6
- firstrade/order.py +15 -16
- firstrade/symbols.py +7 -7
- firstrade/urls.py +8 -4
- firstrade-0.0.33.dist-info/METADATA +106 -0
- firstrade-0.0.33.dist-info/RECORD +11 -0
- {firstrade-0.0.30.dist-info → firstrade-0.0.33.dist-info}/WHEEL +1 -1
- firstrade-0.0.30.dist-info/METADATA +0 -225
- firstrade-0.0.30.dist-info/RECORD +0 -11
- {firstrade-0.0.30.dist-info → firstrade-0.0.33.dist-info/licenses}/LICENSE +0 -0
- {firstrade-0.0.30.dist-info → firstrade-0.0.33.dist-info}/top_level.txt +0 -0
firstrade/account.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import json
|
|
1
2
|
import os
|
|
2
3
|
import pickle
|
|
3
4
|
|
|
@@ -111,7 +112,10 @@ class FTSession:
|
|
|
111
112
|
url=urls.login(),
|
|
112
113
|
data=data,
|
|
113
114
|
)
|
|
114
|
-
|
|
115
|
+
try:
|
|
116
|
+
self.login_json = response.json()
|
|
117
|
+
except json.decoder.JSONDecodeError:
|
|
118
|
+
raise LoginResponseError("Invalid JSON is your account funded?")
|
|
115
119
|
if (
|
|
116
120
|
"mfa" not in self.login_json
|
|
117
121
|
and "ftat" in self.login_json
|
|
@@ -326,16 +330,27 @@ class FTAccountData:
|
|
|
326
330
|
response = self.session.get(urls.account_positions(account))
|
|
327
331
|
return response.json()
|
|
328
332
|
|
|
329
|
-
def get_account_history(self, account):
|
|
333
|
+
def get_account_history(self, account, date_range="ytd", custom_range=None):
|
|
330
334
|
"""Gets account history for a given account.
|
|
331
335
|
|
|
332
336
|
Args:
|
|
333
337
|
account (str): Account number of the account you want to get history for.
|
|
338
|
+
range (str): The range of the history. Defaults to "ytd".
|
|
339
|
+
Available options are
|
|
340
|
+
["today", "1w", "1m", "2m", "mtd", "ytd", "ly", "cust"].
|
|
341
|
+
custom_range (str): The custom range of the history.
|
|
342
|
+
Defaults to None. If range is "cust",
|
|
343
|
+
this parameter is required.
|
|
344
|
+
Format: ["YYYY-MM-DD", "YYYY-MM-DD"].
|
|
334
345
|
|
|
335
346
|
Returns:
|
|
336
347
|
dict: Dict of the response from the API.
|
|
337
348
|
"""
|
|
338
|
-
|
|
349
|
+
if date_range == "cust" and custom_range is None:
|
|
350
|
+
raise ValueError("Custom range is required when date_range is 'cust'.")
|
|
351
|
+
response = self.session.get(
|
|
352
|
+
urls.account_history(account, date_range, custom_range)
|
|
353
|
+
)
|
|
339
354
|
return response.json()
|
|
340
355
|
|
|
341
356
|
def get_orders(self, account):
|
firstrade/exceptions.py
CHANGED
|
@@ -1,44 +1,55 @@
|
|
|
1
1
|
class QuoteError(Exception):
|
|
2
2
|
"""Base class for exceptions in the Quote module."""
|
|
3
|
-
|
|
3
|
+
|
|
4
4
|
|
|
5
5
|
class QuoteRequestError(QuoteError):
|
|
6
6
|
"""Exception raised for errors in the HTTP request during a Quote."""
|
|
7
|
+
|
|
7
8
|
def __init__(self, status_code, message="Error in HTTP request"):
|
|
8
9
|
self.status_code = status_code
|
|
9
10
|
self.message = f"{message}. HTTP status code: {status_code}"
|
|
10
11
|
super().__init__(self.message)
|
|
11
12
|
|
|
13
|
+
|
|
12
14
|
class QuoteResponseError(QuoteError):
|
|
13
15
|
"""Exception raised for errors in the API response."""
|
|
16
|
+
|
|
14
17
|
def __init__(self, symbol, error_message):
|
|
15
18
|
self.symbol = symbol
|
|
16
19
|
self.message = f"Failed to get data for {symbol}. API returned the following error: {error_message}"
|
|
17
20
|
super().__init__(self.message)
|
|
18
|
-
|
|
21
|
+
|
|
22
|
+
|
|
19
23
|
class LoginError(Exception):
|
|
20
24
|
"""Exception raised for errors in the login process."""
|
|
21
|
-
|
|
25
|
+
|
|
22
26
|
|
|
23
27
|
class LoginRequestError(LoginError):
|
|
24
28
|
"""Exception raised for errors in the HTTP request during login."""
|
|
29
|
+
|
|
25
30
|
def __init__(self, status_code, message="Error in HTTP request during login"):
|
|
26
31
|
self.status_code = status_code
|
|
27
32
|
self.message = f"{message}. HTTP status code: {status_code}"
|
|
28
33
|
super().__init__(self.message)
|
|
29
34
|
|
|
35
|
+
|
|
30
36
|
class LoginResponseError(LoginError):
|
|
31
37
|
"""Exception raised for errors in the API response during login."""
|
|
38
|
+
|
|
32
39
|
def __init__(self, error_message):
|
|
33
|
-
self.message =
|
|
40
|
+
self.message = (
|
|
41
|
+
f"Failed to login. API returned the following error: {error_message}"
|
|
42
|
+
)
|
|
34
43
|
super().__init__(self.message)
|
|
35
|
-
|
|
44
|
+
|
|
45
|
+
|
|
36
46
|
class AccountError(Exception):
|
|
37
47
|
"""Base class for exceptions in the Account module."""
|
|
38
|
-
|
|
48
|
+
|
|
39
49
|
|
|
40
50
|
class AccountResponseError(AccountError):
|
|
41
51
|
"""Exception raised for errors in the API response when getting account data."""
|
|
52
|
+
|
|
42
53
|
def __init__(self, error_message):
|
|
43
54
|
self.message = f"Failed to get account data. API returned the following error: {error_message}"
|
|
44
55
|
super().__init__(self.message)
|
firstrade/order.py
CHANGED
|
@@ -149,7 +149,7 @@ class Order:
|
|
|
149
149
|
raise ValueError("AON orders must be a limit order.")
|
|
150
150
|
if order_instruction == OrderInstructions.AON and quantity <= 100:
|
|
151
151
|
raise ValueError("AON orders must be greater than 100 shares.")
|
|
152
|
-
|
|
152
|
+
|
|
153
153
|
data = {
|
|
154
154
|
"symbol": symbol,
|
|
155
155
|
"transaction": order_type,
|
|
@@ -180,17 +180,17 @@ class Order:
|
|
|
180
180
|
return response.json()
|
|
181
181
|
|
|
182
182
|
def place_option_order(
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
194
|
):
|
|
195
195
|
"""
|
|
196
196
|
Builds and places an option order.
|
|
@@ -215,13 +215,12 @@ class Order:
|
|
|
215
215
|
Returns:
|
|
216
216
|
dict: A dictionary containing the order confirmation data.
|
|
217
217
|
"""
|
|
218
|
-
|
|
218
|
+
|
|
219
219
|
if order_instruction == OrderInstructions.AON and price_type != PriceType.LIMIT:
|
|
220
220
|
raise ValueError("AON orders must be a limit order.")
|
|
221
221
|
if order_instruction == OrderInstructions.AON and contracts <= 100:
|
|
222
222
|
raise ValueError("AON orders must be greater than 100 shares.")
|
|
223
|
-
|
|
224
|
-
|
|
223
|
+
|
|
225
224
|
data = {
|
|
226
225
|
"duration": duration,
|
|
227
226
|
"instructions": order_instruction,
|
|
@@ -233,7 +232,7 @@ class Order:
|
|
|
233
232
|
"price_type": price_type,
|
|
234
233
|
}
|
|
235
234
|
if price_type in [PriceType.LIMIT, PriceType.STOP_LIMIT]:
|
|
236
|
-
|
|
235
|
+
data["limit_price"] = price
|
|
237
236
|
if price_type in [PriceType.STOP, PriceType.STOP_LIMIT]:
|
|
238
237
|
data["stop_price"] = stop_price
|
|
239
238
|
|
firstrade/symbols.py
CHANGED
|
@@ -70,9 +70,9 @@ class SymbolQuote:
|
|
|
70
70
|
self.change = response.json()["result"]["change"]
|
|
71
71
|
self.high = response.json()["result"]["high"]
|
|
72
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
|
|
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
76
|
self.last_size = response.json()["result"]["last_size"]
|
|
77
77
|
self.change_color = response.json()["result"]["change_color"]
|
|
78
78
|
self.volume = response.json()["result"]["vol"]
|
|
@@ -88,7 +88,7 @@ class SymbolQuote:
|
|
|
88
88
|
self.realtime = response.json()["result"]["realtime"]
|
|
89
89
|
self.nls = response.json()["result"]["nls"]
|
|
90
90
|
self.shares = response.json()["result"]["shares"]
|
|
91
|
-
|
|
91
|
+
|
|
92
92
|
|
|
93
93
|
class OptionQuote:
|
|
94
94
|
"""
|
|
@@ -112,7 +112,7 @@ class OptionQuote:
|
|
|
112
112
|
self.ft_session = ft_session
|
|
113
113
|
self.symbol = symbol
|
|
114
114
|
self.option_dates = self.get_option_dates(symbol)
|
|
115
|
-
|
|
115
|
+
|
|
116
116
|
def get_option_dates(self, symbol: str):
|
|
117
117
|
"""
|
|
118
118
|
Retrieves the expiration dates for options on a given symbol.
|
|
@@ -129,7 +129,7 @@ class OptionQuote:
|
|
|
129
129
|
"""
|
|
130
130
|
response = self.ft_session.get(url=urls.option_dates(symbol))
|
|
131
131
|
return response.json()
|
|
132
|
-
|
|
132
|
+
|
|
133
133
|
def get_option_quote(self, symbol: str, date: str):
|
|
134
134
|
"""
|
|
135
135
|
Retrieves the quote for a given option symbol.
|
|
@@ -146,7 +146,7 @@ class OptionQuote:
|
|
|
146
146
|
"""
|
|
147
147
|
response = self.ft_session.get(url=urls.option_quotes(symbol, date))
|
|
148
148
|
return response.json()
|
|
149
|
-
|
|
149
|
+
|
|
150
150
|
def get_greek_options(self, symbol: str, exp_date: str):
|
|
151
151
|
"""
|
|
152
152
|
Retrieves the greeks for options on a given symbol.
|
firstrade/urls.py
CHANGED
|
@@ -23,7 +23,9 @@ def account_balances(account):
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def account_positions(account):
|
|
26
|
-
return
|
|
26
|
+
return (
|
|
27
|
+
f"https://api3x.firstrade.com/private/positions?account={account}&per_page=200"
|
|
28
|
+
)
|
|
27
29
|
|
|
28
30
|
|
|
29
31
|
def quote(account, symbol):
|
|
@@ -38,8 +40,10 @@ def order_list(account):
|
|
|
38
40
|
return f"https://api3x.firstrade.com/private/order_status?account={account}"
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
def account_history(account):
|
|
42
|
-
|
|
43
|
+
def account_history(account, date_range, custom_range):
|
|
44
|
+
if custom_range is None:
|
|
45
|
+
return f"https://api3x.firstrade.com/private/account_history?range={date_range}&page=1&account={account}&per_page=1000"
|
|
46
|
+
return f"https://api3x.firstrade.com/private/account_history?range={date_range}&range_arr[]={custom_range[0]}&range_arr[]={custom_range[1]}&page=1&account={account}&per_page=1000"
|
|
43
47
|
|
|
44
48
|
|
|
45
49
|
def cancel_order():
|
|
@@ -67,7 +71,7 @@ def session_headers():
|
|
|
67
71
|
"Accept-Encoding": "gzip",
|
|
68
72
|
"Connection": "Keep-Alive",
|
|
69
73
|
"Host": "api3x.firstrade.com",
|
|
70
|
-
"User-Agent": "okhttp/4.9.2",
|
|
74
|
+
"User-Agent": "okhttp/4.9.2",
|
|
71
75
|
}
|
|
72
76
|
return headers
|
|
73
77
|
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: firstrade
|
|
3
|
+
Version: 0.0.33
|
|
4
|
+
Summary: An unofficial API for Firstrade
|
|
5
|
+
Home-page: https://github.com/MaxxRK/firstrade-api
|
|
6
|
+
Download-URL: https://github.com/MaxxRK/firstrade-api/archive/refs/tags/0033.tar.gz
|
|
7
|
+
Author: MaxxRK
|
|
8
|
+
Author-email: maxxrk@pm.me
|
|
9
|
+
License: MIT
|
|
10
|
+
Keywords: FIRSTRADE,API
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Topic :: Internet :: WWW/HTTP :: Session
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: requests
|
|
25
|
+
Requires-Dist: beautifulsoup4
|
|
26
|
+
Requires-Dist: lxml
|
|
27
|
+
Dynamic: author
|
|
28
|
+
Dynamic: author-email
|
|
29
|
+
Dynamic: classifier
|
|
30
|
+
Dynamic: description
|
|
31
|
+
Dynamic: description-content-type
|
|
32
|
+
Dynamic: download-url
|
|
33
|
+
Dynamic: home-page
|
|
34
|
+
Dynamic: keywords
|
|
35
|
+
Dynamic: license
|
|
36
|
+
Dynamic: license-file
|
|
37
|
+
Dynamic: requires-dist
|
|
38
|
+
Dynamic: summary
|
|
39
|
+
|
|
40
|
+
# firstrade-api
|
|
41
|
+
|
|
42
|
+
A reverse-engineered python API to interact with the Firstrade Trading platform.
|
|
43
|
+
|
|
44
|
+
This is not an official api! This api's functionality may change at any time.
|
|
45
|
+
|
|
46
|
+
This api provides a means of buying and selling stocks through Firstrade. It uses the Session class from requests to get authorization cookies. The rest is done with reverse engineered requests to Firstrade's API.
|
|
47
|
+
|
|
48
|
+
In order to use Fractional shares you must accept the agreement on the website before using it in this API.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Contribution
|
|
53
|
+
|
|
54
|
+
Please feel free to contribute to this project. If you find any bugs, please open an issue.
|
|
55
|
+
|
|
56
|
+
## Disclaimer
|
|
57
|
+
I am not a financial advisor and not affiliated with Firstrade in any way. Use this tool at your own risk. I am not responsible for any losses or damages you may incur by using this project. This tool is provided as-is with no warranty.
|
|
58
|
+
|
|
59
|
+
## Setup
|
|
60
|
+
|
|
61
|
+
Install using pypi:
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
pip install firstrade
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Quikstart
|
|
68
|
+
|
|
69
|
+
The code in `test.py` will:
|
|
70
|
+
- Login and print account info.
|
|
71
|
+
- Get a quote for 'INTC' and print out the information
|
|
72
|
+
- Place a dry run market order for 'INTC' on the first account in the `account_numbers` list
|
|
73
|
+
- Print out the order confirmation
|
|
74
|
+
- Contains a cancel order example
|
|
75
|
+
- Get an option Dates, Quotes, and Greeks
|
|
76
|
+
- Place a dry run option order
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Implemented Features
|
|
80
|
+
|
|
81
|
+
- [x] Login (With all 2FA methods now supported!)
|
|
82
|
+
- [x] Get Quotes
|
|
83
|
+
- [x] Get Account Data
|
|
84
|
+
- [x] Place Orders and Receive order confirmation
|
|
85
|
+
- [x] Get Currently Held Positions
|
|
86
|
+
- [x] Fractional Trading support (thanks to @jiak94)
|
|
87
|
+
- [x] Check on placed order status. (thanks to @Cfomodz)
|
|
88
|
+
- [x] Cancel placed orders
|
|
89
|
+
- [x] Options (Orders, Quotes, Greeks)
|
|
90
|
+
- [x] Order History
|
|
91
|
+
|
|
92
|
+
## TO DO
|
|
93
|
+
|
|
94
|
+
- [ ] Test options fully
|
|
95
|
+
- [ ] Give me some Ideas!
|
|
96
|
+
|
|
97
|
+
## Options
|
|
98
|
+
|
|
99
|
+
### I am very new to options trading and have not fully tested this feature.
|
|
100
|
+
|
|
101
|
+
Please:
|
|
102
|
+
- USE THIS FEATURE LIKE IT IS A ALPHA/BETA
|
|
103
|
+
- PUT IN A GITHUB ISSUE IF YOU FIND ANY PROBLEMS
|
|
104
|
+
|
|
105
|
+
## If you would like to support me, you can do so here:
|
|
106
|
+
[](https://github.com/sponsors/maxxrk)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
firstrade/__init__.py,sha256=fNiWYgSTjElY1MNv0Ug-sVLMTR2z_Ngri_FY7Pekdrw,95
|
|
2
|
+
firstrade/account.py,sha256=wLPlQOzgaaBwb8tvYPcWdhfTtaUEtM54iPBl5dPpVHY,14129
|
|
3
|
+
firstrade/exceptions.py,sha256=OrWB83rc33LSxrI7WxXo4o7FcIfmvPSC9bAY8K1pn7U,1886
|
|
4
|
+
firstrade/order.py,sha256=_b1SnqagwBu7KUmvzSUcp8iMOC3I3k-QDjiDLhlVk7E,8710
|
|
5
|
+
firstrade/symbols.py,sha256=RH36QLx5v3rKPpBidyJFwGJSDkF5u5f2ILTSZNAGXGQ,7903
|
|
6
|
+
firstrade/urls.py,sha256=Iw10isyvoqKwiSl3TVuIbos5INZzIEwpln3HcZ7P5aw,2125
|
|
7
|
+
firstrade-0.0.33.dist-info/licenses/LICENSE,sha256=wPEQjDqm5zMBmEcZp219Labmq_YIjhudpZiUzyVKaFA,1057
|
|
8
|
+
firstrade-0.0.33.dist-info/METADATA,sha256=P5nB38P3U45tTO7kSbuPhuBYUb5DVfXEVcSPKxl55sQ,3367
|
|
9
|
+
firstrade-0.0.33.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
|
|
10
|
+
firstrade-0.0.33.dist-info/top_level.txt,sha256=tdA8v-KDxU1u4VV6soiNWGBlni4ojv_t_j2wFn5nZcs,10
|
|
11
|
+
firstrade-0.0.33.dist-info/RECORD,,
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.1
|
|
2
|
-
Name: firstrade
|
|
3
|
-
Version: 0.0.30
|
|
4
|
-
Summary: An unofficial API for Firstrade
|
|
5
|
-
Home-page: https://github.com/MaxxRK/firstrade-api
|
|
6
|
-
Download-URL: https://github.com/MaxxRK/firstrade-api/archive/refs/tags/0030.tar.gz
|
|
7
|
-
Author: MaxxRK
|
|
8
|
-
Author-email: maxxrk@pm.me
|
|
9
|
-
License: MIT
|
|
10
|
-
Keywords: FIRSTRADE,API
|
|
11
|
-
Classifier: Development Status :: 3 - Alpha
|
|
12
|
-
Classifier: Intended Audience :: Developers
|
|
13
|
-
Classifier: Topic :: Internet :: WWW/HTTP :: Session
|
|
14
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
-
Classifier: Programming Language :: Python :: 3
|
|
16
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
-
Description-Content-Type: text/markdown
|
|
21
|
-
License-File: LICENSE
|
|
22
|
-
Requires-Dist: requests
|
|
23
|
-
Requires-Dist: beautifulsoup4
|
|
24
|
-
Requires-Dist: lxml
|
|
25
|
-
|
|
26
|
-
# firstrade-api
|
|
27
|
-
|
|
28
|
-
A reverse-engineered python API to interact with the Firstrade Trading platform.
|
|
29
|
-
|
|
30
|
-
This is not an official api! This api's functionality may change at any time.
|
|
31
|
-
|
|
32
|
-
This api provides a means of buying and selling stocks through Firstrade. It uses the Session class from requests to get authorization cookies. The rest is done with reverse engineered requests to Firstrade's API.
|
|
33
|
-
|
|
34
|
-
In order to use Fractional shares you must accept the agreement on the website before using it in this API.
|
|
35
|
-
|
|
36
|
-
---
|
|
37
|
-
|
|
38
|
-
## Contribution
|
|
39
|
-
|
|
40
|
-
I am new to coding and new to open-source. I would love any help and suggestions!
|
|
41
|
-
|
|
42
|
-
## Disclaimer
|
|
43
|
-
I am not a financial advisor and not affiliated with Firstrade in any way. Use this tool at your own risk. I am not responsible for any losses or damages you may incur by using this project. This tool is provided as-is with no warranty.
|
|
44
|
-
|
|
45
|
-
## Setup
|
|
46
|
-
|
|
47
|
-
Install using pypi:
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
pip install firstrade
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
## Quikstart
|
|
54
|
-
|
|
55
|
-
The code below will:
|
|
56
|
-
- Login and print account info.
|
|
57
|
-
- Get a quote for 'INTC' and print out the information
|
|
58
|
-
- Place a dry run market order for 'INTC' on the first account in the `account_numbers` list
|
|
59
|
-
- Print out the order confirmation
|
|
60
|
-
- Contains a cancel order example
|
|
61
|
-
- Get an option Dates, Quotes, and Greeks
|
|
62
|
-
- Place a dry run option order
|
|
63
|
-
|
|
64
|
-
```python
|
|
65
|
-
from firstrade import account, order, symbols
|
|
66
|
-
|
|
67
|
-
# Create a session
|
|
68
|
-
ft_ss = account.FTSession(username="", password="", email = "", profile_path="")
|
|
69
|
-
need_code = ft_ss.login()
|
|
70
|
-
if need_code:
|
|
71
|
-
code = input("Please enter the pin sent to your email/phone: ")
|
|
72
|
-
ft_ss.login_two(code)
|
|
73
|
-
|
|
74
|
-
# Get account data
|
|
75
|
-
ft_accounts = account.FTAccountData(ft_ss)
|
|
76
|
-
if len(ft_accounts.account_numbers) < 1:
|
|
77
|
-
raise Exception("No accounts found or an error occured exiting...")
|
|
78
|
-
|
|
79
|
-
# Print ALL account data
|
|
80
|
-
print(ft_accounts.all_accounts)
|
|
81
|
-
|
|
82
|
-
# Print 1st account number.
|
|
83
|
-
print(ft_accounts.account_numbers[0])
|
|
84
|
-
|
|
85
|
-
# Print ALL accounts market values.
|
|
86
|
-
print(ft_accounts.account_balances)
|
|
87
|
-
|
|
88
|
-
# Get quote for INTC
|
|
89
|
-
quote = symbols.SymbolQuote(ft_ss, ft_accounts.account_numbers[0], "INTC")
|
|
90
|
-
print(f"Symbol: {quote.symbol}")
|
|
91
|
-
print(f"Tick: {quote.tick}")
|
|
92
|
-
print(f"Exchange: {quote.exchange}")
|
|
93
|
-
print(f"Bid: {quote.bid}")
|
|
94
|
-
print(f"Ask: {quote.ask}")
|
|
95
|
-
print(f"Last: {quote.last}")
|
|
96
|
-
print(f"Bid Size: {quote.bid_size}")
|
|
97
|
-
print(f"Ask Size: {quote.ask_size}")
|
|
98
|
-
print(f"Last Size: {quote.last_size}")
|
|
99
|
-
print(f"Bid MMID: {quote.bid_mmid}")
|
|
100
|
-
print(f"Ask MMID: {quote.ask_mmid}")
|
|
101
|
-
print(f"Last MMID: {quote.last_mmid}")
|
|
102
|
-
print(f"Change: {quote.change}")
|
|
103
|
-
print(f"High: {quote.high}")
|
|
104
|
-
print(f"Low: {quote.low}")
|
|
105
|
-
print(f"Change Color: {quote.change_color}")
|
|
106
|
-
print(f"Volume: {quote.volume}")
|
|
107
|
-
print(f"Quote Time: {quote.quote_time}")
|
|
108
|
-
print(f"Last Trade Time: {quote.last_trade_time}")
|
|
109
|
-
print(f"Real Time: {quote.realtime}")
|
|
110
|
-
print(f"Fractional: {quote.is_fractional}")
|
|
111
|
-
print(f"Company Name: {quote.company_name}")
|
|
112
|
-
|
|
113
|
-
# Get positions and print them out for an account.
|
|
114
|
-
positions = ft_accounts.get_positions(account=ft_accounts.account_numbers[1])
|
|
115
|
-
print(positions)
|
|
116
|
-
for item in positions["items"]:
|
|
117
|
-
print(
|
|
118
|
-
f"Quantity {item["quantity"]} of security {item["symbol"]} held in account {ft_accounts.account_numbers[1]}"
|
|
119
|
-
)
|
|
120
|
-
|
|
121
|
-
# Get account history (past 200)
|
|
122
|
-
history = ft_accounts.get_account_history(account=ft_accounts.account_numbers[0])
|
|
123
|
-
for item in history["items"]:
|
|
124
|
-
print(f"Transaction: {item["symbol"]} on {item["report_date"]} for {item["amount"]}.")
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
# Create an order object.
|
|
128
|
-
ft_order = order.Order(ft_ss)
|
|
129
|
-
|
|
130
|
-
# Place dry run order and print out order confirmation data.
|
|
131
|
-
order_conf = ft_order.place_order(
|
|
132
|
-
ft_accounts.account_numbers[0],
|
|
133
|
-
symbol="INTC",
|
|
134
|
-
price_type=order.PriceType.LIMIT,
|
|
135
|
-
order_type=order.OrderType.BUY,
|
|
136
|
-
duration=order.Duration.DAY,
|
|
137
|
-
quantity=1,
|
|
138
|
-
dry_run=True,
|
|
139
|
-
)
|
|
140
|
-
|
|
141
|
-
print(order_conf)
|
|
142
|
-
|
|
143
|
-
if "order_id" not in order_conf["result"]:
|
|
144
|
-
print("Dry run complete.")
|
|
145
|
-
print(order_conf["result"])
|
|
146
|
-
else:
|
|
147
|
-
print("Order placed successfully.")
|
|
148
|
-
print(f"Order ID: {order_conf["result"]["order_id"]}.")
|
|
149
|
-
print(f"Order State: {order_conf["result"]["state"]}.")
|
|
150
|
-
|
|
151
|
-
# Cancel placed order
|
|
152
|
-
# cancel = ft_accounts.cancel_order(order_conf['result']["order_id"])
|
|
153
|
-
# if cancel["result"]["result"] == "success":
|
|
154
|
-
# print("Order cancelled successfully.")
|
|
155
|
-
# print(cancel)
|
|
156
|
-
|
|
157
|
-
# Check orders
|
|
158
|
-
recent_orders = ft_accounts.get_orders(ft_accounts.account_numbers[0])
|
|
159
|
-
print(recent_orders)
|
|
160
|
-
|
|
161
|
-
#Get option dates
|
|
162
|
-
option_first = symbols.OptionQuote(ft_ss, "INTC")
|
|
163
|
-
for item in option_first.option_dates["items"]:
|
|
164
|
-
print(f"Expiration Date: {item["exp_date"]} Days Left: {item["day_left"]} Expiration Type: {item["exp_type"]}")
|
|
165
|
-
|
|
166
|
-
# Get option quote
|
|
167
|
-
option_quote = option_first.get_option_quote("INTC", option_first.option_dates["items"][0]["exp_date"])
|
|
168
|
-
print(option_quote)
|
|
169
|
-
|
|
170
|
-
# Get option greeks
|
|
171
|
-
option_greeks = option_first.get_greek_options("INTC", option_first.option_dates["items"][0]["exp_date"])
|
|
172
|
-
print(option_greeks)
|
|
173
|
-
|
|
174
|
-
print(f"Placing dry option order for {option_quote["items"][0]["opt_symbol"]} with a price of {option_quote["items"][0]["ask"]}.")
|
|
175
|
-
print("Symbol readable ticker 'INTC'")
|
|
176
|
-
|
|
177
|
-
# Place dry option order
|
|
178
|
-
option_order = ft_order.place_option_order(
|
|
179
|
-
account=ft_accounts.account_numbers[0],
|
|
180
|
-
option_symbol=option_quote["items"][0]["opt_symbol"],
|
|
181
|
-
order_type=order.OrderType.BUY_OPTION,
|
|
182
|
-
price_type=order.PriceType.MARKET,
|
|
183
|
-
duration=order.Duration.DAY,
|
|
184
|
-
contracts=1,
|
|
185
|
-
dry_run=True,
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
print(option_order)
|
|
189
|
-
|
|
190
|
-
# Delete cookies
|
|
191
|
-
ft_ss.delete_cookies()
|
|
192
|
-
```
|
|
193
|
-
|
|
194
|
-
`You can also find this code in test.py`
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
## Implemented Features
|
|
199
|
-
|
|
200
|
-
- [x] Login (With all 2FA methods now supported!)
|
|
201
|
-
- [x] Get Quotes
|
|
202
|
-
- [x] Get Account Data
|
|
203
|
-
- [x] Place Orders and Receive order confirmation
|
|
204
|
-
- [x] Get Currently Held Positions
|
|
205
|
-
- [x] Fractional Trading support (thanks to @jiak94)
|
|
206
|
-
- [x] Check on placed order status. (thanks to @Cfomodz)
|
|
207
|
-
- [x] Cancel placed orders
|
|
208
|
-
- [x] Options (Orders, Quotes, Greeks)
|
|
209
|
-
- [x] Order History
|
|
210
|
-
|
|
211
|
-
## TO DO
|
|
212
|
-
|
|
213
|
-
- [ ] Test options fully
|
|
214
|
-
- [ ] Give me some Ideas!
|
|
215
|
-
|
|
216
|
-
## Options
|
|
217
|
-
|
|
218
|
-
### I am very new to options trading and have not fully tested this feature.
|
|
219
|
-
|
|
220
|
-
Please:
|
|
221
|
-
- USE THIS FEATURE LIKE IT IS A ALPHA/BETA
|
|
222
|
-
- PUT IN A GITHUB ISSUE IF YOU FIND ANY PROBLEMS
|
|
223
|
-
|
|
224
|
-
## If you would like to support me, you can do so here:
|
|
225
|
-
[](https://github.com/sponsors/maxxrk)
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
firstrade/__init__.py,sha256=fNiWYgSTjElY1MNv0Ug-sVLMTR2z_Ngri_FY7Pekdrw,95
|
|
2
|
-
firstrade/account.py,sha256=U9U-LXmOS3pQK7WjQ3oXqlKvl3r-2kphBPvWGsD3q9Y,13287
|
|
3
|
-
firstrade/exceptions.py,sha256=tL9hXGTZM1Ds0yWNCLSqD7hdcL_157CHLJT0e7AjxOg,1893
|
|
4
|
-
firstrade/order.py,sha256=_IwKsJ58A8KxiiaFFNbGBE7sak4G23s4_uFaCSovx00,8789
|
|
5
|
-
firstrade/symbols.py,sha256=9HKvg-rqX3GNCUKHtIOw81_MNkboamihmAaj6MpheW0,7930
|
|
6
|
-
firstrade/urls.py,sha256=7jeIaRERP8yDbZrnw2QcWn2J0z2MYKSNFJhJvef8I2Q,1859
|
|
7
|
-
firstrade-0.0.30.dist-info/LICENSE,sha256=wPEQjDqm5zMBmEcZp219Labmq_YIjhudpZiUzyVKaFA,1057
|
|
8
|
-
firstrade-0.0.30.dist-info/METADATA,sha256=jNfpF431f4pEIsEBTc3dI8IUlIxhI4TZJcoXp0API8U,7186
|
|
9
|
-
firstrade-0.0.30.dist-info/WHEEL,sha256=HiCZjzuy6Dw0hdX5R3LCFPDmFS4BWl8H-8W39XfmgX4,91
|
|
10
|
-
firstrade-0.0.30.dist-info/top_level.txt,sha256=tdA8v-KDxU1u4VV6soiNWGBlni4ojv_t_j2wFn5nZcs,10
|
|
11
|
-
firstrade-0.0.30.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|