Last active
September 29, 2026 03:43
-
-
Save 5j9/bec9342ae5097af3912cfd42aedf5a1f to your computer and use it in GitHub Desktop.
CLI tool to calculate the return percentage of a TSETMC instrument
This file contains hidden or 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
| # /// script | |
| # requires-python = ">=3.14" | |
| # dependencies = [ | |
| # "cyclopts", | |
| # "jdatetime", | |
| # "polars", | |
| # "tsetmc", | |
| # ] | |
| # /// | |
| """ | |
| CLI tool to calculate the return percentage of a TSETMC instrument | |
| over a given Jalali date range. | |
| Usage: | |
| uv run path/to/script.py --l18 شتاب --from 1405-03-31 --to 1405-06-31 | |
| """ | |
| from datetime import date | |
| from typing import Annotated | |
| from cyclopts import App, Parameter, Token | |
| from jdatetime import date as jdate | |
| from polars import col | |
| from tsetmc.instruments import Instrument | |
| def price_on_or_before(history, target_date: date) -> tuple[date, int]: | |
| row = ( | |
| history.filter(col('date') <= target_date) | |
| .sort('date', descending=True) | |
| .select('date', 'pc') | |
| .head(1) | |
| .collect() | |
| ) | |
| if row.is_empty(): | |
| raise ValueError(f'No price history before {target_date}') | |
| return row['date'][0], row['pc'][0] | |
| async def get_return( | |
| l18: str, | |
| start_date: date, | |
| end_date: date, | |
| ) -> tuple[date, int, date, int, float]: | |
| instrument = await Instrument.from_l18(l18) | |
| history = await instrument.price_history() | |
| actual_start_date, start_price = price_on_or_before(history, start_date) | |
| actual_end_date, end_price = price_on_or_before(history, end_date) | |
| return_pct = (end_price / start_price - 1) * 100 | |
| return ( | |
| actual_start_date, | |
| start_price, | |
| actual_end_date, | |
| end_price, | |
| return_pct, | |
| ) | |
| app = App() | |
| def parse_jdate(type_: type[jdate], tokens: list[Token]) -> jdate: | |
| return type_.fromisoformat(tokens[0].value) | |
| JDate = Annotated[ | |
| jdate, | |
| Parameter( | |
| converter=parse_jdate, | |
| n_tokens=1, | |
| accepts_keys=False, | |
| ), | |
| ] | |
| @app.default | |
| async def main( | |
| *, | |
| l18: str, | |
| from_: JDate, | |
| to: JDate, | |
| ) -> None: | |
| """ | |
| Calculate the return percentage of a TSETMC instrument. | |
| """ | |
| ( | |
| actual_start_date, | |
| start_price, | |
| actual_end_date, | |
| end_price, | |
| return_pct, | |
| ) = await get_return(l18, from_.togregorian(), to.togregorian()) | |
| print(f'Instrument: {l18}') | |
| print(f'Period: {from_.isoformat()} → {to.isoformat()}') | |
| print(f'Actual: {actual_start_date} → {actual_end_date}') | |
| print(f'Prices: {start_price:,} → {end_price:,}') | |
| print(f'Return: {return_pct:.2f}%') | |
| if __name__ == '__main__': | |
| app() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment