BackcastPro 0.6.2__tar.gz → 0.6.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. backcastpro-0.6.4/.claude/hand-off-download.md +59 -0
  2. {backcastpro-0.6.2 → backcastpro-0.6.4}/.claude/settings.local.json +14 -1
  3. {backcastpro-0.6.2 → backcastpro-0.6.4}/.github/workflows/publish-pypi.yml +1 -0
  4. {backcastpro-0.6.2 → backcastpro-0.6.4}/PKG-INFO +1 -1
  5. {backcastpro-0.6.2 → backcastpro-0.6.4}/pyproject.toml +2 -3
  6. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/__init__.py +2 -0
  7. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/_broker.py +9 -2
  8. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/backtest.py +5 -4
  9. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/position.py +24 -1
  10. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_backtest_api.py +2 -2
  11. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_backtest_step_loop.py +2 -9
  12. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_position.py +25 -0
  13. {backcastpro-0.6.2 → backcastpro-0.6.4}/uv.lock +556 -1
  14. {backcastpro-0.6.2 → backcastpro-0.6.4}/.coverage +0 -0
  15. {backcastpro-0.6.2 → backcastpro-0.6.4}/.gcloudignore +0 -0
  16. {backcastpro-0.6.2 → backcastpro-0.6.4}/.github/workflows/docs.yml +0 -0
  17. {backcastpro-0.6.2 → backcastpro-0.6.4}/.github/workflows/publish-dockerhub.yml +0 -0
  18. {backcastpro-0.6.2 → backcastpro-0.6.4}/.gitignore +0 -0
  19. {backcastpro-0.6.2 → backcastpro-0.6.4}/.python-version +0 -0
  20. {backcastpro-0.6.2 → backcastpro-0.6.4}/.vscode/launch.json +0 -0
  21. {backcastpro-0.6.2 → backcastpro-0.6.4}/.vscode/settings.json +0 -0
  22. {backcastpro-0.6.2 → backcastpro-0.6.4}/AGENTS.md +0 -0
  23. {backcastpro-0.6.2 → backcastpro-0.6.4}/CLAUDE.md +0 -0
  24. {backcastpro-0.6.2 → backcastpro-0.6.4}/README.md +0 -0
  25. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-job/.env.example +0 -0
  26. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-job/Dockerfile +0 -0
  27. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-job/update_stocks_price.py +0 -0
  28. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-run/.env.example +0 -0
  29. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-run/Dockerfile +0 -0
  30. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-run/main.py +0 -0
  31. {backcastpro-0.6.2 → backcastpro-0.6.4}/cloud-run/requirements.txt +0 -0
  32. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/cloud-run-updater.md +0 -0
  33. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/design-decisions.md +0 -0
  34. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/developer-guide.md +0 -0
  35. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/examples/SmaCross.py +0 -0
  36. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/examples/marimo_replay.py +0 -0
  37. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/execution-logic-report.md +0 -0
  38. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/how-to-deploy-to-PyPI.md +0 -0
  39. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/img/logo.drawio.svg +0 -0
  40. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/index.md +0 -0
  41. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/privacy.md +0 -0
  42. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/troubleshooting.md +0 -0
  43. {backcastpro-0.6.2 → backcastpro-0.6.4}/docs/tutorial.md +0 -0
  44. {backcastpro-0.6.2 → backcastpro-0.6.4}/mkdocs.yml +0 -0
  45. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/_stats.py +0 -0
  46. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/__init__.py +0 -0
  47. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/cloud_run_client.py +0 -0
  48. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/db_manager.py +0 -0
  49. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/db_stocks_board.py +0 -0
  50. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/db_stocks_daily.py +0 -0
  51. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/db_stocks_info.py +0 -0
  52. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/stocks_board.py +0 -0
  53. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/stocks_info.py +0 -0
  54. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/api/stocks_price.py +0 -0
  55. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/order.py +0 -0
  56. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/BackcastPro/trade.py +0 -0
  57. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/__init__.py +0 -0
  58. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/__init__.py +0 -0
  59. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/lib/__init__.py +0 -0
  60. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/lib/e_api.py +0 -0
  61. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/lib/jquants.py +0 -0
  62. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/lib/kabusap.py +0 -0
  63. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/lib/stooq.py +0 -0
  64. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/lib/util.py +0 -0
  65. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/stocks_board.py +0 -0
  66. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/stocks_info.py +0 -0
  67. {backcastpro-0.6.2 → backcastpro-0.6.4}/src/trading_data/stocks_price.py +0 -0
  68. {backcastpro-0.6.2 → backcastpro-0.6.4}/test_output.txt +0 -0
  69. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/import_equities_trades.py +0 -0
  70. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/import_minute_bars.py +0 -0
  71. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_backtest_set_data.py +0 -0
  72. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_cloud_run_client.py +0 -0
  73. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_cloud_run_main.py +0 -0
  74. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_db_manager.py +0 -0
  75. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_db_stocks_board.py +0 -0
  76. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_db_stocks_daily.py +0 -0
  77. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_db_stocks_info.py +0 -0
  78. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_e_api.py +0 -0
  79. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_j-quants.py +0 -0
  80. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_kabusap.py +0 -0
  81. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_order.py +0 -0
  82. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_relative_size_option_c.py +0 -0
  83. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_stats.py +0 -0
  84. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_stocks_board_wrapper.py +0 -0
  85. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_stocks_info_wrapper.py +0 -0
  86. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_stocks_price_wrapper.py +0 -0
  87. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_stooq.py +0 -0
  88. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_trade.py +0 -0
  89. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_update_stocks_price.py +0 -0
  90. {backcastpro-0.6.2 → backcastpro-0.6.4}/tests/test_util.py +0 -0
@@ -0,0 +1,59 @@
1
+ # J-Quants データダウンロード引継ぎ資料
2
+
3
+ ## 目的
4
+ J-Quants Webサイトから2026年1月(月次分)および2月(日次分)の以下のデータを `S:\j-quants` にダウンロードする。
5
+ - 株価四本値 (Daily Bars)
6
+ - 株価分足 (Minute Bars)
7
+ - 株価ティック (Trades)
8
+
9
+ ## 作業状況
10
+ - ブラウザエージェントは既に J-Quants ダッシュボードにログイン済みです。
11
+ - ファイルキーのパターンは特定済みですが、取得用URL(署名付きURL)は **300秒(5分)で有効期限が切れる** ため、一括でURLを取得してからダウンロードするのではなく、取得後すぐにダウンロードする必要があります。
12
+
13
+ ## 対象ファイルリスト (2026年)
14
+
15
+ ### 1. 2026年1月 (月次一括)
16
+ - `equities_bars_daily_202601.csv.gz`
17
+ - `equities_bars_minute_202601.csv.gz`
18
+ - `equities_trades_202601.csv.gz`
19
+
20
+ ### 2. 2026年2月 (日次)
21
+ 日付: 02, 03, 04, 05, 06, 09, 10, 12, 13, 16, 17, 18
22
+ (例: `equities_bars_daily_20260218.csv.gz`)
23
+
24
+ ## URL取得方法 (ブラウザエージェントへの依頼)
25
+ ダッシュボードページ (`https://jpx-jquants.com/ja/dashboard/downloads/price-data/stocks`) で以下のJavaScriptを実行してURLを取得してください。
26
+
27
+ ```javascript
28
+ (async () => {
29
+ const key = "対象のファイルキー"; // 例: equities/bars/daily/live/equities_bars_daily_20260218.csv.gz
30
+ const res = await fetch('/api/trpc/bulk.bulkGet?batch=1', {
31
+ method: 'POST',
32
+ headers: { 'Content-Type': 'application/json' },
33
+ body: JSON.stringify({ "0": { "json": { "key": key } } })
34
+ });
35
+ const data = await res.json();
36
+ return data[0]?.result?.data?.json; // 署名付きURLが返る
37
+ })()
38
+ ```
39
+
40
+ ### キーの指定ルール
41
+ - **2026/01**: `equities/[カテゴリ]/historical/2026/equities_[名前]_202601.csv.gz`
42
+ - **2026/02**: `equities/[カテゴリ]/live/equities_[名前]_202602[日付].csv.gz`
43
+ - カテゴリ/名前の対応:
44
+ - 株価四本値: `bars/daily` / `bars_daily`
45
+ - 株価分足: `bars/minute` / `bars_minute`
46
+ - 株価ティック: `trades` / `trades`
47
+
48
+ ## ダウンロード実行方法 (PowerShell)
49
+ 取得したURL(`$URL`)を使って、Windowsの `curl.exe` で保存します。
50
+
51
+ ```powershell
52
+ curl.exe -L "$URL" -o "S:\j-quants\filename.csv.gz"
53
+ ```
54
+
55
+ ## 注意事項
56
+ - **有効期限**: URLは5分で切れます。1ファイルずつ「URL取得 -> 即座にダウンロード」のサイクルを繰り返してください。
57
+ - **保存先**: `S:\j-quants` 固定です。
58
+ - **2月のキー**: 現在(2026/02)は `live` パスにあるものが多いですが、失敗した場合は `historical/2026/02/` パスを試してください。
59
+ - **残存ファイル**: `S:\j-quants` に容量0のファイルや無効なXMLエラーが含まれるファイルがある場合は上書きしてください。
@@ -11,7 +11,20 @@
11
11
  "Bash(git -C \"D:/Documents/marimo\" show sasa/markdown:\"frontend/src/components/editor/renderers/cell-3d-wrapper.css\")",
12
12
  "Bash(pushd:*)",
13
13
  "Bash(git stash:*)",
14
- "Read(//c/Users/sasac/AppData/Roaming/marimo/**)"
14
+ "Read(//c/Users/sasac/AppData/Roaming/marimo/**)",
15
+ "Bash(npx:*)",
16
+ "Bash(where:*)",
17
+ "Bash(node -e \"console.log\\(require\\(''child_process''\\).execSync\\(''npx.cmd @playwright/mcp@latest --help''\\).toString\\(\\).substring\\(0, 200\\)\\)\")",
18
+ "Bash(npm ls:*)",
19
+ "Bash(npm cache:*)",
20
+ "Bash(npx.cmd:*)",
21
+ "Bash(claude mcp add:*)",
22
+ "Bash(CLAUDE_CODE_GIT_BASH_PATH=\"/c/Program Files/Git/bin/bash.exe\" claude mcp add --transport stdio --scope user playwright -- npx.cmd @playwright/mcp@latest)",
23
+ "Bash(node -e \":*)",
24
+ "Bash(node -e:*)",
25
+ "mcp__playwright__browser_navigate",
26
+ "mcp__playwright__browser_take_screenshot",
27
+ "Read(//c/Users/sasai/Downloads/logs_57994019762/**)"
15
28
  ],
16
29
  "additionalDirectories": [
17
30
  "d:\\Documents\\BackcastPro",
@@ -43,6 +43,7 @@ jobs:
43
43
 
44
44
  - name: TestPyPIにパブリッシュ
45
45
  if: steps.check_pypi.outputs.exists == 'false'
46
+ continue-on-error: true
46
47
  run: uv publish --publish-url https://test.pypi.org/legacy/
47
48
  env:
48
49
  UV_PUBLISH_TOKEN: ${{ secrets.TEST_PYPI_TOKEN }}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BackcastPro
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: トレーディング戦略のためのPythonバックテストライブラリ
5
5
  Project-URL: Homepage, https://github.com/botterYosuke/BackcastPro/
6
6
  Project-URL: Issues, https://github.com/botterYosuke/BackcastPro/issues
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "BackcastPro"
7
- version = "0.6.2"
7
+ version = "0.6.4"
8
8
  authors = [
9
9
  { name="botterYosuke" },
10
10
  ]
@@ -30,7 +30,7 @@ dependencies = [
30
30
  "duckdb>=0.9.0",
31
31
  "plotly>=5.0.0",
32
32
  "anywidget>=0.9.21",
33
- "msgpack>=1.0.0",
33
+ "msgpack>=1.0.0"
34
34
  ]
35
35
 
36
36
  [project.optional-dependencies]
@@ -70,4 +70,3 @@ cloud-job = [
70
70
  "requests>=2.25.0",
71
71
  "yfinance>=0.2.0",
72
72
  ]
73
-
@@ -11,6 +11,7 @@ BackcastPro をご利用いただきありがとうございます。
11
11
  ※ 使い始めはチュートリアル → 詳細はAPIリファレンスをご参照ください。
12
12
  """
13
13
  from .backtest import Backtest
14
+ from ._broker import BankruptError
14
15
 
15
16
  from .api.stocks_price import get_stock_daily
16
17
  from .api.stocks_board import get_stock_board
@@ -18,6 +19,7 @@ from .api.stocks_info import get_stock_info
18
19
 
19
20
  __all__ = [
20
21
  'Backtest',
22
+ 'BankruptError',
21
23
  'get_stock_daily',
22
24
  'get_stock_board',
23
25
  'get_stock_info',
@@ -17,6 +17,11 @@ if TYPE_CHECKING:
17
17
  pass
18
18
 
19
19
 
20
+ class BankruptError(Exception):
21
+ """エクイティが0以下になった場合に発生する破産例外"""
22
+ pass
23
+
24
+
20
25
  class _Broker:
21
26
  """
22
27
  バックテストにおける証券取引の実行、注文管理、ポジション管理、損益計算を担当します。
@@ -199,7 +204,9 @@ class _Broker:
199
204
  price = self._data[trade.code].Close.iloc[-1]
200
205
  self._close_trade(trade, price, self._current_time)
201
206
  self._cash = 0
202
- raise Exception
207
+ raise BankruptError(
208
+ f"エクイティが0以下になりました (equity={equity:.2f})"
209
+ )
203
210
 
204
211
  def _process_orders(self):
205
212
  data = self._data
@@ -254,7 +261,7 @@ class _Broker:
254
261
  else:
255
262
  # 成行注文(Market-if-touched / market order)
256
263
  # 条件付き注文は常に次の始値で
257
- prev_close = df.Close.iloc[-2]
264
+ prev_close = df.Close.iloc[-2] if len(df) >= 2 else df.Close.iloc[-1]
258
265
  price = prev_close if self._trade_on_close and not order.is_contingent else open
259
266
  if stop_price:
260
267
  price = max(price, stop_price) if order.is_long else min(price, stop_price)
@@ -11,8 +11,9 @@ from typing import Callable, List, Optional, Tuple, Union
11
11
  import numpy as np
12
12
  import pandas as pd
13
13
 
14
- from ._broker import _Broker
14
+ from ._broker import _Broker, BankruptError
15
15
  from ._stats import compute_stats
16
+ from .position import Position
16
17
 
17
18
 
18
19
  class Backtest:
@@ -303,7 +304,7 @@ class Backtest:
303
304
  try:
304
305
  self._broker_instance._data = self._current_data
305
306
  self._broker_instance.next(current_time)
306
- except Exception:
307
+ except BankruptError:
307
308
  self._is_finished = True
308
309
  return False
309
310
 
@@ -456,7 +457,7 @@ class Backtest:
456
457
  このプロパティは後方互換性のために残されています。
457
458
  """
458
459
  if not self._is_started or self._broker_instance is None:
459
- return 0
460
+ return Position._empty()
460
461
  return self._broker_instance.position
461
462
 
462
463
  def position_of(self, code: str) -> int:
@@ -546,7 +547,7 @@ class Backtest:
546
547
  "progress": float(self.progress),
547
548
  "equity": float(self.equity),
548
549
  "cash": float(self.cash),
549
- "position": self.position,
550
+ "position": self.position.size,
550
551
  "positions": positions,
551
552
  "closed_trades": len(self.closed_trades),
552
553
  "step_index": self.step_index,
@@ -19,25 +19,36 @@ class Position:
19
19
  ... # ポジションがあります(ロングまたはショート)
20
20
  """
21
21
 
22
- def __init__(self, broker: '_Broker'):
22
+ def __init__(self, broker: '_Broker | None'):
23
23
  self.__broker = broker
24
24
 
25
+ @classmethod
26
+ def _empty(cls) -> 'Position':
27
+ """ブローカー未設定時のゼロポジションを返す。"""
28
+ return cls(broker=None)
29
+
25
30
  def __bool__(self):
26
31
  return self.size != 0
27
32
 
28
33
  @property
29
34
  def size(self) -> float:
30
35
  """資産単位でのポジションサイズ。ショートポジションの場合は負の値。"""
36
+ if self.__broker is None:
37
+ return 0
31
38
  return sum(trade.size for trade in self.__broker.trades)
32
39
 
33
40
  @property
34
41
  def pl(self) -> float:
35
42
  """現在のポジションの利益(正)または損失(負)を現金単位で。"""
43
+ if self.__broker is None:
44
+ return 0
36
45
  return sum(trade.pl for trade in self.__broker.trades)
37
46
 
38
47
  @property
39
48
  def pl_pct(self) -> float:
40
49
  """現在のポジションの利益(正)または損失(負)をパーセントで。"""
50
+ if self.__broker is None:
51
+ return 0
41
52
  total_invested = sum(trade.entry_price * abs(trade.size) for trade in self.__broker.trades)
42
53
  return (self.pl / total_invested) * 100 if total_invested else 0
43
54
 
@@ -51,10 +62,22 @@ class Position:
51
62
  """ポジションがショート(ポジションサイズが負)の場合True。"""
52
63
  return self.size < 0
53
64
 
65
+ def to_dict(self) -> dict:
66
+ """ポジション情報をシリアライズ可能な辞書として返す。"""
67
+ return {
68
+ "size": self.size,
69
+ "pl": self.pl,
70
+ "pl_pct": self.pl_pct,
71
+ "is_long": self.is_long,
72
+ "is_short": self.is_short,
73
+ }
74
+
54
75
  def close(self, portion: float = 1.):
55
76
  """
56
77
  各アクティブな取引の「一部」を決済することで、ポジションの一部を決済します。詳細は「Trade.close」を参照してください。
57
78
  """
79
+ if self.__broker is None:
80
+ return
58
81
  for trade in self.__broker.trades:
59
82
  trade.close(portion)
60
83
 
@@ -1,11 +1,11 @@
1
1
  """
2
2
  TDD Tests for BackcastPro Public API Extensions
3
3
 
4
- Goal: Add public API for marimo-independent state access and callback mechanism.
4
+ Goal: Add public API for state access and callback mechanism.
5
5
 
6
6
  Phase 1 Tests:
7
7
  1. step_index property - read-only access to _step_index
8
- 2. get_state_snapshot() - returns current state as dict (marimo-independent)
8
+ 2. get_state_snapshot() - returns current state as dict
9
9
  3. add_trade_callback() - multiple callback registration for trade events
10
10
 
11
11
  These tests follow TDD (Red-Green-Refactor):
@@ -2,16 +2,12 @@
2
2
  TDD Tests for Backtest Step Loop Mechanism
3
3
 
4
4
  Goal: Verify that the step loop mechanism works correctly for game loop integration.
5
- This tests the core backtest step behavior that the marimo game loop relies on:
6
5
 
7
6
  1. bt.step() increments _step_index correctly
8
7
  2. bt.step() returns True while there are more steps
9
8
  3. bt.step() returns False when finished
10
9
  4. bt.is_finished reflects the correct state
11
10
  5. Multiple step() calls in a loop work correctly
12
-
13
- These tests do NOT test marimo UI parts (mo.state, mo.Thread),
14
- but focus on the underlying BackcastPro logic.
15
11
  """
16
12
 
17
13
  import pytest
@@ -332,7 +328,7 @@ class TestStepLoop:
332
328
 
333
329
  class TestGameLoopSimulation:
334
330
  """
335
- Simulate the marimo game loop pattern:
331
+ Simulate the game loop pattern:
336
332
 
337
333
  def _game_loop():
338
334
  while bt.is_finished == False:
@@ -346,7 +342,7 @@ class TestGameLoopSimulation:
346
342
 
347
343
  def test_game_loop_pattern_basic(self):
348
344
  """
349
- Test the basic game loop pattern without marimo state.
345
+ Test the basic game loop pattern.
350
346
  """
351
347
  code = "TEST"
352
348
  df = create_sample_df(10)
@@ -625,9 +621,6 @@ class TestMultipleStocksStepLoop:
625
621
  class TestStrategyDataAccess:
626
622
  """
627
623
  Test that strategy can access bt.data during step().
628
-
629
- This is critical for the marimo game loop where the strategy
630
- needs to read current data on each step.
631
624
  """
632
625
 
633
626
  def test_strategy_can_access_data_on_first_step(self):
@@ -30,6 +30,31 @@ def _create_bt(days=30, cash=1000000):
30
30
  return bt
31
31
 
32
32
 
33
+ class TestPositionBeforeStart:
34
+ """未開始状態での Position テスト"""
35
+
36
+ def test_position_before_start_is_falsy(self):
37
+ """未開始状態のポジションはFalse"""
38
+ bt = _create_bt()
39
+ assert not bt.position
40
+
41
+ def test_position_before_start_size_zero(self):
42
+ """未開始状態のポジションサイズは0"""
43
+ bt = _create_bt()
44
+ assert bt.position.size == 0
45
+
46
+ def test_position_before_start_to_dict(self):
47
+ """未開始状態でも to_dict() が正常に動作"""
48
+ bt = _create_bt()
49
+ d = bt.position.to_dict()
50
+ assert d == {"size": 0, "pl": 0, "pl_pct": 0, "is_long": False, "is_short": False}
51
+
52
+ def test_position_before_start_close_noop(self):
53
+ """未開始状態での close() はエラーなし"""
54
+ bt = _create_bt()
55
+ bt.position.close() # should not raise
56
+
57
+
33
58
  class TestPositionBool:
34
59
  """Position のブール値テスト"""
35
60