Initial commit: Add project files
ci / backend (push) Failing after 12s
ci / frontend (push) Failing after 19s
ci / static (push) Failing after 45s

This commit is contained in:
2026-08-02 05:15:36 +09:00
commit dcd1322d41
636 changed files with 122352 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
# 연구 알고리즘 계약 하드닝
`kartsell_policy_contract.py`는 첨부 연구 번들의 결론을 생산코드와 독립적으로 검증하기 위한 dependency-free Golden Contract다.
- `SellRatioOfLot``TargetPortfolioWeightAfter`를 분리한다.
- Hard impairment → Portfolio survival → Profit floor → Concentration/Liquidity → Opportunity cost 순서를 고정한다.
- Point-in-Time 공개시각이 평가시각을 넘으면 실패한다.
- 전략적 코어는 gap/two-close/concentration/opportunity 정책에서 보존되지만, 자본바닥 정책은 생존을 위해 코어 아래로 축소할 수 있다.
- 이 코드는 주문·추천·생산 모델이 아니다.
실행:
```bash
cd research/hardening
python -m unittest -v
```
+9
View File
@@ -0,0 +1,9 @@
test_future_published_at_is_rejected (test_policy_contract.PolicyContractTests.test_future_published_at_is_rejected) ... ok
test_gap_preserves_absolute_core_weight (test_policy_contract.PolicyContractTests.test_gap_preserves_absolute_core_weight) ... ok
test_hard_impairment_wins (test_policy_contract.PolicyContractTests.test_hard_impairment_wins) ... ok
test_survival_can_cross_core (test_policy_contract.PolicyContractTests.test_survival_can_cross_core) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
@@ -0,0 +1,9 @@
test_future_published_at_is_rejected (test_policy_contract.PolicyContractTests.test_future_published_at_is_rejected) ... ok
test_gap_preserves_absolute_core_weight (test_policy_contract.PolicyContractTests.test_gap_preserves_absolute_core_weight) ... ok
test_hard_impairment_wins (test_policy_contract.PolicyContractTests.test_hard_impairment_wins) ... ok
test_survival_can_cross_core (test_policy_contract.PolicyContractTests.test_survival_can_cross_core) ... ok
----------------------------------------------------------------------
Ran 4 tests in 0.000s
OK
@@ -0,0 +1,224 @@
"""Executable research contract for K-ArtSell Aegis sell/reentry policies.
This module is intentionally dependency-free. It is not a production trading engine.
It exists to make policy ordering, units, invariants, and golden vectors reproducible.
"""
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from decimal import Decimal, ROUND_HALF_EVEN
from enum import Enum
from typing import Iterable, Optional, Protocol
ZERO = Decimal("0")
ONE = Decimal("1")
class SellAction(str, Enum):
HOLD = "HOLD"
PARTIAL_SELL = "PARTIAL_SELL"
FULL_SELL = "FULL_SELL"
@dataclass(frozen=True)
class SellInput:
position_lot_id: str
cycle_id: str
evidence_id: str
dataset_id: str
model_version: str
config_version: str
code_sha: str
as_of: datetime
published_at_cutoff: datetime
current_portfolio_weight: Decimal
strategic_core_floor_weight: Decimal
hard_impairment_approved: bool = False
capital_floor_breached: bool = False
survival_sell_ratio_of_lot: Decimal = ZERO
gap_below_floor_atr: Decimal = ZERO
consecutive_close_breaches: int = 0
cooldown_satisfied: bool = True
concentration_sell_ratio_of_lot: Decimal = ZERO
opportunity_edge_lower_bound: Decimal = ZERO
opportunity_sell_ratio_of_lot: Decimal = ZERO
def validate(self) -> None:
if self.published_at_cutoff > self.as_of:
raise ValueError("published_at_cutoff cannot be later than as_of")
for name, value in (
("current_portfolio_weight", self.current_portfolio_weight),
("strategic_core_floor_weight", self.strategic_core_floor_weight),
("survival_sell_ratio_of_lot", self.survival_sell_ratio_of_lot),
("concentration_sell_ratio_of_lot", self.concentration_sell_ratio_of_lot),
("opportunity_sell_ratio_of_lot", self.opportunity_sell_ratio_of_lot),
):
if not ZERO <= value <= ONE:
raise ValueError(f"{name} must be between 0 and 1")
if self.strategic_core_floor_weight > self.current_portfolio_weight:
raise ValueError("strategic core cannot exceed current portfolio weight")
if self.gap_below_floor_atr < ZERO:
raise ValueError("gap_below_floor_atr cannot be negative")
if self.consecutive_close_breaches < 0:
raise ValueError("consecutive_close_breaches cannot be negative")
def max_sell_ratio_preserving_core(self) -> Decimal:
if self.current_portfolio_weight <= ZERO:
return ZERO
sellable_weight = max(ZERO, self.current_portfolio_weight - self.strategic_core_floor_weight)
return min(ONE, sellable_weight / self.current_portfolio_weight)
def target_weight_after(self, ratio: Decimal) -> Decimal:
bounded = min(ONE, max(ZERO, ratio))
return (self.current_portfolio_weight * (ONE - bounded)).quantize(
Decimal("0.00000001"), rounding=ROUND_HALF_EVEN
)
@dataclass(frozen=True)
class SellDecision:
action: SellAction
sell_ratio_of_lot: Decimal
target_portfolio_weight_after: Decimal
policy_id: str
priority: int
reason_code: str
reentry_eligible: bool
class SellPolicy(Protocol):
priority: int
policy_id: str
def evaluate(self, value: SellInput) -> Optional[SellDecision]: ...
class HardImpairmentPolicy:
priority = 1000
policy_id = "ALG-SELL-001"
def evaluate(self, value: SellInput) -> Optional[SellDecision]:
if not value.hard_impairment_approved:
return None
return _decision(value, ONE, self.policy_id, self.priority, "HARD_IMPAIRMENT", False)
class PortfolioSurvivalPolicy:
priority = 900
policy_id = "ALG-SELL-PORT-001"
def evaluate(self, value: SellInput) -> Optional[SellDecision]:
if not value.capital_floor_breached or not value.cooldown_satisfied:
return None
ratio = min(ONE, max(ZERO, value.survival_sell_ratio_of_lot))
return None if ratio == ZERO else _decision(
value, ratio, self.policy_id, self.priority, "PORTFOLIO_SURVIVAL", True
)
class GapFloorPolicy:
priority = 800
policy_id = "ALG-SELL-002"
def evaluate(self, value: SellInput) -> Optional[SellDecision]:
if not value.cooldown_satisfied or value.gap_below_floor_atr < Decimal("1.5"):
return None
ratio = min(Decimal("0.40"), value.max_sell_ratio_preserving_core())
return None if ratio == ZERO else _decision(
value, ratio, self.policy_id, self.priority, "GAP_FLOOR_BREACH", True
)
class TwoClosePolicy:
priority = 700
policy_id = "ALG-SELL-003"
def evaluate(self, value: SellInput) -> Optional[SellDecision]:
if not value.cooldown_satisfied or value.consecutive_close_breaches < 2:
return None
ratio = min(Decimal("0.20"), value.max_sell_ratio_preserving_core())
return None if ratio == ZERO else _decision(
value, ratio, self.policy_id, self.priority, "TWO_CLOSE_FLOOR_BREACH", True
)
class ConcentrationPolicy:
priority = 600
policy_id = "ALG-SELL-004"
def evaluate(self, value: SellInput) -> Optional[SellDecision]:
if not value.cooldown_satisfied:
return None
ratio = min(value.concentration_sell_ratio_of_lot, value.max_sell_ratio_preserving_core())
return None if ratio <= ZERO else _decision(
value, ratio, self.policy_id, self.priority, "CONCENTRATION_OR_LIQUIDITY", True
)
class OpportunityCostPolicy:
priority = 500
policy_id = "ALG-SELL-005"
def evaluate(self, value: SellInput) -> Optional[SellDecision]:
if not value.cooldown_satisfied or value.opportunity_edge_lower_bound <= ZERO:
return None
requested = min(Decimal("0.25"), max(Decimal("0.10"), value.opportunity_sell_ratio_of_lot))
ratio = min(requested, value.max_sell_ratio_preserving_core())
return None if ratio <= ZERO else _decision(
value, ratio, self.policy_id, self.priority, "OPPORTUNITY_REPLACEMENT", True
)
class SellPolicyChain:
def __init__(self, policies: Iterable[SellPolicy]):
self._policies = tuple(sorted(policies, key=lambda p: (-p.priority, p.policy_id)))
def evaluate(self, value: SellInput) -> SellDecision:
value.validate()
for policy in self._policies:
decision = policy.evaluate(value)
if decision is not None:
return decision
return SellDecision(
SellAction.HOLD,
ZERO,
value.current_portfolio_weight,
"ALG-HOLD-001",
0,
"NO_SELL_CONDITION",
False,
)
def default_chain() -> SellPolicyChain:
return SellPolicyChain(
[
OpportunityCostPolicy(),
ConcentrationPolicy(),
TwoClosePolicy(),
GapFloorPolicy(),
PortfolioSurvivalPolicy(),
HardImpairmentPolicy(),
]
)
def _decision(
value: SellInput,
ratio: Decimal,
policy_id: str,
priority: int,
reason: str,
reentry_eligible: bool,
) -> SellDecision:
action = SellAction.FULL_SELL if ratio == ONE else SellAction.PARTIAL_SELL
return SellDecision(
action,
ratio,
value.target_weight_after(ratio),
policy_id,
priority,
reason,
reentry_eligible,
)
@@ -0,0 +1,62 @@
from datetime import datetime, timezone
from decimal import Decimal
import unittest
from kartsell_policy_contract import SellAction, SellInput, default_chain
def base_input(**changes):
values = dict(
position_lot_id="lot-1",
cycle_id="cycle-1",
evidence_id="evidence-1",
dataset_id="dataset-1",
model_version="model-1",
config_version="config-1",
code_sha="sha-1",
as_of=datetime(2026, 8, 1, 7, tzinfo=timezone.utc),
published_at_cutoff=datetime(2026, 8, 1, 6, tzinfo=timezone.utc),
current_portfolio_weight=Decimal("0.60"),
strategic_core_floor_weight=Decimal("0.30"),
)
values.update(changes)
return SellInput(**values)
class PolicyContractTests(unittest.TestCase):
def test_hard_impairment_wins(self):
result = default_chain().evaluate(base_input(
hard_impairment_approved=True,
capital_floor_breached=True,
survival_sell_ratio_of_lot=Decimal("0.5"),
gap_below_floor_atr=Decimal("2"),
))
self.assertEqual("ALG-SELL-001", result.policy_id)
self.assertEqual(SellAction.FULL_SELL, result.action)
self.assertFalse(result.reentry_eligible)
def test_gap_preserves_absolute_core_weight(self):
result = default_chain().evaluate(base_input(
strategic_core_floor_weight=Decimal("0.50"),
gap_below_floor_atr=Decimal("2"),
))
self.assertEqual("ALG-SELL-002", result.policy_id)
self.assertEqual(Decimal("0.50000000"), result.target_portfolio_weight_after)
def test_survival_can_cross_core(self):
result = default_chain().evaluate(base_input(
capital_floor_breached=True,
survival_sell_ratio_of_lot=Decimal("0.50"),
))
self.assertEqual("ALG-SELL-PORT-001", result.policy_id)
self.assertEqual(Decimal("0.30000000"), result.target_portfolio_weight_after)
def test_future_published_at_is_rejected(self):
with self.assertRaises(ValueError):
default_chain().evaluate(base_input(
published_at_cutoff=datetime(2026, 8, 1, 8, tzinfo=timezone.utc)
))
if __name__ == "__main__":
unittest.main()