Skip to content

Commit 5855da7

Browse files
committed
test: Owner Earnings + fraud detection + ST risk
Adds 13 tests covering: - Owner Earnings: basic calc, negative earnings - Maintenance capex: below/above depreciation - Moat score: wide moat, no moat - FraudDetector: empty/healthy/audit fail/CFO negative - ST risk: empty/consecutive losses/healthy 1205 passed (1192 old + 13 new)
1 parent 435e5f9 commit 5855da7

1 file changed

Lines changed: 135 additions & 0 deletions

File tree

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
"""Tests for financial health checks (fraud, ST risk, owner earnings)."""
2+
3+
import pandas as pd
4+
import pytest
5+
6+
from quant_platform.risk.financial_health import (
7+
FraudDetector,
8+
FraudReport,
9+
assess_st_risk,
10+
owner_earnings,
11+
estimate_maintenance_capex,
12+
moat_score,
13+
)
14+
15+
16+
class TestOwnerEarnings:
17+
def test_basic_calculation(self):
18+
result = owner_earnings(
19+
net_income=1000,
20+
depreciation=200,
21+
maintenance_capex=150,
22+
)
23+
assert result["owner_earnings"] == 1050 # 1000 + 200 - 150
24+
assert result["earnings_quality"] == "high"
25+
26+
def test_negative_owner_earnings(self):
27+
result = owner_earnings(
28+
net_income=100,
29+
depreciation=50,
30+
maintenance_capex=300,
31+
)
32+
assert result["owner_earnings"] < 0
33+
assert result["earnings_quality"] == "low"
34+
35+
36+
class TestEstimateMaintenanceCapex:
37+
def test_capex_less_than_depreciation(self):
38+
result = estimate_maintenance_capex(total_capex=100, depreciation=150)
39+
assert result == 100 # All capex is maintenance
40+
41+
def test_capex_exceeds_depreciation(self):
42+
result = estimate_maintenance_capex(total_capex=200, depreciation=100)
43+
# 100 + (200 - 100) * 0.3 = 130
44+
assert result == pytest.approx(130.0)
45+
46+
47+
class TestMoatScore:
48+
def test_wide_moat(self):
49+
result = moat_score(
50+
gross_margin_stability=0.01,
51+
roe_avg=0.25,
52+
debt_equity=0.1,
53+
pricing_power=True,
54+
)
55+
assert result["score"] >= 8
56+
assert "宽" in result["label"]
57+
58+
def test_no_moat(self):
59+
result = moat_score(
60+
gross_margin_stability=0.15,
61+
roe_avg=0.08,
62+
debt_equity=1.5,
63+
pricing_power=False,
64+
)
65+
assert result["score"] < 4
66+
67+
68+
class TestFraudDetector:
69+
def test_empty_df(self):
70+
detector = FraudDetector()
71+
report = detector.analyze(pd.DataFrame())
72+
assert isinstance(report, FraudReport)
73+
assert report.total_score == 0
74+
75+
def test_healthy_company(self):
76+
df = pd.DataFrame({
77+
"gross_margin": [0.40, 0.41, 0.42],
78+
"cfo": [100, 110, 120],
79+
"net_income": [100, 110, 120],
80+
"revenue": [1000, 1100, 1200],
81+
"receivables": [200, 205, 210],
82+
"goodwill": [10, 10, 10],
83+
"net_assets": [500, 550, 600],
84+
})
85+
detector = FraudDetector()
86+
report = detector.analyze(df)
87+
assert report.risk_level == "低风险"
88+
89+
def test_audit_fail_direct_exclude(self):
90+
df = pd.DataFrame({
91+
"audit_opinion": ["无法表示意见"],
92+
"gross_margin": [0.35],
93+
})
94+
detector = FraudDetector()
95+
report = detector.analyze(df)
96+
assert report.risk_level == "直接排除"
97+
98+
def test_cfo_negative_flag(self):
99+
df = pd.DataFrame({
100+
"cfo": [-100, -110, -120],
101+
"net_income": [50, 55, 60],
102+
"gross_margin": [0.35, 0.36, 0.37],
103+
"revenue": [1000, 1100, 1200],
104+
"receivables": [200, 210, 220],
105+
"goodwill": [10, 10, 10],
106+
"net_assets": [500, 550, 600],
107+
})
108+
detector = FraudDetector()
109+
report = detector.analyze(df)
110+
assert report.total_score > 0
111+
assert "中风险" in report.risk_level or "高风险" in report.risk_level
112+
113+
114+
class TestSTRisk:
115+
def test_empty_df(self):
116+
report = assess_st_risk(pd.DataFrame())
117+
assert report.total_score == 0
118+
119+
def test_consecutive_losses(self):
120+
df = pd.DataFrame({
121+
"net_income": [-100, -200, -300],
122+
"revenue": [1e9, 1e9, 1e9],
123+
"net_assets": [500, 300, 100],
124+
})
125+
report = assess_st_risk(df)
126+
assert report.total_score >= 4
127+
128+
def test_healthy(self):
129+
df = pd.DataFrame({
130+
"net_income": [100, 200, 300],
131+
"revenue": [1e9, 2e9, 3e9],
132+
"net_assets": [1000, 1100, 1200],
133+
})
134+
report = assess_st_risk(df)
135+
assert report.risk_level == "低风险"

0 commit comments

Comments
 (0)