feat: Add Phase 4 advanced metrics and components

- Add advanced metrics dashboard with trade analytics
- Add new trading components (EntryTypeAnalysis, MultiDayPositionTracker, NewsEventTracker, etc.)
- Add strategy mode selector and trend confirmation
- Add risk automation panel and slippage correlation analysis
- Add daily trading plan enhancements with modal components
- Add custom hooks (useApi, useLocalStorage, useAdvancedTradeMetrics)
- Add broker service integration and trading API
- Add test setup and vitest configuration
- Include parquet data files for live market data
- Add comprehensive documentation in docs/ folder
This commit is contained in:
Krikorios
2025-11-27 10:23:58 +02:00
parent b5e2b02cb8
commit 48e60d015f
2019 changed files with 39793 additions and 257 deletions
+125
View File
@@ -0,0 +1,125 @@
#!/usr/bin/env python3
"""Utility for priming LiveStore and flushing sample candles to parquet.
This script spins up the configured streaming data provider (local simulator by
default), keeps it running for a short window, and forces flushes so that
parquet partitions are materialized under ``data/parquet/live``.
"""
from __future__ import annotations
import argparse
import asyncio
import contextlib
import os
from pathlib import Path
from typing import Tuple
import sys
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
from app.streaming.live_store import live_store
from app.streaming.data_provider import data_provider
def _parse_args() -> argparse.Namespace:
parser = argparse.ArgumentParser(description="Prime LiveStore and flush parquet output for a symbol/timeframe")
parser.add_argument("--symbol", default="XAUUSD", help="Symbol to stream (default: XAUUSD)")
parser.add_argument("--timeframe", default="1m", help="Timeframe to stream (default: 1m)")
parser.add_argument(
"--duration",
type=int,
default=120,
help="How long to keep the stream hot (seconds, default: 120)",
)
parser.add_argument(
"--flush-interval",
type=int,
default=30,
help="How often to flush to parquet during the run (seconds, default: 30)",
)
return parser.parse_args()
async def _maybe_consume(queue: asyncio.Queue) -> None:
"""Continuously drain the queue so it doesn't grow unbounded."""
try:
while True:
item = await queue.get()
if item is None:
break
except asyncio.CancelledError:
pass
async def _prime_store(symbol: str, timeframe: str, duration: int, flush_interval: int) -> Tuple[int, Path]:
symbol = symbol.upper().replace("/", "")
timeframe = timeframe.lower()
os.makedirs(live_store.root, exist_ok=True)
target_dir = Path(live_store.root) / symbol / timeframe
target_dir.mkdir(parents=True, exist_ok=True)
queue, unsubscribe = await data_provider.subscribe(symbol, timeframe=timeframe)
consumer_task = asyncio.create_task(_maybe_consume(queue))
try:
loop = asyncio.get_running_loop()
start = loop.time()
next_flush = flush_interval
while True:
elapsed = loop.time() - start
if elapsed >= duration:
break
sleep_for = max(0.0, min(next_flush - elapsed, 1.0))
await asyncio.sleep(sleep_for)
elapsed = loop.time() - start
if elapsed + 1e-6 >= next_flush:
await live_store.flush_parquet()
next_flush += flush_interval
finally:
await unsubscribe()
consumer_task.cancel()
with contextlib.suppress(Exception):
await consumer_task
await live_store.flush_parquet()
history = live_store.get_history(symbol, timeframe)
return len(history), target_dir
async def _async_main() -> None:
args = _parse_args()
bars, target_dir = await _prime_store(
symbol=args.symbol,
timeframe=args.timeframe,
duration=args.duration,
flush_interval=args.flush_interval,
)
partitions = sorted(target_dir.glob("date=*"))
total_files = sum(len(list(part.glob("*.parquet"))) for part in partitions)
latest_partition = partitions[-1] if partitions else None
print("\nLiveStore setup complete:")
print(f" Symbol: {args.symbol.upper().replace('/', '')}")
print(f" Timeframe: {args.timeframe}")
print(f" Bars in memory: {bars}")
print(f" Root directory: {target_dir}")
print(f" Partitions found: {len(partitions)}")
print(f" Parquet files: {total_files}")
if latest_partition:
print(f" Latest partition: {latest_partition}")
def main() -> None:
asyncio.run(_async_main())
if __name__ == "__main__":
main()