Compare commits
No commits in common. "cf21ff76a92a3de8f4d3d10821e12d571d64d7fd" and "ea32f064c17a108850b6a71ae5376c6efb316dbb" have entirely different histories.
cf21ff76a9
...
ea32f064c1
1 changed files with 9 additions and 36 deletions
|
|
@ -9,22 +9,9 @@ Adds these tables/columns:
|
||||||
Source for trend: per-flat register_date (objective_lots).
|
Source for trend: per-flat register_date (objective_lots).
|
||||||
Source for mortgage rate: prod cbr_mortgage_series (Sverdl region, latest "ставка ипотечная").
|
Source for mortgage rate: prod cbr_mortgage_series (Sverdl region, latest "ставка ипотечная").
|
||||||
"""
|
"""
|
||||||
import sqlite3
|
import sqlite3, pathlib, psycopg2, datetime as dt, statistics
|
||||||
import pathlib
|
|
||||||
import psycopg2
|
|
||||||
import datetime as dt
|
|
||||||
DB = pathlib.Path(__file__).parent / "analysis.db"
|
DB = pathlib.Path(__file__).parent / "analysis.db"
|
||||||
|
|
||||||
# When v_prior == 0 we cannot compute a real ratio, so we synthesise one that
|
|
||||||
# reflects the *magnitude* of v_rec rather than assigning a flat 2.0.
|
|
||||||
# REF_VELOCITY: monthly flats/corpus that is considered "high activity" — above
|
|
||||||
# this level the district earns a ratio near TREND_CAP_VPRIOR_ZERO; below it
|
|
||||||
# the ratio slides toward neutral (1.0). Calibrated from EKB corpus data where
|
|
||||||
# an active corpus typically records 10–20 units/month; 10 is a conservative
|
|
||||||
# "well-performing" benchmark.
|
|
||||||
_REF_VELOCITY: float = 10.0 # monthly flats per corpus → full-cap reference
|
|
||||||
_TREND_CAP_VPRIOR_ZERO: float = 1.5 # maximum ratio when v_prior == 0 (< hard 2.0 cap)
|
|
||||||
|
|
||||||
EXTRA = """
|
EXTRA = """
|
||||||
CREATE TABLE IF NOT EXISTS macro_context (
|
CREATE TABLE IF NOT EXISTS macro_context (
|
||||||
key TEXT PRIMARY KEY,
|
key TEXT PRIMARY KEY,
|
||||||
|
|
@ -51,13 +38,10 @@ CREATE TABLE IF NOT EXISTS scoring_weights (
|
||||||
def safe_alter(conn, sql):
|
def safe_alter(conn, sql):
|
||||||
for stmt in sql.strip().split(";"):
|
for stmt in sql.strip().split(";"):
|
||||||
s = stmt.strip()
|
s = stmt.strip()
|
||||||
if not s:
|
if not s: continue
|
||||||
continue
|
try: conn.execute(s)
|
||||||
try:
|
|
||||||
conn.execute(s)
|
|
||||||
except sqlite3.OperationalError as e:
|
except sqlite3.OperationalError as e:
|
||||||
if "duplicate column" not in str(e):
|
if "duplicate column" not in str(e): raise
|
||||||
raise
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
local = sqlite3.connect(DB)
|
local = sqlite3.connect(DB)
|
||||||
|
|
@ -104,14 +88,11 @@ def main():
|
||||||
WHERE district IS NOT NULL AND register_date IS NOT NULL""").fetchall()
|
WHERE district IS NOT NULL AND register_date IS NOT NULL""").fetchall()
|
||||||
by_d = {}
|
by_d = {}
|
||||||
for d, r, p, c in rows:
|
for d, r, p, c in rows:
|
||||||
try:
|
try: rd = dt.date.fromisoformat(r[:10])
|
||||||
rd = dt.date.fromisoformat(r[:10])
|
except: continue
|
||||||
except ValueError:
|
|
||||||
continue
|
|
||||||
bucket = "recent" if r >= cut_recent else ("prior" if r >= cut_prior else None)
|
bucket = "recent" if r >= cut_recent else ("prior" if r >= cut_prior else None)
|
||||||
if not bucket:
|
if not bucket: continue
|
||||||
continue
|
by_d.setdefault(d, {"recent":[], "prior":[], "corpuses":set()})
|
||||||
by_d.setdefault(d, {"recent": [], "prior": [], "corpuses": set()})
|
|
||||||
by_d[d][bucket].append(rd)
|
by_d[d][bucket].append(rd)
|
||||||
by_d[d]["corpuses"].add((p, c))
|
by_d[d]["corpuses"].add((p, c))
|
||||||
|
|
||||||
|
|
@ -119,15 +100,7 @@ def main():
|
||||||
n_corp = max(len(info["corpuses"]), 1)
|
n_corp = max(len(info["corpuses"]), 1)
|
||||||
v_rec = len(info["recent"]) / 6 / n_corp
|
v_rec = len(info["recent"]) / 6 / n_corp
|
||||||
v_prior = len(info["prior"]) / 6 / n_corp
|
v_prior = len(info["prior"]) / 6 / n_corp
|
||||||
# v_prior > 0: real ratio; v_prior == 0 && v_rec == 0: neutral (None→1.0);
|
ratio = (v_rec / v_prior) if v_prior > 0 else (None if v_rec == 0 else 2.0)
|
||||||
# v_prior == 0 && v_rec > 0: synthesise ratio scaled by v_rec magnitude so
|
|
||||||
# a tiny v_rec stays near 1.0 while a large v_rec approaches _TREND_CAP_VPRIOR_ZERO.
|
|
||||||
if v_prior > 0:
|
|
||||||
ratio: float | None = v_rec / v_prior
|
|
||||||
elif v_rec == 0:
|
|
||||||
ratio = None
|
|
||||||
else:
|
|
||||||
ratio = 1.0 + min(1.0, v_rec / _REF_VELOCITY) * (_TREND_CAP_VPRIOR_ZERO - 1.0)
|
|
||||||
# Prod-style clamping
|
# Prod-style clamping
|
||||||
trend_factor = max(0.7, min(2.0, ratio)) if ratio else 1.0
|
trend_factor = max(0.7, min(2.0, ratio)) if ratio else 1.0
|
||||||
# sat_factor: sold_pct in district. >50% = mature market (prod logic +30%
|
# sat_factor: sold_pct in district. >50% = mature market (prod logic +30%
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue