Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

在 BackTest 的 simulate() 方法中 trade_detail['hold_volume'] 不應該有小數部分 #273

Closed
sakkyoi opened this issue Nov 22, 2023 · 4 comments
Assignees
Labels
bug Something isn't working

Comments

@sakkyoi
Copy link
Contributor

sakkyoi commented Nov 22, 2023

Describe the bug (please complete the following information)[清晰簡潔的描述 bug]
當執行完 BackTest 的 simulate() 後,呼叫 BackTest.trade_detail 會報錯:

ValidationError: 1 validation error for TradeDetail
hold_volume
  Input should be a valid integer, got a number with a fractional part [type=int_from_float, input_value=5189.385145, input_type=float]
    For further information visit https://errors.pydantic.dev/2.4/v/int_from_float

To Reproduce (please complete the following information)[一步一步說明如何重現 bug]
Steps to reproduce the behavior:

  1. 建立一個 backtest_obj,stock_id 為 2884,期間為2023/01/30~2023/10/31
  2. backtest_obj.add_strategy(ContinueHolding) 然後執行 backtest_obj.simulate()
  3. print(backtest_obj.trade_detail)
  4. 錯誤就會產生

Expected behavior (please complete the following information)[清晰簡潔的描述你預期的結果]
應該要能正常 print 出 backtest_obj.trade_detail

Screenshots[如果方便的話,利用截圖來協助說明你的問題]
None

Desktop (please complete the following information):[提供程式的執行環境]

  • OS: Windows
  • Python Version 3.10
  • FinMind package Version 1.6.3

Additional context[額外的資訊幫助釐清問題]
首先,這個問題如果用 backtest_obj._trade_detail 即可解決,但我認為這是一個不應存在的 bug,因此回報問題。
問題的產生是由於個股資料中,StockEarningsDistribution 欄位可能是一個小數數值。

# in strategies/base.py line 318
def __compute_div_income(trader, cash_div: float, stock_div: float):
    gain_stock_div = stock_div * trader.hold_volume / 10  # 此處 stock_div 為 stock_price 中 StockEarningsDistribution 欄位,可能存在小數
    gain_cash = cash_div * trader.hold_volume
    origin_cost = trader.hold_cost * trader.hold_volume
    trader.hold_volume += gain_stock_div  # 此處直接將 gain_stock_div 加入,導致持有量出現小數部分

股票股利畸零股應被直接換算成現金,因此這個值是否要變成整數部分加入 hold_volume,小數部分加入 Profit (實務上還會被扣匯費)

@linsamtw linsamtw added the bug Something isn't working label Nov 26, 2023
@linsamtw linsamtw assigned linsamtw and unassigned machineCYC Nov 26, 2023
@linsamtw
Copy link
Collaborator

@sakkyoi
我在 colab 執行,是正常的耶,能提供更多資訊嗎?
image

@sakkyoi
Copy link
Contributor Author

sakkyoi commented Mar 1, 2024

@linsamtw
抱歉回晚了,我用 colab 也執行了相同的程式碼,問題是可以再現的,以下是我的執行結果:
image

@sakkyoi
Copy link
Contributor Author

sakkyoi commented Mar 1, 2024

改成直接用 backtest._trade_detail 呼叫交易資料的話,會發現在 hold_volume 欄位中,出現了小數的結果:
image
造成這個結果的來由,是在 strategies/base.py 的第692行,TradeDetail(**row_dict) 的地方 (上圖是 base.py 中的程式碼)。

在 schema/info.py 中 TradeDetail 的 hold_volume 型態定義是 int,
另外,StockEarningsDistribution 是 float,根據 strategies/base.py 第 577 行 (與一開始的 issue 描述有些出入,現在是改用 1.6.4 版做測試):

gain_stock_div = stock_div * trader.hold_volume / 10

stock_div 根據地 554 行,是來自於 StockEarningsDistribution 欄位,這個欄位在原 issue 的測試條件中,是包含小數 0.37877029 的數值,在實務上這也是常發生的事情。這導致了最終 gain_stock_div 有可能是一個包含小數部分的 float,並且在地 580 行被加回 trader.hold_volume 中,該欄位是 int 型態,根據前面的描述,這個值在貼入 TradeDetail 的時候,會被 pydantic 檢查到,並發生錯誤。

@linsamtw
Copy link
Collaborator

已 release to 1.6.6 version

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants