fix: resolve all remaining TODOs, add dev mode auth bypass
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

- Learning engine: expand default weights from 3 to all 9 strategies
- Learning engine: resolve placeholder strategy_id with DB lookup
- Learning engine: pass strategy_sources from trade execution
- Trade executor: respect trading:paused Redis flag in RiskManager
- Portfolio sync: compute actual daily P&L from day-start snapshot
- Portfolio API: cumulative P&L from first snapshot, read pause flag
- Portfolio metrics: compute max drawdown and avg hold duration
- Add strategy_sources field to TradeExecution schema
- Add dev_mode config (TRADING_DEV_MODE) to bypass auth for local dev
- Dashboard: VITE_DEV_MODE bypasses ProtectedRoute and 401 redirects
- Vite proxy target configurable via VITE_API_TARGET
- Add top-level README.md and remaining-work-plan.md
- Update CLAUDE.md with correct counts and remove stale TODOs
- 404 tests passing

Made-with: Cursor
This commit is contained in:
Viktor Barzin 2026-02-25 22:02:25 +00:00
parent 4094e4b10f
commit a3cdd0f1a5
No known key found for this signature in database
GPG key ID: 0EB088298288D958
16 changed files with 511 additions and 45 deletions

View file

@ -231,15 +231,18 @@ class TestSyncOnce:
existing_aapl.qty = 5.0 # old qty
existing_aapl.avg_entry = 140.0 # old entry
# Day-start snapshot query (returns None = first snapshot today)
result_day_start = MagicMock()
result_day_start.scalar_one_or_none.return_value = None
result_aapl = MagicMock()
result_aapl.scalar_one_or_none.return_value = existing_aapl
result_msft = MagicMock()
result_msft.scalar_one_or_none.return_value = None
# First execute call is for the delete of stale positions;
# but within the loop, select calls come first
# Execute calls: day-start snapshot, AAPL lookup, MSFT lookup, DELETE
mock_session.execute = AsyncMock(
side_effect=[result_aapl, result_msft, MagicMock()]
side_effect=[result_day_start, result_aapl, result_msft, MagicMock()]
)
await _sync_once(mock_broker, mock_session_factory)

View file

@ -126,6 +126,51 @@ class TestRiskCheckPasses:
assert reason == "approved"
class TestRiskCheckTradingPaused:
"""Risk check fails when trading is paused via Redis flag."""
@pytest.mark.asyncio
async def test_paused_flag_rejects(self):
config = _make_config()
broker = _mock_broker()
redis_mock = AsyncMock()
redis_mock.get = AsyncMock(return_value=b"1")
rm = RiskManager(config, broker, redis=redis_mock)
signal = _make_signal()
with patch.object(RiskManager, "_is_market_hours", return_value=True):
approved, reason = await rm.check_risk(signal)
assert approved is False
assert reason == "trading_paused"
@pytest.mark.asyncio
async def test_no_pause_flag_passes_through(self):
config = _make_config()
broker = _mock_broker(positions=[], account=_make_account(100_000))
redis_mock = AsyncMock()
redis_mock.get = AsyncMock(return_value=None)
rm = RiskManager(config, broker, redis=redis_mock)
signal = _make_signal()
with patch.object(RiskManager, "_is_market_hours", return_value=True):
approved, reason = await rm.check_risk(signal)
assert approved is True
@pytest.mark.asyncio
async def test_no_redis_skips_pause_check(self):
config = _make_config()
broker = _mock_broker(positions=[], account=_make_account(100_000))
rm = RiskManager(config, broker, redis=None)
signal = _make_signal()
with patch.object(RiskManager, "_is_market_hours", return_value=True):
approved, reason = await rm.check_risk(signal)
assert approved is True
# ---------------------------------------------------------------------------
# RiskManager — max positions exceeded
# ---------------------------------------------------------------------------