refactor(gas): GAS 중복 함수 제거 및 루트 레벨 stale .gs 파일 정리
- gas_apex_alpha_watch.gs: gas_apex_runtime_core.gs의 5개 실 구현을 shadowing하던 stub/구버전 제거 (applyApexMacroAlphaSuiteImpl_, applyApexMacroEventSuiteImpl_, calcConsistencyValidatorV2Impl_, calcMacroEventSynchronizerV1Impl_, calcMacroRegimeAdaptiveGateV2Impl_) - gas_lib.gs: gdf_05_alpha_engines.gs로 이전된 runAlphaFeedbackLoop_, getAlphaFeedbackJson_ 스테일 사본 제거 - 루트 레벨 .gs 8개 삭제: src/gas/ 구조 이전 전 구버전, 배포 경로 밖 dead code Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -474,31 +474,6 @@ function appendAlphaHistory_(ss, aewRows, holdings, dfMap, marketRegime) {
|
||||
});
|
||||
}
|
||||
|
||||
function getAlphaFeedbackJson_() {
|
||||
var defaultPayload = {
|
||||
formula_id: 'ALPHA_FEEDBACK_LOOP_V1',
|
||||
as_of: '',
|
||||
analysis_period: '',
|
||||
status: 'DATA_MISSING',
|
||||
cases_analyzed: 0,
|
||||
grade_count: 0,
|
||||
eligible_t20_fail_rate: null,
|
||||
eligible_t60_fail_rate: null,
|
||||
recommended_filter_adjustments: [],
|
||||
grade_summary: []
|
||||
};
|
||||
try {
|
||||
var settings = readSettingsTab_();
|
||||
var raw = settings['afl_v1_last_result'];
|
||||
if (!raw) return defaultPayload;
|
||||
var payload = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
||||
return payload && typeof payload === 'object' ? payload : defaultPayload;
|
||||
} catch (e) {
|
||||
Logger.log('[AFL] getAlphaFeedbackJson_ error: ' + e.message);
|
||||
return defaultPayload;
|
||||
}
|
||||
}
|
||||
|
||||
// ── settings 탭 읽기 → 사용자 입력 파라미터 (total_asset 등) ────────────────
|
||||
// settings 탭: row2=헤더(key|value|note), row3+=데이터
|
||||
// 없으면 빈 객체 반환 (각 호출처에서 null 처리)
|
||||
@@ -2406,143 +2381,6 @@ function getOrbitGapJson() {
|
||||
};
|
||||
}
|
||||
|
||||
// ── [2026-05-21_AFL_V1] ALPHA_FEEDBACK_LOOP_V1 -- monthly grade analysis ────────
|
||||
function runAlphaFeedbackLoop_() {
|
||||
var ss = getSpreadsheet_();
|
||||
var sheet = ss.getSheetByName("alpha_history");
|
||||
var today = Utilities.formatDate(new Date(), "Asia/Seoul", "yyyy-MM-dd");
|
||||
var monthKey = today.substring(0, 7);
|
||||
var defaultPayload = {
|
||||
formula_id: 'ALPHA_FEEDBACK_LOOP_V1',
|
||||
as_of: today,
|
||||
analysis_period: monthKey,
|
||||
status: 'DATA_MISSING',
|
||||
cases_analyzed: 0,
|
||||
grade_count: 0,
|
||||
eligible_t20_fail_rate: null,
|
||||
eligible_t60_fail_rate: null,
|
||||
recommended_filter_adjustments: [],
|
||||
grade_summary: []
|
||||
};
|
||||
if (!sheet) {
|
||||
writeSettingValue_(ss, 'afl_v1_last_result', JSON.stringify(defaultPayload));
|
||||
Logger.log("[AFL] alpha_history sheet not found");
|
||||
return defaultPayload;
|
||||
}
|
||||
var data = sheet.getDataRange().getValues();
|
||||
if (data.length < 2) {
|
||||
writeSettingValue_(ss, 'afl_v1_last_result', JSON.stringify(defaultPayload));
|
||||
Logger.log("[AFL] alpha_history has no data");
|
||||
return defaultPayload;
|
||||
}
|
||||
|
||||
var hdrRow = data[0];
|
||||
var hdrMap = {};
|
||||
hdrRow.forEach(function(h, i) { hdrMap[h] = i; });
|
||||
|
||||
var gradeStats = {};
|
||||
var analyzedCases = 0;
|
||||
for (var i = 1; i < data.length; i++) {
|
||||
var row = data[i];
|
||||
var grade = String(row[hdrMap['SAQG_Grade_At_Entry']] || '').trim();
|
||||
var t20g = String(row[hdrMap['T20_Alpha_Gate']] || '').trim();
|
||||
var t60g = String(row[hdrMap['T60_Alpha_Gate']] || '').trim();
|
||||
if (!grade) continue;
|
||||
if (!gradeStats[grade]) gradeStats[grade] = { t20_total: 0, t20_pass: 0, t60_total: 0, t60_pass: 0 };
|
||||
var s = gradeStats[grade];
|
||||
var skipVals = { 'NOT_YET': 1, 'EXEMPT': 1, 'DATA_MISSING': 1, '': 1 };
|
||||
var hasT20 = t20g && !skipVals[t20g];
|
||||
var hasT60 = t60g && !skipVals[t60g];
|
||||
if (hasT20) { s.t20_total++; if (t20g === 'T20_ALPHA_PASS') s.t20_pass++; }
|
||||
if (hasT60) { s.t60_total++; if (t60g === 'T60_ALPHA_PASS') s.t60_pass++; }
|
||||
if (hasT20 || hasT60) analyzedCases++;
|
||||
}
|
||||
|
||||
var gradeSummary = [];
|
||||
Object.keys(gradeStats).sort().forEach(function(grade) {
|
||||
var s = gradeStats[grade];
|
||||
var t20FailRate = s.t20_total > 0 ? parseFloat((((s.t20_total - s.t20_pass) / s.t20_total) * 100).toFixed(2)) : null;
|
||||
var t60FailRate = s.t60_total > 0 ? parseFloat((((s.t60_total - s.t60_pass) / s.t60_total) * 100).toFixed(2)) : null;
|
||||
var t20PassRate = s.t20_total > 0 ? parseFloat(((s.t20_pass / s.t20_total) * 100).toFixed(2)) : null;
|
||||
var t60PassRate = s.t60_total > 0 ? parseFloat(((s.t60_pass / s.t60_total) * 100).toFixed(2)) : null;
|
||||
gradeSummary.push({
|
||||
grade: grade,
|
||||
t20_total: s.t20_total,
|
||||
t20_pass: s.t20_pass,
|
||||
t20_pass_rate: t20PassRate,
|
||||
t20_fail_rate: t20FailRate,
|
||||
t60_total: s.t60_total,
|
||||
t60_pass: s.t60_pass,
|
||||
t60_pass_rate: t60PassRate,
|
||||
t60_fail_rate: t60FailRate,
|
||||
status: (s.t20_total >= 10 || s.t60_total >= 10) ? 'ANALYZED' : 'DATA_INSUFFICIENT'
|
||||
});
|
||||
});
|
||||
|
||||
var eligibleRow = gradeStats['ELIGIBLE'] || { t20_total: 0, t20_pass: 0, t60_total: 0, t60_pass: 0 };
|
||||
var eligibleT20FailRate = eligibleRow.t20_total > 0
|
||||
? parseFloat((((eligibleRow.t20_total - eligibleRow.t20_pass) / eligibleRow.t20_total) * 100).toFixed(2))
|
||||
: null;
|
||||
var eligibleT60FailRate = eligibleRow.t60_total > 0
|
||||
? parseFloat((((eligibleRow.t60_total - eligibleRow.t60_pass) / eligibleRow.t60_total) * 100).toFixed(2))
|
||||
: null;
|
||||
var eligibleT20PassRate = eligibleRow.t20_total > 0
|
||||
? parseFloat(((eligibleRow.t20_pass / eligibleRow.t20_total) * 100).toFixed(2))
|
||||
: null;
|
||||
|
||||
var recommendations = [];
|
||||
if (analyzedCases >= 10) {
|
||||
if (eligibleT20FailRate !== null && eligibleT20FailRate > 50) {
|
||||
recommendations.push({
|
||||
filter_id: 'SAQG_F2_RECOVERY_RATIO',
|
||||
current: '1.20',
|
||||
recommended: '1.35',
|
||||
rationale: 'ELIGIBLE T+20 fail rate > 50%',
|
||||
action: 'TIGHTEN'
|
||||
});
|
||||
recommendations.push({
|
||||
filter_id: 'SAQG_F3_EXCESS_DRAWDOWN',
|
||||
current: '5%p',
|
||||
recommended: '4%p',
|
||||
rationale: 'ELIGIBLE T+20 fail rate > 50%',
|
||||
action: 'TIGHTEN'
|
||||
});
|
||||
} else if (eligibleT20PassRate !== null && eligibleT20PassRate > 70 && eligibleRow.t20_total >= 12) {
|
||||
recommendations.push({
|
||||
filter_id: 'SAQG_F3_EXCESS_DRAWDOWN',
|
||||
current: '5%p',
|
||||
recommended: '7%p',
|
||||
rationale: 'ELIGIBLE T+20 success rate > 70% and cases >= 12',
|
||||
action: 'RELAX_REVIEW'
|
||||
});
|
||||
} else {
|
||||
recommendations.push({
|
||||
filter_id: 'SAQG_F1_F2_F3',
|
||||
current: 'UNCHANGED',
|
||||
recommended: 'HOLD',
|
||||
rationale: 'No threshold change supported by current sample',
|
||||
action: 'HOLD'
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var payload = {
|
||||
formula_id: 'ALPHA_FEEDBACK_LOOP_V1',
|
||||
as_of: today,
|
||||
analysis_period: monthKey,
|
||||
status: analyzedCases >= 10 ? 'ANALYZED' : 'DATA_INSUFFICIENT',
|
||||
cases_analyzed: analyzedCases,
|
||||
grade_count: Object.keys(gradeStats).length,
|
||||
eligible_t20_fail_rate: eligibleT20FailRate,
|
||||
eligible_t60_fail_rate: eligibleT60FailRate,
|
||||
recommended_filter_adjustments: analyzedCases >= 10 ? recommendations : [],
|
||||
grade_summary: gradeSummary
|
||||
};
|
||||
writeSettingValue_(ss, 'afl_v1_last_result', JSON.stringify(payload));
|
||||
Logger.log('[AFL] done - ' + payload.grade_count + ' grades analyzed, cases=' + analyzedCases);
|
||||
return payload;
|
||||
}
|
||||
|
||||
// ── E2: 월말 자산 스냅샷 → monthly_history 기록 ─────────────────────────────
|
||||
// 트리거: 매달 마지막 영업일 16:30 독립 실행 OR runDataFeed 완료 후 호출.
|
||||
function runMonthlySnapshot() {
|
||||
|
||||
Reference in New Issue
Block a user