almog / IBKR-to-1325-form

Processes Interactive Brokers CSV statement into Israel tax report form 1325
MIT License
3 stars 1 forks source link

Infer the required currency-rate date range from the input statement #2

Open JacobPyC opened 1 year ago

JacobPyC commented 1 year ago
  1. במכירה הבנתי שהעמלת מכירה לא נסכמת וצריך לקזז אותה לפני האם אתה מבצע את זה?
  2. ההבדלים יהיו קצת שונים מהאקסל של המתורגמן כי השער בBOI והשער שיוצא מGOOGLE FINANCE טיפה שונה למשל ב03/12/2020 בGOOGLE FINANCE השער היה 3.26558 ובBOI השער היה 3.275
  3. אם יהיה אפשר לכתוב טווח של שנים במקרה שקנית מניה ב2020 ומכרת ב2022 זה אפשרי עשיתי פשוט Hard Coded כי אצלי זה נכשל כשלא מצא את תאריך הקנייה אפשר פשוט לבקש 2 פרמטרים או פשוט לבקש טווח ולפרסר
  4. אל תוסיף את העמלה שאתה משלם על שורטים לעמלת המכירה רק מי שמוגדר כסוחר במס הכנסה יכול להזדכות עליהם אז זה לא רלוונטי לרוב האנשים(ראיתי שדיברת על זה קצת עם הבחור השני)
almog commented 1 year ago
  1. כמו שציינת, T. Price הוא מחיר הנייר ולכן העמלה על הטרייד גורעת מההפרש שבין מחיר המכירה למחיר הקניה, כלומר, חיסור ממחיר המכירה (למעשה חיבור כיוון שהעמלה היא מספר שלילי) של ה-ClosedLot הראשון שמשוייך לטרייד של המכירה. אמנם כתבת "מחסיר את העמלות שוב פעם" ו"שוב פעם" נשמע כמו דבר לא נכון לעשות, אבל אני לא רואה בקוד או בהסבר שלך מאיפה מגיעה הטענה שמשהו קורה כאן שוב, כלומר יותר מפעם אחת.
  2. בדקתי עכשיו מול האתר של בנק ישראל והשער שציינת של BOI הוא אכן השער הנכון, אבל אני מבין את הטענה שלך. אני לא מכיר את ה-API של גוגל פייננס ואיזה שער הוא מחזיר עבור תאריך, אבל מניח שזה שער רציף בנקודה שונה באותו יום (למשל חצות או תחילת יום המסחר). אני לא בטוח מה התקנה הרלוונטית של רשות המיסים אומרת לגבי זה, אבל נשמע לי סביר לקחת או את השער היציג של בנק ישראל או את השער הרציף המדוייק של השעה המדוייקת שבא התקיימה העסקה. זה אפשרי ליישם את זה, אבל יצריך הרבה קריאות API, ולדעתי, אלא אם אתה מכיר ויכול להציג חוק שקובע שזה הפתרון שנדרש ע"פ חוק, זה יהיה קצת אוברקיל לנסות לשאוף לרמת הדיוק הזו. אם לעומת זאת, אתה רוצה רק לעשות בדיקה ידנית שמשווה בין התוצר של הסקריפט לאקסל של המתורגמן, צריך להיות די פשוט CSV של שערי מטבע מ-Google Finance עם התאריכים הרלוונטיים (אם כי כאמור לא מצאתי טיעון שתומך בנכונות של השערים האלה דווקא).
  3. נקודה מצויינת, תודה על ההערה, זה אמנם לא הארד-קודד כרגע, אבל כן רץ תחת ההנחה שמדובר על טריידים שמתחילים ומסתיימים באותה שנה, אני באמת מתכוון להסיק את התאריכיך מתוך הסטייטמנט במקום לקבל את זה כקלפט מהמשתמש וזה יפתור את הבעיה הזו. אבל אם אתה רוצה להתגבר על המגבלה הזו באופן זמני, רק כדי לראות אם אין בעיות נוספות, אתה יכול לשנות את השורה הזו לטווח הרצוי:
    rates = fetch_currency_rates(currency, f'{tax_year}-01-01', f'{tax_year}-12-31')
  1. את הטיפול בשורטים עשיתי בהתאם להמלצות שעלו מדיון קצר בתגובות אצל המתורגמן, אבל אם מה שאתה אומר זה נכון, תוכל להפנות אותי לאיזשהו מקור רשמי או אפילו דיון רשת כלשהו בנושא שעולה ממנו הטענה הזו לגבי שורטים? תודה.

אגב, בכל מקרה ה-join בין המכירה לעמלות צד קניה קצת יותר מסובך ממה שקיוויתי (כתבתי על זה בתגובה האחרונה ב-#1). בהנתן הקושי הטכני, יכול להיות שפתרון פשוט יותר, אבל לא בטוח אם קביל, הוא להוסיף שורת הפסד על כל העמלות באשר הן. בעיה אחת עם זה היא איזה שער מטבע לקחת. ספציפית עבור הדו"ח שלי, נראה לי שאני מוכן גם לקחת את השער המינימלי של השנה כדי לא להכנס לקן השרצים הזה. :) אם היה אפשר להוסיף את העמלות ל-ClosedLot ב-CSV (ולא רק ל-Trade) זה היה הופך את זה להרבה יותר קל. אם במקרה תגלה אפשרות כזו, אשמח לדעת.

JacobPyC commented 1 year ago
  1. אוי כן בדקתי שוב כן זה מדויק הוספתי במקום להחסיר
  2. התכוונתי יותר בקטע שאם אנשים ישוו את הפלט לחישובי העבר שלהם שנעשו עם האקסל שהמתורגמן הכין זה לאו דווקא יהיה זהה אבל הם צריכים לדעת שהחישובים שלך זהים בפועל הבנתי שמספיק למס הכנסה את הממוצע השנתי כל עוד אתה עקבי ולא מתי שזה משתלם 3.כן עשיתי את זה כבר וזה עובד בסדר גמור אתה מתכוון שפשוט תעבור על הכל ותבחר נקודות קיצון מרגיש לי קצת לא יעיל אבל תכלס זה עדיף מלסמוך על קלט מהמשתמש
  3. יש תגובה של המתורגמן אצלו בפוסט "לגבי מארג'ין, ניתן לקזז רק אם אתה סוחר מקצועי, אבל אז חל על הרווחים שלך מס הכנסה שולי, ביטוח לאומי ומע"מ." בכללי קראתי גם בטלגרם של ALFIE שריבית לא ניתן לקזז אבל יכול להיות שאני טועה וזה לא נחשב ריבית לא מכיר דרך להוסיף עמלות לClosedLot לצערי
almog commented 1 year ago
  1. אני מסכים, ולכן הערתי שיהיה קל לדעתי להריץ את הסקריפט גם מול גוגל פייננס כדי לבצע השוואה. אם אתה בעצמך רוצה להשוות, אני יכול להוסיף פונקציה שעושה את זה. כדאי אבל לדעתי לבדוק קודם שהערכים שהזנת בעצמך בגיליון של המתורגמן זהים לאלה שהסקריפט מייצר בעמודות הרלוונטיות (תאריכים וערכים נקובים אם אני לא טועה). תעדכן אותי אם זה משהו שאתה רוצה לנסות ואני אשתדל להכניס את זה.
  2. עכשיו הבנתי אותך, אתה מתכוון ל-Borrowing Fee על השאלת הנייר (וכן כל ריבית שאתה אולי משלם על מרג'ין אם אתה לוקח הלוואת מארג'ין אבל זה משהו שלא ייחודי למכירת שורט). לא הייתי מודע לזה אבל הבנתי אותך לא נכון בהתחלה — חשבתי שאתה טוען שמה שקורה כרגע בקוד ביחס לשורט, כלומר הוספת עמלת הטרייד הסוגר לערך הנקוב במכירה (בדיוק כמו בפוזיציית לונג). תודה על המידע!
JacobPyC commented 1 year ago

רציתי להוסיף מודול של Google Finance conversion rate אבל מסתבר שהם לא מייחצנים אותו יותר למשתמשים רגילים יותר באסה אני אעשה השוואה בינך לבין האקסל של המתורגמן גם בנוגע לשנה שעברה

תצטרך להתייחס לזה או להתעלם כי זה מעיף לי את הריצה "Notes" בדוח של שנה שעברה הייתה לי שורת ולאחר מכן הוא נופל על Index contains duplicate entries, cannot reshape mask.sum() הוא תפס את מספר העסקאות הנכון self.index מציג מספר גדול פי ארבע מזה

almog commented 1 year ago

אם אתה רוצה, אתה יכול להשתמש בגוגל דוקס בשביל לייצר את ה-CSV. שמת כאן את התאריכים של 2020-2022 ושער המטבע מגוגל פייננס לכל תאריך: https://docs.google.com/spreadsheets/d/1CNzvsqyhQostmuLtozYs-063nEYVZ-d8yHK9nm31-ws/edit?usp=sharing

לגבי החלק השני, אני מבין שאתה אומר שמשהו או כמה דברים השתבשו (עם הסקריפט בלי קשר לשערי המטבע?), אבל זה בערך כל מה שיכולתי להבין שקרה מהטקסט. נראה לי שהשילוב של אנגלית ועברית לא עוזר לזה להיות ברור. אני אשמח להבין את הבעיה, אבל אני אצטרך שתסביר לי לאט יותר. תודה!

JacobPyC commented 1 year ago

Im getting an exception "index contains duplicate entires,cannot reshape" For some reason self.index way bigger than mask.sum 85>25

almog commented 1 year ago

Can you share the traceback? While it might not be trivial to find the bug without the input data it'd be even more challenging without having full error message stack trace.

JacobPyC commented 1 year ago

I forgot to mention I also got this exception "Unexpected header {header}'" because i had a header named "Notes" in my ibkr statement image

JacobPyC commented 1 year ago
    if mask.sum() < len(self.index):
        raise ValueError("Index contains duplicate entries, cannot reshape")

self.index is way bigger than mask.sum

full traceback here: Traceback (most recent call last): File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\IBKR-to-1325-form-main\src__main.py", line 115, in main() File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\IBKR-to-1325-form-main\src__main__.py", line 34, in main schema_name_to_df = parse_statement(statement_csv_path) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\IBKR-to-1325-form-main\src\parse_statement.py", line 34, in parse_statement commit_schema() File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\IBKR-to-1325-form-main\src\parse_statement.py", line 8, in commit_schema df = df.stack().str.replace(',','').unstack() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\venv\Lib\site-packages\pandas\core\series.py", line 4313, in unstack return unstack(self, level, fill_value) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\venv\Lib\site-packages\pandas\core\reshape\reshape.py", line 488, in unstack unstacker = _Unstacker( ^^^^^^^^^^^ File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\venv\Lib\site-packages\pandas\core\reshape\reshape.py", line 136, in init__ self._make_selectors() File "C:\Users\jacob\Downloads\Files\IBKR-to-1325-form-main\venv\Lib\site-packages\pandas\core\reshape\reshape.py", line 188, in _make_selectors raise ValueError("Index contains duplicate entries, cannot reshape") ValueError: Index contains duplicate entries, cannot reshape

almog commented 1 year ago

I think it might imply that there are duplicate entries in one of the schemas of your input CSV and that messes with the stack/unstack attempt at removing the thousand separator without being specific about the target column (cleaning the data prior to applying pandas.to_numeric).

You can test if that's the problem by replacing the this line:

df = df.stack().str.replace(',','').unstack()

With this one:

for col in df.columns:
    if df[col].dtype == 'object':
        df[col] = df[col].str.replace(',', '')

Let me know if it helped. Thanks.

Regarding the unexpected header — you can add it to the ignore list (elif header in ['Total', 'SubTotal', 'Notes']:) Since I'm not aware of any public specification of IBKR's statement CSV, the whole process is a trial and error, therefore this list exist and is WIP.

JacobPyC commented 1 year ago

found minor edge case last year i got some HTZGQ stocks for any HTZ stock and then sold it there is no buying price so your code prolly skipped it not sure if it worth to address it also add (elif header in ['Total', 'SubTotal', 'Notes','']:) to your code

almog commented 1 year ago
  1. So the the patch worked but you had to also add the empty string to the ignore list? Can you tell me under what schema these types appeared? If so, can copy the relevant CSV line so that I can get some context before adding an additional item to the list (or instead abandoning the ignore-list altogether, I only added it for documentation purpose and in order to be defensive when it comes to an unknown schema).
  2. Regarding the edge case that you experienced: you didn't mention it but I gather that it's about a stock who's trading symbol has changed, right? Could you please paste an example of how the CSV would look like for such pair of Trade and ClosedLot?
JacobPyC commented 1 year ago
  1. ignore it maybe i edited the CSV and forgot about that, adding only "Notes" to the list is fine
  2. nope, in that case, the ticker will be changed too, the edge case happens when you get cash and/or another stock/s for each stock you have (WORK, HTZGQ) but its more IBKR's custom report problem and it will be more pain in the ass to address image
mirono commented 1 year ago

אני מניח שאני "הבחור השני"... לגבי עמלת השורטים, יכול להיות שאתה צודק. השנה הגשתי את הדו"ח למס הכנסה בסה"כ - כלמר לא יצרתי נספח 1325 אלא לקחתי את רווח/הפסד ההון השנתי הכולל מוכפל בשער דולר אמצע שנה. קיזזתי מזה גם את עמלות ההשאלה וכל ההוצאות האחרות שהיו לי באינטראקטיב, והשומה התקבלה.

JacobPyC commented 1 year ago

אני מניח שאני "הבחור השני"... לגבי עמלת השורטים, יכול להיות שאתה צודק. השנה הגשתי את הדו"ח למס הכנסה בסה"כ - כלמר לא יצרתי נספח 1325 אלא לקחתי את רווח/הפסד ההון השנתי הכולל מוכפל בשער דולר אמצע שנה. קיזזתי מזה גם את עמלות ההשאלה וכל ההוצאות האחרות שהיו לי באינטראקטיב, והשומה התקבלה. יכול להיות נפלת על פקיד שומה שזרק קצת או לא ממש בדק אבל אם בשנה אחת תיפול על אחד קצת יותר by the book הוא יכול לבקש ממך גם להגיש שוב רטרואקטיבית על שנים עברו הפעם עם נספחים ואז כנראה שתעבור בדיקה יותר לעומק אני במקומך הייתי נמנע מלעשות את זה שוב:) אני הבנתי קצת מאוחר שכדי להגיש דוח שנתי צריך לבקש קודם כל לשלוח בקשה לפתיחת תיק 93 ורק אז לדווח ואי אפשר לעשות את זה כשאתה על המצב הדיפולטיבי( החזר מס) בפועל לנציג לא ממש היה אכפת הוא פשוט שינה לי את סוג התיק וזהו ושאני חושב על זה אני כבר שנתיים מקזז רווחי ריבית מהשאלת מניות עם הפסדי הון בפועל זה לא אמור להיות אפשרי(ריבית אמורה להיות הכנסה שלא מיגיעה אישית) אבל אני סומך על זה שמס הכנסה פחות מבין במושגים כמו stock yield enhancement אבל קצת יותר ישים לב אם לא תגיש בכלל 1325

mirono commented 1 year ago

@JacobPyC יכול להיות שאתה צודק אבל בהעדר סקריפט ראוי לשמו היה בלתי אפשרי להגיש 1325 עם מאות תנועות... לגבי מצב התיק - הייתי נמנע בכלל מהגשה אם לא הייתי מחוייב בשל "הכנסה גבוהה".

JacobPyC commented 1 year ago

@JacobPyC יכול להיות שאתה צודק אבל בהעדר סקריפט ראוי לשמו היה בלתי אפשרי להגיש 1325 עם מאות תנועות... לגבי מצב התיק - הייתי נמנע בכלל מהגשה אם לא הייתי מחוייב בשל "הכנסה גבוהה". נמנע מהגשה ואז מתישהו כי יש אמנה בין רשויות המיסים היו עולים עליך והיית משלם 4% עם הצמדה על הרווחים לא כיף טוב שנה הבאה כבר תוכל להגיש נורמלי מה שהוא הכין פחות או יותר עובד

masimo12358 commented 1 year ago

לשנה הבאה זה צעד בכיוון הנכון, אבל לא מספק - יש לי גם חשבון בטריידסטיישן וגם להם צריך לפתח סקריפט דומה :(