Skip to content

Commit

Permalink
Optionally maintain a high water mark w/ CCs
Browse files Browse the repository at this point in the history
When rolling covered calls, we may want to avoid rolling strikes down.
Instead, we might want to maintain the high water mark for our CCs to
prevent missing out on a rebound.

This can be configured globally with
`roll_when.calls.maintain_high_water_mark` or per-symbol with
`symbols.<symbol>.calls.maintain_high_water_mark`.
  • Loading branch information
brndnmtthws committed Dec 16, 2023
1 parent 33744eb commit 9cc6b74
Show file tree
Hide file tree
Showing 6 changed files with 50 additions and 2 deletions.
21 changes: 21 additions & 0 deletions thetagang.toml
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,25 @@ credit_only = false
# rolling calls regardless of the total quantity.
has_excess = true

# We can optionally maintain the high water mark for covered calls by never
# rolling CCs down, only out. We do this because it's not uncommon for a sharp
# bounce up after a stock/index falls enough to make the contract eligible for
# rolling. If we roll the CC down and out, we might be capping our gains when it
# eventually does bounce back. The trade off here is that you end up with
# slightly less downside protection.
#
# This only applies to rolling calls, and is essentially equivalent to setting
# the minimum strike of the next contract to the previous strike and only
# allowing the value to ratchet upwards.
#
# While this is off by default, it would be sensible to turn it on if you're
# trying to capture upside, rather than protect downside.
#
# This value can also be set per-symbol, with
# `symbols.<symbol>.calls.maintain_high_water_mark` (see `symbols.QQQ` example
# below).
maintain_high_water_mark = false

[roll_when.puts]
# Roll puts if they're in the money. Defaults to false if not specified.
itm = false
Expand Down Expand Up @@ -289,6 +308,8 @@ write_threshold = 0.01 # 1%, absolute value
[symbols.QQQ.calls]
strike_limit = 100.0 # never write a call with a strike below $100

maintain_high_water_mark = true # maintain the high water mark when rolling calls

[symbols.TLT]
weight = 0.2
# parts = 20
Expand Down
2 changes: 2 additions & 0 deletions thetagang/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ def validate_config(config):
Optional("itm"): bool,
Optional("credit_only"): bool,
Optional("has_excess"): bool,
Optional("maintain_high_water_mark"): bool,
},
Optional("puts"): {
Optional("itm"): bool,
Expand Down Expand Up @@ -147,6 +148,7 @@ def validate_config(config):
Optional("write_threshold"): And(float, lambda n: 0 <= n <= 1),
Optional("write_threshold_sigma"): And(float, lambda n: n > 0),
Optional("strike_limit"): And(float, lambda n: n > 0),
Optional("maintain_high_water_mark"): bool,
},
Optional("puts"): {
Optional("delta"): And(float, lambda n: 0 <= n <= 1),
Expand Down
7 changes: 6 additions & 1 deletion thetagang/config_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,12 @@
"roll_when": {
"min_pnl": 0.0,
"close_at_pnl": 1.0,
"calls": {"itm": True, "credit_only": False, "has_excess": True},
"calls": {
"itm": True,
"credit_only": False,
"has_excess": True,
"maintain_high_water_mark": False,
},
"puts": {"itm": False, "credit_only": False, "has_excess": True},
},
"vix_call_hedge": {
Expand Down
3 changes: 3 additions & 0 deletions thetagang/portfolio_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
get_target_delta,
get_write_threshold_perc,
get_write_threshold_sigma,
maintain_high_water_mark,
midpoint_or_market_price,
net_option_positions,
portfolio_positions_to_dict,
Expand Down Expand Up @@ -1194,6 +1195,8 @@ def roll_positions(
max([strike_limit or 0] + average_cost),
2,
)
if maintain_high_water_mark(self.config, symbol):
strike_limit = max([strike_limit, position.contract.strike])

elif right.startswith("P"):
strike_limit = round(
Expand Down
10 changes: 9 additions & 1 deletion thetagang/thetagang.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
get_target_delta,
get_write_threshold_perc,
get_write_threshold_sigma,
maintain_high_water_mark,
)

from .portfolio_manager import PortfolioManager
Expand Down Expand Up @@ -163,7 +164,12 @@ def start(config_path, without_ibc=False):
"=",
f"{config['roll_when']['calls']['has_excess']}",
)

config_table.add_row(
"",
"Calls: maintain high water mark",
"=",
f"{config['roll_when']['calls']['maintain_high_water_mark']}",
)
config_table.add_section()
config_table.add_row("[spring_green1]When writing new contracts")
config_table.add_row(
Expand Down Expand Up @@ -247,6 +253,7 @@ def start(config_path, without_ibc=False):
symbols_table.add_column("Call delta", justify="right")
symbols_table.add_column("Call strike limit", justify="right")
symbols_table.add_column("Call threshold", justify="right")
symbols_table.add_column("HWM", justify="right")
symbols_table.add_column("Put delta", justify="right")
symbols_table.add_column("Put strike limit", justify="right")
symbols_table.add_column("Put threshold", justify="right")
Expand All @@ -259,6 +266,7 @@ def start(config_path, without_ibc=False):
f"{ffmt(get_write_threshold_sigma(config, symbol, 'C'))}σ"
if get_write_threshold_sigma(config, symbol, "C")
else pfmt(get_write_threshold_perc(config, symbol, "C")),
str(maintain_high_water_mark(config, symbol)),
ffmt(get_target_delta(config, symbol, "P")),
dfmt(get_strike_limit(config, symbol, "P")),
f"{ffmt(get_write_threshold_sigma(config, symbol, 'P'))}σ"
Expand Down
9 changes: 9 additions & 0 deletions thetagang/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -227,3 +227,12 @@ def algo_params_from(params):

def get_minimum_credit(config: dict) -> float:
return config["orders"].get("minimum_credit", 0.0)


def maintain_high_water_mark(config: dict, symbol: str) -> bool:
if (
"calls" in config["symbols"][symbol]
and "maintain_high_water_mark" in config["symbols"][symbol]["calls"]
):
return config["symbols"][symbol]["calls"]["maintain_high_water_mark"]
return config["roll_when"]["calls"]["maintain_high_water_mark"]

0 comments on commit 9cc6b74

Please sign in to comment.