forked from itsjafer/schwab-api
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request itsjafer#49 from 4rumprom/main
Adding option strategies support
- Loading branch information
Showing
4 changed files
with
310 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
from schwab_api import Schwab | ||
from dotenv import load_dotenv | ||
import os | ||
import pandas as pd | ||
import pprint | ||
|
||
load_dotenv() | ||
|
||
username = os.getenv("SCHWAB_USERNAME") | ||
password = os.getenv("SCHWAB_PASSWORD") | ||
totp_secret = os.getenv("SCHWAB_TOTP") | ||
|
||
# Initialize our schwab instance | ||
api = Schwab() | ||
|
||
# Login using playwright | ||
print("Logging into Schwab") | ||
logged_in = api.login( | ||
username=username, | ||
password=password, | ||
totp_secret=totp_secret # Get this using itsjafer.com/#/schwab. | ||
) | ||
|
||
# Get quotes for options. | ||
option_chain = api.get_options_chains_v2('$RUT') #try also with parameter greeks = True | ||
|
||
# The json output is deeply nested so here is how you can work with it: | ||
# Normalizing the data into a pandas DataFrame | ||
df1 = pd.json_normalize(option_chain,['Expirations','Chains','Legs'],[['Expirations','ExpirationGroup']]) | ||
# Normalizing Expirations.ExpirationGroup | ||
df2 = pd.json_normalize(df1['Expirations.ExpirationGroup']) | ||
# Dropping the column Expirations.ExpirationGroup in df1 and concatenating the two dataframes (side by side) | ||
df1.drop('Expirations.ExpirationGroup',axis=1, inplace=True) | ||
df = pd.concat([df1,df2],axis=1) | ||
# Converting strings to numbers when relevant. Keeping strings is conversion is not possible. | ||
df = df.apply(lambda col: pd.to_numeric(col, errors='coerce')).fillna(df) | ||
|
||
# Let's isolate options with closest expiration date: | ||
closest_expiration_options = df[(df.DaysUntil==df.DaysUntil.min())] | ||
|
||
# Let's find the call and put options with closest strike price to current price: | ||
# First let's grab the current price. No need to use api.quote_v2(), it's already in chains | ||
current_price = float(option_chain['UnderlyingData']['Last']) | ||
# Finding the index of the closest strike prices | ||
ATM_call_index = abs(closest_expiration_options[closest_expiration_options.OptionType=="C"].Strk - current_price).idxmin() | ||
ATM_put_index = abs(closest_expiration_options[closest_expiration_options.OptionType=="P"].Strk - current_price).idxmin() | ||
# Grabbing the rows at those indexes | ||
ATM_call_option = closest_expiration_options.iloc[ATM_call_index] | ||
ATM_put_option = closest_expiration_options.iloc[ATM_put_index] | ||
print(f"Call and Put ATM options (At The Money) with the closest expiration:") | ||
print(f"Call: {ATM_call_option.Sym} Ask: {ATM_call_option.Ask} Bid: {ATM_call_option.Bid}") | ||
print(f"Put: {ATM_put_option.Sym} Ask: {ATM_put_option.Ask} Bid: {ATM_put_option.Bid}") | ||
|
||
# Now let's place an at the money straddle for the closest expiration date | ||
# Preparing the parameters | ||
# Setting the straddle strategy code: | ||
strategy = 226 # for more codes, look at the comment section of option_trade_v2(). | ||
symbols = [ATM_call_option.Sym,ATM_put_option.Sym] | ||
instructions = ["BTO","BTO"] #Buy To Open. To close the position, it would be STC (Sell To Close) | ||
quantities = [1,1] | ||
# Note that the elements are paired. So the first symbol of the list will be associated with the first element of instructions and quantities. | ||
account_info = api.get_account_info_v2() | ||
account_id = next(iter(account_info)) | ||
order_type = 202 #net debit. 201 for net credit. You probably should avoid 49 market with options... | ||
# Let's set the limit price at the median between bid and ask. | ||
limit_price = (ATM_call_option.Ask + ATM_call_option.Bid + ATM_put_option.Ask + ATM_put_option.Bid) / 2 | ||
# Let's place the trade: | ||
messages, success = api.option_trade_v2( | ||
strategy=strategy, | ||
symbols = symbols, | ||
instructions=instructions, | ||
quantities=quantities, | ||
account_id=account_id, | ||
order_type = order_type, | ||
dry_run=True, | ||
limit_price = limit_price | ||
) | ||
|
||
print("The order verification was " + "successful" if success else "unsuccessful") | ||
print("The order verification produced the following messages: ") | ||
pprint.pprint(messages) | ||
|
||
# Happy coding!! |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
schwab_api | ||
python-dotenv==0.16.0 | ||
python-dotenv==0.16.0 | ||
pandas==2.2.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters