feat: add orderbook shadow training pipeline

This commit is contained in:
Курнат Андрей
2026-07-15 09:44:29 +03:00
parent f7a625586e
commit 5d8ad1437e
19 changed files with 1486 additions and 23 deletions
+43 -4
View File
@@ -22,6 +22,10 @@ param(
[int]$HoldoutWindow = 0,
[string]$Interval = "",
[string]$EnvFile = "",
[string]$OrderbookDb = "",
[int]$OrderbookMinSamplesPerBucket = 0,
[int]$OrderbookMinCoveredBuckets = 0,
[int]$OrderbookMinSymbols = 0,
[switch]$Pooled,
[switch]$SkipGuard,
[switch]$ResumeCandidate
@@ -124,6 +128,10 @@ if ($HoldoutWindow -le 0) { $HoldoutWindow = if ($env:TORCH_RETRAIN_HOLDOUT_WIND
if (-not $Interval -and $env:TORCH_RETRAIN_INTERVAL) { $Interval = $env:TORCH_RETRAIN_INTERVAL }
if (-not $EnvFile -and $env:TORCH_RETRAIN_ENV) { $EnvFile = $env:TORCH_RETRAIN_ENV }
if (-not $EnvFile -and (Test-Path (Join-Path $RepoRoot ".env"))) { $EnvFile = Join-Path $RepoRoot ".env" }
if (-not $OrderbookDb -and $env:TORCH_ORDERBOOK_DB) { $OrderbookDb = $env:TORCH_ORDERBOOK_DB }
if ($OrderbookMinSamplesPerBucket -le 0) { $OrderbookMinSamplesPerBucket = if ($env:TORCH_ORDERBOOK_MIN_SAMPLES_PER_BUCKET) { [int]$env:TORCH_ORDERBOOK_MIN_SAMPLES_PER_BUCKET } else { 20 } }
if ($OrderbookMinCoveredBuckets -le 0) { $OrderbookMinCoveredBuckets = if ($env:TORCH_ORDERBOOK_MIN_COVERED_BUCKETS) { [int]$env:TORCH_ORDERBOOK_MIN_COVERED_BUCKETS } else { 240 } }
if ($OrderbookMinSymbols -le 0) { $OrderbookMinSymbols = if ($env:TORCH_ORDERBOOK_MIN_SYMBOLS) { [int]$env:TORCH_ORDERBOOK_MIN_SYMBOLS } else { 2 } }
$ModelFile = if ($env:TIME_SERIES_LSTM_MODEL_PATH) { $env:TIME_SERIES_LSTM_MODEL_PATH } else { Join-Path $RuntimeDir "lstm_forecaster.json" }
if (-not [System.IO.Path]::IsPathRooted($ModelFile)) { $ModelFile = Join-Path $RepoRoot $ModelFile }
@@ -131,6 +139,10 @@ $CandidateFile = Join-Path $RuntimeDir "lstm_forecaster.candidate.json"
$CurrentCalibration = Join-Path $RuntimeDir "torch_guard_current.json"
$CandidateCalibration = Join-Path $RuntimeDir "torch_guard_candidate.json"
$GuardReport = Join-Path $RuntimeDir "torch_retrain_guard.json"
$ShadowModelFile = Join-Path $RuntimeDir "lstm_forecaster.shadow.json"
$ShadowCalibration = Join-Path $RuntimeDir "torch_shadow_calibration.json"
$ShadowGuard = Join-Path $RuntimeDir "torch_shadow_guard.json"
$ShadowMode = -not [string]::IsNullOrWhiteSpace($OrderbookDb)
$mutex = New-Object System.Threading.Mutex($false, "TradeBotTorchRecurrentRetrainer")
$hasLock = $false
@@ -177,6 +189,14 @@ try {
if ($Features) { $trainerArgs += @("--features", $Features) }
if ($ContextSymbols) { $trainerArgs += @("--context-symbols", $ContextSymbols) }
if ($Seed -gt 0) { $trainerArgs += @("--seed", $Seed.ToString()) }
if ($OrderbookDb) {
$trainerArgs += @(
"--orderbook-db", $OrderbookDb,
"--orderbook-min-samples-per-bucket", $OrderbookMinSamplesPerBucket.ToString(),
"--orderbook-min-covered-buckets", $OrderbookMinCoveredBuckets.ToString(),
"--orderbook-min-symbols", $OrderbookMinSymbols.ToString()
)
}
Push-Location $RepoRoot
$pushedLocation = $true
@@ -216,6 +236,12 @@ try {
)
if ($Symbols) { $calibrationBaseArgs += @("--symbols", $Symbols) }
if ($EnvFile) { $calibrationBaseArgs += @("--env", $EnvFile) }
if ($OrderbookDb) {
$calibrationBaseArgs += @(
"--orderbook-db", $OrderbookDb,
"--orderbook-min-samples-per-bucket", $OrderbookMinSamplesPerBucket.ToString()
)
}
if (Test-Path $ModelFile) {
Write-RetrainLog "Calibrating current artifact for guard."
@@ -243,13 +269,14 @@ try {
}
Write-RetrainLog "Running retrain guard."
$GuardTarget = if ($ShadowMode) { $ShadowModelFile } else { $ModelFile }
$guardArgs = @(
"-u",
"tools\accept_torch_candidate.py",
"--current-report", $CurrentCalibration,
"--candidate-report", $CandidateCalibration,
"--candidate-artifact", $CandidateFile,
"--target-artifact", $ModelFile,
"--target-artifact", $GuardTarget,
"--report", $GuardReport
)
$guardExitCode = Invoke-LoggedNativeCommand -FilePath $python -ArgumentList $guardArgs -LogPath $LogFile
@@ -261,10 +288,22 @@ try {
throw "Retrain guard failed with exit code $guardExitCode."
}
if (Test-Path $CandidateCalibration) {
Copy-Item -Force -LiteralPath $CandidateCalibration -Destination (Join-Path $RuntimeDir "torch_threshold_calibration.json")
Write-RetrainLog "Updated active threshold calibration: $(Join-Path $RuntimeDir "torch_threshold_calibration.json")"
if ($ShadowMode) {
Copy-Item -Force -LiteralPath $CandidateCalibration -Destination $ShadowCalibration
Copy-Item -Force -LiteralPath $GuardReport -Destination $ShadowGuard
Write-RetrainLog "Candidate passed offline gate and was staged for shadow only: $ShadowModelFile"
}
else {
Copy-Item -Force -LiteralPath $CandidateCalibration -Destination (Join-Path $RuntimeDir "torch_threshold_calibration.json")
Write-RetrainLog "Updated active threshold calibration: $(Join-Path $RuntimeDir "torch_threshold_calibration.json")"
}
}
if ($ShadowMode) {
Write-RetrainLog "Candidate accepted by offline guard. Active artifact was not changed: $ModelFile"
}
else {
Write-RetrainLog "Candidate accepted by guard. Active artifact: $ModelFile"
}
Write-RetrainLog "Candidate accepted by guard. Active artifact: $ModelFile"
}
catch {
Write-RetrainLog "ERROR: $($_.Exception.Message)"