Skip to content

Commit

Permalink
Fix ttm issue (#24)
Browse files Browse the repository at this point in the history
* Update version in pyproject.toml

* Add 1 day to data.days_to_target to account for the expiration day

* Add 1 day to data._days_to_maturity to account for the expiration day

* Update test_core.py

* Update .gitignore

* Update models.py

* Update calendar_spread.ipynb, .gitignore, CHANGELOG.md, poetry.lock and pyproject.toml

* Add 1 to `time_to_target` and `time_to_maturity` in `engine.py`

* Update Jupyter notebooks, replacing the `StrategyEngine` class by the `run_strategy()` function

* Update CHANGELOG.md

* Black files in `tests`

* Black `engine.py`, `models.py` and `support.py`

* Update CHANGELOG.md

* Change variable `project_target_ranges` in `models.py` and `engines.py` to `profit_target_ranges`

* Black again `engine.py`

* Update version in `__init__.py`
  • Loading branch information
rgaveiga authored Jun 25, 2024
1 parent d0fdb0a commit bf94e57
Show file tree
Hide file tree
Showing 16 changed files with 2,124 additions and 315 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,4 @@
dist/
dist/
__pycache__
run_*.py
.ipynb_checkpoints
13 changes: 10 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,20 @@
# CHANGELOG

## 1.2.1 (2024-06-03)

- Add 1 to `time_to_target` and `time_to_maturity` in `engine.py` to consider the target and expiration dates as trading days in the calculations
- Change Jupyter notebooks in the `examples` directory to utilize the `run_strategy()` function for performing options strategy calculations, instead of using the `StrategyEngine` class (deprecated)
- Correct the PoP Calculator notebook
- Change the name of variable `project_target_ranges` in `models.py` and `engine.py` to `profit_target_ranges`

## 1.2.0 (2024-03-31)

- Add functions to run engine
- Add functions to run engine.

## 1.1.0 (2024-03-24)

- Refactor the engine's `run` method for readability.
- Accept dictionary of inputs to `StratgyEngine` init.
- Refactor the engine's `run` method for readability
- Accept dictionary of inputs to `StratgyEngine` init

## 1.0.1 (2024-03-18)

Expand Down
56 changes: 27 additions & 29 deletions examples/calendar_spread.ipynb

Large diffs are not rendered by default.

150 changes: 77 additions & 73 deletions examples/call_spread.ipynb

Large diffs are not rendered by default.

85 changes: 46 additions & 39 deletions examples/covered_call.ipynb

Large diffs are not rendered by default.

67 changes: 36 additions & 31 deletions examples/naked_call.ipynb

Large diffs are not rendered by default.

51 changes: 30 additions & 21 deletions examples/pop_calculator.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
},
{
"cell_type": "code",
"execution_count": 2,
"execution_count": 1,
"metadata": {
"ExecuteTime": {
"end_time": "2024-03-15T17:51:40.653496Z",
Expand All @@ -29,7 +29,7 @@
"\n",
"from scipy import stats\n",
"\n",
"from optionlab import VERSION, get_d1_d2, get_pop"
"from optionlab import VERSION, get_d1_d2, get_pop, ProbabilityOfProfitInputs"
]
},
{
Expand All @@ -46,8 +46,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Python version: 3.10.13 (main, Aug 24 2023, 12:59:26) [Clang 15.0.0 (clang-1500.0.40.1)]\n",
"optionlab version: 1.0.0\n"
"Python version: 3.11.7 | packaged by Anaconda, Inc. | (main, Dec 15 2023, 18:05:47) [MSC v.1916 64 bit (AMD64)]\n",
"optionlab version: 1.2.0\n"
]
}
],
Expand Down Expand Up @@ -76,12 +76,12 @@
},
"outputs": [],
"source": [
"stockprice = 100.0\n",
"stock_price = 100.0\n",
"s1, s2 = 95, 105\n",
"interestrate = 1.0\n",
"dividendyield = 0.0\n",
"interest_rate = 1.0\n",
"dividend_yield = 0.0\n",
"volatility = 20.0\n",
"days2maturity = 60"
"days_to_maturity = 60"
]
},
{
Expand All @@ -102,10 +102,10 @@
},
"outputs": [],
"source": [
"interestrate = interestrate / 100\n",
"interest_rate = interest_rate / 100\n",
"volatility = volatility / 100\n",
"dividendyield = dividendyield / 100\n",
"time_to_maturity = days2maturity / 365"
"dividend_yield = dividend_yield / 100\n",
"time_to_maturity = days_to_maturity / 365"
]
},
{
Expand Down Expand Up @@ -136,10 +136,10 @@
"source": [
"d2 = [\n",
" get_d1_d2(\n",
" stockprice, s1, interestrate, volatility, time_to_maturity, dividendyield\n",
" stock_price, s1, interest_rate, volatility, time_to_maturity, dividend_yield\n",
" )[1],\n",
" get_d1_d2(\n",
" stockprice, s2, interestrate, volatility, time_to_maturity, dividendyield\n",
" stock_price, s2, interest_rate, volatility, time_to_maturity, dividend_yield\n",
" )[1],\n",
"]\n",
"pop1 = stats.norm.cdf(d2[0]) - stats.norm.cdf(d2[1])\n",
Expand Down Expand Up @@ -174,20 +174,29 @@
"source": [
"pop2 = get_pop(\n",
" [[s1, s2]],\n",
" \"black-scholes\",\n",
" stock_price=stockprice,\n",
" volatility=volatility,\n",
" interest_rate=interestrate,\n",
" years_to_maturity=time_to_maturity,\n",
" dividend_yield=dividendyield,\n",
" ProbabilityOfProfitInputs(\n",
" source=\"black-scholes\",\n",
" stock_price=stock_price,\n",
" volatility=volatility,\n",
" interest_rate=interest_rate,\n",
" years_to_maturity=time_to_maturity,\n",
" dividend_yield=dividend_yield,\n",
" ),\n",
")\n",
"print(\"===> Probability of Profit (PoP) from getPoP(): %.2f\" % (pop2 * 100))"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
Expand All @@ -201,7 +210,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.7.6"
"version": "3.11.7"
}
},
"nbformat": 4,
Expand Down
2 changes: 1 addition & 1 deletion optionlab/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import typing


VERSION = "1.2.0"
VERSION = "1.2.1"


if typing.TYPE_CHECKING:
Expand Down
19 changes: 11 additions & 8 deletions optionlab/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,6 @@ def _init_inputs(inputs: Inputs) -> EngineData:
data._days_to_maturity.append(data.days_to_target)
data._use_bs.append(False)
elif isinstance(strategy.expiration, dt.date) and inputs.start_date:

if inputs.discard_nonbusiness_days:
n_discarded_days = get_nonbusiness_days(
inputs.start_date, strategy.expiration, inputs.country
Expand Down Expand Up @@ -141,7 +140,9 @@ def _run(data: EngineData) -> EngineData:
"""
inputs = data.inputs

time_to_target = data.days_to_target / data._days_in_year
time_to_target = (
data.days_to_target + 1
) / data._days_in_year # To consider the target date as a trading day
data.cost = [0.0] * len(data.type)

data.profit = zeros((len(data.type), data.stock_price_array.shape[0]))
Expand Down Expand Up @@ -198,7 +199,7 @@ def _run(data: EngineData) -> EngineData:
data._profit_target_range = get_profit_range(
data.stock_price_array, data.strategy_profit, inputs.profit_target
)
data.project_target_probability = get_pop(data._profit_target_range, pop_inputs)
data.profit_target_probability = get_pop(data._profit_target_range, pop_inputs)

if inputs.loss_limit is not None:
data._loss_limit_rangesm = get_profit_range(
Expand Down Expand Up @@ -236,7 +237,9 @@ def _run_option_calcs(data: EngineData, i: int) -> EngineData:

return data

time_to_maturity = data._days_to_maturity[i] / data._days_in_year
time_to_maturity = (
data._days_to_maturity[i] + 1
) / data._days_in_year # To consider the expiration date as a trading day
bs = get_bs_info(
inputs.stock_price,
data.strike[i],
Expand Down Expand Up @@ -279,8 +282,8 @@ def _run_option_calcs(data: EngineData, i: int) -> EngineData:

if data._use_bs[i]:
target_to_maturity = (
data._days_to_maturity[i] - data.days_to_target
) / data._days_in_year
data._days_to_maturity[i] - data.days_to_target + 1
) / data._days_in_year # To consider the expiration date as a trading day

data.profit[i], data.cost[i] = get_pl_profile_bs(
type,
Expand Down Expand Up @@ -410,9 +413,9 @@ def _generate_outputs(data: EngineData) -> Outputs:

if inputs.profit_target is not None:
optional_outputs["probability_of_profit_target"] = (
data.project_target_probability
data.profit_target_probability
)
optional_outputs["project_target_ranges"] = data._profit_target_range
optional_outputs["profit_target_ranges"] = data._profit_target_range

if inputs.loss_limit is not None:
optional_outputs["probability_of_loss_limit"] = data.loss_limit_probability
Expand Down
8 changes: 5 additions & 3 deletions optionlab/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ class StockStrategy(BaseStrategy):
negative, it means that the position is closed and the
difference between this price and the current price is
considered in the payoff calculation.
"""

type: Literal["stock"] = "stock"
Expand Down Expand Up @@ -194,6 +195,7 @@ class Inputs(BaseModel):
mc_prices_number : int, optional
Number of random terminal prices to be generated when calculationg
the average profit and loss of a strategy. Default is 100,000.
"""

stock_price: float = Field(gt=0)
Expand Down Expand Up @@ -323,7 +325,7 @@ class EngineData(EngineDataResults):
theta: list[float] = []
cost: list[float] = []
profit_probability: float = 0.0
project_target_probability: float = 0.0
profit_target_probability: float = 0.0
loss_limit_probability: float = 0.0


Expand Down Expand Up @@ -358,7 +360,7 @@ class Outputs(BaseModel):
Maximum return of the strategy within the stock price domain.
probability_of_profit_target : float, optional
Probability of the strategy yielding at least the profit target.
project_target_ranges : list, optional
profit_target_ranges : list, optional
A list of minimum and maximum stock prices defining
ranges in which the strategy makes at least the profit
target.
Expand Down Expand Up @@ -390,7 +392,7 @@ class Outputs(BaseModel):
theta: list[float]
vega: list[float]
probability_of_profit_target: float | None = None
project_target_ranges: list[Range] | None = None
profit_target_ranges: list[Range] | None = None
probability_of_loss_limit: float | None = None
average_profit_from_mc: float | None = None
average_loss_from_mc: float | None = None
Expand Down
1 change: 0 additions & 1 deletion optionlab/support.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,6 @@ def get_pop(
return pop

if isinstance(inputs, ProbabilityOfProfitInputs):

stock_price = inputs.stock_price
volatility = inputs.volatility
years_to_maturity = inputs.years_to_maturity
Expand Down
Loading

0 comments on commit bf94e57

Please sign in to comment.