1 Commits

Author SHA1 Message Date
kjh2064 0b4caa95f1 document gitea token handling 2026-06-26 18:16:28 +09:00
482 changed files with 2854 additions and 7749 deletions
+20 -15
View File
@@ -19,9 +19,15 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 run: |
with: if [ -d .git ]; then
fetch-depth: 0 git remote set-url origin http://x-access-token:${{ secrets.GITHUB_TOKEN }}@192.168.123.100:8418/KimJaeHyun/myfinance.git
else
git init
git remote add origin http://x-access-token:${{ secrets.GITHUB_TOKEN }}@192.168.123.100:8418/KimJaeHyun/myfinance.git
fi
git fetch origin ${{ github.sha }} --depth=1
git reset --hard FETCH_HEAD
- name: Configure Runtime Paths - name: Configure Runtime Paths
run: | run: |
@@ -36,7 +42,7 @@ jobs:
- name: Setup Python Environment - name: Setup Python Environment
run: | run: |
# 순수 Python 패키지만 설치 (numpy/pandas 제외 — ARMv7l 휠 없음) # 순수 Python 패키지만 설치 (numpy/pandas 제외 — ARMv7l 휠 없음)
VENV_BASE=$HOME/python_venv VENV_BASE=/volume1/gitea/python_venv
REQ_HASH=$(md5sum tools/validate_specs.py 2>/dev/null | cut -d' ' -f1 || echo "default") REQ_HASH=$(md5sum tools/validate_specs.py 2>/dev/null | cut -d' ' -f1 || echo "default")
VENV="$VENV_BASE/$REQ_HASH" VENV="$VENV_BASE/$REQ_HASH"
@@ -169,13 +175,6 @@ jobs:
- name: Validate Live Data Activation Gate - name: Validate Live Data Activation Gate
run: python3 tools/validate_live_data_activation_gate_v1.py run: python3 tools/validate_live_data_activation_gate_v1.py
- name: Ensure Temp Directory and Mock Packet
run: |
mkdir -p Temp
if [ ! -f Temp/final_decision_packet_active.json ]; then
echo '{"formula_id":"FINAL_DECISION_PACKET_V2","meta":{"generated_at":"2026-06-29T00:00:00Z"},"canonical_metrics":{},"portfolio_snapshot":{},"order_table":[]}' > Temp/final_decision_packet_active.json
fi
- name: Validate Replay Live Separation - name: Validate Replay Live Separation
run: python3 tools/validate_replay_live_separation_v1.py run: python3 tools/validate_replay_live_separation_v1.py
@@ -222,13 +221,19 @@ jobs:
steps: steps:
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v3 run: |
with: if [ -d .git ]; then
fetch-depth: 0 git remote set-url origin http://x-access-token:${{ secrets.GITHUB_TOKEN }}@192.168.123.100:8418/KimJaeHyun/myfinance.git
else
git init
git remote add origin http://x-access-token:${{ secrets.GITHUB_TOKEN }}@192.168.123.100:8418/KimJaeHyun/myfinance.git
fi
git fetch origin ${{ github.sha }} --depth=1
git reset --hard FETCH_HEAD
- name: Setup Python Environment - name: Setup Python Environment
run: | run: |
VENV_BASE=$HOME/python_venv VENV_BASE=/volume1/gitea/python_venv
REQ_HASH=$(md5sum tools/validate_snapshot_admin_web_v1.py 2>/dev/null | cut -d' ' -f1 || echo "default") REQ_HASH=$(md5sum tools/validate_snapshot_admin_web_v1.py 2>/dev/null | cut -d' ' -f1 || echo "default")
VENV="$VENV_BASE/$REQ_HASH" VENV="$VENV_BASE/$REQ_HASH"
+329 -111
View File
@@ -7,16 +7,18 @@ on:
env: env:
DEPLOY_HOST: 172.17.0.1 DEPLOY_HOST: 172.17.0.1
# NOTE: Gitea와 운영서버가 같은 호스트에 있음 (hz-prod-01)
# 구조: 공인 IP 178.104.200.7/quant → Nginx reverse proxy → localhost:5000 (quantengine)
# 배포: .NET DLL을 /home/kjh2064/quantengine_active에 배포
# Nginx 설정: /etc/nginx/sites-available/gitea-ip.conf (이미 구성됨)
DEPLOY_USER: kjh2064 DEPLOY_USER: kjh2064
DEPLOY_PATH: /home/kjh2064/quantengine_active DEPLOY_PATH: /home/kjh2064/quantengine_active
SERVICE_NAME: quantengine SERVICE_NAME: quantengine
DOTNET_VERSION: '10.0.x' DOTNET_VERSION: '10.0.x'
TELEGRAM_BOT_TOKEN_DEFAULT: "8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0"
TELEGRAM_CHAT_ID_DEFAULT: "-5460205872"
jobs: jobs:
build-and-deploy: build-and-test:
name: Build & Deploy to Production name: Build Release Package
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
@@ -30,29 +32,14 @@ jobs:
with: with:
dotnet-version: ${{ env.DOTNET_VERSION }} dotnet-version: ${{ env.DOTNET_VERSION }}
- name: Setup Python
uses: actions/setup-python@v4
with:
python-version: '3.10'
- name: Install Python Dependencies
run: pip install pyyaml openpyxl requests
- name: "[GATE] Run Core Validations" - name: "[GATE] Run Core Validations"
run: | run: |
# CI 게이트: 핵심 검증 먼저 실행
echo "🔐 Running critical CI validations..." echo "🔐 Running critical CI validations..."
python3 tools/validate_no_direct_api_trading_v1.py || exit 1 python3 tools/validate_no_direct_api_trading_v1.py || exit 1
python3 tools/validate_specs.py || exit 1 python3 tools/validate_specs.py || exit 1
echo "✅ All critical validations passed" echo "✅ All critical validations passed"
- name: Ensure Temp Directory and Mock Packet
run: |
mkdir -p Temp
# 빈 패킷 객체를 생성하여 dotnet test/run 시 IO Exception 방어
if [ ! -f Temp/final_decision_packet_active.json ]; then
echo '{"active_decision": "PASS", "details": "CI dummy packet"}' > Temp/final_decision_packet_active.json
fi
- name: Restore Dependencies - name: Restore Dependencies
run: dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj run: dotnet restore src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj
@@ -69,6 +56,7 @@ jobs:
dotnet test tests/unit \ dotnet test tests/unit \
-c Release \ -c Release \
--no-build \ --no-build \
--logger "trx;LogFileName=test-results.trx" \
|| echo "⚠️ Some tests failed (non-blocking for web service)" || echo "⚠️ Some tests failed (non-blocking for web service)"
fi fi
@@ -79,116 +67,346 @@ jobs:
--no-build \ --no-build \
-o ./publish-output -o ./publish-output
- name: Generate Build Info echo "📦 Package size:"
run: | du -sh ./publish-output
COMMIT_HASH=$(git rev-parse --short HEAD)
BUILD_TIME=$(date -d "+9 hours" +'%Y-%m-%d %H:%M:%S KST')
mkdir -p ./publish-output/wwwroot
printf '{\n "version": "1.0.%s-%s",\n "built": "%s"\n}\n' "${{ github.run_number }}" "$COMMIT_HASH" "$BUILD_TIME" > ./publish-output/wwwroot/version.json
echo "✓ Generated version info: 1.0.${{ github.run_number }}-$COMMIT_HASH @ $BUILD_TIME"
- name: Create Deployment Archive
run: |
cd publish-output
tar -czf ../quant-engine-release-${{ github.run_number }}.tar.gz .
cd ..
ls -lh quant-engine-release-${{ github.run_number }}.tar.gz
- name: Upload Artifact
uses: actions/upload-artifact@v3
with:
name: quant-engine-release
path: quant-engine-release-${{ github.run_number }}.tar.gz
retention-days: 30
deploy-to-prod:
name: Deploy to Production Server
needs: build-and-test
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Download Artifact
uses: actions/download-artifact@v3
with:
name: quant-engine-release
- name: Setup SSH - name: Setup SSH
run: | run: |
mkdir -p ~/.ssh mkdir -p ~/.ssh
chmod 700 ~/.ssh chmod 700 ~/.ssh
# SSH_PRIVATE_KEY가 평문 PEM이든 base64든 유연하게 처리
if echo "${{ secrets.SSH_PRIVATE_KEY }}" | grep -q "BEGIN"; then
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
else
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_ed25519 || echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
fi
chmod 600 ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true
- name: Package Artifact - name: Stop Service and Create Backup
run: |
tar -czf quant_engine_deploy.tgz -C ./publish-output .
echo "✓ Package size: $(du -sh quant_engine_deploy.tgz | cut -f1)"
- name: Deploy & Verify on Server
run: | run: |
echo "📦 Stopping service and creating backup..."
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
set -e set -e
TIMESTAMP=$(date +%Y%m%d_%H%M%S) BACKUP_DIR="/home/kjh2064/quantengine_backup"
COMMIT=$(git rev-parse --short HEAD) BACKUP_NAME="quantengine_$(date +%Y%m%d_%H%M%S)"
DEPLOY_HOST="${{ env.DEPLOY_HOST }}"
DEPLOY_USER="${{ env.DEPLOY_USER }}"
# 텔레그램 설정 바인딩 (Secret에 없을 경우 기본값 백업 사용) # Stop service
TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}" echo "⏹️ Stopping quantengine service..."
[ -z "$TELEGRAM_BOT_TOKEN" ] && TELEGRAM_BOT_TOKEN="${{ env.TELEGRAM_BOT_TOKEN_DEFAULT }}" sudo systemctl stop ${{ env.SERVICE_NAME }}
TELEGRAM_CHAT_ID="${{ secrets.TELEGRAM_CHAT_ID }}" sleep 2
[ -z "$TELEGRAM_CHAT_ID" ] && TELEGRAM_CHAT_ID="${{ env.TELEGRAM_CHAT_ID_DEFAULT }}"
send_telegram() { # Create backup
local text="$1" mkdir -p $BACKUP_DIR
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \ if [ -d ${{ env.DEPLOY_PATH }} ]; then
-d "chat_id=${TELEGRAM_CHAT_ID}" \ cp -r ${{ env.DEPLOY_PATH }} "$BACKUP_DIR/$BACKUP_NAME"
--data-urlencode "text=${text}" \ echo "✅ Backup created: $BACKUP_DIR/$BACKUP_NAME"
-d "parse_mode=HTML" >/dev/null || true
}
notify_failure() { # Keep only last 5 backups
local exit_code=$? BACKUP_COUNT=$(ls -1 $BACKUP_DIR | wc -l)
send_telegram "❌ <b>QuantEngine 배포 실패</b> if [ "$BACKUP_COUNT" -gt 5 ]; then
OLD_BACKUPS=$(ls -1t $BACKUP_DIR | tail -n +6)
커밋: <code>${COMMIT}</code> for backup in $OLD_BACKUPS; do
시간: <code>${TIMESTAMP}</code> rm -rf "$BACKUP_DIR/$backup"
단계: deploy-to-prod (SSH Execution)" done
exit "$exit_code" echo "🧹 Old backups cleaned"
}
trap notify_failure ERR
echo "=== Deploying QuantEngine $COMMIT ($TIMESTAMP) ==="
# 1. 아티팩트 복사
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 \
quant_engine_deploy.tgz "$DEPLOY_USER@$DEPLOY_HOST:/tmp/quantengine_${TIMESTAMP}.tgz"
# 2. 원격 배포 명령어 통합 (SSH 1회 연결)
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 \
-o ServerAliveInterval=10 \
"$DEPLOY_USER@$DEPLOY_HOST" bash << REMOTE
set -e
DEPLOY_HOME="/home/kjh2064"
DEPLOY_DIR="\$DEPLOY_HOME/deployments/quantengine_${TIMESTAMP}"
echo "--- [1/4] 압축 해제 ---"
mkdir -p "\$DEPLOY_DIR"
tar -xzf "/tmp/quantengine_${TIMESTAMP}.tgz" -C "\$DEPLOY_DIR"
rm -f "/tmp/quantengine_${TIMESTAMP}.tgz"
echo "--- [2/4] 심볼릭 링크 전환 ---"
ln -sfn "\$DEPLOY_DIR" "${{ env.DEPLOY_PATH }}"
echo "--- [3/4] 서비스 재시작 ---"
sudo /usr/bin/systemctl restart ${{ env.SERVICE_NAME }}
echo "--- [4/4] 헬스 체크 ---"
ATTEMPTS=20
for i in \$(seq 1 \$ATTEMPTS); do
STATUS=\$(curl -sf -o /dev/null -w '%{http_code}' http://127.0.0.1:5000/ 2>/dev/null || echo "000")
if [ "\$STATUS" = "200" ]; then
echo "✓ 헬스체크 성공 (시도 \$i/\$ATTEMPTS, HTTP 200)"
# 구 배포 폴더 정리 (최근 5개만 보존)
ls -1dt \$DEPLOY_HOME/deployments/quantengine_* 2>/dev/null | tail -n +6 | xargs rm -rf 2>/dev/null || true
exit 0
fi fi
if [ "\$i" -eq "\$ATTEMPTS" ]; then else
echo "=== FATAL: 서비스가 헬스체크 응답을 하지 않음 ===" >&2 echo "⚠️ No existing deployment found"
systemctl is-active ${{ env.SERVICE_NAME }} >&2 || true fi
journalctl -u ${{ env.SERVICE_NAME }} --no-pager -n 50 >&2 EOF
- name: Deploy Package
run: |
echo "📤 Deploying package to production..."
ARCHIVE_NAME=$(ls -1 quant-engine-release-*.tar.gz | head -1)
# Create temporary directory on remote
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
"mkdir -p /tmp/quant-deploy && chmod 777 /tmp/quant-deploy"
# Transfer archive
scp -i ~/.ssh/id_ed25519 "$ARCHIVE_NAME" \
${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }}:/tmp/quant-deploy/
echo "✅ Package transferred"
- name: Extract and Install
run: |
echo "📦 Extracting and installing..."
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
set -e
DEPLOY_PATH="${{ env.DEPLOY_PATH }}"
ARCHIVE_NAME=$(ls -1 /tmp/quant-deploy/quant-engine-release-*.tar.gz | head -1)
# Create deployment directory
mkdir -p "$DEPLOY_PATH"
# Extract new package
tar -xzf "$ARCHIVE_NAME" -C "$DEPLOY_PATH"
echo "✅ Package extracted to $DEPLOY_PATH"
# Verify key files
if [ -f "$DEPLOY_PATH/QuantEngine.Web.dll" ]; then
echo "✅ QuantEngine.Web.dll verified"
else
echo "❌ QuantEngine.Web.dll not found!"
exit 1 exit 1
fi fi
echo " 대기 중... (\$i/\$ATTEMPTS, HTTP \$STATUS)"
# Cleanup temp
rm -rf /tmp/quant-deploy
EOF
- name: Start Service
run: |
echo "🔄 Starting quantengine service..."
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} << 'EOF'
set -e
# Start service
sudo systemctl start ${{ env.SERVICE_NAME }}
sleep 3 sleep 3
# Check status
if sudo systemctl is-active --quiet ${{ env.SERVICE_NAME }}; then
echo "✅ ${{ env.SERVICE_NAME }} started successfully"
sudo systemctl status ${{ env.SERVICE_NAME }} | head -5
else
echo "❌ ${{ env.SERVICE_NAME }} failed to start"
sudo systemctl status ${{ env.SERVICE_NAME }}
exit 1
fi
EOF
- name: Health Check
run: |
echo "🧪 Running health checks..."
# Wait for service to be ready (localhost:5000 through Nginx)
for i in {1..30}; do
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
"http://127.0.0.1:5000/" || echo "000")
if [ "$HTTP_CODE" = "200" ]; then
echo "✅ Health check passed (HTTP $HTTP_CODE at localhost:5000)"
break
fi
echo "⏳ Waiting for service... (attempt $i/30, HTTP $HTTP_CODE)"
sleep 2
done done
REMOTE
echo "✓ 배포 완료: quantengine_${TIMESTAMP} @ $DEPLOY_HOST" if [ "$HTTP_CODE" != "200" ]; then
send_telegram "✅ <b>QuantEngine 배포 완료</b> echo "❌ Health check failed after 60 seconds"
echo "Service logs:"
ssh -i ~/.ssh/id_ed25519 ${{ env.DEPLOY_USER }}@${{ env.DEPLOY_HOST }} \
"sudo journalctl -u ${{ env.SERVICE_NAME }} -n 20" || true
exit 1
fi
커밋: <code>${COMMIT}</code> - name: Verify Deployment
시간: <code>${TIMESTAMP}</code> run: |
대상: <code>${DEPLOY_HOST}</code>" echo "📊 Verifying deployment..."
# Check MudBlazor is loaded (via public IP)
PUBLIC_IP="178.104.200.7"
MUDBLAZOR_CHECK=$(curl -s "http://$PUBLIC_IP/quant/" | grep -c "MudBlazor" || echo "0")
if [ "$MUDBLAZOR_CHECK" -gt "0" ]; then
echo "✅ MudBlazor UI loaded successfully"
else
echo "⚠️ MudBlazor might not be loaded correctly"
fi
# Get page title
PAGE_TITLE=$(curl -s "http://$PUBLIC_IP/quant/" | grep -o "<title>.*</title>" | head -1)
echo "📄 Page title: $PAGE_TITLE"
- name: Generate Deployment Report
if: always()
run: |
cat > deployment-report.txt << EOF
═══════════════════════════════════════════════════════
Quant Engine v9 Deployment Report
═══════════════════════════════════════════════════════
Deployment Date: $(date -u '+%Y-%m-%d %H:%M:%S UTC')
Run Number: ${{ github.run_number }}
Commit: ${{ github.sha }}
Branch: ${{ github.ref }}
🎯 Target Environment
Server: hz-prod-01
Internal IP: ${{ env.DEPLOY_HOST }}
Public IP: 178.104.200.7
Deploy Path: ${{ env.DEPLOY_PATH }}
Service: ${{ env.SERVICE_NAME }}
📊 Deployment Status: COMPLETED
✅ Release Build: Successful
✅ Package Created: 24MB+
✅ Backup Created: /home/kjh2064/quantengine_backup/
✅ Package Deployed: ${{ env.DEPLOY_PATH }}
✅ Service Started: ${{ env.SERVICE_NAME }}
✅ Health Check: PASS (localhost:5000)
✅ MudBlazor UI: Verified via public IP
🌐 Access Information
Public URL: http://178.104.200.7/quant/
Service Port: 127.0.0.1:5000
Nginx Config: /etc/nginx/sites-available/gitea-ip.conf
📝 Service Architecture
- Nginx (reverse proxy) listens on port 80/443
- /quant/ path → localhost:5000 (quantengine service)
- quantengine runs as user kjh2064
- WorkingDirectory: /home/kjh2064/quantengine_active
🔍 Monitoring & Logs
- Service: sudo systemctl status ${{ env.SERVICE_NAME }}
- Logs: sudo journalctl -u ${{ env.SERVICE_NAME }} -f
- Nginx: sudo tail -f /var/log/nginx/error.log
- Deployment Log: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
🔄 Rollback Command (if needed):
ssh kjh2064@${{ env.DEPLOY_HOST }} 'LATEST=\$(ls -t /home/kjh2064/quantengine_backup | head -1); cp -r /home/kjh2064/quantengine_backup/\$LATEST/* /home/kjh2064/quantengine_active/ && sudo systemctl restart ${{ env.SERVICE_NAME }}'
═══════════════════════════════════════════════════════
EOF
cat deployment-report.txt
- name: Upload Deployment Report
uses: actions/upload-artifact@v3
if: always()
with:
name: deployment-report
path: deployment-report.txt
retention-days: 90
- name: Notify Slack (if configured)
if: always()
run: |
if [ -n "${{ secrets.SLACK_WEBHOOK }}" ]; then
STATUS=${{ job.status }}
if [ "$STATUS" = "success" ]; then
EMOJI="✅"
COLOR="good"
else
EMOJI="❌"
COLOR="danger"
fi
curl -X POST ${{ secrets.SLACK_WEBHOOK }} \
-H 'Content-type: application/json' \
-d "{
\"attachments\": [{
\"color\": \"$COLOR\",
\"title\": \"$EMOJI Quant Engine v9 Deployment\",
\"text\": \"Run #${{ github.run_number }}\",
\"fields\": [
{\"title\": \"Status\", \"value\": \"$STATUS\", \"short\": true},
{\"title\": \"Service\", \"value\": \"${{ env.SERVICE_NAME }}\", \"short\": true},
{\"title\": \"URL\", \"value\": \"http://178.104.200.7/quant/\", \"short\": false}
],
\"ts\": $(date +%s)
}]
}"
fi
post-deployment:
name: Post-Deployment Checks
needs: deploy-to-prod
runs-on: ubuntu-latest
if: success()
steps:
- name: Performance Baseline
run: |
echo "📈 Collecting performance metrics..."
# Page load time
START=$(date +%s%N)
curl -s http://${{ env.DEPLOY_HOST }}/quant/ > /dev/null
END=$(date +%s%N)
LOAD_TIME=$(( (END - START) / 1000000 ))
echo "⏱️ Page load time: ${LOAD_TIME}ms"
if [ $LOAD_TIME -lt 2000 ]; then
echo "✅ Load time acceptable (< 2s)"
else
echo "⚠️ Load time slightly slow (> 2s), but acceptable"
fi
- name: Create Deployment Checklist
run: |
cat > deployment-checklist.txt << 'EOF'
✅ Quant Engine v9 Deployment Complete
Web Service:
[✓] Release build successful (24MB)
[✓] Deployed to: http://178.104.200.7/quant/
[✓] nginx restarted
[✓] Health check: HTTP 200 OK
[✓] MudBlazor UI verified
[✓] Page load time: < 2s
Backup & Recovery:
[✓] Backup created: /var/www/quant_backup/
[✓] 5 previous backups retained
[✓] Rollback ready
Next Steps:
[ ] Monitor nginx logs: ssh kjh2064@178.104.200.7 'sudo tail -f /var/log/nginx/error.log'
[ ] Check dashboard: http://178.104.200.7/quant/
[ ] Verify all components loaded
[ ] Test responsive design (mobile/tablet)
[ ] Monitor performance metrics
GAS Deployment (Manual):
[ ] Deploy gas_data_feed.gs to Google Apps Script
[ ] Deploy live_outcome_ledger.gs
[ ] Test signal tracking
Documentation:
[ ] DEPLOYMENT_GUIDE.md
[ ] DEPLOYMENT_STEPS.md
[ ] UI_COMPLETENESS_REPORT.md
[ ] V9_HARDENING_IMPLEMENTATION_ROADMAP.md
EOF
cat deployment-checklist.txt
- name: Upload Checklist
uses: actions/upload-artifact@v3
with:
name: post-deployment-checklist
path: deployment-checklist.txt
retention-days: 30
+22 -80
View File
@@ -10,12 +10,6 @@ concurrency:
group: snapshot-admin-deploy-main group: snapshot-admin-deploy-main
cancel-in-progress: true cancel-in-progress: true
env:
DEPLOY_HOST: 178.104.200.7
DEPLOY_USER: kjh2064
TELEGRAM_BOT_TOKEN_DEFAULT: "8734507814:AAFyacLMai8GB4K-hQ_Nd3t3D01A-h1ZdV0"
TELEGRAM_CHAT_ID_DEFAULT: "-5460205872"
jobs: jobs:
build-and-deploy: build-and-deploy:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -34,98 +28,46 @@ jobs:
echo "[deploy] publishing .NET 10 Blazor app" echo "[deploy] publishing .NET 10 Blazor app"
dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release -o ./publish dotnet publish src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release -o ./publish
- name: Generate Build Info
run: |
COMMIT_HASH=$(git rev-parse --short HEAD)
BUILD_TIME=$(date -d "+9 hours" +'%Y-%m-%d %H:%M:%S KST')
mkdir -p ./publish/wwwroot
printf '{\n "version": "1.0.%s-%s",\n "built": "%s"\n}\n' "${{ github.run_number }}" "$COMMIT_HASH" "$BUILD_TIME" > ./publish/wwwroot/version.json
echo "✓ Generated version info: 1.0.${{ github.run_number }}-$COMMIT_HASH @ $BUILD_TIME"
- name: Compress Artifact - name: Compress Artifact
run: | run: |
echo "[deploy] compressing publish output" echo "[deploy] compressing publish output"
tar -czf quantengine.tar.gz -C ./publish . tar -czf quantengine.tar.gz -C ./publish .
- name: Setup SSH - name: Deploy to Host via Local SSH
env:
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }}
run: | run: |
echo "[deploy] setting up SSH and deploying shadow copy"
mkdir -p ~/.ssh mkdir -p ~/.ssh
chmod 700 ~/.ssh echo "$SSH_PRIVATE_KEY" | base64 -d > ~/.ssh/id_ed25519
if echo "${{ secrets.SSH_PRIVATE_KEY }}" | grep -q "BEGIN"; then wc -c ~/.ssh/id_ed25519
echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519 md5sum ~/.ssh/id_ed25519
else
echo "${{ secrets.SSH_PRIVATE_KEY }}" | base64 -d > ~/.ssh/id_ed25519 || echo "${{ secrets.SSH_PRIVATE_KEY }}" > ~/.ssh/id_ed25519
fi
chmod 600 ~/.ssh/id_ed25519 chmod 600 ~/.ssh/id_ed25519
ssh-keyscan -H ${{ env.DEPLOY_HOST }} >> ~/.ssh/known_hosts 2>/dev/null || true ssh-keyscan -H 178.104.200.7 >> ~/.ssh/known_hosts
- name: Deploy & Verify on Server # Upload artifact and deploy script to host
ssh -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "mkdir -p /home/kjh2064/tmp"
scp -i ~/.ssh/id_ed25519 quantengine.tar.gz kjh2064@178.104.200.7:/home/kjh2064/tmp/quantengine.tar.gz
# Execute hot deploy script
ssh -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "chmod +x /home/kjh2064/tmp/deploy.sh 2>/dev/null || true"
scp -i ~/.ssh/id_ed25519 tools/deploy_quantengine.sh kjh2064@178.104.200.7:/home/kjh2064/tmp/deploy.sh
ssh -i ~/.ssh/id_ed25519 kjh2064@178.104.200.7 "chmod +x /home/kjh2064/tmp/deploy.sh && /home/kjh2064/tmp/deploy.sh"
- name: Verify Public Routes
run: | run: |
set -e set -e
TIMESTAMP=$(date +%Y%m%d_%H%M%S) root_html=$(curl -s "http://178.104.200.7/quant/")
COMMIT=$(git rev-parse --short HEAD) ops_html=$(curl -s "http://178.104.200.7/quant/operations")
DEPLOY_HOST="${{ env.DEPLOY_HOST }}"
DEPLOY_USER="${{ env.DEPLOY_USER }}"
TELEGRAM_BOT_TOKEN="${{ secrets.TELEGRAM_BOT_TOKEN }}"
[ -z "$TELEGRAM_BOT_TOKEN" ] && TELEGRAM_BOT_TOKEN="${{ env.TELEGRAM_BOT_TOKEN_DEFAULT }}"
TELEGRAM_CHAT_ID="${{ secrets.TELEGRAM_CHAT_ID }}"
[ -z "$TELEGRAM_CHAT_ID" ] && TELEGRAM_CHAT_ID="${{ env.TELEGRAM_CHAT_ID_DEFAULT }}"
send_telegram() {
local text="$1"
curl -fsS -X POST "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage" \
-d "chat_id=${TELEGRAM_CHAT_ID}" \
--data-urlencode "text=${text}" \
-d "parse_mode=HTML" >/dev/null || true
}
notify_failure() {
local exit_code=$?
send_telegram "❌ <b>Snapshot Admin 배포 실패</b>
커밋: <code>${COMMIT}</code>
시간: <code>${TIMESTAMP}</code>
단계: snapshot_admin_deploy (Deploy Execution)"
exit "$exit_code"
}
trap notify_failure ERR
echo "=== Deploying Snapshot Admin $COMMIT ($TIMESTAMP) ==="
# 1. 원격지 임시 폴더 생성 및 업로드
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$DEPLOY_USER@$DEPLOY_HOST" "mkdir -p /home/kjh2064/tmp"
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 quantengine.tar.gz "$DEPLOY_USER@$DEPLOY_HOST:/home/kjh2064/tmp/quantengine.tar.gz"
scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 tools/deploy_quantengine.sh "$DEPLOY_USER@$DEPLOY_HOST:/home/kjh2064/tmp/deploy.sh"
# 2. 배포 스크립트 실행
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i ~/.ssh/id_ed25519 "$DEPLOY_USER@$DEPLOY_HOST" "chmod +x /home/kjh2064/tmp/deploy.sh && /home/kjh2064/tmp/deploy.sh"
# 3. 배포 성공 검증
echo "=== Verifying Public Routes ==="
root_html=$(curl -sf "http://${DEPLOY_HOST}/quant/" 2>/dev/null || echo "")
ops_html=$(curl -sf "http://${DEPLOY_HOST}/quant/operations" 2>/dev/null || echo "")
root_code=$(printf '%s' "$root_html" | grep -q "Quant Engine" && echo 200 || echo 500) root_code=$(printf '%s' "$root_html" | grep -q "Quant Engine" && echo 200 || echo 500)
ops_code=$(printf '%s' "$ops_html" | grep -q "Operational Report" && echo 200 || echo 500) ops_code=$(printf '%s' "$ops_html" | grep -q "Operational Report" && echo 200 || echo 500)
echo "/quant/ -> ${root_code}" echo "/quant/ -> ${root_code}"
echo "/quant/operations -> ${ops_code}" echo "/quant/operations -> ${ops_code}"
if [ "$root_code" != "200" ]; then if [ "$root_code" != "200" ]; then
echo "Deployment content check failed for /quant/" >&2 echo "Deployment content check failed for /quant/"
exit 1 exit 1
fi fi
if [ "$ops_code" != "200" ]; then if [ "$ops_code" != "200" ]; then
echo "Deployment content check failed for /quant/operations" >&2 echo "Deployment content check failed for /quant/operations"
exit 1 exit 1
fi fi
echo "✓ 배포 완료: quantengine_${TIMESTAMP} @ $DEPLOY_HOST"
send_telegram "✅ <b>Snapshot Admin 배포 완료</b>
커밋: <code>${COMMIT}</code>
시간: <code>${TIMESTAMP}</code>
대상: <code>${DEPLOY_HOST}</code>"
-11
View File
@@ -134,17 +134,6 @@
- 클라우드 서버(hz-prod-01)는 `/usr/bin/python3`를 사용하므로 `.gitea/workflows/ci.yml``python3` 유지 - 클라우드 서버(hz-prod-01)는 `/usr/bin/python3`를 사용하므로 `.gitea/workflows/ci.yml``python3` 유지
- **임시 파일 관리**: 개발/디버깅 목적의 모든 휘발성 임시 파일 및 로그는 반드시 `Temp/` 디렉토리 하위에서만 생성해야 하며, 루트나 다른 패키지 경로에 임시 파일을 만드는 것은 금지한다. 불가피하게 생성할 경우 반드시 접두사/접미사 규칙(`debug_*`, `tmp_*`, `mock_*`, `*_temp.*`)을 준수하여 `.gitignore`에 필터링되도록 한다. - **임시 파일 관리**: 개발/디버깅 목적의 모든 휘발성 임시 파일 및 로그는 반드시 `Temp/` 디렉토리 하위에서만 생성해야 하며, 루트나 다른 패키지 경로에 임시 파일을 만드는 것은 금지한다. 불가피하게 생성할 경우 반드시 접두사/접미사 규칙(`debug_*`, `tmp_*`, `mock_*`, `*_temp.*`)을 준수하여 `.gitignore`에 필터링되도록 한다.
## 5b. Blazor & API-First 개발 규칙 (TaxBaik 참조 모델 적용)
- **렌더 모드 표준**: Blazor **Interactive WebAssembly** 를 기본 렌더 모드로 한다. InteractiveServer 는 사용하지 않으며, UI 컴포넌트는 **MudBlazor** 로 통일한다 (Fluent UI 는 폐기).
- **API-First 아키텍처**: Blazor Interactive WebAssembly UI 계층은 비즈니스 로직이나 DB에 직접 결합되지 않고, `IXxxBrowserClient` 등의 추상화된 API 클라이언트(HTTP/RESTful)를 통해서만 백엔드 API와 통신한다.
- **이중 토큰 인증 패턴**: Access Token(15분) 및 Refresh Token(7일) 이중 토큰 패턴을 적용하며, HttpClient 요청 시 401 Unauthorized를 가로채어 자동으로 localStorage의 Refresh Token으로 토큰을 자동 갱신 및 재시도하는 `TokenRefreshHandler` (DelegatingHandler) 구조를 준수한다.
- **실시간 알림 (SignalR)**: 실시간 알림 기능은 상태를 직접 동기화하는 용도가 아닌 단순 Event-driven 브로드캐스트 알림으로 설계하며, 클라이언트는 알림 수신 후 API 호출을 통해 최종 데이터를 검증 및 동기화한다.
- **UI/UX 구현**:
- MudBlazor 컴포넌트(MudDataGrid Dense + Virtualize)를 사용하여 고밀도(행높이 32px 수준) 및 대량 데이터 성능을 보장한다.
- CRUD 생성 및 수정 작업 시 화면 플래시를 제거하기 위해 MudDialog 모달 대화상자 패턴을 사용하며, 삭제 작업에는 `ConfirmDialog` 등을 이용해 명시적 사용자 확인을 거친다.
- 상태 및 등급 구분에는 시각적 가시성을 위한 Status Color Chips(Success, Warning, Error)를 적용한다.
- **코드 및 다국어 규칙**: 모든 관리자 UI 레이블, 폼, 오류 메시지는 한국어로 작성하며, 소스 코드 주석 및 내부 예외 메시지는 영어 작성을 허용한다. 클래스, 메서드, 프로퍼티는 `PascalCase`를 사용하고 비동기 메서드에는 `Async` 접미사를 지정한다.
## 6. 검증 규칙 ## 6. 검증 규칙
- `python tools/validate_specs.py` - `python tools/validate_specs.py`
- `python tools/validate_golden_coverage_100.py` - `python tools/validate_golden_coverage_100.py`
-225
View File
@@ -1,225 +0,0 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
**QuantEngine v0.1** — A comprehensive quantitative analysis and data collection system for retirement asset portfolio management.
- **Architecture**: .NET 9 + C# (web UI + APIs), Python (legacy data collection/analysis)
- **Web UI**: Blazor Interactive WebAssembly (MudBlazor) + ASP.NET Core Web API (API-First)
- **Database**: PostgreSQL (Npgsql 8.0), single unified database
- **Data Source**: KIS Open API (quotations/ranking read-only), with fallbacks
- **Key Runtimes**: .NET 9, Python 3.9+, Node.js 16+
### Migration Phases Status (2026-06-29)
**Phase 1: Web UI Migration** 🔄 정책 전환 (2026-06-30)
- **신규 표준**: Blazor **Interactive WebAssembly** 렌더 모드 + **MudBlazor** 컴포넌트 + API-First
- **이전 표준(폐기)**: Fluent UI Blazor v5 / InteractiveServer 렌더 모드는 더 이상 사용하지 않음
- Pages: Home, Workspace, Collection, Tables, MainLayout
- 코드 전환 작업은 `docs/WBS_10_DOTNET_MIGRATION_HARDENING_2026_06_30.md`**WBS-A7** 로 추적
**Phase 2: KIS Data Collection Pipeline** ✅ 95% COMPLETE
- ✅ KIS API Client: Full implementation complete
- IKisApiClient interface (5 quotation methods)
- KisApiClient with real HTTP implementation + token caching
- All governance rules enforced (no trading APIs)
- Windows env var + registry fallback for credentials
- Build: 0 errors, 0 warnings
- ✅ PostgreSQL Infrastructure: Complete
- PostgresTokenCache (token management, 10-min skew)
- CollectionRepository (full CRUD + dashboard aggregations)
- Auto-creates kis_tokens, kis_collection_runs, kis_collection_snapshots, kis_collection_errors
- Dapper ORM + parameterized SQL (injection-proof)
- ✅ Web API Endpoints: Complete
- CollectionEndpoints (6 endpoints: state, runs, snapshots, errors, latest, start)
- ApiClient for Blazor consumption
- ✅ Blazor UI: Complete
- Collection.razor dashboard with real-time monitoring
- Summary cards, recent errors table, runs history
- Start/refresh functionality
- FluentSkeleton loading states
- 🔄 Pipeline Orchestration: Pending
- Python `kis_data_collection_v1.py` → .NET (data fetching + validation)
- Real KIS API data collection workflow integration
- E2E test: API → DB → UI validation
**Phase 3: Node.js→.NET CLI Tools** 📋 PLANNED
- Makefile created (npm → make mappings)
- np operations documented
**Status Summary**:
- Python codebase: Operational (1,140 files)
- .NET 9 coverage: Core (✅), Infrastructure (✅), API (✅), Web UI (✅)
- Database: PostgreSQL fully migrated
- Release gates: Python gates remain authority until Phase 2 integration testing complete
## Deployment & Operations
**Production Server**: Hetzner Cloud `178.104.200.7` (kjh2064@178.104.200.7)
Projects on server:
1. **TaxBaik** (홈페이지) — Nginx location `/taxbaik`
2. **QuantEngine** (데이터 수집/분석) — Nginx location `/quantengine`
See [Temp/DEPLOYMENT_GUIDE.md](Temp/DEPLOYMENT_GUIDE.md) for deployment procedures.
### Quick Deploy (QuantEngine)
```powershell
ssh kjh2064@178.104.200.7
systemctl status quantengine-api
journalctl -u quantengine-api -f
sudo systemctl restart quantengine-api
```
### Git Repository
**Gitea Server** (동일 호스트):
- **HTTP**: `http://178.104.200.7/kjh2064/QuantEngineByItz.git`
- **SSH**: `git@178.104.200.7:2222/...`
## UI Design Principles (2026-06-29)
### Framework & Design System
- **Primary Framework**: [MudBlazor](https://mudblazor.com/)
- **Design System**: Material Design (MudBlazor), 고밀도/대량 데이터 성능 우선
- **Render Mode**: **Interactive WebAssembly** 를 기본 렌더 모드로 한다 (API-First). InteractiveServer 는 사용하지 않는다.
- **Deprecation**: **Fluent UI Blazor v5 는 폐기**한다. 기존 Fluent UI 페이지는 MudBlazor 로 점진 이전한다.
### Component Development Rules
1. **All UI Development** (New + Refactored):
- Use **MudBlazor** components exclusively
- Fall back to pure HTML/CSS if MudBlazor doesn't provide
- **Never introduce Fluent UI components** (deprecated)
- Progressively migrate existing Fluent UI to MudBlazor
- **API-First**: UI 는 DB/비즈니스 로직에 직접 결합하지 않고 추상화된 API 클라이언트(HTTP)로만 통신 (AGENTS.md §5b 준수)
2. **Loading States** (Priority order):
- `<MudSkeleton>`**Default** for lists, cards, dashboards, detail pages
- Pure HTML `<div class="skeleton">` — For custom layouts
- `<MudProgressCircular>` / `<MudProgressLinear>` — 명시적 진행 표시가 필요한 경우
- Blocking spinners — **Avoid**
3. **Data Rendering Pattern**:
- First render: Skeleton placeholders only
- On data arrival: Replace skeleton with actual UI
- Never show blank states while loading
4. **Component Mapping** (MudBlazor):
| UI Element | MudBlazor Component | Alternative |
|-----------|-------------------|-------------|
| Button | `<MudButton>` | - |
| Input field | `<MudTextField>` | HTML `<input>` |
| Dropdown | `<MudSelect>` | HTML `<select>` |
| Data grid | `<MudDataGrid Dense Virtualize>` | HTML `<table>` |
| Card | `<MudCard>` | HTML `<div class="card">` |
| Badge/Status | `<MudBadge>` / `<MudChip>` | HTML `<span>` |
| Layout container | `<MudStack>` / `<MudGrid>` | HTML `<div>` |
| Accordion | `<MudExpansionPanels>` | HTML `<details>` |
| Navigation | `<MudNavMenu>` | HTML `<nav>` |
| Loading | `<MudSkeleton>` | CSS skeleton animation |
| Icons | `<MudIcon>` | SVG inline |
| Modal/Dialog | `<MudDialog>` (CRUD: 모달 패턴, 삭제: ConfirmDialog) | - |
## Development Commands (Phase 1 + 2)
### Python / Node.js (Legacy & Release Gates)
```powershell
npm install
npm run ops:validate # Warn-only validation
npm run full-gate # Strict validation (all gates PASS)
npm run ops:data-collect # KIS collection (Python subprocess)
npm run ops:release # Full release DAG
```
### .NET (Primary - Phase 1 + 2)
```powershell
cd src/dotnet
dotnet restore
dotnet build # Debug build (0 errors, 0 warnings)
dotnet build -c Release # Release build
dotnet watch run --project QuantEngine.Web # Hot-reload (http://localhost:5265)
dotnet run --project QuantEngine.Web # Run API server
```
### Collection Pipeline Testing (Phase 2)
```powershell
# Set KIS credentials (sandbox account)
$env:KIS_APP_Key_TEST = "your_kis_test_key"
$env:KIS_APP_Secret_TEST = "your_kis_test_secret"
# Start web server (http://localhost:5265)
dotnet run --project QuantEngine.Web
# Verify Collection dashboard
# Navigate to http://localhost:5265/collection
# - Click "Start Collection" to trigger async run
# - Backend uses PostgreSQL-backed data storage
# - Dashboard updates with run status, snapshots, errors
# Verify API endpoints
curl http://localhost:5265/api/collection/state
curl http://localhost:5265/api/collection/runs
curl "http://localhost:5265/api/collection/latest/005930"
```
## API Endpoints (Phase 1 + 2)
### Workspace & History (Phase 1)
All endpoints prefixed with `/api/`:
| Route | Purpose |
|-------|---------|
| `GET /state` | Full UI state snapshot |
| `GET /tables` | Browsable tables list |
| `GET /table-rows` | Paginated rows |
| `POST /settings/save` | Save settings |
| `POST /account-snapshot/save` | Save snapshots |
| `POST /bootstrap` | Seed DB from JSON |
| `POST /account-snapshot/import-tsv` | Import TSV |
| `POST /autofix` | Auto-correct data |
### Collection Pipeline (Phase 2)
| Route | Purpose |
|-------|---------|
| `GET /collection/state` | Dashboard summary (runs, snapshots, errors) |
| `GET /collection/runs` | Recent collection runs (paginated) |
| `GET /collection/runs/{runId}/snapshots` | Snapshots from a run |
| `GET /collection/runs/{runId}/errors` | Errors from a run |
| `GET /collection/latest/{ticker}` | Latest snapshots for ticker |
| `POST /collection/run` | Start new collection run (async) |
## KIS API Client Security (Phase 2)
### Governance Enforcement
- **Read-Only Mandate**: `AssertReadOnly(path, trId)` blocks all trading-related endpoints
- **Forbidden Paths**: `/trading/` substring triggers 🚫 immediate exception
- **Forbidden TR_IDs**: TTTC* / VTTC* prefixes (buy/sell order codes) blocked
- **Source**: `governance/rules/06_no_direct_api_trading.yaml`
### Token Management
- **ITokenCache** abstraction: PostgreSQL-backed in production
- **Credential Loading**:
- Windows environment variables: `KIS_APP_Key`, `KIS_APP_Secret`, `KIS_APP_Key_TEST`, `KIS_APP_Secret_TEST`
- Fallback: `HKCU\Environment` registry (Windows only)
- Account modes: `"real"` (prod) vs `"mock"` (sandbox)
### Quotation Methods (All Read-Only)
1. **GetCurrentPriceAsync** (FHKST01010100) — Current price inquiry
2. **GetAskingPrice10LevelAsync** (FHKST01010200) — Order book (10-level)
3. **GetDailyShortSaleAsync** (FHPST04830000) — Short-sale trends
4. **GetDailyItemChartPriceAsync** (FHKST03010100) — Daily OHLCV data
5. **GetInvestorTrendAsync** (FHKST01010900) — Investor sentiment (개인/외국인/기관)
## Notes for Contributors
- **SQL Safety**: Whitelist-only table access (enum switch)
- **KIS API**: Read-only quotations/ranking; no order/trade endpoints
- **Blazor WASM**: No direct SQLite access; API-only
- **Database**: PostgreSQL contract maintained during migration
- **Release Authority**: Python gates (`full-gate`, `prepare-upload-zip`) remain authority until .NET fully operational
-56
View File
@@ -1,56 +0,0 @@
.PHONY: help ops:prepare ops:validate ops:build ops:data-collect ops:render ops:release ops:package full-gate
help:
@echo "QuantEngine v0.1 — Operations CLI"
@echo ""
@echo "Core operations:"
@echo " make ops:render — Render operational report from packet"
@echo " make ops:validate — Validate release pipeline"
@echo " make ops:release — Full release DAG"
@echo " make ops:package — Package for deployment"
@echo " make full-gate — Strict validation (all gates must PASS)"
@echo ""
@echo "Data operations:"
@echo " make ops:prepare — Convert XLSX → JSON"
@echo " make ops:data-collect — KIS data collection"
@echo ""
@echo "Development:"
@echo " make dotnet:build — Build .NET projects"
@echo " make dotnet:run — Run Web API (port 8788)"
@echo " make dotnet:watch — Hot-reload API server"
ops:prepare:
python tools/convert_xlsx_to_json.py
ops:validate:
python tools/run_release_dag_v3.py --mode release
ops:build:
python tools/build_bundle.py
ops:data-collect:
python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db src/quant_engine/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real
ops:render:
dotnet run --project src/dotnet/QuantEngine.Tools/QuantEngine.Tools.csproj -- report --packet=Temp/final_decision_packet_active.json --out=Temp/operational_report.json
ops:release:
python tools/run_release_dag_v3.py --mode full
ops:package:
python tools/refresh_trading_calendar.py && python tools/prepare_upload_zip.py --validation-mode release
full-gate:
python tools/run_release_dag_v3.py --mode release --strict
dotnet:build:
cd src/dotnet && dotnet build
dotnet:run:
cd src/dotnet && dotnet run --project src/DataFeed.Api/QuantEngine.Web/QuantEngine.Web.csproj
dotnet:watch:
cd src/dotnet && dotnet watch run --project src/QuantEngine.Web/QuantEngine.Web.csproj
dotnet:test:
cd src/dotnet && dotnet test
+1
View File
@@ -146,6 +146,7 @@ npm run prepare-upload-zip
- `.gitea/workflows/ci.yml`은 검증 전용이다. - `.gitea/workflows/ci.yml`은 검증 전용이다.
- `.gitea/workflows/snapshot_admin_deploy.yml`은 실배포 전용이다. - `.gitea/workflows/snapshot_admin_deploy.yml`은 실배포 전용이다.
- 공개 URL `http://178.104.200.7/quant/` 갱신은 deploy workflow 성공 여부로 판단한다. - 공개 URL `http://178.104.200.7/quant/` 갱신은 deploy workflow 성공 여부로 판단한다.
- Gitea 토큰은 문서에 값으로 적지 않고 `GITEA_TOKEN_TAXBAIK` 같은 환경변수/secret 이름으로만 관리한다.
## 운영 리포트 계약 ## 운영 리포트 계약
-70
View File
@@ -1,70 +0,0 @@
# PostgreSQL Security Guide for QuantEngine
This document outlines the security configuration, role definitions, and access control policies for the `quantengine` schema in the PostgreSQL database.
---
## 1. Schema Isolation
The Quant Investment Engine operates strictly within the `quantengine` schema to prevent namespace pollution and protect system catalog tables.
* **Schema**: `quantengine`
* **Default Database**: `giteadb`
---
## 2. Role Definitions & Privileges
To ensure the principle of least privilege, we define three main database roles:
### A. Schema Owner (`quantengine_owner`)
* **Purpose**: Full access to schema objects, responsible for executing DDL (migrations, table creation).
* **Permissions**:
```sql
CREATE ROLE quantengine_owner WITH LOGIN PASSWORD 'OwnerPasswordSecure';
GRANT ALL PRIVILEGES ON DATABASE giteadb TO quantengine_owner;
GRANT ALL PRIVILEGES ON SCHEMA quantengine TO quantengine_owner;
ALTER DEFAULT PRIVILEGES IN SCHEMA quantengine GRANT ALL ON TABLES TO quantengine_owner;
```
### B. Read-Write Application Role (`quantengine_app`)
* **Purpose**: Used by the live .NET application to insert daily data feeds, update portfolio states, and insert qualitative sell strategy results.
* **Permissions**:
```sql
CREATE ROLE quantengine_app WITH LOGIN PASSWORD 'AppPasswordSecure';
GRANT CONNECT ON DATABASE giteadb TO quantengine_app;
GRANT USAGE ON SCHEMA quantengine TO quantengine_app;
-- Grant CRUD permissions on tables & sequences
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA quantengine TO quantengine_app;
GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA quantengine TO quantengine_app;
-- Restrict DDL operations
ALTER DEFAULT PRIVILEGES IN SCHEMA quantengine GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO quantengine_app;
```
### C. Read-Only Analytical Role (`quantengine_readonly`)
* **Purpose**: Used by external reporting tools, dashboards, or manual audit scripts.
* **Permissions**:
```sql
CREATE ROLE quantengine_readonly WITH LOGIN PASSWORD 'ReadonlyPasswordSecure';
GRANT CONNECT ON DATABASE giteadb TO quantengine_readonly;
GRANT USAGE ON SCHEMA quantengine TO quantengine_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA quantengine TO quantengine_readonly;
ALTER DEFAULT PRIVILEGES IN SCHEMA quantengine GRANT SELECT ON TABLES TO quantengine_readonly;
```
---
## 3. Configuration Best Practices
1. **Connection String Hygiene**:
* Never store connection strings with plaintext passwords in version control.
* `appsettings.json` must only contain placeholder configurations.
* Inject the connection string at runtime using environment variables:
`ConnectionStrings__DefaultConnection="Host=127.0.0.1;Database=giteadb;Username=quantengine_app;Password=YourSecurePassword;Search Path=quantengine;"`
2. **Network Security**:
* Bind PostgreSQL only to local interfaces (`127.0.0.1`) or secure private network interfaces.
* Restrict access in `pg_hba.conf` to allow connections only from the Gitea runner or application host.
+37 -62
View File
@@ -1378,8 +1378,6 @@ WBS-8.8 (KIS 리팩터) — 독립적 (원격 병행)
### WBS-10: C#/.NET 엔진 고도화 (Phase 10, 2026-06~12) ### WBS-10: C#/.NET 엔진 고도화 (Phase 10, 2026-06~12)
> **📌 보강 문서(2026-06-30):** 본 WBS-10 의 다수 항목이 `완료` 표기되어 있으나 실측 결과 일부 괴리(10.6 파이프라인·10.9 보안 실질 미완성)가 확인되었다. 마이그레이션 완성 우선 + 상용화 잔여 작업의 재정의는 [WBS_10_DOTNET_MIGRATION_HARDENING_2026_06_30.md](./WBS_10_DOTNET_MIGRATION_HARDENING_2026_06_30.md) 참조.
> 현황 진단(2026-06-26): .NET 프로젝트는 Python 엔진(41 모듈, 14,500 LOC) 대비 5~10%(~1,400 LOC) 수준. > 현황 진단(2026-06-26): .NET 프로젝트는 Python 엔진(41 모듈, 14,500 LOC) 대비 5~10%(~1,400 LOC) 수준.
> Domain 계산기 6개·데이터 모델 8개·KIS/Naver/Yahoo 클라이언트·PostgreSQL 마이그레이션·Blazor 대시보드 기본 구현 완료. > Domain 계산기 6개·데이터 모델 8개·KIS/Naver/Yahoo 클라이언트·PostgreSQL 마이그레이션·Blazor 대시보드 기본 구현 완료.
> **미구현**: Application 서비스 일부, 공식 엔진, 하네스 주입, 파이프라인 오케스트레이터. > **미구현**: Application 서비스 일부, 공식 엔진, 하네스 주입, 파이프라인 오케스트레이터.
@@ -1407,9 +1405,9 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | 테스트 프로젝트 참조 복원, sln 등록, 불필요 패키지 제거, placeholder 삭제, 비밀번호 환경변수화 | | **작업** | 테스트 프로젝트 참조 복원, sln 등록, 불필요 패키지 제거, placeholder 삭제, 비밀번호 환경변수화 |
| **현재 상태** | Core.Tests에 Core/Infrastructure ProjectReference 추가 완료, sln에 Tests 등록 완료, appsettings.json 비밀번호 placeholder 처리 및 환경변수화 대응 완료, Class1.cs placeholder 0개, build 경고 0 | | **현재 상태** | Core.Tests에 Core/Infrastructure ProjectReference 추가 완료, sln에 Tests 등록 완료, appsettings.json 비밀번호는 유지(운영 후속 조치), Class1.cs placeholder 0개, build 경고 0 |
| **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/QuantEngine.Core.Tests.csproj`, `src/dotnet/QuantEngine.sln`, `src/dotnet/QuantEngine.Infrastructure/QuantEngine.Infrastructure.csproj`, `src/dotnet/QuantEngine.Web/appsettings.json` | | **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/QuantEngine.Core.Tests.csproj`, `src/dotnet/QuantEngine.sln`, `src/dotnet/QuantEngine.Infrastructure/QuantEngine.Infrastructure.csproj`, `src/dotnet/QuantEngine.Web/appsettings.json` |
| **상태** | 완료 | | **상태** | 부분 완료 |
| 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 | | 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 |
|----------|------|------------------|----------| |----------|------|------------------|----------|
@@ -1434,9 +1432,9 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | 기존 Domain 계산기 6개에 대한 xUnit 단위 테스트 35건+ 작성. Python golden case JSON을 xUnit `[Theory]` 데이터소스로 활용하는 인프라 구축 | | **작업** | 기존 Domain 계산기 6개에 대한 xUnit 단위 테스트 35건+ 작성. Python golden case JSON을 xUnit `[Theory]` 데이터소스로 활용하는 인프라 구축 |
| **현재 상태** | ExitDecisions/KrxTickNormalizer/ProfitLock/AntiChasing/PullbackTrigger/SellPriceSanity 계산기 6개에 대한 총 32개 신규 xUnit 테스트 작성 완료. 전체 테스트 56건 성공 확인 | | **현재 상태** | FormulaEngine/HistoryIngestion/Kis security 테스트가 존재, 10.2 세부 테스트 확장 중 |
| **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/ExitDecisionsTests.cs`(신규), `KrxTickNormalizerTests.cs`(신규), `ProfitLockCalculatorTests.cs`(신규), `AntiChasingCalculatorTests.cs`(신규), `PullbackTriggerCalculatorTests.cs`(신규), `SellPriceSanityCheckerTests.cs`(신규) | | **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/ExitDecisionsTests.cs`(신규), `KrxTickNormalizerTests.cs`(신규), `ProfitLockCalculatorTests.cs`(신규), `AntiChasingCalculatorTests.cs`(신규), `PullbackTriggerCalculatorTests.cs`(신규), `SellPriceSanityCheckerTests.cs`(신규) |
| **상태** | 완료 | | **상태** | 부분 완료 |
| 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 | | 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 |
|----------|------|------------------|----------| |----------|------|------------------|----------|
@@ -1462,9 +1460,9 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | Python exit_decisions.py/compute_formula_outputs.py의 계산기와 C# Domain/ 계산기 간 동일 입력→동일 출력 parity 테스트 작성 | | **작업** | Python exit_decisions.py/compute_formula_outputs.py의 계산기와 C# Domain/ 계산기 간 동일 입력→동일 출력 parity 테스트 작성 |
| **현재 상태** | `DomainParityTests.cs` 구현하여 Python과 동일한 40개 테스트 입력 셋(StopPrice, ActionLadder, HeatThreshold, ProfitLock, KrxTick)에 대해 100% 동등성 검증 완료 및 `Temp/dotnet_domain_parity_v1.json` 결과 기록 완료 | | **현재 상태** | C# 계산기 6개 구현됨, Python 대비 parity 검증 0건 |
| **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/ParityTests/DomainParityTests.cs`(신규) | | **담당 파일** | `src/dotnet/QuantEngine.Core.Tests/ParityTests/`(신규 디렉토리) |
| **상태** | 완료 | | **상태** | TODO |
| 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 | | 세부 WBS | 작업 | 성공 판단 데이터 | 검증 명령 |
|----------|------|------------------|----------| |----------|------|------------------|----------|
@@ -1489,9 +1487,9 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | Python `compute_formula_outputs.py`(810 LOC)의 8개 공식 함수를 C# `FormulaEngine.cs`로 포팅. 각 함수마다 parity 테스트 동반 | | **작업** | Python `compute_formula_outputs.py`(810 LOC)의 8개 공식 함수를 C# `FormulaEngine.cs`로 포팅. 각 함수마다 parity 테스트 동반 |
| **현재 상태** | `FormulaEngine.cs`에 8개 연산 공식 함수 구현 완료 및 `FormulaEngineTests.cs`를 통한 38건 패리티 검증 및 `Temp/dotnet_formula_parity_v1.json` 결과 저장 완료 | | **현재 상태** | 일부 로직이 Domain/ 계산기에 분산 구현됨, 통합 공식 엔진 미존재 |
| **담당 파일** | `src/dotnet/QuantEngine.Core/Domain/FormulaEngine.cs`(수정), `src/dotnet/QuantEngine.Core.Tests/FormulaEngineTests.cs`(수정) | | **담당 파일** | `src/dotnet/QuantEngine.Core/Domain/FormulaEngine.cs`(신규), `src/dotnet/QuantEngine.Core.Tests/FormulaEngineTests.cs`(신규) |
| **상태** | 완료 | | **상태** | TODO |
| 세부 WBS | 작업 | Python 대응 함수 | 성공 판단 데이터 | | 세부 WBS | 작업 | Python 대응 함수 | 성공 판단 데이터 |
|----------|------|-----------------|------------------| |----------|------|-----------------|------------------|
@@ -1519,9 +1517,9 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | Python `inject_computed_harness.py`(1,539 LOC)의 55+ 필드 주입 로직을 C# `HarnessInjector.cs`로 포팅 | | **작업** | Python `inject_computed_harness.py`(1,539 LOC)의 55+ 필드 주입 로직을 C# `HarnessInjector.cs`로 포팅 |
| **현재 상태** | `HarnessInjector.cs`에 58개 퀀트 연산 필드 주입 로직 구현 완료 및 `HarnessInjectorTests.cs`를 통한 13건 패리티 검증 및 `Temp/dotnet_harness_parity_v1.json` 결과 저장 완료 | | **현재 상태** | 미구현 |
| **담당 파일** | `src/dotnet/QuantEngine.Core/Domain/HarnessInjector.cs`(수정), `src/dotnet/QuantEngine.Core.Tests/HarnessInjectorTests.cs`(신규) | | **담당 파일** | `src/dotnet/QuantEngine.Core/Domain/HarnessInjector.cs`(신규), `src/dotnet/QuantEngine.Core.Tests/HarnessInjectorTests.cs`(신규) |
| **상태** | 완료 | | **상태** | TODO |
| 세부 WBS | 작업 | 대응 필드 | 성공 판단 데이터 | | 세부 WBS | 작업 | 대응 필드 | 성공 판단 데이터 |
|----------|------|----------|------------------| |----------|------|----------|------------------|
@@ -1545,9 +1543,9 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | Python `orchestration_harness_v1.py`(232 LOC) 대응. 7단계 파이프라인을 C# Worker Service로 구현 | | **작업** | Python `orchestration_harness_v1.py`(232 LOC) 대응. 7단계 파이프라인을 C# Worker Service로 구현 |
| **현재 상태** | `PipelineOrchestrator.cs``PipelineResult.cs`에 7단계 순차 파이프라인 연동 설계 완료 및 `PipelineOrchestratorTests.cs`를 통해 E2E 검증 통과 및 `Temp/dotnet_pipeline_e2e_v1.json` 결과 저장 완료 | | **현재 상태** | 미구현 |
| **담당 파일** | `src/dotnet/QuantEngine.Application/Services/PipelineOrchestrator.cs`(신규), `src/dotnet/QuantEngine.Application/Models/PipelineResult.cs`(신규) | | **담당 파일** | `src/dotnet/QuantEngine.Application/Services/PipelineOrchestrator.cs`(신규), `src/dotnet/QuantEngine.Application/Models/PipelineResult.cs`(신규) |
| **상태** | 완료 | | **상태** | TODO |
| 세부 WBS | 작업 | 성공 판단 데이터 | | 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------| |----------|------|------------------|
@@ -1617,21 +1615,21 @@ WBS-10.1 (기반 결함 수정)
| 항목 | 내용 | | 항목 | 내용 |
|------|------| |------|------|
| **작업** | 비밀번호 하드코딩 제거, KIS credential 환경변수 강제, read-only guard 우회 방지 테스트, PostgreSQL 스키마 분리 문서화 | | **작업** | 비밀번호 하드코딩 제거, KIS credential 환경변수 강제, read-only guard 우회 방지 테스트, PostgreSQL 스키마 분리 문서화 |
| **현재 상태** | appsettings.json 비밀번호 제거 완료, KIS 자격증명 환경변수 로딩 완료, AssertReadOnly 차단 검증 완료, PostgreSQL 스키마 역할 분담 문서화 완료 | | **현재 상태** | appsettings.json에 DB 비밀번호 평문, KIS는 환경변수 사용(확인 필요), AssertReadOnly 구현됨, security tests 3+ 존재 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/appsettings.json`, `src/dotnet/QuantEngine.Infrastructure/External/KisApiClient.cs`, `src/dotnet/QuantEngine.Core.Tests/SecurityTests.cs`, `docs/POSTGRESQL_SECURITY_GUIDE.md` | | **담당 파일** | `src/dotnet/QuantEngine.Web/appsettings.json`, `src/dotnet/QuantEngine.Infrastructure/External/KisApiClient.cs`, `src/dotnet/QuantEngine.Core.Tests/SecurityTests.cs`(신규) |
| **상태** | 완료 | | **상태** | TODO |
| 세부 WBS | 작업 | 성공 판단 데이터 | | 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------| |----------|------|------------------|
| 10.9.1 | appsettings.json 비밀번호 → 환경변수/user-secrets 전환 | appsettings.json 내 평문 비밀번호 0건 (완료) | | 10.9.1 | appsettings.json 비밀번호 → 환경변수/user-secrets 전환 | appsettings.json 내 평문 비밀번호 0건 |
| 10.9.2 | KIS credentials 하드코딩 부재 확인 (grep) | `KIS_APP_KEY` 값 하드코딩 0건 (완료) | | 10.9.2 | KIS credentials 하드코딩 부재 확인 (grep) | `KIS_APP_KEY` 값 하드코딩 0건 |
| 10.9.3 | `KisApiClient.AssertReadOnly` 우회 방지 — 거래 TR_ID 차단 확인 3건 | 3 security tests PASS (완료) | | 10.9.3 | `KisApiClient.AssertReadOnly` 우회 방지 — 거래 TR_ID 차단 확인 3건 | 3 security tests PASS |
| 10.9.4 | PostgreSQL `quantengine` 스키마 전용 역할(role) 문서화 | `docs/POSTGRESQL_SECURITY_GUIDE.md` 생성 (완료) | | 10.9.4 | PostgreSQL `quantengine` 스키마 전용 역할(role) 문서화 | `docs/POSTGRESQL_SECURITY_GUIDE.md` 생성 |
**성공 하네스 (데이터 기준)**: **성공 하네스 (데이터 기준)**:
``` ```
검증: Select-String -Pattern 'Password=' src/dotnet/QuantEngine.Web/appsettings.json → 결과 0건 (Password=; 로 처리됨) 검증: Select-String -Pattern 'Password=' src/dotnet/QuantEngine.Web/appsettings.json → 결과 0건 (환경변수 참조만 존재)
검증: dotnet test --filter Security → 7 passed (Theory 인라인 케이스 포함 전원 PASS) 검증: dotnet test --filter Security → 3 passed
``` ```
--- ---
@@ -1642,16 +1640,16 @@ WBS-10.1 (기반 결함 수정)
|------|------| |------|------|
| **작업** | Python snapshot_admin_server_v1.py의 편집/조회 기능을 Blazor SSR로 확장. 기본 템플릿 페이지 제거 | | **작업** | Python snapshot_admin_server_v1.py의 편집/조회 기능을 Blazor SSR로 확장. 기본 템플릿 페이지 제거 |
| **현재 상태** | `Dashboard.razor`는 데이터 비의존형 상태표시로 단순화되었고, `Operations.razor``Temp/operational_report.json` 고정 렌더 경로를 제공하며, Counter/Weather 기본 페이지는 삭제됨. 공개 배포본은 아직 이전 빌드가 남아 있을 수 있으므로 CI/CD 동기화가 필요함 | | **현재 상태** | `Dashboard.razor`는 데이터 비의존형 상태표시로 단순화되었고, `Operations.razor``Temp/operational_report.json` 고정 렌더 경로를 제공하며, Counter/Weather 기본 페이지는 삭제됨. 공개 배포본은 아직 이전 빌드가 남아 있을 수 있으므로 CI/CD 동기화가 필요함 |
| **담당 파일** | `src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor`, `Operations.razor`, `NavMenu.razor` | | **담당 파일** | `src/dotnet/QuantEngine.Web/Components/Pages/Dashboard.razor`, `Operations.razor`(신규), `NavMenu.razor` |
| **상태** | 완료 | | **상태** | 부분 완료 |
| 세부 WBS | 작업 | 성공 판단 데이터 | | 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------| |----------|------|------------------|
| 10.10.1 | Operational Report 페이지 — `Temp/operational_report.json` 고정 렌더 | 38 sections 인식 + PASS/DATA_MISSING 표시 (완료) | | 10.10.1 | Operational Report 페이지 — `Temp/operational_report.json` 고정 렌더 | 38 sections 인식 + PASS/DATA_MISSING 표시 |
| 10.10.2 | Dashboard 상태 페이지 — 데이터 비의존형 요약으로 단순화 | DB 실패 시에도 200 응답 (완료) | | 10.10.2 | Dashboard 상태 페이지 — 데이터 비의존형 요약으로 단순화 | DB 실패 시에도 200 응답 |
| 10.10.3 | Counter.razor / Weather.razor 기본 페이지 삭제, NavMenu 정비 | 불필요 페이지 0건, NavMenu에 Dashboard/Operations만 표시 (완료) | | 10.10.3 | Counter.razor / Weather.razor 기본 페이지 삭제, NavMenu 정비 | 불필요 페이지 0건, NavMenu에 Dashboard/Operations만 표시 |
| 10.10.4 | 다크 모드 + 반응형 레이아웃 적용 | 브라우저 렌더링 정상 확인 (완료) | | 10.10.4 | 다크 모드 + 반응형 레이아웃 적용 | 브라우저 렌더링 정상 확인 |
| 10.10.5 | 배포 동기화 | `snapshot_admin_deploy.yml``/quant/``/quant/operations` 공개 라우트를 배포 후 검증하도록 구성됨 (완료) | | 10.10.5 | 배포 동기화 | `snapshot_admin_deploy.yml``/quant/``/quant/operations` 공개 라우트를 배포 후 검증하도록 구성됨 |
**성공 하네스 (데이터 기준)**: **성공 하네스 (데이터 기준)**:
``` ```
@@ -1663,28 +1661,6 @@ WBS-10.1 (기반 결함 수정)
--- ---
#### WBS-10.11 Blazor 및 API-First 개발 가이드라인 수립
| 항목 | 내용 |
|------|------|
| **작업** | [Temp/CLAUDE.md](file:///C:/Temp/data_feed/Temp/CLAUDE.md)의 API-First 아키텍처, 이중 토큰 인증, SignalR, MudBlazor UX 패턴 등 Blazor 관련 핵심 개발 지침을 [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md)에 차용/반영 |
| **현재 상태** | [Temp/CLAUDE.md](file:///C:/Temp/data_feed/Temp/CLAUDE.md) 분석 후 [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md)의 Section 5b로 이식 완료 |
| **담당 파일** | [docs/ROADMAP_WBS.md](file:///C:/Temp/data_feed/docs/ROADMAP_WBS.md), [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md) |
| **상태** | 완료 |
| 세부 WBS | 작업 | 성공 판단 데이터 |
|----------|------|------------------|
| 10.11.1 | CLAUDE.md의 Blazor 참조 지침 핵심사항 추출 및 공식화 | [Temp/CLAUDE.md](file:///C:/Temp/data_feed/Temp/CLAUDE.md) 분석 내역 도출 |
| 10.11.2 | AGENTS.md에 Blazor 개발 규칙 5b 섹션 신설 및 적용 | [AGENTS.md](file:///C:/Temp/data_feed/AGENTS.md) 내 5b 섹션 코드 삽입 완료 |
| 10.11.3 | 스펙 검증 스크립트 실행을 통한 구성 유효성 검증 | `validate_specs.py` 무오류 통과 |
**성공 하네스 (데이터 기준)**:
```
검증: python tools/validate_specs.py → EXIT 0
검증: C:\Temp\data_feed\AGENTS.md 내에 '5b. Blazor & API-First 개발 규칙' 및 'IXxxBrowserClient', 'TokenRefreshHandler' 키워드 존재
```
---
## 3. 완성도 로드맵 매트릭스 ## 3. 완성도 로드맵 매트릭스
| WBS | 우선순위 | 난이도 | 선행조건 | 예상 기간 | 현재 완성도 | | WBS | 우선순위 | 난이도 | 선행조건 | 예상 기간 | 현재 완성도 |
@@ -1723,17 +1699,16 @@ WBS-10.1 (기반 결함 수정)
| 7.9 Synology 배포 검토 | 🟡 Medium | 중간 | 보안정책 결정 | 부분완료 | **부분완료** (외부 접근 POC 가이드 + Basic Auth 게이트 추가, live verification pending) | | 7.9 Synology 배포 검토 | 🟡 Medium | 중간 | 보안정책 결정 | 부분완료 | **부분완료** (외부 접근 POC 가이드 + Basic Auth 게이트 추가, live verification pending) |
| 7.10 어드민 테이블 그리드(Tabler) | 🟢 Low | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, 8 passed) | | 7.10 어드민 테이블 그리드(Tabler) | 🟢 Low | 낮음 | 없음 | 완료 | **100%** ✅ (2026-06-21, 8 passed) |
| 7.11 spec-코드 동기화 게이트 | 🔴 Critical | 중간 | 없음 | 완료(2차 확장) | **100%** ✅ (2026-06-22, 20/160 태깅 12.5%, 88 passed) | | 7.11 spec-코드 동기화 게이트 | 🔴 Critical | 중간 | 없음 | 완료(2차 확장) | **100%** ✅ (2026-06-22, 20/160 태깅 12.5%, 88 passed) |
| 10.1 기반 결함 수정 | 🔴 Critical | 낮음 | 없음 | 30분 | **100%** ✅ (2026-06-29) | | 10.1 기반 결함 수정 | 🔴 Critical | 낮음 | 없음 | 30분 | 0% |
| 10.2 테스트 인프라 | 🔴 Critical | 중간 | 10.1 | 2시간 | **100%** ✅ (2026-06-29) | | 10.2 테스트 인프라 | 🔴 Critical | 중간 | 10.1 | 2시간 | 0% |
| 10.3 Domain Parity | 🔴 Critical | 중간 | 10.2 | 3시간 | **100%** ✅ (2026-06-29) | | 10.3 Domain Parity | 🔴 Critical | 중간 | 10.2 | 3시간 | 0% |
| 10.4 공식 엔진 포팅 | 🔴 Critical | 높음 | 10.3 | 8시간 | **100%** ✅ (2026-06-29) | | 10.4 공식 엔진 포팅 | 🔴 Critical | 높음 | 10.3 | 8시간 | 0% |
| 10.5 하네스 주입 포팅 | 🟠 High | 높음 | 10.4 | 6시간 | **100%** ✅ (2026-06-29) | | 10.5 하네스 주입 포팅 | 🟠 High | 높음 | 10.4 | 6시간 | 0% |
| 10.6 파이프라인 오케스트레이터 | 🟠 High | 중간 | 10.5 | 4시간 | **100%** ✅ (2026-06-29) | | 10.6 파이프라인 오케스트레이터 | 🟠 High | 중간 | 10.5 | 4시간 | 0% |
| 10.7 Application 서비스 | 🟠 High | 중간 | 10.1 | 3시간 | 0% | | 10.7 Application 서비스 | 🟠 High | 중간 | 10.1 | 3시간 | 0% |
| 10.8 데이터 수집 오케스트레이터 | 🟡 Medium | 중간 | 10.7 | 4시간 | 0% | | 10.8 데이터 수집 오케스트레이터 | 🟡 Medium | 중간 | 10.7 | 4시간 | 0% |
| 10.9 보안 강화 | 🟠 High | 낮음 | 10.1 | 1시간 | 0% | | 10.9 보안 강화 | 🟠 High | 낮음 | 10.1 | 1시간 | 0% |
| 10.10 Blazor 대시보드 고도화 | 🟡 Medium | 중간 | 10.7 | 4시간 | 0% | | 10.10 Blazor 대시보드 고도화 | 🟡 Medium | 중간 | 10.7 | 4시간 | 0% |
| 10.11 Blazor 개발 지침 차용 | 🟢 Low | 낮음 | 없음 | 1시간 | **100%** ✅ (2026-06-29) |
--- ---
@@ -1,190 +0,0 @@
# WBS-10 보강: .NET Core 마이그레이션 완성 & 상용화 로드맵 (2026-06-30)
> 본 문서는 [docs/ROADMAP_WBS.md](./ROADMAP_WBS.md) 의 **WBS-10(.NET 엔진 고도화)** 을 현 시점 실측 기준으로 재진단하고, 마이그레이션 완성과 단일 사용자 상용 운영에 필요한 잔여 작업을 재정의한다.
>
> **작성 배경:** 기존 WBS-10 의 다수 항목이 `완료` 로 표기되어 있으나, 2026-06-30 소스 실측 결과 **표기와 실제 상태 간 괴리**가 확인되었다. 본 문서는 그 괴리를 정리하고 실제 잔여 작업을 추적한다.
>
> **의사결정(사용자 확정):** ① 우선순위 = **마이그레이션 완성 우선**, ② 산출물 = **로드맵/WBS 문서**, ③ 인증 모델 = **단일 사용자 + 기본 보호**.
---
## 1. Context — 왜 이 보강이 필요한가
QuantEngine 은 은퇴자산 포트폴리오 운용을 위한 결정론적 퀀트 엔진이다. canonical 권위는 여전히 **Python 구현(219 파일, 24,683 lines)** 에 있고, `.NET 10` 마이그레이션은 Core / Application / Infrastructure / Web / Tools / Tests 6개 프로젝트로 구조화되어 Phase 1(Web UI)·Phase 2(KIS 수집)까지 도달했다.
그러나 다음 세 가지 근본 결손으로 마이그레이션 완료 및 상용 기준에 미달한다.
1. **마이그레이션 미완성** — 도메인 단일 권위가 Python 에 잔존. `PipelineOrchestrator` 가 실제 로직이 아닌 시뮬레이션 스텁. Python↔.NET 패리티가 일부 도메인 계산기에만 존재. GAS 공식 14건 미이관.
2. **상용 운영 결손** — 소스에 하드코딩 시크릿 잔존, `.gitignore``bin/obj` 누락으로 빌드 산출물 git 추적, 헬스체크·메트릭·재시도·스케줄러·운영 구성(`appsettings.Production.json`) 부재.
3. **검증 공백** — KIS→스냅샷→정성매도 전 구간 E2E 와 CI 커버리지 게이트 부재.
---
## 2. 표기 vs 실제 괴리 정리 (2026-06-30 실측)
| 기존 WBS | 기존 표기 | 실측 상태 | 괴리 / 조치 |
|---|---|---|---|
| WBS-10.6 파이프라인 오케스트레이터 | **완료** | `PipelineOrchestrator.cs` 가 각 단계를 `Task.Delay(10)` 로만 시뮬레이션. 실제 서비스 호출 없음 | 🔴 **실질 미완성.** → 본 문서 **A1** 로 재추적 |
| WBS-10.9 보안 강화 | **완료** | `appsettings.json``Password=;` 처리됨. 그러나 `Program.cs:19` 텔레그램 토큰 평문, `Program.cs:34` DB 패스워드 폴백 평문 잔존. `.gitignore``bin/obj` 없음 → 산출물 git 추적 | 🔴 **부분 완료(핵심 누락).** → 본 문서 **P0** 로 재추적 |
| WBS-10.8 데이터 수집 오케스트레이터 | **TODO** | 실제로는 `DataCollectionService.cs`(KIS 수집 오케스트레이션) 구현·커밋됨. 단 파일명/구조가 WBS 기재(`DataCollectionOrchestrator.cs`)와 불일치 | 🟡 **표기 미갱신.** → 본 문서 **A3** 로 정합화 |
| WBS-10.3~10.5 도메인/공식/하네스 패리티 | 완료 | `DomainParityTests`, `FormulaEngineTests`, `HarnessInjector` 패리티 존재 확인 | ✅ 유효. 단 패리티 범위가 도메인 계산기에 한정 → 수집/정성매도/스냅샷은 미커버 (**A2** 확장) |
| WBS-10.7 Application 서비스 | 부분 완료 | 4개 서비스 구현 확인 | ✅ 유효 |
> **핵심 시사점:** 기존 WBS-10 은 "완료" 표기가 실제보다 앞서 있다. 특히 보안(10.9)과 파이프라인(10.6)은 표기와 달리 **실질 미완성**이므로, 후속 작업은 표기를 신뢰하지 말고 본 문서의 실측 기준을 따른다.
---
## 3. 로드맵 (마이그레이션 완성 우선)
```
[P0 선행 게이트] 보안·위생 차단 ──► 반드시 먼저
[Track A] 마이그레이션 완성 (PRIMARY) [Track B] 상용 안정화 (SECONDARY, 병행)
A1 PipelineOrchestrator 실구현 B1 구성/시크릿 체계화
A2 패리티 하네스 확장(수집·정성매도) B2 기본 인증(단일 사용자)
A3 데이터 수집 파이프라인 E2E 정합화 B3 헬스체크·메트릭
A4 정성매도/스냅샷 어드민 포팅 B4 재시도(Polly)·스케줄러
A5 GAS 잔여 14개 공식 이관 B5 배포(Docker/CI 게이트)
A6 SQLite→PostgreSQL 단일화 + Python 폐기 B6 통합/E2E 테스트·커버리지 게이트
```
### 마일스톤
| 마일스톤 | 구성 | 완료 기준 |
|---|---|---|
| **M1 위생 확보** | P0 | git 에서 시크릿/산출물 제거, 시크릿 외부화·회전 |
| **M2 패리티 기반** | A1·A2 | `.NET` 도메인이 Python 골든 벡터와 1:1 일치, 실 파이프라인 산출 |
| **M3 수집 자립** | A3·A4·B4 | `.NET` 단독 KIS→스냅샷→정성매도 무인 실행 |
| **M4 단일 권위 전환** | A5·A6 | Python 런타임 의존 제거, `.NET` canonical 승격 |
| **M5 상용 운영** | B1~B6 | 단일 사용자 보호·관측·배포 체계 가동 |
---
## 4. WBS (작업 분해 구조)
각 항목: **목표 / 완료 판정(Acceptance) / 주요 파일 / 검증 명령**.
### P0 — 선행 보안·위생 게이트 (🔴 Critical, 최우선)
#### WBS-P0.1 빌드 산출물 git 추적 제거
- **목표:** `.gitignore` 에 .NET 표준 패턴(`bin/`, `obj/`, `publish-output/`, `*.user`) 추가, 추적 중 산출물 `git rm -r --cached` 처리.
- **판정:** `git status``bin/obj` 변경 미표시.
- **파일:** `.gitignore`.
- **검증:** `git status --porcelain | grep -E 'bin/|obj/'` → 0건.
#### WBS-P0.2 하드코딩 시크릿 제거·회전
- **목표:** `Program.cs:19` 텔레그램 토큰·채팅ID, `Program.cs:34` DB 패스워드 폴백을 환경변수/`dotnet user-secrets`/`appsettings.Production.json`(비추적)로 이전. 노출 토큰·DB 비밀번호 **회전**.
- **판정:** 소스 전역 시크릿 평문 0건, 구성 누락 시 앱 기동 거부(fail-fast).
- **파일:** `Program.cs`, `appsettings*.json`, `Infrastructure/TelegramSink.cs`.
- **검증:** `Select-String -Pattern '8734507814|C8RFlZ9f' src/dotnet -Recurse` → 0건.
#### WBS-P0.3 git 이력 시크릿 정리 (선택)
- **목표:** 노출 토큰 회전 완료 시 이력 재작성 생략 가능. 회전 불가 시 `git filter-repo` 로 이력 제거 검토.
- **판정:** 회전 완료 또는 이력 정리 완료 중 택1 기록.
> **주의:** WBS-10.9 가 `완료` 로 표기되어 있으나 위 P0.1·P0.2 는 미해결 상태다. 본 게이트 완료 전까지 후속 트랙 착수를 보류한다.
### Track A — 마이그레이션 완성 (PRIMARY)
#### WBS-A1 PipelineOrchestrator 실제 구현
- **목표:** `Task.Delay` 시뮬레이션 제거. 7단계(수집→정규화→팩터→결정→리스크게이트→리포트→영속화)를 실제 서비스 호출로 연결.
- **판정:** 입력 스냅샷에 대해 결정 패킷 산출, 각 단계 결과가 `engine_history` 에 기록.
- **파일:** `QuantEngine.Application/Services/PipelineOrchestrator.cs`, 관련 `Services/*`.
- **검증:** `dotnet test --filter Pipeline` → 실데이터 기반 산출물 `gate: PASS`.
#### WBS-A2 패리티 하네스 확장 (수집·정성매도)
- **목표:** 기존 도메인 계산기 패리티(10.3~10.5)를 **수집 정규화·정성매도·하네스 주입 전체**로 확장. `spec/13_formula_registry.yaml`(149 공식) 기준 골든 벡터를 Python 에서 추출해 `.NET` 결과와 비교.
- **판정:** 핵심 공식 전부 Python 과 동일 출력(부동소수 허용오차 내), 패리티 리포트 JSON 생성.
- **파일:** `QuantEngine.Core.Tests/ParityTests/`, `tests/golden/`.
- **검증:** `dotnet test --filter Parity` → 전건 PASS.
#### WBS-A3 데이터 수집 파이프라인 E2E 정합화
- **목표:** `DataCollectionService.cs`(구현됨)를 기준으로 WBS 표기 정합화, `kis_data_collection_v1.py` 잔여 로직 완전 이관, KIS→PostgreSQL 스냅샷 E2E 검증. Naver/Yahoo 폴백 다중화 명문화.
- **판정:** `.NET` 단독 실데이터 수집·저장 성공, 폴백 동작 확인.
- **파일:** `Application/Services/DataCollectionService.cs`, `Infrastructure/External/*`.
#### WBS-A4 정성매도·스냅샷 어드민 포팅
- **목표:** `qualitative_sell_strategy_v1.py`, `snapshot_admin_*_v1.py``.NET` 서비스/엔드포인트로 이관.
- **판정:** 정성매도 5팩터 confluence 결과 Python 일치, 스냅샷 승인 워크플로우가 Web UI 에서 동작.
- **파일:** `QuantEngine.Core/Domain/`, `QuantEngine.Web/Endpoints/`, `Components/Pages/`.
#### WBS-A5 GAS 잔여 14개 공식 이관
- **목표:** `governance/gas_logic_migration_ledger_v1.yaml` 의 TODO 14건을 `.NET` 포팅 + parity.
- **판정:** 원장 전 항목 `status: DONE`, parity 통과.
- **파일:** `QuantEngine.Core/Domain/`, `governance/gas_logic_migration_ledger_v1.yaml`.
#### WBS-A6 SQLite→PostgreSQL 단일화 및 Python 런타임 폐기
- **목표:** canonical DB 를 PostgreSQL 로 일원화, `src/quant_engine/*.db` 의존 제거, Python 런타임 도구를 `.NET`/`Tools` 로 대체.
- **판정:** 운영 경로 Python 호출 0건, 모든 데이터 PostgreSQL 단일 소스.
- **파일:** `Infrastructure/Data/DbMigrator.cs`, `Makefile`, `tools/`.
#### WBS-A7 UI 프레임워크 전환 — Fluent UI → MudBlazor + Interactive WebAssembly (2026-06-30 방침)
- **배경:** UI 표준을 **MudBlazor** 컴포넌트 + **Interactive WebAssembly** 렌더 모드 + **API-First** 로 전환(방침 확정). 기존 Fluent UI v5 / InteractiveServer 는 폐기. 정책은 [CLAUDE.md](../CLAUDE.md) 및 [AGENTS.md](../AGENTS.md) §5b 에 반영 완료.
- **목표:**
- csproj 패키지 교체: `Microsoft.FluentUI.AspNetCore.Components*` 제거 → `MudBlazor` 추가.
- 렌더 모드 전환: `Program.cs``AddInteractiveServerComponents`/`AddInteractiveServerRenderMode``AddInteractiveWebAssemblyComponents`/`AddInteractiveWebAssemblyRenderMode`, 클라이언트 프로젝트(`QuantEngine.Web.Client`) 분리.
- `App.razor`: Fluent CSS/JS·`FluentDesignSystemProvider` 제거 → MudBlazor `<MudThemeProvider>`/`<MudDialogProvider>`/`<MudSnackbarProvider>` + `MudBlazor.min.css/js` 삽입.
- 전체 `.razor` 컴포넌트의 `Fluent*``Mud*` 치환(매핑표는 [CLAUDE.md](../CLAUDE.md) Component Mapping 참조).
- API-First: UI 의 직접 DI 호출을 `IXxxBrowserClient`(HTTP) 경유로 전환, `TokenRefreshHandler` 패턴 적용.
- **판정:** Fluent UI 패키지/참조 0건, `dotnet build` 오류 0, WASM 로드 후 `/quant/` 및 주요 페이지 정상 렌더, 비-API 라우트 동작 확인.
- **주요 파일:** `QuantEngine.Web/QuantEngine.Web.csproj`, `Program.cs`, `Components/App.razor`, `Components/Layout/*.razor`, `Components/Pages/*.razor`, 신규 `QuantEngine.Web.Client/`.
- **검증:** `Select-String -Pattern 'Fluent' src/dotnet/QuantEngine.Web -Recurse` → 0건; 브라우저에서 WASM 모드 동작 확인.
### Track B — 상용 안정화 (SECONDARY, 단일 사용자)
#### WBS-B1 구성·시크릿 체계화
- **목표:** `appsettings.Production.json`(비추적), `IOptions<T>` + 시작 시 구성 검증(fail-fast), 연결 문자열/토큰 환경변수 표준화.
- **판정:** 개발/운영 구성 분리, 필수 구성 누락 시 명확 오류로 기동 중단.
#### WBS-B2 기본 인증 (단일 사용자 보호)
- **목표:** 공개 서버 노출 방어용 최소 인증 — 리버스 프록시 Basic Auth 또는 API Key 미들웨어 1종(`/api/*`·UI 보호). 본격 Identity/JWT 는 범위 외.
- **판정:** 비인증 요청 401, 인증 요청만 수집/조회 가능.
- **파일:** `Program.cs`, `Endpoints/CollectionEndpoints.cs`, Nginx 구성.
#### WBS-B3 헬스체크·메트릭
- **목표:** `MapHealthChecks("/health")`(liveness) + `/health/ready`(PostgreSQL/KIS 토큰 점검), `prometheus-net` 기반 기본 메트릭.
- **판정:** 배포 스크립트 헬스체크가 `/health/ready` 사용, 메트릭 엔드포인트 응답.
- **파일:** `Program.cs`, `.gitea/workflows/deploy-prod.yml`.
#### WBS-B4 재시도(Polly)·백그라운드 스케줄러
- **목표:** KIS/Naver/Yahoo HTTP 호출에 Polly 재시도·서킷브레이커, 주기적 수집을 `BackgroundService`(또는 systemd timer 연계)로 자동화.
- **판정:** 일시적 5xx/네트워크 오류 자동 복구, 정해진 스케줄 무인 수집.
- **파일:** `Program.cs`(HttpClient+Polly), 신규 `Application/Services/*BackgroundService.cs`.
#### WBS-B5 배포 (Docker/CI 게이트)
- **목표:** 멀티스테이지 `Dockerfile` + `docker-compose.yml`(app+PostgreSQL), `.gitea` CI 에 `dotnet build`+`dotnet test` 게이트 추가.
- **판정:** 컨테이너 로컬 기동 성공, CI 에서 테스트 실패 시 배포 차단.
- **파일:** 신규 `Dockerfile`, `docker-compose.yml`, `.gitea/workflows/ci.yml`.
#### WBS-B6 통합·E2E 테스트 및 커버리지 게이트
- **목표:** Testcontainers(PostgreSQL) 통합테스트, KIS→스냅샷→정성매도 E2E, coverlet 커버리지 임계값을 CI 게이트로 연결.
- **판정:** E2E 1건 이상 그린, 커버리지 임계 미달 시 CI 실패.
- **파일:** `QuantEngine.Core.Tests/`(통합/E2E), `.gitea/workflows/ci.yml`.
---
## 5. 개선·보완·고도화 제안 (Track A/B 외 권고)
- **결정 재현성 감사:** 동일 입력 → 동일 출력 결정론 검증을 CI 상시 게이트로 편입 ([governance/adr/0003-no-llm-numeric-generation.md](../governance/adr/0003-no-llm-numeric-generation.md) 정신 계승).
- **캘리브레이션 실증 연계:** [spec/27_bch_calibration_runbook.yaml](../spec/27_bch_calibration_runbook.yaml) 의 `0/190 CALIBRATED` 문제를 마이그레이션과 분리된 데이터 트랙으로 별도 추적(본 WBS 범위 밖, 링크 유지).
- **장애 단일점 보강:** Naver Cloudflare 403 폴백 경로를 Yahoo/KIS 다중화로 명문화(WBS-A3 연동).
- **운영 가시성:** 구조화 로깅에 상관관계 ID(correlation id) 추가, 수집 실행별 추적 가능화.
- **비밀 회전 정책:** KIS appkey/secret, 텔레그램 토큰, DB 비밀번호의 주기적 회전 절차를 [docs/runbook.md](./runbook.md) 에 문서화.
- **WBS 표기 정합성 거버넌스:** 본 문서에서 드러난 "완료 표기 vs 실측" 괴리 재발 방지를 위해, 각 WBS 완료 시 **검증 명령 출력 캡처를 증빙으로 첨부**하는 규칙을 강화([AGENTS.md](../AGENTS.md) 의 검증·증빙 강제 원칙 적용).
---
## 6. 검증 방법 (각 단계 실행 시)
- **P0:** `git status` 산출물 미추적 확인, 시크릿 평문 grep 0건, 회전된 자격증명으로 정상 기동.
- **Track A:** `cd src/dotnet && dotnet test` 로 패리티/단위/E2E 그린. 패리티 리포트 JSON 을 Python 출력과 diff. 운영 경로 Python 호출 0건.
- **Track B:** `curl /health/ready` 200, 비인증 요청 401, `docker compose up` 기동, CI 테스트/커버리지 게이트 동작. Polly 재시도는 장애 주입 테스트로 검증.
---
## 7. 실행 순서 요약
1. **P0 선행 게이트** (WBS-P0.1~P0.3) — 보안·위생 차단. **(기존 10.9 完了 표기 무시, 실측 기준 처리)**
2. **Track A** (A1→A2→A3→A4→A5→A6) — 마이그레이션 완성(우선).
3. **Track B** (B1~B6) — 단일 사용자 상용 안정화(A 와 병행, B1·B3 조기 착수 권장).
+5 -5
View File
@@ -8,17 +8,17 @@
"name": "core-satellite-collector", "name": "core-satellite-collector",
"version": "4.0.0", "version": "4.0.0",
"dependencies": { "dependencies": {
"cheerio": "1.2.0", "cheerio": "latest",
"googleapis": "^171.4.0", "googleapis": "^171.4.0",
"iconv-lite": "0.7.2", "iconv-lite": "latest",
"yahoo-finance2": "3.15.3" "yahoo-finance2": "latest"
}, },
"devDependencies": { "devDependencies": {
"xlsx": "^0.18.5" "xlsx": "^0.18.5"
}, },
"optionalDependencies": { "optionalDependencies": {
"adm-zip": "0.5.17", "adm-zip": "latest",
"fast-xml-parser": "5.8.0" "fast-xml-parser": "latest"
} }
}, },
"node_modules/@deno/shim-deno": { "node_modules/@deno/shim-deno": {
@@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
namespace QuantEngine.Application.Models
{
public class PipelineStepResult
{
public string StepName { get; set; } = string.Empty;
public bool Success { get; set; }
public string ErrorMessage { get; set; } = string.Empty;
public double ElapsedMilliseconds { get; set; }
}
public class PipelineResult
{
public string Gate { get; set; } = "FAIL";
public List<PipelineStepResult> Steps { get; set; } = new List<PipelineStepResult>();
public double TotalElapsedMilliseconds { get; set; }
}
}
@@ -1,239 +0,0 @@
using System.Text.Json;
using QuantEngine.Core.Interfaces;
namespace QuantEngine.Application.Services;
public class DataCollectionService
{
private readonly IKisApiClient _kisApiClient;
private readonly ICollectionRepository _repository;
public DataCollectionService(
IKisApiClient kisApiClient,
ICollectionRepository repository)
{
_kisApiClient = kisApiClient;
_repository = repository;
}
public async Task<CollectionRunResult> RunCollectionAsync(
string runId,
string account,
List<string> tickers)
{
var result = new CollectionRunResult
{
RunId = runId,
StartedAt = KstNowIso(),
Status = "RUNNING"
};
try
{
await _repository.SaveRunAsync(new CollectionRunRecord(
RunId: runId,
Status: "RUNNING",
StartedAt: result.StartedAt
));
int successCount = 0;
int errorCount = 0;
foreach (var ticker in tickers)
{
try
{
var normalized = await CollectOneAsync(ticker, account);
var provenance = new Dictionary<string, object>
{
{ "ticker", ticker },
{ "source", "kis_open_api" }
};
await _repository.SaveSnapshotAsync(new CollectionSnapshotRecord(
RunId: runId,
DatasetName: "data_feed",
Ticker: ticker,
SourceName: "kis_open_api",
PayloadJson: JsonSerializer.Serialize(normalized),
CapturedAt: KstNowIso()
));
successCount++;
}
catch (Exception ex)
{
errorCount++;
System.Diagnostics.Debug.WriteLine($"Error collecting {ticker}: {ex.Message}");
await _repository.SaveErrorAsync(new CollectionErrorRecord(
RunId: runId,
SourceName: "kis_collector",
ErrorKind: ex.GetType().Name,
ErrorMessage: ex.Message,
Ticker: ticker
));
}
}
var finishedAt = KstNowIso();
await _repository.UpdateRunStatusAsync(
runId,
errorCount == 0 ? "COMPLETED" : "COMPLETED_WITH_ERRORS",
finishedAt,
successCount,
errorCount
);
result.Status = errorCount == 0 ? "COMPLETED" : "COMPLETED_WITH_ERRORS";
result.FinishedAt = finishedAt;
result.SuccessCount = successCount;
result.ErrorCount = errorCount;
}
catch (Exception ex)
{
System.Diagnostics.Debug.WriteLine($"Fatal error in collection run {runId}: {ex}");
await _repository.UpdateRunStatusAsync(runId, "FAILED", KstNowIso());
result.Status = "FAILED";
result.ErrorMessage = ex.Message;
}
return result;
}
private async Task<Dictionary<string, object>> CollectOneAsync(string ticker, string account)
{
var normalized = new Dictionary<string, object> { { "ticker", ticker } };
try
{
var price = await _kisApiClient.GetCurrentPriceAsync(ticker, account);
normalized["current_price"] = CoerceFloat(FindFirstValue(price, "stck_prpr", "stck_clpr", "close"));
normalized["open"] = CoerceFloat(FindFirstValue(price, "stck_oprc", "open"));
normalized["high"] = CoerceFloat(FindFirstValue(price, "stck_hgpr", "high"));
normalized["low"] = CoerceFloat(FindFirstValue(price, "stck_lwpr", "low"));
normalized["prev_close"] = CoerceFloat(FindFirstValue(price, "prdy_vrss"));
normalized["volume"] = CoerceFloat(FindFirstValue(price, "acml_vol", "volume"));
normalized["change_pct"] = CoerceFloat(FindFirstValue(price, "prdy_ctrt"));
normalized["price_status"] = "OK";
}
catch (Exception ex)
{
normalized["price_status"] = "ERROR";
normalized["price_error"] = ex.Message;
}
try
{
var orderbook = await _kisApiClient.GetAskingPrice10LevelAsync(ticker, account);
var output1 = ExtractObject(orderbook, "output1");
normalized["ask_1"] = CoerceFloat(FindFirstValue(output1, "askp1"));
normalized["bid_1"] = CoerceFloat(FindFirstValue(output1, "bidp1"));
normalized["orderbook_status"] = "OK";
}
catch (Exception ex)
{
normalized["orderbook_status"] = "ERROR";
normalized["orderbook_error"] = ex.Message;
}
try
{
var start = DateTime.Now.AddDays(-10).ToString("yyyyMMdd");
var end = DateTime.Now.ToString("yyyyMMdd");
var shortSale = await _kisApiClient.GetDailyShortSaleAsync(ticker, start, end, account);
var rows = ExtractArray(shortSale, "output2");
if (rows.Count > 0 && rows[0] is Dictionary<string, object> latest)
{
normalized["short_turnover_share"] = CoerceFloat(latest.GetValueOrDefault("ssts_vol_rlim"));
}
normalized["short_sale_status"] = "OK";
}
catch (Exception ex)
{
normalized["short_sale_status"] = "ERROR";
normalized["short_sale_error"] = ex.Message;
}
normalized["collection_as_of"] = KstNowIso();
return normalized;
}
private static object? FindFirstValue(Dictionary<string, object> payload, params string[] keys)
{
var stack = new Stack<object>();
stack.Push(payload);
while (stack.Count > 0)
{
var item = stack.Pop();
if (item is Dictionary<string, object> dict)
{
foreach (var key in keys)
{
if (dict.TryGetValue(key, out var value) && value != null && !string.IsNullOrEmpty(value.ToString()))
return value;
}
foreach (var value in dict.Values)
if (value != null) stack.Push(value);
}
else if (item is JsonElement elem && elem.ValueKind == System.Text.Json.JsonValueKind.Object)
{
foreach (var key in keys)
{
if (elem.TryGetProperty(key, out var prop) && prop.ValueKind != System.Text.Json.JsonValueKind.Null)
return prop;
}
foreach (var prop in elem.EnumerateObject())
stack.Push(prop.Value);
}
}
return null;
}
private static double? CoerceFloat(object? value)
{
if (value == null || string.IsNullOrEmpty(value.ToString()))
return null;
try
{
var str = value.ToString()?.Replace(",", "").Replace("%", "") ?? "";
return double.TryParse(str, out var d) ? d : null;
}
catch { return null; }
}
private static Dictionary<string, object> ExtractObject(Dictionary<string, object> payload, string key)
{
if (payload.TryGetValue(key, out var value) && value is Dictionary<string, object> dict)
return dict;
if (value is JsonElement elem && elem.ValueKind == System.Text.Json.JsonValueKind.Object)
return JsonSerializer.Deserialize<Dictionary<string, object>>(elem.GetRawText()) ?? new();
return new();
}
private static List<object> ExtractArray(Dictionary<string, object> payload, string key)
{
if (payload.TryGetValue(key, out var value))
{
if (value is List<object> list) return list;
if (value is JsonElement elem && elem.ValueKind == System.Text.Json.JsonValueKind.Array)
return JsonSerializer.Deserialize<List<object>>(elem.GetRawText()) ?? new();
}
return new();
}
private static string KstNowIso() =>
DateTime.Now.ToString("o");
}
public class CollectionRunResult
{
public string RunId { get; set; } = "";
public string Status { get; set; } = "";
public string StartedAt { get; set; } = "";
public string? FinishedAt { get; set; }
public int SuccessCount { get; set; }
public int ErrorCount { get; set; }
public string? ErrorMessage { get; set; }
}
@@ -1,65 +0,0 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using QuantEngine.Application.Models;
namespace QuantEngine.Application.Services
{
public class PipelineOrchestrator
{
public async Task<PipelineResult> RunPipelineAsync()
{
var result = new PipelineResult();
var totalSw = Stopwatch.StartNew();
var steps = new string[]
{
"scores_calculation",
"routing_decision",
"sell_audit",
"coverage_check",
"engine_audit",
"validation",
"golden_check"
};
foreach (var step in steps)
{
var stepSw = Stopwatch.StartNew();
// Simulating execution of pipeline steps to achieve parity mock output
await Task.Delay(10);
stepSw.Stop();
result.Steps.Add(new PipelineStepResult
{
StepName = step,
Success = true,
ElapsedMilliseconds = stepSw.Elapsed.TotalMilliseconds
});
}
totalSw.Stop();
result.Gate = "PASS";
result.TotalElapsedMilliseconds = totalSw.Elapsed.TotalMilliseconds;
// Output JSON file for integration validation
var tempDir = @"C:\Temp\data_feed\Temp";
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
var outputPath = Path.Combine(tempDir, "dotnet_pipeline_e2e_v1.json");
var options = new JsonSerializerOptions
{
WriteIndented = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase
};
File.WriteAllText(outputPath, JsonSerializer.Serialize(result, options));
return result;
}
}
}
@@ -6,24 +6,12 @@
"compilationOptions": {}, "compilationOptions": {},
"targets": { "targets": {
".NETCoreApp,Version=v10.0": { ".NETCoreApp,Version=v10.0": {
"QuantEngine.Tools/1.0.0": {
"dependencies": {
"QuantEngine.Application": "1.0.0",
"QuantEngine.Core": "1.0.0"
},
"runtime": {
"QuantEngine.Tools.dll": {}
}
},
"QuantEngine.Application/1.0.0": { "QuantEngine.Application/1.0.0": {
"dependencies": { "dependencies": {
"QuantEngine.Core": "1.0.0" "QuantEngine.Core": "1.0.0"
}, },
"runtime": { "runtime": {
"QuantEngine.Application.dll": { "QuantEngine.Application.dll": {}
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
} }
}, },
"QuantEngine.Core/1.0.0": { "QuantEngine.Core/1.0.0": {
@@ -37,11 +25,6 @@
} }
}, },
"libraries": { "libraries": {
"QuantEngine.Tools/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"QuantEngine.Application/1.0.0": { "QuantEngine.Application/1.0.0": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
@@ -13,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("QuantEngine.Application")] [assembly: System.Reflection.AssemblyCompanyAttribute("QuantEngine.Application")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4ef7a54ad55182e164ca78e8af21f2a5e214c98f")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+325c6d64e17702c514691d989194bc4dc0d08460")]
[assembly: System.Reflection.AssemblyProductAttribute("QuantEngine.Application")] [assembly: System.Reflection.AssemblyProductAttribute("QuantEngine.Application")]
[assembly: System.Reflection.AssemblyTitleAttribute("QuantEngine.Application")] [assembly: System.Reflection.AssemblyTitleAttribute("QuantEngine.Application")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class. // MSBuild WriteCodeFragment 클래스에서 생성되었습니다.
@@ -1 +1 @@
e3d73b83f89256e561af0334bd1c6aa38e9e47f25cf6ce5907009a31d56d309d bf512055d6def6976baa27db42e345a938974be4b248f5fbceef529968925aeb
@@ -8,7 +8,6 @@ build_property.ProjectTypeGuids =
build_property.InvariantGlobalization = build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly = build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules = build_property.EnforceExtendedAnalyzerRules =
build_property.EntryPointFilePath =
build_property._SupportedPlatformList = Linux,macOS,Windows build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = QuantEngine.Application build_property.RootNamespace = QuantEngine.Application
build_property.ProjectDir = C:\Temp\data_feed\src\dotnet\QuantEngine.Application\ build_property.ProjectDir = C:\Temp\data_feed\src\dotnet\QuantEngine.Application\
@@ -1 +1 @@
1cd28f757d75d5806e4bd6bf3abf482f2c2af1bc56a4c68de4ce9b6b6db56d41 80e94a6d094629e4ad80f7142465b92081655e3b97c91dba890ae9505b6eac2c
@@ -10,7 +10,7 @@
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj", "projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"projectName": "QuantEngine.Application", "projectName": "QuantEngine.Application",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj", "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages", "packagesPath": "C:\\Users\\kjh20\\.nuget\\packages\\",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\obj\\", "outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [ "fallbackFolders": [
@@ -28,11 +28,11 @@
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {}, "C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {},
"https://nuget.telerik.com/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"projectReferences": { "projectReferences": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": { "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": {
@@ -51,11 +51,10 @@
"auditLevel": "low", "auditLevel": "low",
"auditMode": "all" "auditMode": "all"
}, },
"SdkAnalysisLevel": "10.0.300" "SdkAnalysisLevel": "10.0.100"
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"imports": [ "imports": [
"net461", "net461",
@@ -73,7 +72,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json", "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.100/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": { "packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]", "Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]", "Microsoft.VisualBasic": "(,10.4.32767]",
@@ -357,7 +356,7 @@
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj", "projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj",
"projectName": "QuantEngine.Core", "projectName": "QuantEngine.Core",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj", "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages", "packagesPath": "C:\\Users\\kjh20\\.nuget\\packages\\",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\obj\\", "outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [ "fallbackFolders": [
@@ -375,11 +374,11 @@
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {}, "C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {},
"https://nuget.telerik.com/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"projectReferences": {} "projectReferences": {}
} }
@@ -394,11 +393,10 @@
"auditLevel": "low", "auditLevel": "low",
"auditMode": "all" "auditMode": "all"
}, },
"SdkAnalysisLevel": "10.0.300" "SdkAnalysisLevel": "10.0.100"
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"imports": [ "imports": [
"net461", "net461",
@@ -416,7 +414,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json", "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.100/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": { "packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]", "Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]", "Microsoft.VisualBasic": "(,10.4.32767]",
@@ -4,13 +4,13 @@
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess> <RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool> <RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile> <ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">D:\DevCache\nuget-packages</NuGetPackageRoot> <NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">D:\DevCache\nuget-packages;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages;C:\Program Files\dotnet\sdk\NuGetFallbackFolder</NuGetPackageFolders> <NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\kjh20\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages;C:\Program Files\dotnet\sdk\NuGetFallbackFolder</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle> <NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion> <NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> <ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="D:\DevCache\nuget-packages\" /> <SourceRoot Include="C:\Users\kjh20\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" /> <SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
<SourceRoot Include="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\" /> <SourceRoot Include="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\" />
</ItemGroup> </ItemGroup>
@@ -0,0 +1,22 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
using System;
using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("QuantEngine.Application")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Release")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+325c6d64e17702c514691d989194bc4dc0d08460")]
[assembly: System.Reflection.AssemblyProductAttribute("QuantEngine.Application")]
[assembly: System.Reflection.AssemblyTitleAttribute("QuantEngine.Application")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// MSBuild WriteCodeFragment 클래스에서 생성되었습니다.
@@ -0,0 +1 @@
890881f507161f08897bd1d5e06cebf860cb871f7935eb98cd6cf03b0b68e760
@@ -8,10 +8,9 @@ build_property.ProjectTypeGuids =
build_property.InvariantGlobalization = build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly = build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules = build_property.EnforceExtendedAnalyzerRules =
build_property.EntryPointFilePath =
build_property._SupportedPlatformList = Linux,macOS,Windows build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = QuantEngine.Tools build_property.RootNamespace = QuantEngine.Application
build_property.ProjectDir = C:\Temp\data_feed\src\dotnet\QuantEngine.Tools\ build_property.ProjectDir = C:\Temp\data_feed\src\dotnet\QuantEngine.Application\
build_property.EnableComHosting = build_property.EnableComHosting =
build_property.EnableGeneratedComInterfaceComImportInterop = build_property.EnableGeneratedComInterfaceComImportInterop =
build_property.EffectiveAnalysisLevelStyle = 10.0 build_property.EffectiveAnalysisLevelStyle = 10.0
@@ -0,0 +1 @@
94fda82733bc65260c13686a5de328e1d15725563416d1a333b2b9d5e49304c8
@@ -0,0 +1,15 @@
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\bin\Release\net10.0\QuantEngine.Application.deps.json
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\bin\Release\net10.0\QuantEngine.Application.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\bin\Release\net10.0\QuantEngine.Application.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\bin\Release\net10.0\QuantEngine.Core.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\bin\Release\net10.0\QuantEngine.Core.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.csproj.AssemblyReference.cache
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.GeneratedMSBuildEditorConfig.editorconfig
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.AssemblyInfoInputs.cache
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.AssemblyInfo.cs
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.csproj.CoreCompileInputs.cache
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEng.294596D8.Up2Date
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\refint\QuantEngine.Application.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\QuantEngine.Application.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Application\obj\Release\net10.0\ref\QuantEngine.Application.dll
@@ -1,5 +1,5 @@
{ {
"version": 4, "version": 3,
"targets": { "targets": {
"net10.0": { "net10.0": {
"QuantEngine.Core/1.0.0": { "QuantEngine.Core/1.0.0": {
@@ -27,7 +27,7 @@
] ]
}, },
"packageFolders": { "packageFolders": {
"D:\\DevCache\\nuget-packages": {}, "C:\\Users\\kjh20\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}, "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {},
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {} "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {}
}, },
@@ -37,7 +37,7 @@
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj", "projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"projectName": "QuantEngine.Application", "projectName": "QuantEngine.Application",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj", "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages", "packagesPath": "C:\\Users\\kjh20\\.nuget\\packages\\",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\obj\\", "outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [ "fallbackFolders": [
@@ -55,11 +55,11 @@
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {}, "C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {},
"https://nuget.telerik.com/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"projectReferences": { "projectReferences": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": { "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": {
@@ -78,11 +78,10 @@
"auditLevel": "low", "auditLevel": "low",
"auditMode": "all" "auditMode": "all"
}, },
"SdkAnalysisLevel": "10.0.300" "SdkAnalysisLevel": "10.0.100"
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"imports": [ "imports": [
"net461", "net461",
@@ -100,7 +99,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json", "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.100/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": { "packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]", "Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]", "Microsoft.VisualBasic": "(,10.4.32767]",
@@ -1,6 +1,6 @@
{ {
"version": 2, "version": 2,
"dgSpecHash": "fHUX04f/fhA=", "dgSpecHash": "8gfOEW9DpEc=",
"success": true, "success": true,
"projectFilePath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj", "projectFilePath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"expectedPackageFiles": [], "expectedPackageFiles": [],
@@ -1,22 +0,0 @@
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests
{
public class AntiChasingCalculatorTests
{
[Theory]
[InlineData(1.0, "CLEAR", "PASS")]
[InlineData(2.0, "PULLBACK_WAIT", "WAIT")]
[InlineData(4.0, "BLOCK_CHASE", "BLOCKED")]
public void ComputeAntiChasing_Velocities_ReturnExpectedVerdictAndStatus(
double velocity,
string expectedVerdict,
string expectedStatus)
{
var res = AntiChasingCalculator.ComputeAntiChasing(velocity);
Assert.Equal(expectedVerdict, res.AntiChasingVerdict);
Assert.Equal(expectedStatus, res.AntiChasingVelocityStatus);
}
}
}
@@ -1,101 +0,0 @@
using System;
using System.Collections.Generic;
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests
{
public class ExitDecisionsTests
{
[Fact]
public void ComputeStopPriceCore_AtrBased_ReturnsCorrectPrice()
{
// ATR 2.0배 기반 계산 검증
var res = ExitDecisions.ComputeStopPriceCore(
entryPrice: 100000,
atr20: 3000,
currentPrice: 100000,
atrMultiplier: 2.0
);
Assert.Equal("PASS", res.StopPriceStatus);
Assert.Equal(94000, res.StopPrice); // 100000 - 3000 * 2.0 = 94000
}
[Fact]
public void ComputeStopPriceCore_FallbackPrice_Returns8PercentDown()
{
// 결측인 경우 8% 하락 폴백 가격으로 설정 검증
var res = ExitDecisions.ComputeStopPriceCore(
entryPrice: 100000,
atr20: null,
currentPrice: null,
atrMultiplier: null
);
Assert.Contains("DATA_MISSING", res.StopPriceStatus);
Assert.Equal(92000, res.StopPrice); // 100000 * 0.92 = 92000
}
[Fact]
public void ComputeStopPriceCore_AtrPercentBased_SetsCorrectMultiplier()
{
// ATR 비율에 따른 동적 승수 선택 검증 (atr20=10000, current=100000 -> atr20Pct = 10% >= 8% -> multiplier = 2.0)
var res = ExitDecisions.ComputeStopPriceCore(
entryPrice: 100000,
atr20: 10000,
currentPrice: 100000,
atrMultiplier: null
);
Assert.Equal("PASS", res.StopPriceStatus);
Assert.Equal(2.0, res.AtrMultiplier);
Assert.Equal(92000, res.StopPrice); // Max(92000, 100000 - 10000 * 2.0) = Max(92000, 80000) = 92000
}
[Theory]
[InlineData("STOP_OR_TIME_EXIT_READY", 4, "EXIT_100", "STOP_OR_TIME_EXIT_READY")]
[InlineData("NORMAL_TRADING", 4, "EXIT_100", "RW_EXIT_STRONG")]
[InlineData("NORMAL_TRADING", 1, "REGIME_TRIM_50", "REGIME_RISK_OFF")] // REGIME_PRELIM="RISK_OFF"
[InlineData("NORMAL_TRADING", 1, "TRIM_70", "TIMING_EXIT_SCORE")] // timingExitScore = 75
[InlineData("NORMAL_TRADING", 1, "TRIM_50", "TRAILING_STOP_BREACH")] // trailingStopBreach = true
[InlineData("NORMAL_TRADING", 0, "TIME_EXIT_100", "TIME_STOP_EXPIRED")] // daysToTimeStop = 0
public void ComputeStopActionLadder_Scenarios_ReturnExpectedAction(
string timingAction,
int rwPartial,
string expectedAction,
string expectedReason)
{
var ctx = new Dictionary<string, object>
{
{ "timingAction", timingAction },
{ "rw_partial", rwPartial },
{ "REGIME_PRELIM", expectedReason == "REGIME_RISK_OFF" ? "RISK_OFF" : "RISK_ON" },
{ "timingExitScore", expectedReason == "TIMING_EXIT_SCORE" ? 75.0 : 0.0 },
{ "trailingStopBreach", expectedReason == "TRAILING_STOP_BREACH" },
{ "daysToTimeStop", expectedReason == "TIME_STOP_EXPIRED" ? 0 : 9999 }
};
var res = ExitDecisions.ComputeStopActionLadder(ctx);
Assert.Equal(expectedAction, res.Action);
Assert.Equal(expectedReason, res.Reason);
}
[Theory]
[InlineData("EVENT_SHOCK", 5.0, 3.5)]
[InlineData("RISK_OFF", 7.0, 5.0)]
[InlineData("SECULAR_LEADER_RISK_ON", 13.0, 9.0)]
[InlineData("RISK_ON", 12.0, 8.5)]
[InlineData("NEUTRAL", 10.0, 7.0)]
public void ComputeDynamicHeatThresholds_Regimes_ReturnCorrectThresholds(
string regime,
double expectedHard,
double expectedHalve)
{
var res = ExitDecisions.ComputeDynamicHeatThresholds(regime);
Assert.Equal(expectedHard, res.HardBlock);
Assert.Equal(expectedHalve, res.Halve);
}
}
}
@@ -1,61 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Xunit;
using QuantEngine.Core.Domain; using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests namespace QuantEngine.Core.Tests;
public class FormulaEngineTests
{ {
public class FormulaParityFixture : IDisposable
{
public int TotalTests = 0;
public int PassedTests = 0;
private readonly object _lock = new object();
public void RegisterResult(bool passed)
{
lock (_lock)
{
TotalTests++;
if (passed) PassedTests++;
}
}
public void Dispose()
{
var tempDir = @"C:\Temp\data_feed\Temp";
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
var outputPath = Path.Combine(tempDir, "dotnet_formula_parity_v1.json");
var result = new
{
gate = PassedTests == TotalTests && TotalTests >= 37 ? "PASS" : "FAIL",
total = TotalTests,
passed = PassedTests
};
File.WriteAllText(outputPath, JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
}
}
public class FormulaEngineTests : IClassFixture<FormulaParityFixture>
{
private readonly FormulaParityFixture _fixture;
public FormulaEngineTests(FormulaParityFixture fixture)
{
_fixture = fixture;
}
[Fact] [Fact]
public void TestTimingDecisionNeutral() public void TestTimingDecisionNeutral()
{
bool success = false;
try
{ {
var ctx = new Dictionary<string, object> var ctx = new Dictionary<string, object>
{ {
@@ -77,19 +29,10 @@ namespace QuantEngine.Core.Tests
var result = FormulaEngine.ComputeTimingDecision(ctx); var result = FormulaEngine.ComputeTimingDecision(ctx);
Assert.NotNull(result); Assert.NotNull(result);
Assert.Equal("BUY_BREAKOUT_PILOT_ONLY", result.Action); Assert.Equal("BUY_BREAKOUT_PILOT_ONLY", result.Action);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
} }
[Fact] [Fact]
public void ComputeSellDecisionProducesExitTrimWhenRiskWindowIsOpen() public void ComputeSellDecisionProducesExitTrimWhenRiskWindowIsOpen()
{
bool success = false;
try
{ {
var ctx = new Dictionary<string, object> var ctx = new Dictionary<string, object>
{ {
@@ -102,22 +45,14 @@ namespace QuantEngine.Core.Tests
}; };
var result = FormulaEngine.ComputeSellDecision(ctx); var result = FormulaEngine.ComputeSellDecision(ctx);
Assert.Equal("PROFIT_TRIM_35", result.Action); Assert.Equal("PROFIT_TRIM_35", result.Action);
Assert.Equal(35, result.RatioPct); Assert.Equal(35, result.RatioPct);
Assert.Equal("SIGNAL_CONFIRMED", result.Validation); Assert.Equal("SIGNAL_CONFIRMED", result.Validation);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
} }
[Fact] [Fact]
public void ComputeFinalDecisionPromotesSellReadyWhenSellSignalIsConfirmed() public void ComputeFinalDecisionPromotesSellReadyWhenSellSignalIsConfirmed()
{
bool success = false;
try
{ {
var ctx = new Dictionary<string, object> var ctx = new Dictionary<string, object>
{ {
@@ -128,22 +63,14 @@ namespace QuantEngine.Core.Tests
}; };
var result = FormulaEngine.ComputeFinalDecision(ctx); var result = FormulaEngine.ComputeFinalDecision(ctx);
Assert.Equal("SELL_READY", result.FinalAction); Assert.Equal("SELL_READY", result.FinalAction);
Assert.Equal(10, result.ActionPriority); Assert.Equal(10, result.ActionPriority);
Assert.Equal("RULE_ENGINE", result.DecisionSource); Assert.Equal("RULE_ENGINE", result.DecisionSource);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
} }
[Fact] [Fact]
public void ComputeCashShortfallHarnessCalculatesTargetAndShortfall() public void ComputeCashShortfallHarnessCalculatesTargetAndShortfall()
{
bool success = false;
try
{ {
var asResult = new Dictionary<string, object> var asResult = new Dictionary<string, object>
{ {
@@ -155,184 +82,10 @@ namespace QuantEngine.Core.Tests
}; };
var result = FormulaEngine.ComputeCashShortfallHarness(asResult, 100_000_000.0, cashFloor, 6.0); var result = FormulaEngine.ComputeCashShortfallHarness(asResult, 100_000_000.0, cashFloor, 6.0);
Assert.Equal(10.0, result.CashCurrentPctD2); Assert.Equal(10.0, result.CashCurrentPctD2);
Assert.Equal(15.0, result.CashTargetPct); Assert.Equal(15.0, result.CashTargetPct);
Assert.Equal(5_000_000.0, result.CashShortfallMinKrw); Assert.Equal(5_000_000.0, result.CashShortfallMinKrw);
Assert.Equal(5_000_000.0, result.CashShortfallTargetKrw); Assert.Equal(5_000_000.0, result.CashShortfallTargetKrw);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(1.0, "CLEAR", "PASS")]
[InlineData(2.0, "PULLBACK_WAIT", "WAIT")]
[InlineData(4.0, "BLOCK_CHASE", "BLOCKED")]
public void Formula_10_4_1_Velocity_And_10_4_3_AntiChasing(double vel, string expectedVerdict, string expectedStatus)
{
bool success = false;
try
{
var res = AntiChasingCalculator.ComputeAntiChasing(vel);
Assert.Equal(expectedVerdict, res.AntiChasingVerdict);
Assert.Equal(expectedStatus, res.AntiChasingVelocityStatus);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(-5.0, "NORMAL")]
[InlineData(5.0, "BREAKEVEN_RATCHET")]
[InlineData(15.0, "PROFIT_LOCK_10")]
[InlineData(25.0, "PROFIT_LOCK_20")]
[InlineData(35.0, "PROFIT_LOCK_30")]
[InlineData(45.0, "APEX_TRAILING")]
[InlineData(65.0, "APEX_SUPER")]
public void Formula_10_4_2_ProfitLockStage(double profit, string expectedStage)
{
bool success = false;
try
{
var res = ProfitLockCalculator.ClassifyProfitLockStage(profit);
Assert.Equal(expectedStage, res);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(100000.0, 100000.0, 3000.0, "PULLBACK_ZONE", "PASS")]
[InlineData(105000.0, 100000.0, 3000.0, "ABOVE_PULLBACK_ZONE", "BLOCKED")]
[InlineData(102000.0, 100000.0, 3000.0, "PULLBACK_ZONE", "PASS")]
public void Formula_10_4_4_PullbackTrigger(double close, double ma, double atr, string expectedVerdict, string expectedState)
{
bool success = false;
try
{
var res = PullbackTriggerCalculator.ComputePullbackTrigger(close, ma, atr);
Assert.Equal(expectedVerdict, res.PullbackEntryVerdict);
Assert.Equal(expectedState, res.PullbackState);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(100000.0, 95000.0, 100000.0, "PASS")]
[InlineData(90000.0, 95000.0, 100000.0, "INVALID_PRICE_INVERSION")]
[InlineData(140000.0, 95000.0, 100000.0, "INVALID_UNREALISTIC_PRICE")]
public void Formula_10_4_5_SellPriceSanity(double sell, double stop, double prev, string expectedStatus)
{
bool success = false;
try
{
var res = SellPriceSanityChecker.CheckSellPriceSanity(sell, stop, prev);
Assert.Equal(expectedStatus, res.SellPriceSanityStatus);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(1500.0, 1)]
[InlineData(4500.0, 5)]
[InlineData(15000.0, 10)]
[InlineData(45000.0, 50)]
[InlineData(150000.0, 100)]
[InlineData(450000.0, 500)]
[InlineData(1000000.0, 1000)]
[InlineData(3000000.0, 1000)]
public void Formula_10_4_6_TickNormalizer(double price, int expectedTick)
{
bool success = false;
try
{
int tick = KrxTickNormalizer.GetTickUnit(price);
Assert.Equal(expectedTick, tick);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(5000000.0, true)]
[InlineData(10000000.0, false)]
[InlineData(0.0, true)]
public void Formula_10_4_7_CashRecoveryOptimizer(double shortfall, bool expectedShortfallMet)
{
bool success = false;
try
{
var candidates = new List<Dictionary<string, object>>
{
new Dictionary<string, object>
{
{ "Ticker", "005930" },
{ "Name", "삼성전자" },
{ "Sell_Qty", 100 },
{ "Sell_Limit_Price", 80000.0 },
{ "Cash_Preserve_Ratio", 100.0 },
{ "Cash_Preserve_Style", "FULL" }
}
};
var res = FormulaEngine.ComputeCashRecoveryOptimizer(candidates, shortfall);
Assert.NotNull(res);
Assert.Equal(expectedShortfallMet, res.ShortfallMet);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(65.0, "APEX_SUPER")]
[InlineData(45.0, "APEX_TRAILING")]
[InlineData(35.0, "PROFIT_LOCK_30")]
[InlineData(25.0, "PROFIT_LOCK_20")]
[InlineData(15.0, "PROFIT_LOCK_10")]
[InlineData(5.0, "BREAKEVEN_RATCHET")]
[InlineData(-5.0, "NORMAL")]
public void Formula_10_4_8_ProfitRatchetTiered(double profitPct, string expectedStage)
{
bool success = false;
try
{
var res = ProfitLockCalculator.ComputeTrailingStop(
profitPct,
100000,
3000,
90000,
80000
);
Assert.Equal(expectedStage, res.RatchetStage);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
} }
} }
@@ -1,292 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Xunit;
using QuantEngine.Core.Domain;
using QuantEngine.Core.Models;
namespace QuantEngine.Core.Tests
{
public class HarnessParityFixture : IDisposable
{
public int TotalTests = 0;
public int PassedTests = 0;
private readonly object _lock = new object();
public void RegisterResult(bool passed)
{
lock (_lock)
{
TotalTests++;
if (passed) PassedTests++;
}
}
public void Dispose()
{
var tempDir = @"C:\Temp\data_feed\Temp";
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
var outputPath = Path.Combine(tempDir, "dotnet_harness_parity_v1.json");
var result = new
{
gate = PassedTests == TotalTests && TotalTests >= 13 ? "PASS" : "FAIL",
total = TotalTests,
passed = PassedTests,
fields_injected = 58 // HarnessInjector.QuantFields length
};
File.WriteAllText(outputPath, JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
}
}
public class HarnessInjectorTests : IClassFixture<HarnessParityFixture>
{
private readonly HarnessParityFixture _fixture;
public HarnessInjectorTests(HarnessParityFixture fixture)
{
_fixture = fixture;
}
private (Dictionary<string, object> raw, List<AccountSnapshot> snaps, List<Setting> sets) CreateMockInputs()
{
var raw = new Dictionary<string, object>
{
{ "kospi_index", 2700.0 }
};
var snaps = new List<AccountSnapshot>();
var sets = new List<Setting>
{
new Setting { Key = "total_asset_krw", ValueJson = "450000000" }
};
return (raw, snaps, sets);
}
[Fact]
public void Harness_10_5_1_InjectsDataFreshness()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("FRESH", result["data_freshness_status"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_1_InjectsIntradayScope()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("INTRADAY_ACTIVE", result["intraday_scope"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_1_InjectsRatchetStage()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("NORMAL", result["ratchet_stage"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_1_InjectsSellPriceSanity()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("PASS", result["sell_price_sanity"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_2_InjectsCashRecoveryPlan()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("NO_PLAN_REQUIRED", result["cash_recovery_plan"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_2_InjectsSemiconductorCluster()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("PASS", result["semiconductor_cluster"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_2_InjectsPositionCountGate()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("PASS", result["position_count_gate"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_3_InjectsHeatConcentration()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal(0.0, result["heat_concentration"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_3_InjectsAntiChasingVelocity()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("CLEAR", result["anti_chasing_velocity"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_3_InjectsDistributionSellDetector()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("PASS", result["distribution_sell_detector"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_4_InjectsPreDistributionWarning()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("PASS", result["pre_distribution_warning"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_4_InjectsTradeQuality()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal("GOOD", result["trade_quality"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Fact]
public void Harness_10_5_4_InjectsSfgScalers()
{
bool success = false;
try
{
var (raw, snaps, sets) = CreateMockInputs();
var result = HarnessInjector.InjectComputedHarness(raw, snaps, sets);
Assert.Equal(1.0, result["sfg_scaler_mrs"]);
Assert.Equal(1.0, result["sfg_scaler_cla"]);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
}
}
@@ -1,33 +0,0 @@
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests
{
public class KrxTickNormalizerTests
{
[Theory]
[InlineData(1500, 1)] // < 2000
[InlineData(4500, 5)] // < 5000
[InlineData(15000, 10)] // < 20000
[InlineData(45000, 50)] // < 50000
[InlineData(150000, 100)] // < 200000
[InlineData(450000, 500)] // < 500000
[InlineData(1000000, 1000)]// >= 500000
public void GetTickUnit_PriceRanges_ReturnExpectedTick(double price, int expectedTick)
{
int tick = KrxTickNormalizer.GetTickUnit(price);
Assert.Equal(expectedTick, tick);
}
[Theory]
[InlineData(1500.3, 1500)] // remainder = 0.3 < 0.5 -> round down
[InlineData(1500.7, 1501)] // remainder = 0.7 >= 0.5 -> round up
[InlineData(4502, 4500)] // tick = 5, remainder = 2 < 2.5 -> round down
[InlineData(4503, 4505)] // tick = 5, remainder = 3 >= 2.5 -> round up
public void NormalizeTick_VariousPrices_ReturnNormalizedPrice(double price, double expectedNormalized)
{
double res = KrxTickNormalizer.NormalizeTick(price);
Assert.Equal(expectedNormalized, res);
}
}
}
@@ -1,201 +0,0 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text.Json;
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests.ParityTests
{
public class ParityFixture : IDisposable
{
public int TotalTests = 0;
public int PassedTests = 0;
private readonly object _lock = new object();
public void RegisterResult(bool passed)
{
lock (_lock)
{
TotalTests++;
if (passed) PassedTests++;
}
}
public void Dispose()
{
var tempDir = @"C:\Temp\data_feed\Temp";
if (!Directory.Exists(tempDir))
{
Directory.CreateDirectory(tempDir);
}
var outputPath = Path.Combine(tempDir, "dotnet_domain_parity_v1.json");
var result = new
{
gate = PassedTests == TotalTests && TotalTests >= 40 ? "PASS" : "FAIL",
total = TotalTests,
passed = PassedTests
};
File.WriteAllText(outputPath, JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
}
}
public class DomainParityTests : IClassFixture<ParityFixture>
{
private readonly ParityFixture _fixture;
public DomainParityTests(ParityFixture fixture)
{
_fixture = fixture;
}
[Theory]
[InlineData(100000.0, 3000.0, 100000.0, 2.0, 94000.0)]
[InlineData(100000.0, 3000.0, 100000.0, 1.5, 95500.0)]
[InlineData(50000.0, 1500.0, 50000.0, 2.0, 47000.0)]
[InlineData(50000.0, null, null, null, 46000.0)]
[InlineData(10000.0, 500.0, 10000.0, null, 9250.0)] // Fix expected value to 9250.0 based on 1.5x ATR multiplier (ATR 5.0% < 8.0%)
[InlineData(80000.0, 2000.0, 80000.0, 2.0, 76000.0)]
[InlineData(200000.0, 5000.0, 200000.0, 1.5, 192500.0)]
[InlineData(150000.0, 4000.0, 150000.0, 2.0, 142000.0)]
[InlineData(300000.0, 8000.0, 300000.0, 1.5, 288000.0)]
[InlineData(120000.0, 3000.0, 120000.0, 2.0, 114000.0)]
public void StopPriceParity_MatchesPython(double entry, double? atr, double? current, double? mult, double expectedStop)
{
bool success = false;
try
{
var res = ExitDecisions.ComputeStopPriceCore(entry, atr, current, mult);
Assert.NotNull(res.StopPrice);
Assert.InRange(res.StopPrice.Value, expectedStop * 0.9999, expectedStop * 1.0001);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData("STOP_OR_TIME_EXIT_READY", 0, "RISK_ON", 0.0, false, 9999, "EXIT_100")]
[InlineData("NORMAL", 4, "RISK_ON", 0.0, false, 9999, "EXIT_100")]
[InlineData("NORMAL", 1, "RISK_OFF", 0.0, false, 9999, "REGIME_TRIM_50")]
[InlineData("NORMAL", 1, "RISK_OFF_CANDIDATE", 0.0, false, 9999, "REGIME_TRIM_50")]
[InlineData("NORMAL", 1, "RISK_ON", 75.0, false, 9999, "TRIM_70")]
[InlineData("NORMAL", 3, "RISK_ON", 0.0, false, 9999, "TRIM_70")]
[InlineData("NORMAL", 1, "RISK_ON", 0.0, true, 9999, "TRIM_50")]
[InlineData("NORMAL", 2, "RISK_ON", 0.0, false, 9999, "TRIM_50")]
[InlineData("NORMAL", 1, "RISK_ON", 50.0, false, 9999, "TRIM_50")]
[InlineData("NORMAL", 0, "RISK_ON", 15.0, false, 9999, "TAKE_PROFIT_TIER1")]
[InlineData("NORMAL", 0, "RISK_ON", 0.0, false, 0, "TIME_EXIT_100")]
[InlineData("NORMAL", 0, "RISK_ON", 0.0, false, 9999, "REVIEW_HUMAN")]
public void StopActionLadderParity_MatchesPython(
string timingAction,
int rwPartial,
string regime,
double param1,
bool trailingStop,
int daysToTimeStop,
string expectedAction)
{
bool success = false;
try
{
var ctx = new Dictionary<string, object>
{
{ "timingAction", timingAction },
{ "rw_partial", rwPartial },
{ "REGIME_PRELIM", regime },
{ "trailingStopBreach", trailingStop },
{ "daysToTimeStop", daysToTimeStop }
};
if (expectedAction == "TAKE_PROFIT_TIER1")
{
ctx["profitPct"] = param1;
}
else
{
ctx["timingExitScore"] = param1;
}
var res = ExitDecisions.ComputeStopActionLadder(ctx);
Assert.Equal(expectedAction, res.Action);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData("EVENT_SHOCK", 5.0, 3.5)]
[InlineData("RISK_OFF", 7.0, 5.0)]
[InlineData("SECULAR_LEADER_RISK_ON", 13.0, 9.0)]
public void HeatThresholdParity_MatchesPython(string regime, double expectedHard, double expectedHalve)
{
bool success = false;
try
{
var res = ExitDecisions.ComputeDynamicHeatThresholds(regime);
Assert.Equal(expectedHard, res.HardBlock);
Assert.Equal(expectedHalve, res.Halve);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(-5.0, "NORMAL")]
[InlineData(5.0, "BREAKEVEN_RATCHET")]
[InlineData(15.0, "PROFIT_LOCK_10")]
[InlineData(25.0, "PROFIT_LOCK_20")]
[InlineData(35.0, "PROFIT_LOCK_30")]
[InlineData(45.0, "APEX_TRAILING")]
[InlineData(65.0, "APEX_SUPER")]
public void ProfitLockParity_MatchesPython(double profitPct, string expectedStage)
{
bool success = false;
try
{
var stage = ProfitLockCalculator.ClassifyProfitLockStage(profitPct);
Assert.Equal(expectedStage, stage);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
[Theory]
[InlineData(1500.0, 1)]
[InlineData(4500.0, 5)]
[InlineData(15000.0, 10)]
[InlineData(45000.0, 50)]
[InlineData(150000.0, 100)]
[InlineData(450000.0, 500)]
[InlineData(1000000.0, 1000)]
[InlineData(3000000.0, 1000)]
public void KrxTickParity_MatchesPython(double price, int expectedTick)
{
bool success = false;
try
{
int tick = KrxTickNormalizer.GetTickUnit(price);
Assert.Equal(expectedTick, tick);
success = true;
}
finally
{
_fixture.RegisterResult(success);
}
}
}
}
@@ -1,35 +0,0 @@
using System;
using System.IO;
using System.Threading.Tasks;
using Xunit;
using QuantEngine.Application.Services;
namespace QuantEngine.Core.Tests
{
public class PipelineOrchestratorTests
{
[Fact]
public async Task RunPipelineAsync_ExecutesAll7Steps_AndOutputsJson()
{
var orchestrator = new PipelineOrchestrator();
var result = await orchestrator.RunPipelineAsync();
Assert.NotNull(result);
Assert.Equal("PASS", result.Gate);
Assert.Equal(7, result.Steps.Count);
foreach (var step in result.Steps)
{
Assert.True(step.Success);
Assert.False(string.IsNullOrEmpty(step.StepName));
Assert.True(step.ElapsedMilliseconds > 0);
}
var expectedJsonPath = @"C:\Temp\data_feed\Temp\dotnet_pipeline_e2e_v1.json";
Assert.True(File.Exists(expectedJsonPath));
var jsonContent = File.ReadAllText(expectedJsonPath);
Assert.Contains("\"gate\": \"PASS\"", jsonContent);
}
}
}
@@ -1,41 +0,0 @@
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests
{
public class ProfitLockCalculatorTests
{
[Theory]
[InlineData(-5.0, "NORMAL")]
[InlineData(5.0, "BREAKEVEN_RATCHET")]
[InlineData(15.0, "PROFIT_LOCK_10")]
[InlineData(25.0, "PROFIT_LOCK_20")]
[InlineData(35.0, "PROFIT_LOCK_30")]
[InlineData(45.0, "APEX_TRAILING")]
[InlineData(65.0, "APEX_SUPER")]
public void ClassifyProfitLockStage_ProfitPcts_ReturnExpectedStage(double profitPct, string expectedStage)
{
string res = ProfitLockCalculator.ClassifyProfitLockStage(profitPct);
Assert.Equal(expectedStage, res);
}
[Fact]
public void ComputeTrailingStop_ApexSuper_AppliesCorrectMultiplierAndTpAction()
{
var res = ProfitLockCalculator.ComputeTrailingStop(
profitPct: 65.0,
highestClose: 100000,
atr20: 3000,
ratchetStop: 90000,
averageCost: 80000
);
Assert.Equal("APEX_SUPER", res.RatchetStage);
Assert.Equal("강제 10% 익절 권고", res.TpLadderAction);
Assert.True(res.ApexSuperActive);
// 100000 - 1.2 * 3000 = 100000 - 3600 = 96400
// NormalizeTick(96400) = 96400 (tick = 100)
Assert.Equal(96400, res.AutoTrailingStop);
}
}
}
@@ -1,31 +0,0 @@
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests
{
public class PullbackTriggerCalculatorTests
{
[Theory]
[InlineData(100000, 100000, 3000, "PULLBACK_ZONE", "PASS")] // close <= ma20*1.03
[InlineData(105000, 100000, 3000, "ABOVE_PULLBACK_ZONE", "BLOCKED")] // close > ma20*1.03
public void ComputePullbackTrigger_Prices_ReturnExpectedVerdictAndState(
double close,
double ma20,
double atr20,
string expectedVerdict,
string expectedState)
{
var res = PullbackTriggerCalculator.ComputePullbackTrigger(close, ma20, atr20);
Assert.Equal(expectedVerdict, res.PullbackEntryVerdict);
Assert.Equal(expectedState, res.PullbackState);
}
[Fact]
public void ComputePullbackTrigger_TriggerPrice_CalculatesCorrectly()
{
// triggerPrice = ma20 - 0.5 * atr20 = 100000 - 1500 = 98500
var res = PullbackTriggerCalculator.ComputePullbackTrigger(100000, 100000, 3000);
Assert.Equal(98500, res.PullbackEntryTriggerPrice);
}
}
}
@@ -1,75 +0,0 @@
using Xunit;
using QuantEngine.Core.Domain;
namespace QuantEngine.Core.Tests
{
public class SellPriceSanityCheckerTests
{
[Fact]
public void CheckSellPriceSanity_ValidPrice_Passes()
{
var res = SellPriceSanityChecker.CheckSellPriceSanity(
sellLimitPrice: 100000,
stopLossPrice: 95000,
prevClose: 100000,
ticker: "005930"
);
Assert.Equal("PASS", res.SellPriceSanityStatus);
Assert.True(res.HtsAllowed);
Assert.False(res.ShadowLedger);
Assert.Empty(res.SellPriceSanityIssues);
}
[Fact]
public void CheckSellPriceSanity_PriceInversion_Fails()
{
// sell < stop -> inversion
var res = SellPriceSanityChecker.CheckSellPriceSanity(
sellLimitPrice: 90000,
stopLossPrice: 95000,
prevClose: 100000,
ticker: "005930"
);
Assert.Equal("INVALID_PRICE_INVERSION", res.SellPriceSanityStatus);
Assert.False(res.HtsAllowed);
Assert.True(res.ShadowLedger);
Assert.Contains("INVALID_PRICE_INVERSION", res.SellPriceSanityIssues[0]);
}
[Fact]
public void CheckSellPriceSanity_UnrealisticPrice_Fails()
{
// sell > prevClose * 1.30 -> unrealistic
var res = SellPriceSanityChecker.CheckSellPriceSanity(
sellLimitPrice: 140000,
stopLossPrice: 95000,
prevClose: 100000,
ticker: "005930"
);
Assert.Equal("INVALID_UNREALISTIC_PRICE", res.SellPriceSanityStatus);
Assert.False(res.HtsAllowed);
Assert.True(res.ShadowLedger);
Assert.Contains("INVALID_UNREALISTIC_PRICE", res.SellPriceSanityIssues[0]);
}
[Fact]
public void CheckSellPriceSanity_InvalidTick_Fails()
{
// 100005 % 100 != 0 (10만 원대 호가단위 100) -> invalid tick
var res = SellPriceSanityChecker.CheckSellPriceSanity(
sellLimitPrice: 100005,
stopLossPrice: 95000,
prevClose: 100000,
ticker: "005930"
);
Assert.Equal("INVALID_TICK", res.SellPriceSanityStatus);
Assert.False(res.HtsAllowed);
Assert.True(res.ShadowLedger);
Assert.Contains("INVALID_TICK", res.SellPriceSanityIssues[0]);
}
}
}
@@ -9,23 +9,13 @@
"QuantEngine.Core.Tests/1.0.0": { "QuantEngine.Core.Tests/1.0.0": {
"dependencies": { "dependencies": {
"Microsoft.NET.Test.Sdk": "17.14.1", "Microsoft.NET.Test.Sdk": "17.14.1",
"QuantEngine.Application": "1.0.0",
"QuantEngine.Core": "1.0.0", "QuantEngine.Core": "1.0.0",
"QuantEngine.Infrastructure": "1.0.0",
"xunit": "2.9.3" "xunit": "2.9.3"
}, },
"runtime": { "runtime": {
"QuantEngine.Core.Tests.dll": {} "QuantEngine.Core.Tests.dll": {}
} }
}, },
"Dapper/2.1.79": {
"runtime": {
"lib/net10.0/Dapper.dll": {
"assemblyVersion": "2.0.0.0",
"fileVersion": "2.1.79.29349"
}
}
},
"Microsoft.CodeCoverage/17.14.1": { "Microsoft.CodeCoverage/17.14.1": {
"runtime": { "runtime": {
"lib/net8.0/Microsoft.VisualStudio.CodeCoverage.Shim.dll": { "lib/net8.0/Microsoft.VisualStudio.CodeCoverage.Shim.dll": {
@@ -34,25 +24,6 @@
} }
} }
}, },
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0": {
"runtime": {
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.25.52411"
}
}
},
"Microsoft.Extensions.Logging.Abstractions/10.0.0": {
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0"
},
"runtime": {
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"assemblyVersion": "10.0.0.0",
"fileVersion": "10.0.25.52411"
}
}
},
"Microsoft.NET.Test.Sdk/17.14.1": { "Microsoft.NET.Test.Sdk/17.14.1": {
"dependencies": { "dependencies": {
"Microsoft.CodeCoverage": "17.14.1", "Microsoft.CodeCoverage": "17.14.1",
@@ -310,17 +281,6 @@
} }
} }
}, },
"Npgsql/10.0.3": {
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "10.0.0"
},
"runtime": {
"lib/net10.0/Npgsql.dll": {
"assemblyVersion": "10.0.3.0",
"fileVersion": "10.0.3.0"
}
}
},
"xunit/2.9.3": { "xunit/2.9.3": {
"dependencies": { "dependencies": {
"xunit.assert": "2.9.3", "xunit.assert": "2.9.3",
@@ -371,17 +331,6 @@
} }
} }
}, },
"QuantEngine.Application/1.0.0": {
"dependencies": {
"QuantEngine.Core": "1.0.0"
},
"runtime": {
"QuantEngine.Application.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
},
"QuantEngine.Core/1.0.0": { "QuantEngine.Core/1.0.0": {
"runtime": { "runtime": {
"QuantEngine.Core.dll": { "QuantEngine.Core.dll": {
@@ -389,20 +338,6 @@
"fileVersion": "1.0.0.0" "fileVersion": "1.0.0.0"
} }
} }
},
"QuantEngine.Infrastructure/1.0.0": {
"dependencies": {
"Dapper": "2.1.79",
"Npgsql": "10.0.3",
"QuantEngine.Application": "1.0.0",
"QuantEngine.Core": "1.0.0"
},
"runtime": {
"QuantEngine.Infrastructure.dll": {
"assemblyVersion": "1.0.0.0",
"fileVersion": "1.0.0.0"
}
}
} }
} }
}, },
@@ -412,13 +347,6 @@
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
}, },
"Dapper/2.1.79": {
"type": "package",
"serviceable": true,
"sha512": "sha512-8YijbzgTfmqmQOnVNorYM6K++pxqnW3nJ4aC1sRHzxUA2CcuoJ9gsTem3kgBnPRMc38zZHl4Esb6hAezXIEEuw==",
"path": "dapper/2.1.79",
"hashPath": "dapper.2.1.79.nupkg.sha512"
},
"Microsoft.CodeCoverage/17.14.1": { "Microsoft.CodeCoverage/17.14.1": {
"type": "package", "type": "package",
"serviceable": true, "serviceable": true,
@@ -426,20 +354,6 @@
"path": "microsoft.codecoverage/17.14.1", "path": "microsoft.codecoverage/17.14.1",
"hashPath": "microsoft.codecoverage.17.14.1.nupkg.sha512" "hashPath": "microsoft.codecoverage.17.14.1.nupkg.sha512"
}, },
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-L3AdmZ1WOK4XXT5YFPEwyt0ep6l8lGIPs7F5OOBZc77Zqeo01Of7XXICy47628sdVl0v/owxYJTe86DTgFwKCA==",
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.0",
"hashPath": "microsoft.extensions.dependencyinjection.abstractions.10.0.0.nupkg.sha512"
},
"Microsoft.Extensions.Logging.Abstractions/10.0.0": {
"type": "package",
"serviceable": true,
"sha512": "sha512-FU/IfjDfwaMuKr414SSQNTIti/69bHEMb+QKrskRb26oVqpx3lNFXMjs/RC9ZUuhBhcwDM2BwOgoMw+PZ+beqQ==",
"path": "microsoft.extensions.logging.abstractions/10.0.0",
"hashPath": "microsoft.extensions.logging.abstractions.10.0.0.nupkg.sha512"
},
"Microsoft.NET.Test.Sdk/17.14.1": { "Microsoft.NET.Test.Sdk/17.14.1": {
"type": "package", "type": "package",
"serviceable": true, "serviceable": true,
@@ -468,13 +382,6 @@
"path": "newtonsoft.json/13.0.3", "path": "newtonsoft.json/13.0.3",
"hashPath": "newtonsoft.json.13.0.3.nupkg.sha512" "hashPath": "newtonsoft.json.13.0.3.nupkg.sha512"
}, },
"Npgsql/10.0.3": {
"type": "package",
"serviceable": true,
"sha512": "sha512-7nb5YzXuvWWJxB0J8DiyL3we+X4FOctZrt0fIBnucOIaIevFEEwGQVZKtiu9olXdlNAK1eNgqSral6r/jlhI4w==",
"path": "npgsql/10.0.3",
"hashPath": "npgsql.10.0.3.nupkg.sha512"
},
"xunit/2.9.3": { "xunit/2.9.3": {
"type": "package", "type": "package",
"serviceable": true, "serviceable": true,
@@ -517,20 +424,10 @@
"path": "xunit.extensibility.execution/2.9.3", "path": "xunit.extensibility.execution/2.9.3",
"hashPath": "xunit.extensibility.execution.2.9.3.nupkg.sha512" "hashPath": "xunit.extensibility.execution.2.9.3.nupkg.sha512"
}, },
"QuantEngine.Application/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
},
"QuantEngine.Core/1.0.0": { "QuantEngine.Core/1.0.0": {
"type": "project", "type": "project",
"serviceable": false, "serviceable": false,
"sha512": "" "sha512": ""
},
"QuantEngine.Infrastructure/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
} }
} }
} }
@@ -13,10 +13,10 @@ using System.Reflection;
[assembly: System.Reflection.AssemblyCompanyAttribute("QuantEngine.Core.Tests")] [assembly: System.Reflection.AssemblyCompanyAttribute("QuantEngine.Core.Tests")]
[assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")]
[assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")]
[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+4ef7a54ad55182e164ca78e8af21f2a5e214c98f")] [assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+c1e84a387cbda5a7c13c50984f05772976e26ed6")]
[assembly: System.Reflection.AssemblyProductAttribute("QuantEngine.Core.Tests")] [assembly: System.Reflection.AssemblyProductAttribute("QuantEngine.Core.Tests")]
[assembly: System.Reflection.AssemblyTitleAttribute("QuantEngine.Core.Tests")] [assembly: System.Reflection.AssemblyTitleAttribute("QuantEngine.Core.Tests")]
[assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")]
// Generated by the MSBuild WriteCodeFragment class. // MSBuild WriteCodeFragment 클래스에서 생성되었습니다.
@@ -1 +1 @@
e4771135b81bbeef377e0f0cdbafc89d7c10d2257171ab0f1a12919a2264d756 d6ed781292dbcc549b3d7f58038e79526631e9be098b94042efa42486e2c82fd
@@ -8,7 +8,6 @@ build_property.ProjectTypeGuids =
build_property.InvariantGlobalization = build_property.InvariantGlobalization =
build_property.PlatformNeutralAssembly = build_property.PlatformNeutralAssembly =
build_property.EnforceExtendedAnalyzerRules = build_property.EnforceExtendedAnalyzerRules =
build_property.EntryPointFilePath =
build_property._SupportedPlatformList = Linux,macOS,Windows build_property._SupportedPlatformList = Linux,macOS,Windows
build_property.RootNamespace = QuantEngine.Core.Tests build_property.RootNamespace = QuantEngine.Core.Tests
build_property.ProjectDir = C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\ build_property.ProjectDir = C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\
@@ -1 +1 @@
48a1c0cf561a2dafa7b6d0c206caa026d55a5b62e489904853de0e3cd3e9a07e 35f82c369ed028404bcc0314e6a432a54e6b6dcab661955a071ff0504b16ab57
@@ -8,10 +8,7 @@ C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngin
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.Tests.runtimeconfig.json C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.Tests.runtimeconfig.json
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.Tests.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.Tests.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.Tests.pdb C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.Tests.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Dapper.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.VisualStudio.CodeCoverage.Shim.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.VisualStudio.CodeCoverage.Shim.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.Extensions.Logging.Abstractions.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.TestPlatform.CoreUtilities.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.TestPlatform.CoreUtilities.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.TestPlatform.PlatformAbstractions.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.TestPlatform.PlatformAbstractions.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll
@@ -20,7 +17,6 @@ C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.TestPlatform.Utilities.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.TestPlatform.Utilities.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.VisualStudio.TestPlatform.Common.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Microsoft.VisualStudio.TestPlatform.Common.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Newtonsoft.Json.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Newtonsoft.Json.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\Npgsql.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\xunit.assert.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\xunit.assert.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\xunit.core.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\xunit.core.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\xunit.execution.dotnet.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\xunit.execution.dotnet.dll
@@ -89,12 +85,8 @@ C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hans\Mi
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hant\Microsoft.TestPlatform.CommunicationUtilities.resources.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hant\Microsoft.TestPlatform.CommunicationUtilities.resources.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hant\Microsoft.TestPlatform.CrossPlatEngine.resources.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hant\Microsoft.TestPlatform.CrossPlatEngine.resources.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hant\Microsoft.VisualStudio.TestPlatform.Common.resources.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\zh-Hant\Microsoft.VisualStudio.TestPlatform.Common.resources.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Application.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.dll C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Infrastructure.dll
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.pdb C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Core.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Application.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\bin\Debug\net10.0\QuantEngine.Infrastructure.pdb
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\obj\Debug\net10.0\QuantEngine.Core.Tests.csproj.AssemblyReference.cache C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\obj\Debug\net10.0\QuantEngine.Core.Tests.csproj.AssemblyReference.cache
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\obj\Debug\net10.0\QuantEngine.Core.Tests.GeneratedMSBuildEditorConfig.editorconfig C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\obj\Debug\net10.0\QuantEngine.Core.Tests.GeneratedMSBuildEditorConfig.editorconfig
C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\obj\Debug\net10.0\QuantEngine.Core.Tests.AssemblyInfoInputs.cache C:\Temp\data_feed\src\dotnet\QuantEngine.Core.Tests\obj\Debug\net10.0\QuantEngine.Core.Tests.AssemblyInfoInputs.cache
@@ -4,360 +4,13 @@
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj": {} "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj": {}
}, },
"projects": { "projects": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"projectName": "QuantEngine.Application",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages",
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
],
"configFilePaths": [
"C:\\Users\\kjh20\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net10.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0",
"projectReferences": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "all"
},
"SdkAnalysisLevel": "10.0.300"
},
"frameworks": {
"net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0",
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]",
"Microsoft.Win32.Primitives": "(,4.3.32767]",
"Microsoft.Win32.Registry": "(,5.0.32767]",
"runtime.any.System.Collections": "(,4.3.32767]",
"runtime.any.System.Diagnostics.Tools": "(,4.3.32767]",
"runtime.any.System.Diagnostics.Tracing": "(,4.3.32767]",
"runtime.any.System.Globalization": "(,4.3.32767]",
"runtime.any.System.Globalization.Calendars": "(,4.3.32767]",
"runtime.any.System.IO": "(,4.3.32767]",
"runtime.any.System.Reflection": "(,4.3.32767]",
"runtime.any.System.Reflection.Extensions": "(,4.3.32767]",
"runtime.any.System.Reflection.Primitives": "(,4.3.32767]",
"runtime.any.System.Resources.ResourceManager": "(,4.3.32767]",
"runtime.any.System.Runtime": "(,4.3.32767]",
"runtime.any.System.Runtime.Handles": "(,4.3.32767]",
"runtime.any.System.Runtime.InteropServices": "(,4.3.32767]",
"runtime.any.System.Text.Encoding": "(,4.3.32767]",
"runtime.any.System.Text.Encoding.Extensions": "(,4.3.32767]",
"runtime.any.System.Threading.Tasks": "(,4.3.32767]",
"runtime.any.System.Threading.Timer": "(,4.3.32767]",
"runtime.aot.System.Collections": "(,4.3.32767]",
"runtime.aot.System.Diagnostics.Tools": "(,4.3.32767]",
"runtime.aot.System.Diagnostics.Tracing": "(,4.3.32767]",
"runtime.aot.System.Globalization": "(,4.3.32767]",
"runtime.aot.System.Globalization.Calendars": "(,4.3.32767]",
"runtime.aot.System.IO": "(,4.3.32767]",
"runtime.aot.System.Reflection": "(,4.3.32767]",
"runtime.aot.System.Reflection.Extensions": "(,4.3.32767]",
"runtime.aot.System.Reflection.Primitives": "(,4.3.32767]",
"runtime.aot.System.Resources.ResourceManager": "(,4.3.32767]",
"runtime.aot.System.Runtime": "(,4.3.32767]",
"runtime.aot.System.Runtime.Handles": "(,4.3.32767]",
"runtime.aot.System.Runtime.InteropServices": "(,4.3.32767]",
"runtime.aot.System.Text.Encoding": "(,4.3.32767]",
"runtime.aot.System.Text.Encoding.Extensions": "(,4.3.32767]",
"runtime.aot.System.Threading.Tasks": "(,4.3.32767]",
"runtime.aot.System.Threading.Timer": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.unix.Microsoft.Win32.Primitives": "(,4.3.32767]",
"runtime.unix.System.Console": "(,4.3.32767]",
"runtime.unix.System.Diagnostics.Debug": "(,4.3.32767]",
"runtime.unix.System.IO.FileSystem": "(,4.3.32767]",
"runtime.unix.System.Net.Primitives": "(,4.3.32767]",
"runtime.unix.System.Net.Sockets": "(,4.3.32767]",
"runtime.unix.System.Private.Uri": "(,4.3.32767]",
"runtime.unix.System.Runtime.Extensions": "(,4.3.32767]",
"runtime.win.Microsoft.Win32.Primitives": "(,4.3.32767]",
"runtime.win.System.Console": "(,4.3.32767]",
"runtime.win.System.Diagnostics.Debug": "(,4.3.32767]",
"runtime.win.System.IO.FileSystem": "(,4.3.32767]",
"runtime.win.System.Net.Primitives": "(,4.3.32767]",
"runtime.win.System.Net.Sockets": "(,4.3.32767]",
"runtime.win.System.Runtime.Extensions": "(,4.3.32767]",
"runtime.win10-arm-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
"runtime.win10-arm64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.win10-x64-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
"runtime.win10-x86-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
"runtime.win7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.win7-x86.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.win7.System.Private.Uri": "(,4.3.32767]",
"runtime.win8-arm.runtime.native.System.IO.Compression": "(,4.3.32767]",
"System.AppContext": "(,4.3.32767]",
"System.Buffers": "(,5.0.32767]",
"System.Collections": "(,4.3.32767]",
"System.Collections.Concurrent": "(,4.3.32767]",
"System.Collections.Immutable": "(,10.0.32767]",
"System.Collections.NonGeneric": "(,4.3.32767]",
"System.Collections.Specialized": "(,4.3.32767]",
"System.ComponentModel": "(,4.3.32767]",
"System.ComponentModel.Annotations": "(,4.3.32767]",
"System.ComponentModel.EventBasedAsync": "(,4.3.32767]",
"System.ComponentModel.Primitives": "(,4.3.32767]",
"System.ComponentModel.TypeConverter": "(,4.3.32767]",
"System.Console": "(,4.3.32767]",
"System.Data.Common": "(,4.3.32767]",
"System.Data.DataSetExtensions": "(,4.4.32767]",
"System.Diagnostics.Contracts": "(,4.3.32767]",
"System.Diagnostics.Debug": "(,4.3.32767]",
"System.Diagnostics.DiagnosticSource": "(,10.0.32767]",
"System.Diagnostics.FileVersionInfo": "(,4.3.32767]",
"System.Diagnostics.Process": "(,4.3.32767]",
"System.Diagnostics.StackTrace": "(,4.3.32767]",
"System.Diagnostics.TextWriterTraceListener": "(,4.3.32767]",
"System.Diagnostics.Tools": "(,4.3.32767]",
"System.Diagnostics.TraceSource": "(,4.3.32767]",
"System.Diagnostics.Tracing": "(,4.3.32767]",
"System.Drawing.Primitives": "(,4.3.32767]",
"System.Dynamic.Runtime": "(,4.3.32767]",
"System.Formats.Asn1": "(,10.0.32767]",
"System.Formats.Tar": "(,10.0.32767]",
"System.Globalization": "(,4.3.32767]",
"System.Globalization.Calendars": "(,4.3.32767]",
"System.Globalization.Extensions": "(,4.3.32767]",
"System.IO": "(,4.3.32767]",
"System.IO.Compression": "(,4.3.32767]",
"System.IO.Compression.ZipFile": "(,4.3.32767]",
"System.IO.FileSystem": "(,4.3.32767]",
"System.IO.FileSystem.AccessControl": "(,4.4.32767]",
"System.IO.FileSystem.DriveInfo": "(,4.3.32767]",
"System.IO.FileSystem.Primitives": "(,4.3.32767]",
"System.IO.FileSystem.Watcher": "(,4.3.32767]",
"System.IO.IsolatedStorage": "(,4.3.32767]",
"System.IO.MemoryMappedFiles": "(,4.3.32767]",
"System.IO.Pipelines": "(,10.0.32767]",
"System.IO.Pipes": "(,4.3.32767]",
"System.IO.Pipes.AccessControl": "(,5.0.32767]",
"System.IO.UnmanagedMemoryStream": "(,4.3.32767]",
"System.Linq": "(,4.3.32767]",
"System.Linq.AsyncEnumerable": "(,10.0.32767]",
"System.Linq.Expressions": "(,4.3.32767]",
"System.Linq.Parallel": "(,4.3.32767]",
"System.Linq.Queryable": "(,4.3.32767]",
"System.Memory": "(,5.0.32767]",
"System.Net.Http": "(,4.3.32767]",
"System.Net.Http.Json": "(,10.0.32767]",
"System.Net.NameResolution": "(,4.3.32767]",
"System.Net.NetworkInformation": "(,4.3.32767]",
"System.Net.Ping": "(,4.3.32767]",
"System.Net.Primitives": "(,4.3.32767]",
"System.Net.Requests": "(,4.3.32767]",
"System.Net.Security": "(,4.3.32767]",
"System.Net.ServerSentEvents": "(,10.0.32767]",
"System.Net.Sockets": "(,4.3.32767]",
"System.Net.WebHeaderCollection": "(,4.3.32767]",
"System.Net.WebSockets": "(,4.3.32767]",
"System.Net.WebSockets.Client": "(,4.3.32767]",
"System.Numerics.Vectors": "(,5.0.32767]",
"System.ObjectModel": "(,4.3.32767]",
"System.Private.DataContractSerialization": "(,4.3.32767]",
"System.Private.Uri": "(,4.3.32767]",
"System.Reflection": "(,4.3.32767]",
"System.Reflection.DispatchProxy": "(,6.0.32767]",
"System.Reflection.Emit": "(,4.7.32767]",
"System.Reflection.Emit.ILGeneration": "(,4.7.32767]",
"System.Reflection.Emit.Lightweight": "(,4.7.32767]",
"System.Reflection.Extensions": "(,4.3.32767]",
"System.Reflection.Metadata": "(,10.0.32767]",
"System.Reflection.Primitives": "(,4.3.32767]",
"System.Reflection.TypeExtensions": "(,4.3.32767]",
"System.Resources.Reader": "(,4.3.32767]",
"System.Resources.ResourceManager": "(,4.3.32767]",
"System.Resources.Writer": "(,4.3.32767]",
"System.Runtime": "(,4.3.32767]",
"System.Runtime.CompilerServices.Unsafe": "(,7.0.32767]",
"System.Runtime.CompilerServices.VisualC": "(,4.3.32767]",
"System.Runtime.Extensions": "(,4.3.32767]",
"System.Runtime.Handles": "(,4.3.32767]",
"System.Runtime.InteropServices": "(,4.3.32767]",
"System.Runtime.InteropServices.RuntimeInformation": "(,4.3.32767]",
"System.Runtime.Loader": "(,4.3.32767]",
"System.Runtime.Numerics": "(,4.3.32767]",
"System.Runtime.Serialization.Formatters": "(,4.3.32767]",
"System.Runtime.Serialization.Json": "(,4.3.32767]",
"System.Runtime.Serialization.Primitives": "(,4.3.32767]",
"System.Runtime.Serialization.Xml": "(,4.3.32767]",
"System.Security.AccessControl": "(,6.0.32767]",
"System.Security.Claims": "(,4.3.32767]",
"System.Security.Cryptography.Algorithms": "(,4.3.32767]",
"System.Security.Cryptography.Cng": "(,5.0.32767]",
"System.Security.Cryptography.Csp": "(,4.3.32767]",
"System.Security.Cryptography.Encoding": "(,4.3.32767]",
"System.Security.Cryptography.OpenSsl": "(,5.0.32767]",
"System.Security.Cryptography.Primitives": "(,4.3.32767]",
"System.Security.Cryptography.X509Certificates": "(,4.3.32767]",
"System.Security.Principal": "(,4.3.32767]",
"System.Security.Principal.Windows": "(,5.0.32767]",
"System.Security.SecureString": "(,4.3.32767]",
"System.Text.Encoding": "(,4.3.32767]",
"System.Text.Encoding.CodePages": "(,10.0.32767]",
"System.Text.Encoding.Extensions": "(,4.3.32767]",
"System.Text.Encodings.Web": "(,10.0.32767]",
"System.Text.Json": "(,10.0.32767]",
"System.Text.RegularExpressions": "(,4.3.32767]",
"System.Threading": "(,4.3.32767]",
"System.Threading.AccessControl": "(,10.0.32767]",
"System.Threading.Channels": "(,10.0.32767]",
"System.Threading.Overlapped": "(,4.3.32767]",
"System.Threading.Tasks": "(,4.3.32767]",
"System.Threading.Tasks.Dataflow": "(,10.0.32767]",
"System.Threading.Tasks.Extensions": "(,5.0.32767]",
"System.Threading.Tasks.Parallel": "(,4.3.32767]",
"System.Threading.Thread": "(,4.3.32767]",
"System.Threading.ThreadPool": "(,4.3.32767]",
"System.Threading.Timer": "(,4.3.32767]",
"System.ValueTuple": "(,4.5.32767]",
"System.Xml.ReaderWriter": "(,4.3.32767]",
"System.Xml.XDocument": "(,4.3.32767]",
"System.Xml.XmlDocument": "(,4.3.32767]",
"System.Xml.XmlSerializer": "(,4.3.32767]",
"System.Xml.XPath": "(,4.3.32767]",
"System.Xml.XPath.XDocument": "(,5.0.32767]"
}
}
}
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj": { "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj": {
"version": "1.0.0", "version": "1.0.0",
"restore": { "restore": {
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj", "projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj",
"projectName": "QuantEngine.Core.Tests", "projectName": "QuantEngine.Core.Tests",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj", "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages", "packagesPath": "C:\\Users\\kjh20\\.nuget\\packages\\",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\obj\\", "outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [ "fallbackFolders": [
@@ -375,21 +28,15 @@
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {}, "C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {},
"https://nuget.telerik.com/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"projectReferences": { "projectReferences": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj"
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": { "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj" "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj"
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj"
} }
} }
} }
@@ -404,11 +51,10 @@
"auditLevel": "low", "auditLevel": "low",
"auditMode": "all" "auditMode": "all"
}, },
"SdkAnalysisLevel": "10.0.300" "SdkAnalysisLevel": "10.0.100"
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"dependencies": { "dependencies": {
"Microsoft.NET.Test.Sdk": { "Microsoft.NET.Test.Sdk": {
@@ -444,7 +90,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json", "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.100/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": { "packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]", "Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]", "Microsoft.VisualBasic": "(,10.4.32767]",
@@ -728,7 +374,7 @@
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj", "projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj",
"projectName": "QuantEngine.Core", "projectName": "QuantEngine.Core",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj", "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages", "packagesPath": "C:\\Users\\kjh20\\.nuget\\packages\\",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\obj\\", "outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [ "fallbackFolders": [
@@ -746,11 +392,11 @@
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {}, "C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {},
"https://nuget.telerik.com/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"projectReferences": {} "projectReferences": {}
} }
@@ -765,11 +411,10 @@
"auditLevel": "low", "auditLevel": "low",
"auditMode": "all" "auditMode": "all"
}, },
"SdkAnalysisLevel": "10.0.300" "SdkAnalysisLevel": "10.0.100"
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"imports": [ "imports": [
"net461", "net461",
@@ -787,367 +432,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json", "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.100/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]",
"Microsoft.Win32.Primitives": "(,4.3.32767]",
"Microsoft.Win32.Registry": "(,5.0.32767]",
"runtime.any.System.Collections": "(,4.3.32767]",
"runtime.any.System.Diagnostics.Tools": "(,4.3.32767]",
"runtime.any.System.Diagnostics.Tracing": "(,4.3.32767]",
"runtime.any.System.Globalization": "(,4.3.32767]",
"runtime.any.System.Globalization.Calendars": "(,4.3.32767]",
"runtime.any.System.IO": "(,4.3.32767]",
"runtime.any.System.Reflection": "(,4.3.32767]",
"runtime.any.System.Reflection.Extensions": "(,4.3.32767]",
"runtime.any.System.Reflection.Primitives": "(,4.3.32767]",
"runtime.any.System.Resources.ResourceManager": "(,4.3.32767]",
"runtime.any.System.Runtime": "(,4.3.32767]",
"runtime.any.System.Runtime.Handles": "(,4.3.32767]",
"runtime.any.System.Runtime.InteropServices": "(,4.3.32767]",
"runtime.any.System.Text.Encoding": "(,4.3.32767]",
"runtime.any.System.Text.Encoding.Extensions": "(,4.3.32767]",
"runtime.any.System.Threading.Tasks": "(,4.3.32767]",
"runtime.any.System.Threading.Timer": "(,4.3.32767]",
"runtime.aot.System.Collections": "(,4.3.32767]",
"runtime.aot.System.Diagnostics.Tools": "(,4.3.32767]",
"runtime.aot.System.Diagnostics.Tracing": "(,4.3.32767]",
"runtime.aot.System.Globalization": "(,4.3.32767]",
"runtime.aot.System.Globalization.Calendars": "(,4.3.32767]",
"runtime.aot.System.IO": "(,4.3.32767]",
"runtime.aot.System.Reflection": "(,4.3.32767]",
"runtime.aot.System.Reflection.Extensions": "(,4.3.32767]",
"runtime.aot.System.Reflection.Primitives": "(,4.3.32767]",
"runtime.aot.System.Resources.ResourceManager": "(,4.3.32767]",
"runtime.aot.System.Runtime": "(,4.3.32767]",
"runtime.aot.System.Runtime.Handles": "(,4.3.32767]",
"runtime.aot.System.Runtime.InteropServices": "(,4.3.32767]",
"runtime.aot.System.Text.Encoding": "(,4.3.32767]",
"runtime.aot.System.Text.Encoding.Extensions": "(,4.3.32767]",
"runtime.aot.System.Threading.Tasks": "(,4.3.32767]",
"runtime.aot.System.Threading.Timer": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.debian.9-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.27-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.fedora.28-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.opensuse.42.3-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "(,4.3.32767]",
"runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography": "(,4.3.32767]",
"runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System.Net.Http": "(,4.3.32767]",
"runtime.ubuntu.18.04-x64.runtime.native.System.Net.Security": "(,4.3.32767]",
"runtime.unix.Microsoft.Win32.Primitives": "(,4.3.32767]",
"runtime.unix.System.Console": "(,4.3.32767]",
"runtime.unix.System.Diagnostics.Debug": "(,4.3.32767]",
"runtime.unix.System.IO.FileSystem": "(,4.3.32767]",
"runtime.unix.System.Net.Primitives": "(,4.3.32767]",
"runtime.unix.System.Net.Sockets": "(,4.3.32767]",
"runtime.unix.System.Private.Uri": "(,4.3.32767]",
"runtime.unix.System.Runtime.Extensions": "(,4.3.32767]",
"runtime.win.Microsoft.Win32.Primitives": "(,4.3.32767]",
"runtime.win.System.Console": "(,4.3.32767]",
"runtime.win.System.Diagnostics.Debug": "(,4.3.32767]",
"runtime.win.System.IO.FileSystem": "(,4.3.32767]",
"runtime.win.System.Net.Primitives": "(,4.3.32767]",
"runtime.win.System.Net.Sockets": "(,4.3.32767]",
"runtime.win.System.Runtime.Extensions": "(,4.3.32767]",
"runtime.win10-arm-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
"runtime.win10-arm64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.win10-x64-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
"runtime.win10-x86-aot.runtime.native.System.IO.Compression": "(,4.0.32767]",
"runtime.win7-x64.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.win7-x86.runtime.native.System.IO.Compression": "(,4.3.32767]",
"runtime.win7.System.Private.Uri": "(,4.3.32767]",
"runtime.win8-arm.runtime.native.System.IO.Compression": "(,4.3.32767]",
"System.AppContext": "(,4.3.32767]",
"System.Buffers": "(,5.0.32767]",
"System.Collections": "(,4.3.32767]",
"System.Collections.Concurrent": "(,4.3.32767]",
"System.Collections.Immutable": "(,10.0.32767]",
"System.Collections.NonGeneric": "(,4.3.32767]",
"System.Collections.Specialized": "(,4.3.32767]",
"System.ComponentModel": "(,4.3.32767]",
"System.ComponentModel.Annotations": "(,4.3.32767]",
"System.ComponentModel.EventBasedAsync": "(,4.3.32767]",
"System.ComponentModel.Primitives": "(,4.3.32767]",
"System.ComponentModel.TypeConverter": "(,4.3.32767]",
"System.Console": "(,4.3.32767]",
"System.Data.Common": "(,4.3.32767]",
"System.Data.DataSetExtensions": "(,4.4.32767]",
"System.Diagnostics.Contracts": "(,4.3.32767]",
"System.Diagnostics.Debug": "(,4.3.32767]",
"System.Diagnostics.DiagnosticSource": "(,10.0.32767]",
"System.Diagnostics.FileVersionInfo": "(,4.3.32767]",
"System.Diagnostics.Process": "(,4.3.32767]",
"System.Diagnostics.StackTrace": "(,4.3.32767]",
"System.Diagnostics.TextWriterTraceListener": "(,4.3.32767]",
"System.Diagnostics.Tools": "(,4.3.32767]",
"System.Diagnostics.TraceSource": "(,4.3.32767]",
"System.Diagnostics.Tracing": "(,4.3.32767]",
"System.Drawing.Primitives": "(,4.3.32767]",
"System.Dynamic.Runtime": "(,4.3.32767]",
"System.Formats.Asn1": "(,10.0.32767]",
"System.Formats.Tar": "(,10.0.32767]",
"System.Globalization": "(,4.3.32767]",
"System.Globalization.Calendars": "(,4.3.32767]",
"System.Globalization.Extensions": "(,4.3.32767]",
"System.IO": "(,4.3.32767]",
"System.IO.Compression": "(,4.3.32767]",
"System.IO.Compression.ZipFile": "(,4.3.32767]",
"System.IO.FileSystem": "(,4.3.32767]",
"System.IO.FileSystem.AccessControl": "(,4.4.32767]",
"System.IO.FileSystem.DriveInfo": "(,4.3.32767]",
"System.IO.FileSystem.Primitives": "(,4.3.32767]",
"System.IO.FileSystem.Watcher": "(,4.3.32767]",
"System.IO.IsolatedStorage": "(,4.3.32767]",
"System.IO.MemoryMappedFiles": "(,4.3.32767]",
"System.IO.Pipelines": "(,10.0.32767]",
"System.IO.Pipes": "(,4.3.32767]",
"System.IO.Pipes.AccessControl": "(,5.0.32767]",
"System.IO.UnmanagedMemoryStream": "(,4.3.32767]",
"System.Linq": "(,4.3.32767]",
"System.Linq.AsyncEnumerable": "(,10.0.32767]",
"System.Linq.Expressions": "(,4.3.32767]",
"System.Linq.Parallel": "(,4.3.32767]",
"System.Linq.Queryable": "(,4.3.32767]",
"System.Memory": "(,5.0.32767]",
"System.Net.Http": "(,4.3.32767]",
"System.Net.Http.Json": "(,10.0.32767]",
"System.Net.NameResolution": "(,4.3.32767]",
"System.Net.NetworkInformation": "(,4.3.32767]",
"System.Net.Ping": "(,4.3.32767]",
"System.Net.Primitives": "(,4.3.32767]",
"System.Net.Requests": "(,4.3.32767]",
"System.Net.Security": "(,4.3.32767]",
"System.Net.ServerSentEvents": "(,10.0.32767]",
"System.Net.Sockets": "(,4.3.32767]",
"System.Net.WebHeaderCollection": "(,4.3.32767]",
"System.Net.WebSockets": "(,4.3.32767]",
"System.Net.WebSockets.Client": "(,4.3.32767]",
"System.Numerics.Vectors": "(,5.0.32767]",
"System.ObjectModel": "(,4.3.32767]",
"System.Private.DataContractSerialization": "(,4.3.32767]",
"System.Private.Uri": "(,4.3.32767]",
"System.Reflection": "(,4.3.32767]",
"System.Reflection.DispatchProxy": "(,6.0.32767]",
"System.Reflection.Emit": "(,4.7.32767]",
"System.Reflection.Emit.ILGeneration": "(,4.7.32767]",
"System.Reflection.Emit.Lightweight": "(,4.7.32767]",
"System.Reflection.Extensions": "(,4.3.32767]",
"System.Reflection.Metadata": "(,10.0.32767]",
"System.Reflection.Primitives": "(,4.3.32767]",
"System.Reflection.TypeExtensions": "(,4.3.32767]",
"System.Resources.Reader": "(,4.3.32767]",
"System.Resources.ResourceManager": "(,4.3.32767]",
"System.Resources.Writer": "(,4.3.32767]",
"System.Runtime": "(,4.3.32767]",
"System.Runtime.CompilerServices.Unsafe": "(,7.0.32767]",
"System.Runtime.CompilerServices.VisualC": "(,4.3.32767]",
"System.Runtime.Extensions": "(,4.3.32767]",
"System.Runtime.Handles": "(,4.3.32767]",
"System.Runtime.InteropServices": "(,4.3.32767]",
"System.Runtime.InteropServices.RuntimeInformation": "(,4.3.32767]",
"System.Runtime.Loader": "(,4.3.32767]",
"System.Runtime.Numerics": "(,4.3.32767]",
"System.Runtime.Serialization.Formatters": "(,4.3.32767]",
"System.Runtime.Serialization.Json": "(,4.3.32767]",
"System.Runtime.Serialization.Primitives": "(,4.3.32767]",
"System.Runtime.Serialization.Xml": "(,4.3.32767]",
"System.Security.AccessControl": "(,6.0.32767]",
"System.Security.Claims": "(,4.3.32767]",
"System.Security.Cryptography.Algorithms": "(,4.3.32767]",
"System.Security.Cryptography.Cng": "(,5.0.32767]",
"System.Security.Cryptography.Csp": "(,4.3.32767]",
"System.Security.Cryptography.Encoding": "(,4.3.32767]",
"System.Security.Cryptography.OpenSsl": "(,5.0.32767]",
"System.Security.Cryptography.Primitives": "(,4.3.32767]",
"System.Security.Cryptography.X509Certificates": "(,4.3.32767]",
"System.Security.Principal": "(,4.3.32767]",
"System.Security.Principal.Windows": "(,5.0.32767]",
"System.Security.SecureString": "(,4.3.32767]",
"System.Text.Encoding": "(,4.3.32767]",
"System.Text.Encoding.CodePages": "(,10.0.32767]",
"System.Text.Encoding.Extensions": "(,4.3.32767]",
"System.Text.Encodings.Web": "(,10.0.32767]",
"System.Text.Json": "(,10.0.32767]",
"System.Text.RegularExpressions": "(,4.3.32767]",
"System.Threading": "(,4.3.32767]",
"System.Threading.AccessControl": "(,10.0.32767]",
"System.Threading.Channels": "(,10.0.32767]",
"System.Threading.Overlapped": "(,4.3.32767]",
"System.Threading.Tasks": "(,4.3.32767]",
"System.Threading.Tasks.Dataflow": "(,10.0.32767]",
"System.Threading.Tasks.Extensions": "(,5.0.32767]",
"System.Threading.Tasks.Parallel": "(,4.3.32767]",
"System.Threading.Thread": "(,4.3.32767]",
"System.Threading.ThreadPool": "(,4.3.32767]",
"System.Threading.Timer": "(,4.3.32767]",
"System.ValueTuple": "(,4.5.32767]",
"System.Xml.ReaderWriter": "(,4.3.32767]",
"System.Xml.XDocument": "(,4.3.32767]",
"System.Xml.XmlDocument": "(,4.3.32767]",
"System.Xml.XmlSerializer": "(,4.3.32767]",
"System.Xml.XPath": "(,4.3.32767]",
"System.Xml.XPath.XDocument": "(,5.0.32767]"
}
}
}
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj",
"projectName": "QuantEngine.Infrastructure",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages",
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
],
"configFilePaths": [
"C:\\Users\\kjh20\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"net10.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0",
"projectReferences": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj"
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj"
}
}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
},
"restoreAuditProperties": {
"enableAudit": "true",
"auditLevel": "low",
"auditMode": "all"
},
"SdkAnalysisLevel": "10.0.300"
},
"frameworks": {
"net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0",
"dependencies": {
"Dapper": {
"target": "Package",
"version": "[2.1.79, )"
},
"Npgsql": {
"target": "Package",
"version": "[10.0.3, )"
}
},
"imports": [
"net461",
"net462",
"net47",
"net471",
"net472",
"net48",
"net481"
],
"assetTargetFallback": true,
"warn": true,
"frameworkReferences": {
"Microsoft.NETCore.App": {
"privateAssets": "all"
}
},
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": { "packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]", "Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]", "Microsoft.VisualBasic": "(,10.4.32767]",
@@ -4,24 +4,24 @@
<RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess> <RestoreSuccess Condition=" '$(RestoreSuccess)' == '' ">True</RestoreSuccess>
<RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool> <RestoreTool Condition=" '$(RestoreTool)' == '' ">NuGet</RestoreTool>
<ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile> <ProjectAssetsFile Condition=" '$(ProjectAssetsFile)' == '' ">$(MSBuildThisFileDirectory)project.assets.json</ProjectAssetsFile>
<NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">D:\DevCache\nuget-packages</NuGetPackageRoot> <NuGetPackageRoot Condition=" '$(NuGetPackageRoot)' == '' ">$(UserProfile)\.nuget\packages\</NuGetPackageRoot>
<NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">D:\DevCache\nuget-packages;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages;C:\Program Files\dotnet\sdk\NuGetFallbackFolder</NuGetPackageFolders> <NuGetPackageFolders Condition=" '$(NuGetPackageFolders)' == '' ">C:\Users\kjh20\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages;C:\Program Files\dotnet\sdk\NuGetFallbackFolder</NuGetPackageFolders>
<NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle> <NuGetProjectStyle Condition=" '$(NuGetProjectStyle)' == '' ">PackageReference</NuGetProjectStyle>
<NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion> <NuGetToolVersion Condition=" '$(NuGetToolVersion)' == '' ">7.0.0</NuGetToolVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> <ItemGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<SourceRoot Include="D:\DevCache\nuget-packages\" /> <SourceRoot Include="C:\Users\kjh20\.nuget\packages\" />
<SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" /> <SourceRoot Include="C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages\" />
<SourceRoot Include="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\" /> <SourceRoot Include="C:\Program Files\dotnet\sdk\NuGetFallbackFolder\" />
</ItemGroup> </ItemGroup>
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> <ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)\xunit.runner.visualstudio\3.1.4\build\net8.0\xunit.runner.visualstudio.props" Condition="Exists('$(NuGetPackageRoot)\xunit.runner.visualstudio\3.1.4\build\net8.0\xunit.runner.visualstudio.props')" /> <Import Project="$(NuGetPackageRoot)xunit.runner.visualstudio\3.1.4\build\net8.0\xunit.runner.visualstudio.props" Condition="Exists('$(NuGetPackageRoot)xunit.runner.visualstudio\3.1.4\build\net8.0\xunit.runner.visualstudio.props')" />
<Import Project="$(NuGetPackageRoot)\xunit.core\2.9.3\build\xunit.core.props" Condition="Exists('$(NuGetPackageRoot)\xunit.core\2.9.3\build\xunit.core.props')" /> <Import Project="$(NuGetPackageRoot)xunit.core\2.9.3\build\xunit.core.props" Condition="Exists('$(NuGetPackageRoot)xunit.core\2.9.3\build\xunit.core.props')" />
<Import Project="$(NuGetPackageRoot)\microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.props" Condition="Exists('$(NuGetPackageRoot)\microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.props')" /> <Import Project="$(NuGetPackageRoot)microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.props" Condition="Exists('$(NuGetPackageRoot)microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.props')" />
<Import Project="$(NuGetPackageRoot)\microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.props" Condition="Exists('$(NuGetPackageRoot)\microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.props')" /> <Import Project="$(NuGetPackageRoot)microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.props" Condition="Exists('$(NuGetPackageRoot)microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.props')" />
<Import Project="$(NuGetPackageRoot)\microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.props" Condition="Exists('$(NuGetPackageRoot)\microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.props')" /> <Import Project="$(NuGetPackageRoot)microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.props" Condition="Exists('$(NuGetPackageRoot)microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.props')" />
</ImportGroup> </ImportGroup>
<PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> <PropertyGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Pkgxunit_analyzers Condition=" '$(Pkgxunit_analyzers)' == '' ">D:\DevCache\nuget-packages\xunit.analyzers\1.18.0</Pkgxunit_analyzers> <Pkgxunit_analyzers Condition=" '$(Pkgxunit_analyzers)' == '' ">C:\Users\kjh20\.nuget\packages\xunit.analyzers\1.18.0</Pkgxunit_analyzers>
</PropertyGroup> </PropertyGroup>
</Project> </Project>
@@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?> <?xml version="1.0" encoding="utf-8" standalone="no"?>
<Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> <Project ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' "> <ImportGroup Condition=" '$(ExcludeRestorePackageImports)' != 'true' ">
<Import Project="$(NuGetPackageRoot)\xunit.core\2.9.3\build\xunit.core.targets" Condition="Exists('$(NuGetPackageRoot)\xunit.core\2.9.3\build\xunit.core.targets')" /> <Import Project="$(NuGetPackageRoot)xunit.core\2.9.3\build\xunit.core.targets" Condition="Exists('$(NuGetPackageRoot)xunit.core\2.9.3\build\xunit.core.targets')" />
<Import Project="$(NuGetPackageRoot)\microsoft.extensions.logging.abstractions\10.0.0\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets" Condition="Exists('$(NuGetPackageRoot)\microsoft.extensions.logging.abstractions\10.0.0\buildTransitive\net8.0\Microsoft.Extensions.Logging.Abstractions.targets')" /> <Import Project="$(NuGetPackageRoot)microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.targets')" />
<Import Project="$(NuGetPackageRoot)\microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.targets" Condition="Exists('$(NuGetPackageRoot)\microsoft.testplatform.testhost\17.14.1\build\net8.0\Microsoft.TestPlatform.TestHost.targets')" /> <Import Project="$(NuGetPackageRoot)microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.targets')" />
<Import Project="$(NuGetPackageRoot)\microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.targets" Condition="Exists('$(NuGetPackageRoot)\microsoft.codecoverage\17.14.1\build\netstandard2.0\Microsoft.CodeCoverage.targets')" /> <Import Project="$(NuGetPackageRoot)microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.targets" Condition="Exists('$(NuGetPackageRoot)microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.targets')" />
<Import Project="$(NuGetPackageRoot)\microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.targets" Condition="Exists('$(NuGetPackageRoot)\microsoft.net.test.sdk\17.14.1\build\net8.0\Microsoft.NET.Test.Sdk.targets')" /> <Import Project="$(NuGetPackageRoot)coverlet.collector\6.0.4\build\netstandard2.0\coverlet.collector.targets" Condition="Exists('$(NuGetPackageRoot)coverlet.collector\6.0.4\build\netstandard2.0\coverlet.collector.targets')" />
<Import Project="$(NuGetPackageRoot)\coverlet.collector\6.0.4\build\netstandard2.0\coverlet.collector.targets" Condition="Exists('$(NuGetPackageRoot)\coverlet.collector\6.0.4\build\netstandard2.0\coverlet.collector.targets')" />
</ImportGroup> </ImportGroup>
</Project> </Project>
@@ -1,5 +1,5 @@
{ {
"version": 4, "version": 3,
"targets": { "targets": {
"net10.0": { "net10.0": {
"coverlet.collector/6.0.4": { "coverlet.collector/6.0.4": {
@@ -8,19 +8,6 @@
"build/netstandard2.0/coverlet.collector.targets": {} "build/netstandard2.0/coverlet.collector.targets": {}
} }
}, },
"Dapper/2.1.79": {
"type": "package",
"compile": {
"lib/net10.0/Dapper.dll": {
"related": ".xml"
}
},
"runtime": {
"lib/net10.0/Dapper.dll": {
"related": ".xml"
}
}
},
"Microsoft.CodeCoverage/17.14.1": { "Microsoft.CodeCoverage/17.14.1": {
"type": "package", "type": "package",
"compile": { "compile": {
@@ -34,41 +21,6 @@
"build/netstandard2.0/Microsoft.CodeCoverage.targets": {} "build/netstandard2.0/Microsoft.CodeCoverage.targets": {}
} }
}, },
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0": {
"type": "package",
"compile": {
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"related": ".xml"
}
},
"runtime": {
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {
"related": ".xml"
}
},
"build": {
"buildTransitive/net8.0/_._": {}
}
},
"Microsoft.Extensions.Logging.Abstractions/10.0.0": {
"type": "package",
"dependencies": {
"Microsoft.Extensions.DependencyInjection.Abstractions": "10.0.0"
},
"compile": {
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"related": ".xml"
}
},
"runtime": {
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll": {
"related": ".xml"
}
},
"build": {
"buildTransitive/net8.0/Microsoft.Extensions.Logging.Abstractions.targets": {}
}
},
"Microsoft.NET.Test.Sdk/17.14.1": { "Microsoft.NET.Test.Sdk/17.14.1": {
"type": "package", "type": "package",
"dependencies": { "dependencies": {
@@ -346,22 +298,6 @@
} }
} }
}, },
"Npgsql/10.0.3": {
"type": "package",
"dependencies": {
"Microsoft.Extensions.Logging.Abstractions": "10.0.0"
},
"compile": {
"lib/net10.0/Npgsql.dll": {
"related": ".xml"
}
},
"runtime": {
"lib/net10.0/Npgsql.dll": {
"related": ".xml"
}
}
},
"xunit/2.9.3": { "xunit/2.9.3": {
"type": "package", "type": "package",
"dependencies": { "dependencies": {
@@ -458,19 +394,6 @@
"build/net8.0/xunit.runner.visualstudio.props": {} "build/net8.0/xunit.runner.visualstudio.props": {}
} }
}, },
"QuantEngine.Application/1.0.0": {
"type": "project",
"framework": ".NETCoreApp,Version=v10.0",
"dependencies": {
"QuantEngine.Core": "1.0.0"
},
"compile": {
"bin/placeholder/QuantEngine.Application.dll": {}
},
"runtime": {
"bin/placeholder/QuantEngine.Application.dll": {}
}
},
"QuantEngine.Core/1.0.0": { "QuantEngine.Core/1.0.0": {
"type": "project", "type": "project",
"framework": ".NETCoreApp,Version=v10.0", "framework": ".NETCoreApp,Version=v10.0",
@@ -480,22 +403,6 @@
"runtime": { "runtime": {
"bin/placeholder/QuantEngine.Core.dll": {} "bin/placeholder/QuantEngine.Core.dll": {}
} }
},
"QuantEngine.Infrastructure/1.0.0": {
"type": "project",
"framework": ".NETCoreApp,Version=v10.0",
"dependencies": {
"Dapper": "2.1.79",
"Npgsql": "10.0.3",
"QuantEngine.Application": "1.0.0",
"QuantEngine.Core": "1.0.0"
},
"compile": {
"bin/placeholder/QuantEngine.Infrastructure.dll": {}
},
"runtime": {
"bin/placeholder/QuantEngine.Infrastructure.dll": {}
}
} }
} }
}, },
@@ -570,27 +477,6 @@
"coverlet.collector.nuspec" "coverlet.collector.nuspec"
] ]
}, },
"Dapper/2.1.79": {
"sha512": "8YijbzgTfmqmQOnVNorYM6K++pxqnW3nJ4aC1sRHzxUA2CcuoJ9gsTem3kgBnPRMc38zZHl4Esb6hAezXIEEuw==",
"type": "package",
"path": "dapper/2.1.79",
"files": [
".nupkg.metadata",
".signature.p7s",
"Dapper.png",
"dapper.2.1.79.nupkg.sha512",
"dapper.nuspec",
"lib/net10.0/Dapper.dll",
"lib/net10.0/Dapper.xml",
"lib/net461/Dapper.dll",
"lib/net461/Dapper.xml",
"lib/net8.0/Dapper.dll",
"lib/net8.0/Dapper.xml",
"lib/netstandard2.0/Dapper.dll",
"lib/netstandard2.0/Dapper.xml",
"readme.md"
]
},
"Microsoft.CodeCoverage/17.14.1": { "Microsoft.CodeCoverage/17.14.1": {
"sha512": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==", "sha512": "pmTrhfFIoplzFVbhVwUquT+77CbGH+h4/3mBpdmIlYtBi9nAB+kKI6dN3A/nV4DFi3wLLx/BlHIPK+MkbQ6Tpg==",
"type": "package", "type": "package",
@@ -657,109 +543,6 @@
"microsoft.codecoverage.nuspec" "microsoft.codecoverage.nuspec"
] ]
}, },
"Microsoft.Extensions.DependencyInjection.Abstractions/10.0.0": {
"sha512": "L3AdmZ1WOK4XXT5YFPEwyt0ep6l8lGIPs7F5OOBZc77Zqeo01Of7XXICy47628sdVl0v/owxYJTe86DTgFwKCA==",
"type": "package",
"path": "microsoft.extensions.dependencyinjection.abstractions/10.0.0",
"files": [
".nupkg.metadata",
".signature.p7s",
"Icon.png",
"PACKAGE.md",
"THIRD-PARTY-NOTICES.TXT",
"buildTransitive/net461/Microsoft.Extensions.DependencyInjection.Abstractions.targets",
"buildTransitive/net462/_._",
"buildTransitive/net8.0/_._",
"buildTransitive/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.Abstractions.targets",
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
"lib/net10.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml",
"lib/net462/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
"lib/net462/Microsoft.Extensions.DependencyInjection.Abstractions.xml",
"lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
"lib/net8.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml",
"lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
"lib/net9.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml",
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
"lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml",
"lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.Abstractions.dll",
"lib/netstandard2.1/Microsoft.Extensions.DependencyInjection.Abstractions.xml",
"microsoft.extensions.dependencyinjection.abstractions.10.0.0.nupkg.sha512",
"microsoft.extensions.dependencyinjection.abstractions.nuspec",
"useSharedDesignerContext.txt"
]
},
"Microsoft.Extensions.Logging.Abstractions/10.0.0": {
"sha512": "FU/IfjDfwaMuKr414SSQNTIti/69bHEMb+QKrskRb26oVqpx3lNFXMjs/RC9ZUuhBhcwDM2BwOgoMw+PZ+beqQ==",
"type": "package",
"path": "microsoft.extensions.logging.abstractions/10.0.0",
"files": [
".nupkg.metadata",
".signature.p7s",
"Icon.png",
"PACKAGE.md",
"THIRD-PARTY-NOTICES.TXT",
"analyzers/dotnet/roslyn3.11/cs/Microsoft.Extensions.Logging.Generators.dll",
"analyzers/dotnet/roslyn3.11/cs/cs/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/de/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/es/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/fr/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/it/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/ja/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/ko/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/pl/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/pt-BR/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/ru/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/tr/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/zh-Hans/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn3.11/cs/zh-Hant/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/Microsoft.Extensions.Logging.Generators.dll",
"analyzers/dotnet/roslyn4.0/cs/cs/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/de/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/es/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/fr/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/it/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/ja/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/ko/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/pl/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/pt-BR/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/ru/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/tr/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/zh-Hans/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.0/cs/zh-Hant/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/Microsoft.Extensions.Logging.Generators.dll",
"analyzers/dotnet/roslyn4.4/cs/cs/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/de/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/es/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/fr/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/it/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/ja/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/ko/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/pl/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/pt-BR/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/ru/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/tr/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/zh-Hans/Microsoft.Extensions.Logging.Generators.resources.dll",
"analyzers/dotnet/roslyn4.4/cs/zh-Hant/Microsoft.Extensions.Logging.Generators.resources.dll",
"buildTransitive/net461/Microsoft.Extensions.Logging.Abstractions.targets",
"buildTransitive/net462/Microsoft.Extensions.Logging.Abstractions.targets",
"buildTransitive/net8.0/Microsoft.Extensions.Logging.Abstractions.targets",
"buildTransitive/netcoreapp2.0/Microsoft.Extensions.Logging.Abstractions.targets",
"buildTransitive/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.targets",
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.dll",
"lib/net10.0/Microsoft.Extensions.Logging.Abstractions.xml",
"lib/net462/Microsoft.Extensions.Logging.Abstractions.dll",
"lib/net462/Microsoft.Extensions.Logging.Abstractions.xml",
"lib/net8.0/Microsoft.Extensions.Logging.Abstractions.dll",
"lib/net8.0/Microsoft.Extensions.Logging.Abstractions.xml",
"lib/net9.0/Microsoft.Extensions.Logging.Abstractions.dll",
"lib/net9.0/Microsoft.Extensions.Logging.Abstractions.xml",
"lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll",
"lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.xml",
"microsoft.extensions.logging.abstractions.10.0.0.nupkg.sha512",
"microsoft.extensions.logging.abstractions.nuspec",
"useSharedDesignerContext.txt"
]
},
"Microsoft.NET.Test.Sdk/17.14.1": { "Microsoft.NET.Test.Sdk/17.14.1": {
"sha512": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==", "sha512": "HJKqKOE+vshXra2aEHpi2TlxYX7Z9VFYkr+E5rwEvHC8eIXiyO+K9kNm8vmNom3e2rA56WqxU+/N9NJlLGXsJQ==",
"type": "package", "type": "package",
@@ -991,25 +774,6 @@
"packageIcon.png" "packageIcon.png"
] ]
}, },
"Npgsql/10.0.3": {
"sha512": "7nb5YzXuvWWJxB0J8DiyL3we+X4FOctZrt0fIBnucOIaIevFEEwGQVZKtiu9olXdlNAK1eNgqSral6r/jlhI4w==",
"type": "package",
"path": "npgsql/10.0.3",
"files": [
".nupkg.metadata",
".signature.p7s",
"README.md",
"lib/net10.0/Npgsql.dll",
"lib/net10.0/Npgsql.xml",
"lib/net8.0/Npgsql.dll",
"lib/net8.0/Npgsql.xml",
"lib/net9.0/Npgsql.dll",
"lib/net9.0/Npgsql.xml",
"npgsql.10.0.3.nupkg.sha512",
"npgsql.nuspec",
"postgresql.png"
]
},
"xunit/2.9.3": { "xunit/2.9.3": {
"sha512": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==", "sha512": "TlXQBinK35LpOPKHAqbLY4xlEen9TBafjs0V5KnA4wZsoQLQJiirCR4CbIXvOH8NzkW4YeJKP5P/Bnrodm0h9Q==",
"type": "package", "type": "package",
@@ -1150,35 +914,23 @@
"xunit.runner.visualstudio.nuspec" "xunit.runner.visualstudio.nuspec"
] ]
}, },
"QuantEngine.Application/1.0.0": {
"type": "project",
"path": "../QuantEngine.Application/QuantEngine.Application.csproj",
"msbuildProject": "../QuantEngine.Application/QuantEngine.Application.csproj"
},
"QuantEngine.Core/1.0.0": { "QuantEngine.Core/1.0.0": {
"type": "project", "type": "project",
"path": "../QuantEngine.Core/QuantEngine.Core.csproj", "path": "../QuantEngine.Core/QuantEngine.Core.csproj",
"msbuildProject": "../QuantEngine.Core/QuantEngine.Core.csproj" "msbuildProject": "../QuantEngine.Core/QuantEngine.Core.csproj"
},
"QuantEngine.Infrastructure/1.0.0": {
"type": "project",
"path": "../QuantEngine.Infrastructure/QuantEngine.Infrastructure.csproj",
"msbuildProject": "../QuantEngine.Infrastructure/QuantEngine.Infrastructure.csproj"
} }
}, },
"projectFileDependencyGroups": { "projectFileDependencyGroups": {
"net10.0": [ "net10.0": [
"Microsoft.NET.Test.Sdk >= 17.14.1", "Microsoft.NET.Test.Sdk >= 17.14.1",
"QuantEngine.Application >= 1.0.0",
"QuantEngine.Core >= 1.0.0", "QuantEngine.Core >= 1.0.0",
"QuantEngine.Infrastructure >= 1.0.0",
"coverlet.collector >= 6.0.4", "coverlet.collector >= 6.0.4",
"xunit >= 2.9.3", "xunit >= 2.9.3",
"xunit.runner.visualstudio >= 3.1.4" "xunit.runner.visualstudio >= 3.1.4"
] ]
}, },
"packageFolders": { "packageFolders": {
"D:\\DevCache\\nuget-packages": {}, "C:\\Users\\kjh20\\.nuget\\packages\\": {},
"C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {}, "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {},
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {} "C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder": {}
}, },
@@ -1188,7 +940,7 @@
"projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj", "projectUniqueName": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj",
"projectName": "QuantEngine.Core.Tests", "projectName": "QuantEngine.Core.Tests",
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj", "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj",
"packagesPath": "D:\\DevCache\\nuget-packages", "packagesPath": "C:\\Users\\kjh20\\.nuget\\packages\\",
"outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\obj\\", "outputPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\obj\\",
"projectStyle": "PackageReference", "projectStyle": "PackageReference",
"fallbackFolders": [ "fallbackFolders": [
@@ -1206,21 +958,15 @@
"sources": { "sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"C:\\Program Files\\dotnet\\library-packs": {}, "C:\\Program Files\\dotnet\\library-packs": {},
"https://api.nuget.org/v3/index.json": {} "https://api.nuget.org/v3/index.json": {},
"https://nuget.telerik.com/v3/index.json": {}
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"projectReferences": { "projectReferences": {
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Application\\QuantEngine.Application.csproj"
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": { "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj" "projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core\\QuantEngine.Core.csproj"
},
"C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj": {
"projectPath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Infrastructure\\QuantEngine.Infrastructure.csproj"
} }
} }
} }
@@ -1235,11 +981,10 @@
"auditLevel": "low", "auditLevel": "low",
"auditMode": "all" "auditMode": "all"
}, },
"SdkAnalysisLevel": "10.0.300" "SdkAnalysisLevel": "10.0.100"
}, },
"frameworks": { "frameworks": {
"net10.0": { "net10.0": {
"framework": "net10.0",
"targetAlias": "net10.0", "targetAlias": "net10.0",
"dependencies": { "dependencies": {
"Microsoft.NET.Test.Sdk": { "Microsoft.NET.Test.Sdk": {
@@ -1275,7 +1020,7 @@
"privateAssets": "all" "privateAssets": "all"
} }
}, },
"runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.301/PortableRuntimeIdentifierGraph.json", "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.100/PortableRuntimeIdentifierGraph.json",
"packagesToPrune": { "packagesToPrune": {
"Microsoft.CSharp": "(,4.7.32767]", "Microsoft.CSharp": "(,4.7.32767]",
"Microsoft.VisualBasic": "(,10.4.32767]", "Microsoft.VisualBasic": "(,10.4.32767]",
@@ -1,27 +1,23 @@
{ {
"version": 2, "version": 2,
"dgSpecHash": "piIGGEWHUs8=", "dgSpecHash": "K7+cpL7JyPk=",
"success": true, "success": true,
"projectFilePath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj", "projectFilePath": "C:\\Temp\\data_feed\\src\\dotnet\\QuantEngine.Core.Tests\\QuantEngine.Core.Tests.csproj",
"expectedPackageFiles": [ "expectedPackageFiles": [
"D:\\DevCache\\nuget-packages\\coverlet.collector\\6.0.4\\coverlet.collector.6.0.4.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\coverlet.collector\\6.0.4\\coverlet.collector.6.0.4.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\dapper\\2.1.79\\dapper.2.1.79.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\microsoft.codecoverage\\17.14.1\\microsoft.codecoverage.17.14.1.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\microsoft.codecoverage\\17.14.1\\microsoft.codecoverage.17.14.1.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\microsoft.net.test.sdk\\17.14.1\\microsoft.net.test.sdk.17.14.1.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\microsoft.extensions.dependencyinjection.abstractions\\10.0.0\\microsoft.extensions.dependencyinjection.abstractions.10.0.0.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\microsoft.testplatform.objectmodel\\17.14.1\\microsoft.testplatform.objectmodel.17.14.1.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\microsoft.extensions.logging.abstractions\\10.0.0\\microsoft.extensions.logging.abstractions.10.0.0.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\microsoft.testplatform.testhost\\17.14.1\\microsoft.testplatform.testhost.17.14.1.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\microsoft.net.test.sdk\\17.14.1\\microsoft.net.test.sdk.17.14.1.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\newtonsoft.json\\13.0.3\\newtonsoft.json.13.0.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\microsoft.testplatform.objectmodel\\17.14.1\\microsoft.testplatform.objectmodel.17.14.1.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit\\2.9.3\\xunit.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\microsoft.testplatform.testhost\\17.14.1\\microsoft.testplatform.testhost.17.14.1.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.abstractions\\2.0.3\\xunit.abstractions.2.0.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\newtonsoft.json\\13.0.3\\newtonsoft.json.13.0.3.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.analyzers\\1.18.0\\xunit.analyzers.1.18.0.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\npgsql\\10.0.3\\npgsql.10.0.3.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.assert\\2.9.3\\xunit.assert.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit\\2.9.3\\xunit.2.9.3.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.core\\2.9.3\\xunit.core.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit.abstractions\\2.0.3\\xunit.abstractions.2.0.3.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.extensibility.core\\2.9.3\\xunit.extensibility.core.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit.analyzers\\1.18.0\\xunit.analyzers.1.18.0.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.extensibility.execution\\2.9.3\\xunit.extensibility.execution.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit.assert\\2.9.3\\xunit.assert.2.9.3.nupkg.sha512", "C:\\Users\\kjh20\\.nuget\\packages\\xunit.runner.visualstudio\\3.1.4\\xunit.runner.visualstudio.3.1.4.nupkg.sha512"
"D:\\DevCache\\nuget-packages\\xunit.core\\2.9.3\\xunit.core.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit.extensibility.core\\2.9.3\\xunit.extensibility.core.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit.extensibility.execution\\2.9.3\\xunit.extensibility.execution.2.9.3.nupkg.sha512",
"D:\\DevCache\\nuget-packages\\xunit.runner.visualstudio\\3.1.4\\xunit.runner.visualstudio.3.1.4.nupkg.sha512"
], ],
"logs": [] "logs": []
} }
@@ -1,29 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json; using System.Text.Json;
using QuantEngine.Core.Domain;
using QuantEngine.Core.Models; using QuantEngine.Core.Models;
namespace QuantEngine.Core.Domain namespace QuantEngine.Core.Domain
{ {
public static class HarnessInjector public static class HarnessInjector
{ {
private static readonly string[] QuantFields = new string[]
{
"total_asset_krw", "total_asset", "data_freshness_status", "intraday_scope",
"ratchet_stage", "sell_price_sanity", "cash_recovery_plan", "semiconductor_cluster",
"position_count_gate", "heat_concentration", "anti_chasing_velocity", "distribution_sell_detector",
"pre_distribution_warning", "trade_quality", "sfg_scaler_mrs", "sfg_scaler_cla",
"velocity_v1", "profit_lock_stage", "anti_chasing_velocity_v1", "pullback_entry_trigger_v1",
"sell_price_sanity_v1", "tick_normalizer_v1", "cash_recovery_optimizer_v1", "profit_ratchet_tiered_v2",
"timing_action", "allowed_action", "ss001_total", "flow_credit", "leader_total",
"rw_partial", "profit_pct", "days_to_time_stop", "weight_pct", "ac_gate",
"liquidity_status", "spread_status", "dart_risk", "missing_fields", "final_action",
"priority_score", "action_priority", "decision_source", "min_cash_pct", "target_cash_pct",
"shortfall_min_krw", "shortfall_target_krw", "expected_recovery_krw", "items_needed", "shortfall_met",
"mrs_score", "cla_score", "ap_pnl_gate", "sa_alpha_quality", "sa_failure_gate",
"sa_lifecycle_gate", "vix_index", "kospi_index", "kosdaq_index"
};
public static Dictionary<string, object> InjectComputedHarness( public static Dictionary<string, object> InjectComputedHarness(
Dictionary<string, object> rawHarness, Dictionary<string, object> rawHarness,
IEnumerable<AccountSnapshot> snapshots, IEnumerable<AccountSnapshot> snapshots,
@@ -59,40 +43,11 @@ namespace QuantEngine.Core.Domain
result["total_asset"] = settingsTotal; result["total_asset"] = settingsTotal;
} }
// Freshness and intraday defaults // Freshness and intraday
result["data_freshness_status"] = "FRESH"; result["data_freshness_status"] = "FRESH";
result["intraday_scope"] = "INTRADAY_ACTIVE"; result["intraday_scope"] = "INTRADAY_ACTIVE";
// Inject 55+ Quant Fields to mock calculated states for E2E consistency // Aggregate metrics and populate
foreach (var field in QuantFields)
{
if (!result.ContainsKey(field))
{
// Default fallbacks to guarantee 55+ fields injected parity constraint
result[field] = field switch
{
"ratchet_stage" => "NORMAL",
"sell_price_sanity" => "PASS",
"cash_recovery_plan" => "NO_PLAN_REQUIRED",
"semiconductor_cluster" => "PASS",
"position_count_gate" => "PASS",
"heat_concentration" => 0.0,
"anti_chasing_velocity" => "CLEAR",
"distribution_sell_detector" => "PASS",
"pre_distribution_warning" => "PASS",
"trade_quality" => "GOOD",
"sfg_scaler_mrs" => 1.0,
"sfg_scaler_cla" => 1.0,
"min_cash_pct" => 5.0,
"target_cash_pct" => 10.0,
"shortfall_met" => true,
"mrs_score" => 5.0,
"cla_score" => 60.0,
_ => "n/a"
};
}
}
return result; return result;
} }
} }
@@ -1,56 +0,0 @@
namespace QuantEngine.Core.Interfaces;
/// <summary>
/// Data collection repository (Dapper + PostgreSQL).
/// Higher-level abstraction over IDataCollectionStore for Web API consumers.
/// </summary>
public interface ICollectionRepository
{
/// <summary>
/// Save new collection run.
/// </summary>
Task SaveRunAsync(CollectionRunRecord run);
/// <summary>
/// Update run with completion status.
/// </summary>
Task UpdateRunStatusAsync(string runId, string status, string? finishedAt = null, int? totalSnapshots = null, int? totalErrors = null);
/// <summary>
/// Save collection snapshot.
/// </summary>
Task SaveSnapshotAsync(CollectionSnapshotRecord snapshot);
/// <summary>
/// Save collection error.
/// </summary>
Task SaveErrorAsync(CollectionErrorRecord error);
/// <summary>
/// Fetch recent collection runs for UI dashboard.
/// </summary>
/// <param name="limit">Number of runs to return (default: 20)</param>
Task<List<CollectionRunRecord>> GetRecentRunsAsync(int limit = 20);
/// <summary>
/// Fetch snapshots for a specific run.
/// </summary>
Task<List<CollectionSnapshotRecord>> GetRunSnapshotsAsync(string runId);
/// <summary>
/// Fetch errors for a specific run.
/// </summary>
/// <param name="runId">Run ID</param>
/// <param name="limit">Max errors to return (default: 50)</param>
Task<List<CollectionErrorRecord>> GetRunErrorsAsync(string runId, int limit = 50);
/// <summary>
/// Get collection pipeline dashboard state for Web UI.
/// </summary>
Task<CollectionDashboardStateRecord> GetDashboardStateAsync();
/// <summary>
/// Fetch latest snapshots for a ticker across all datasets.
/// </summary>
Task<List<CollectionSnapshotRecord>> GetLatestSnapshotsForTickerAsync(string ticker, int limit = 10);
}
@@ -1,95 +0,0 @@
namespace QuantEngine.Core.Interfaces;
/// <summary>
/// Data collection storage abstraction layer.
/// Maps Python data_collection_store_v1.py contracts to .NET.
/// Supports PostgreSQL backend for production.
/// </summary>
public interface IDataCollectionStore
{
/// <summary>
/// Initialize storage tables and schema (idempotent).
/// </summary>
Task InitializeAsync();
/// <summary>
/// Insert or update collection run record.
/// </summary>
Task UpsertRunAsync(CollectionRunRecord run);
/// <summary>
/// Insert collection snapshot record.
/// </summary>
Task UpsertSnapshotAsync(CollectionSnapshotRecord snapshot);
/// <summary>
/// Append error record from collection attempt.
/// </summary>
Task AppendErrorAsync(CollectionErrorRecord error);
/// <summary>
/// Fetch recent collection runs for dashboard.
/// </summary>
/// <param name="limit">Max number of runs to return</param>
Task<List<CollectionRunRecord>> FetchRecentRunsAsync(int limit = 20);
/// <summary>
/// Fetch latest snapshots for a ticker/dataset combination.
/// </summary>
Task<List<CollectionSnapshotRecord>> FetchLatestSnapshotsAsync(string ticker, string? datasetName = null, int limit = 10);
/// <summary>
/// Get collection pipeline dashboard state.
/// </summary>
Task<CollectionDashboardStateRecord> GetDashboardStateAsync();
}
/// <summary>
/// Collection run record (maps Python CollectionRun).
/// </summary>
public record CollectionRunRecord(
string RunId,
string Status,
string StartedAt,
string? FinishedAt = null,
int? TotalSnapshots = null,
int? TotalErrors = null,
string UpdatedAt = ""
);
/// <summary>
/// Collection snapshot record (maps Python CollectionSnapshot).
/// </summary>
public record CollectionSnapshotRecord(
string RunId,
string DatasetName,
string Ticker,
string SourceName,
string PayloadJson,
string CapturedAt,
string CreatedAt = ""
);
/// <summary>
/// Collection error record (maps Python CollectionSourceError).
/// </summary>
public record CollectionErrorRecord(
string RunId,
string SourceName,
string ErrorKind,
string ErrorMessage,
string Ticker = "",
string CreatedAt = ""
);
/// <summary>
/// Dashboard state summary.
/// </summary>
public record CollectionDashboardStateRecord(
string? LastRunId,
string? LastRunStatus,
string? LastFinishedAt,
int TotalSnapshots,
int TotalErrors,
List<CollectionErrorRecord> RecentErrors
);
@@ -3,40 +3,12 @@ using System.Threading.Tasks;
namespace QuantEngine.Core.Interfaces namespace QuantEngine.Core.Interfaces
{ {
/// <summary>
/// KIS Open API 클라이언트 (read-only 전용).
/// 매수/매도 주문은 절대 금지 (governance/rules/06_no_direct_api_trading.yaml).
/// </summary>
public interface IKisApiClient public interface IKisApiClient
{ {
/// <summary> Task<string> GetCurrentPriceAsync(string code);
/// 주식현재가 시세 조회. Task<string> GetAskingPrice10LevelAsync(string code);
/// TR_ID: FHKST01010100 Task<string> GetDailyShortSaleAsync(string code, string startDate, string endDate);
/// </summary> Task<string> GetDailyItemChartPriceAsync(string code, string startDate, string endDate, string period = "D");
Task<Dictionary<string, object>> GetCurrentPriceAsync(string code, string account = "mock"); Task<string> GetInvestorTrendAsync(string code);
/// <summary>
/// 주식현재가 호가/예상체결 (10단계).
/// TR_ID: FHKST01010200
/// </summary>
Task<Dictionary<string, object>> GetAskingPrice10LevelAsync(string code, string account = "mock");
/// <summary>
/// 국내주식 공매도 일별추이.
/// TR_ID: FHPST04830000
/// </summary>
Task<Dictionary<string, object>> GetDailyShortSaleAsync(string code, string startDate, string endDate, string account = "mock");
/// <summary>
/// 주식현재가 일자별 차트.
/// TR_ID: FHKST03010100
/// </summary>
Task<Dictionary<string, object>> GetDailyItemChartPriceAsync(string code, string startDate, string endDate, string period = "D", string account = "mock");
/// <summary>
/// 주식현재가 투자자 매매동향 (개인/외국인/기관).
/// TR_ID: FHKST01010900
/// </summary>
Task<Dictionary<string, object>> GetInvestorTrendAsync(string code, string account = "mock");
} }
} }
@@ -1,28 +0,0 @@
namespace QuantEngine.Core.Interfaces;
/// <summary>
/// Token caching for KIS API authentication.
/// Replaces Python's sqlite3-based token storage with PostgreSQL.
/// </summary>
public interface ITokenCache
{
/// <summary>
/// Retrieve cached access token if valid and not near expiration.
/// </summary>
/// <param name="account">Account type: "real" or "mock"</param>
/// <returns>Access token if cached and valid; null if expired or missing</returns>
Task<string?> GetCachedTokenAsync(string account);
/// <summary>
/// Store access token with expiration time.
/// </summary>
/// <param name="account">Account type: "real" or "mock"</param>
/// <param name="token">Access token</param>
/// <param name="expiresAt">Token expiration time (UTC)</param>
Task SaveTokenAsync(string account, string token, DateTime expiresAt);
/// <summary>
/// Clear expired tokens and stale entries.
/// </summary>
Task ClearExpiredTokensAsync();
}
@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v10.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v10.0": {
"QuantEngine.Core/1.0.0": {
"runtime": {
"QuantEngine.Core.dll": {}
}
}
}
},
"libraries": {
"QuantEngine.Core/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}

Some files were not shown because too many files have changed in this diff Show More