The option_transactions table shows options trades executed on the thinkorswim trading platform. Query the table to calculate the profit/loss made on each trade.
order_id | option_quote |
---|---|
1339553203 | TOSWeb BOT +1 RBLX 100 (Weeklys) 13 AUG 21 85 CALL @.82 |
1339551295 | TOSWeb BOT +1 RBLX 100 (Weeklys) 13 AUG 21 85 CALL @1.00 |
1339551281 | TOSWeb SOLD -2 RBLX 100 (Weeklys) 13 AUG 21 85 CALL @2.10 |
1339551278 | TOSWeb BOT +1 TSLA 100 (Weeklys) 13 AUG 21 750 CALL @5.50 |
1339551271 | TOSWeb SOLD -1 TSLA 100 (Weeklys) 13 AUG 21 750 CALL @7.50 |
1339551264 | TOSWeb BOT +5 QS 100 (Weeklys) 13 AUG 21 24 PUT @.50 |
1339551235 | TOSWeb SOLD -5 QS 100 (Weeklys) 13 AUG 21 24 PUT @.25 |
1339551224 | TOSWeb BOT +2 AAPL 100 (Weeklys) 13 AUG 21 150 CALL @2.00 |
1339551219 | TOSWeb SOLD -1 AAPL 100 (Weeklys) 13 AUG 21 150 CALL @2.40 |
1339139768 | TOSWeb SOLD -1 AAPL 100 (Weeklys) 13 AUG 21 150 CALL @2.60 |
The anatomy of an options quote can be found here while this video provides additional information on how to read an options quote.
Before getting into the query let’s explain TOSWeb BOT +1 RBLX 100 (Weeklys) 13 AUG 21 85 CALL @.82
to ensure that it’s understandable:
TOSWeb
- The thinkorsiwm platform. This can be ignoredBOT
- The type of transaction. Can be either “BOT” (contract was bought) or “SOLD” (contract was sold)+1
- The number of contracts bought or soldRBLX
- The underlying stock symbol100 (Weeklys)
- Can be ignored as it does not provide any needed information13 AUG 21
- The date that the contract expires85
- The strike price of the stockCALL
- The type of option. Can be either “CALL” or “PUT”@.82
- The per contract cost of the optionThe first two rows in the option_transactions table shows that 2 RBLX calls with a strike price of $85 and an expiration of 08/13 were bought for a total price of $1.82. The third row shows that both contracts were sold for a price of $2.10 per contract. Thus, the total profit made on this trade was $2.38
with clean_options as (
select
stock,
transaction_type,
contract,sum(num_contracts * price) as total_price,
from (
select
option_quote,trim(regexp_extract(option_quote, r"(.*? ){2}")) as transaction_type,
cast(regexp_extract(option_quote, r"\d+") as numeric) as num_contracts,
"(.*? ){4}") as stock,
regexp_extract(option_quote, r"\(Weeklys\)?(.*)@") as contract,
regexp_extract(option_quote, rcast(regexp_extract(option_quote, r"\@(.*)$") as numeric) as price
from option_transactions)
group by
stock,
transaction_type,
contract
),
as (
buy_to_open select
stock,
contract,as buy_price
total_price from clean_options
where transaction_type = 'BOT'
),
as (
sell_to_close select
stock,
contract,as sell_price
total_price from clean_options
where transaction_type = 'SOLD'
)
select
buy_to_open.stock,
buy_to_open.contract,
buy_to_open.buy_price,
sell_to_close.sell_price,- buy_to_open.buy_price) as profit_loss
(sell_to_close.sell_price from
buy_to_openinner join sell_to_close on
= sell_to_close.stock and
buy_to_open.stock = sell_to_close.contract
buy_to_open.contract order by profit_loss desc
stock | contract | buy_price | sell_price | profit_loss |
---|---|---|---|---|
RBLX | 13 AUG 21 85 CALL | 1.82 | 4.20 | 2.38 |
TSLA | 13 AUG 21 750 CALL | 5.50 | 7.50 | 2.00 |
AAPL | 13 AUG 21 150 CALL | 4.00 | 5.00 | 1.00 |
QS | 13 AUG 21 24 PUT | 2.50 | 1.25 | -1.25 |
<- option_transactions %>%
clean_options mutate(
transaction_type = word(option_quote, 2),
num_contracts = abs(parse_number((word(option_quote, 3)))),
stock = word(option_quote, 4),
contract = word(option_quote, 7, 11),
price = parse_number(word(option_quote, -1)),
total_price = num_contracts * price
%>%
) group_by(stock, transaction_type, contract) %>%
summarise(total_price = sum(total_price)) %>%
ungroup()
= clean_options %>%
buy_to_open filter(transaction_type == 'BOT') %>%
select(stock, contract, buy_price = total_price)
= clean_options %>%
sell_to_close filter(transaction_type == 'SOLD') %>%
select(stock, contract, sell_price = total_price)
<- buy_to_open %>%
profit_loss inner_join(sell_to_close, by = c("stock", "contract")) %>%
mutate(profit_loss = sell_price - buy_price) %>%
arrange(desc(profit_loss))
stock | contract | buy_price | sell_price | profit_loss |
---|---|---|---|---|
RBLX | 13 AUG 21 85 CALL | 1.82 | 4.20 | 2.38 |
TSLA | 13 AUG 21 750 CALL | 5.50 | 7.50 | 2.00 |
AAPL | 13 AUG 21 150 CALL | 4.00 | 5.00 | 1.00 |
QS | 13 AUG 21 24 PUT | 2.50 | 1.25 | -1.25 |
= r.option_transactions
option_transactions
= (
clean_options
option_transactions
.assign(= lambda x: x["option_quote"].str.split(" "),
option_quote = lambda x: x["option_quote"].str[1],
transaction_type = lambda x: x["option_quote"].str[2].apply(int).abs(),
num_contracts = lambda x: x["option_quote"].str[3],
stock = lambda x: x["option_quote"].str[6:10].str.join(" "),
contract = lambda x: x["option_quote"].str[-1].str.replace("@", "").apply(float),
price = lambda x: x["num_contracts"] * x["price"]
total_price
)'stock','transaction_type','contract'])['total_price']
.groupby([sum()
.
.reset_index()
)
= (
buy_to_open 'transaction_type'] == 'BOT']
clean_options[clean_options['stock', 'contract', 'total_price']]
[["total_price": "buy_price"}, axis='columns')
.rename({
)
= (
sell_to_close 'transaction_type'] == 'SOLD']
clean_options[clean_options['stock', 'contract', 'total_price']]
[["total_price": "sell_price"}, axis = 'columns')
.rename({
)
= (
profit_loss
buy_to_open='inner', left_on=['stock', 'contract'], right_on=['stock', 'contract'])
.merge(sell_to_close, how= lambda x: x["sell_price"] - x["buy_price"])
.assign(profit_loss ='profit_loss', ascending=False)
.sort_values(by )
stock | contract | buy_price | sell_price | profit_loss | |
---|---|---|---|---|---|
2 | RBLX | 13 AUG 21 85 | 1.82 | 4.20 | 2.38 |
3 | TSLA | 13 AUG 21 750 | 5.50 | 7.50 | 2.00 |
0 | AAPL | 13 AUG 21 150 | 4.00 | 5.00 | 1.00 |
1 | QS | 13 AUG 21 24 | 2.50 | 1.25 | -1.25 |