https://github.com/davidhintelmann/datareader_tmx
Get Stock Option Prices from TMX (Montreal Exchange)
https://github.com/davidhintelmann/datareader_tmx
montreal options python tmx
Last synced: 7 months ago
JSON representation
Get Stock Option Prices from TMX (Montreal Exchange)
- Host: GitHub
- URL: https://github.com/davidhintelmann/datareader_tmx
- Owner: davidhintelmann
- License: mit
- Created: 2020-10-17T03:52:54.000Z (about 5 years ago)
- Default Branch: main
- Last Pushed: 2025-02-20T05:57:09.000Z (8 months ago)
- Last Synced: 2025-03-25T08:01:44.195Z (7 months ago)
- Topics: montreal, options, python, tmx
- Language: Jupyter Notebook
- Homepage:
- Size: 3.06 MB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
If you are looking for the original repo please checkout `og` branch.
# Montreal Exchange (TMX)
The [Montreal Exchange](https://www.m-x.ca/accueil_en.php) is the oldest exchange in Canada and has a very important history to Canada's economy. Taken from Wikipedia,
>The Montreal Exchange, formerly the Montreal Stock Exchange (MSE), is a derivatives exchange, located in Montreal, Quebec, Canada that trades futures contracts and options on equities, indices, currencies, ETFs, energy and interest rates. Since 1965, it has been located in the Tour de la Bourse (Stock Exchange Tower), Montreal's third-tallest building. It is owned by the Toronto-based TMX Group.
This README file is to showcase the functionality of this small web scraping module. It can be used to get options prices, both calls and puts for index and ETF options, equity options, currency options, and weekly options.
These prices are then displayed in a Pandas Dataframe for further analysis. This could include simple plots for visualizing the data or creating machine learning model or neural network to predict future prices. One could also use [Black-Scholes](https://en.wikipedia.org/wiki/Black–Scholes_model) model to gain further insight.
---
# Requirements
1. Python **3.11.5**
2. Pandas **2.1.0** (older version will probably work)
3. Requests **2.31.0** (older version will probably work)
4. bs4 **4.12.2** (for BeautifulSoup)
- lxml **5.3.0** used with bs4 to process xml and html
---
We start by showing a list of options available from [TMX](https://m-x.ca/nego_liste_en.php). There is one parameter for the function `get_list()` and it can take one of four stings:
'Index'/'ETF', 'Equity', 'Currency', or 'Weekly'.
The webpage looks like: 
```python
get_list('equity')
```
Name of underlying instrument
Option symbol
Underlying symbol
0
Aecon Group Inc.
ARE
ARE
1
AGF Management Ltd., Cl. B
AGF
AGF.B
2
Agnico Eagle Mines Limited
AEM
AEM
3
Air Canada
AC
AC
4
Alamos Gold Inc.
AGI
AGI
...
...
...
...
264
Wheaton Precious Metals Corp.
WPM
WPM
265
Whitecap Resources Inc.
WCP
WCP
266
Winpak Ltd.
WPK
WPK
267
WSP Global Inc.
WSP
WSP
268
Yamana Gold Inc.
YRI
YRI
269 rows × 3 columns
The above DataFrame that we get with `get_list('equity')` is from:

Now we can grab stock prices for Air Canada (ticker symbol AC) so we can compare them to the stock options:
```python
get_stock('AC')
```
TICKER
Last price
Net change
Bid price
Ask price
0
AC
15.520
0.000
15.520
15.550
We can also input a list of stock symbols to get a Pandas DataFrame of said stocks:
```python
get_stock(['AC','ARE'])
```
TICKER
Last price
Net change
Bid price
Ask price
0
AC
15.520
0.000
15.520
15.550
1
ARE
14.220
0.100
14.200
14.250
Finally, we obtain the a Pandas Dataframe of TMX stock options for [Air Canada](https://m-x.ca/nego_cotes_en.php?symbol=AC*) which comes from:

```python
get('AC')
```
Call
Bid price
Ask price
Last price
Impl. vol.
Open int.
Vol.
Strike
Put
Bid price_
Ask price_
Last price.1
Impl. vol_
Open int_
Vol_
0
Oct 23, 2020-W
2.05
2.20
2.20
86.5%
10
0
13.5
Oct 23, 2020-W
0.01
0.12
0.12
75.8%
17
0
1
Oct 23, 2020-W
1.57
1.77
1.77
79.0%
0
0
14.0
Oct 23, 2020-W
0.08
0.14
0.14
71.0%
30
0
2
Oct 23, 2020-W
1.12
1.24
1.24
62.5%
10
0
14.5
Oct 23, 2020-W
0.10
0.18
0.18
59.2%
23
10
3
Oct 23, 2020-W
0.73
0.84
0.84
57.0%
7
0
15.0
Oct 23, 2020-W
0.19
0.29
0.29
53.8%
57
7
4
Oct 23, 2020-W
0.44
0.54
0.54
56.0%
47
141
15.5
Oct 23, 2020-W
0.37
0.53
0.53
53.7%
46
1
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
...
153
Jan 20, 2023
3.55
7.35
7.35
76.4%
2
0
21.0
Jan 20, 2023
8.85
12.50
12.50
74.4%
0
0
154
Jan 20, 2023
3.30
7.20
7.20
76.3%
0
0
22.0
Jan 20, 2023
9.60
13.30
13.30
74.2%
0
0
155
Jan 20, 2023
3.10
7.00
7.00
76.2%
0
0
23.0
Jan 20, 2023
10.35
14.10
14.10
73.8%
0
0
156
Jan 20, 2023
2.84
6.80
6.80
75.6%
0
0
24.0
Jan 20, 2023
11.10
14.85
14.85
73.0%
0
0
157
Jan 20, 2023
3.15
5.00
5.00
69.3%
32
7
25.0
Jan 20, 2023
11.90
15.75
15.75
73.3%
0
0
158 rows × 15 columns
```python
import pandas as pd
import requests
from bs4 import BeautifulSoup
```
```python
"""
Get a list of options from https://m-x.ca/nego_liste_en.php
TMX website
Index and ETF options
Equity options
Currency options
Weekly options
"""
def get_list(market=str) -> pd.DataFrame:
tmx = "https://m-x.ca/nego_liste_en.php" # TMX website, where data is taken from
#check that parameter is of type string
is_str1 = isinstance(market, str)
if not is_str1:
raise TypeError("market parameter must be of type string")
try:
market = market.lower()
except Exception as e:
print(e)
else:
if market == 'index' or market == 'etf':
market = 0
elif market == 'equity':
market = 1
elif market == 'currency':
market = 2
elif market == 'weekly':
market = 3
else:
raise Exception("Did not enter market type, choose from Index or ETF, Equity, Currency, Weekly.")
df = pd.read_html(tmx)
return df[market]
```
```python
"""
Get options prices at predetermined dates from TMX website
Call/Puts
strike price
Bid/Ask spreads
open interest
implied volatility
volume
"""
def get(ticker_symbol=str) -> pd.DataFrame:
tmx = "https://m-x.ca/nego_cotes_en.php" # TMX website, where data is taken from
is_str1 = isinstance(ticker_symbol, str)
if not is_str1:
raise TypeError("ticker_symbol parameter must be of type string")
try:
ticker_symbol = ticker_symbol.upper()
except Exception as e:
print(e)
else:
url = tmx + '?symbol=' + ticker_symbol + '*'
df = pd.read_html(url)
df[0].rename(columns={'Bid price.1':'Bid price_', 'Ask price.1':'Ask price_', 'Last Price.1':'Last Price_',
'Impl. vol..1':'Impl. vol_', 'Open int..1':'Open int_', 'Vol..1':'Vol_'}, inplace=True)
return df[0].iloc[:-1] #do not include last row, rubbish information
```
```python
"""
Get stock price from TMX to compare to strike price
can accept string or list of strings
"""
def get_stock(ticker_symbol=str) -> pd.DataFrame:
tmx = "https://m-x.ca/nego_cotes_en.php" # TMX website, where data is taken from
#check that parameter is of type string
is_str1 = checktype(ticker_symbol)
if not is_str1:
raise TypeError("market parameter must be of type string")
#download stock price, remember it is 15 minutes delayed
try:
symbols = []
for n in ticker_symbol:
symbols.append(n.upper())
except Exception as e:
print(e)
else:
price_dict = {}
is_list = isinstance(ticker_symbol, list)
if is_list:
df_list = []
for m in symbols:
URL = tmx + '?symbol=' + m + '*'
response = requests.get(URL)
soup = BeautifulSoup(response.text, 'html.parser')
x = soup.find('div', class_ = 'quote-info', attrs = 'ul')
y = x.ul.text.split('\n')[1:-2]
price_dict['TICKER'] = m
for z in y:
key, value = z.split(':')
price_dict[key] = value
tmp_df = pd.DataFrame.from_dict(price_dict, orient='index').T
df_list.append(tmp_df)
return pd.concat(df_list, ignore_index=True)
else:
ticker_symbol = ticker_symbol.upper()
URL = tmx + '?symbol=' + ticker_symbol + '*'
response = requests.get(URL)
soup = BeautifulSoup(response.text, 'html.parser')
x = soup.find('div', class_ = 'quote-info', attrs = 'ul')
y = x.ul.text.split('\n')[1:-2]
price_dict['TICKER'] = ticker_symbol
for z in y:
key, value = z.split(':')
price_dict[key] = value
tmp_df = pd.DataFrame.from_dict(price_dict, orient='index').T
return tmp_df
```
```python
def checktype(obj):
return bool(obj) and all(isinstance(elem, str) for elem in obj)
```