finchal / reference.py
SeaWolf-AI's picture
show received entries before scoring begins
cab04ab verified
Raw
History Blame Contribute Delete
5.9 kB
# -*- coding: utf-8 -*-
"""기쀀뢄포 β€” 'μ‹€λ ₯ 0μ΄μ—ˆλ‹€λ©΄ μ–΄λ””κΉŒμ§€ κ°”μ„κΉŒ'λ₯Ό κ³„μ‚°ν•œλ‹€.
두 κ°€μ§€λ₯Ό κ΅¬λΆ„ν•œλ‹€.
κ°œλ§‰ μ „ μΆ”μ •μΉ˜ (announce)
μ°Έκ°€μžμ—κ²Œ "이 μ’…λͺ©μ€ 운으둜 μ΄λ§ŒνΌκΉŒμ§€ λ‚˜μ˜¨λ‹€"κ³  미리 μ•Œλ €μ£ΌλŠ” κ°’.
κ³Όκ±° 5년을 블둝 λΆ€νŠΈμŠ€νŠΈλž©ν•΄μ„œ λ‚Έλ‹€. ν™”λ©΄μ˜ '운의 ν•œκ³„μ„ '에 μ“΄λ‹€.
μ±„μ μš© κΈ°μ€€ (live)
μ‹€μ œ μˆœμœ„λ₯Ό μ •ν•˜λŠ” κ°’. μ‹œμ¦Œμ΄ μ§„ν–‰λœ 만큼의 **μ‹€μ œ 경둜** μœ„μ—μ„œ
μ‹€λ ₯ 0 μ°Έκ°€μžλ₯Ό 돌렀 λ§Œλ“ λ‹€. 맀일 λ‹€μ‹œ κ³„μ‚°ν•œλ‹€.
μ™œ λ‚˜λˆ„λŠ”κ°€
기쀀을 κ³Όκ±° λΆ€νŠΈμŠ€νŠΈλž©μœΌλ‘œ κ³ μ •ν•˜λ©΄, μ‹œμ¦Œ 쀑 κ·Έ μ’…λͺ©μ΄ 크게 였λ₯Ό λ•Œ μ°Έκ°€μž
μ „μ›μ˜ 성적이 ν•¨κ»˜ λΆ€ν’€λ €μ Έ μˆœμœ„κ°€ μ‹œμž₯ λ°©ν–₯을 따라간닀. μ˜¬λ°”λ₯Έ λ°˜μ‚¬μ‹€μ€
'μ‹œμž₯이 μ‹€μ œλ‘œ κ·Έλ ‡κ²Œ μ›€μ§μ˜€μ„ λ•Œ 운으둜 λ‚Ό 수 μžˆμ—ˆλ˜ 성적'이닀.
πŸ”‘ κ°œλ§‰ 전에 κ³ μ •Β·κ³΅κ°œν•˜λŠ” 것은 μˆ«μžκ°€ μ•„λ‹ˆλΌ **방법**이닀.
숫자λ₯Ό κ³ μ •ν•˜λ©΄ μ‹œμž₯이 μ˜ˆμƒκ³Ό λ‹€λ₯΄κ²Œ μ›€μ§μ˜€μ„ λ•Œ 채점이 ν†΅μ§Έλ‘œ μ–΄κΈ‹λ‚œλ‹€.
πŸ”‘ κΈ°μ€€ μ°Έκ°€μžλ„ μ°Έκ°€μžμ™€ λ˜‘κ°™μ΄ 수수료λ₯Ό λ‚Έλ‹€. μ•ˆ 물리면 μ°Έκ°€μžλ§Œ λΉ„μš©μ„
μ§€κ³  μ‹œμž‘ν•œλ‹€. μˆ˜μˆ˜λ£Œκ°€ 높은 μ’…λͺ©μΌμˆ˜λ‘ κ·Έ 차이가 컀진닀.
"""
import os, json, warnings
import numpy as np, pandas as pd
warnings.filterwarnings("ignore")
ROOT = os.path.dirname(os.path.abspath(__file__))
BLOCK = 5
N_LIVE = 4000
N_ANNOUNCE = 20000
def _sim(returns, n, expo, fee, seed, block=None):
"""μ‹€λ ₯ 0 μ°Έκ°€μž nλͺ…μ˜ μ΅œμ’… 수읡λ₯ . expo 만큼의 μ‹œκ°„μ„ ν¬μ§€μ…˜μ— λ…ΈμΆœν•œλ‹€."""
g = np.random.default_rng(seed)
r = np.asarray(returns, dtype=float)
L = len(r)
out = np.empty(n)
for i in range(n):
if block: # λΆ€νŠΈμŠ€νŠΈλž© (κ°œλ§‰ μ „ μΆ”μ •)
nb = int(np.ceil(L / block))
st = g.integers(0, max(len(r) - block, 1), size=nb)
path = np.concatenate([r[j:j+block] for j in st])[:L]
else: # μ‹€μ œ 경둜 (채점)
path = r
act = g.random(L) < expo
pos = np.where(act, g.choice([-1.0, 1.0], size=L), 0.0)
cost = np.abs(np.diff(np.r_[0.0, pos])) * fee
out[i] = np.prod(1 + np.r_[0.0, pos[:-1]] * path - cost) - 1
return np.sort(out)
def live(prices, fee, expo=2/3, n=N_LIVE, seed=None):
"""μ±„μ μš© β€” μ‹œμ¦Œ κ²½κ³ΌλΆ„μ˜ μ‹€μ œ 경둜 μœ„μ—μ„œ λ§Œλ“ λ‹€. 맀일 λ‹€μ‹œ λΆ€λ₯Έλ‹€.
seed λ₯Ό λ‚ μ§œλ‘œ μ£Όλ©΄ 같은 λ‚  같은 값이 λ‚˜μ™€ μž¬ν˜„μ΄ λœλ‹€.
"""
r = pd.Series(prices, dtype=float).sort_index().pct_change().fillna(0.0).values
s = seed if seed is not None else int(pd.Timestamp.utcnow().strftime("%Y%m%d"))
return _sim(r, n, expo, fee, s)
def announce(returns, fee, days, expo=2/3, n=N_ANNOUNCE, seed=20260824):
"""κ°œλ§‰ μ „ μΆ”μ • β€” κ³Όκ±°λ₯Ό 블둝 λΆ€νŠΈμŠ€νŠΈλž©ν•΄μ„œ 'μ΄λ§ŒνΌκΉŒμ§€ λ‚˜μ˜¨λ‹€'λ₯Ό λ‚Έλ‹€."""
r = np.asarray(returns, dtype=float)
# μ‹œμ¦Œ 길이만큼만 쓰도둝 잘라 뢙인닀
reps = int(np.ceil(days / len(r))) + 1
pool = np.tile(r, reps)[:max(len(r), days * 3)]
return _sim(pool[:days] if len(pool) >= days else pool, n, expo, fee, seed, block=BLOCK)
def pct_of(ret, refv):
import bisect
return bisect.bisect_left(refv, ret) / len(refv)
def score_of(ret, refv):
import math
p = min(pct_of(ret, refv), 1 - 1e-6)
return -math.log10(max(1.0 - p, 1e-6))
# ─────────────────────── κ°œλ§‰ 곡지값 생성 ───────────────────────
if __name__ == "__main__":
import seasons as S
import scoring as SC
import yfinance as yf
s1 = S.SEASONS[1]
days = (pd.Timestamp(s1["closes"]) - pd.Timestamp(s1["opens"])).days
print("μ‹œμ¦Œ 1 κ°œλ§‰ 곡지값 β€” %d일 Β· 수수료 반영\n" % days)
print(" %-6s %-8s %8s %11s %11s %11s"
% ("μ’…λͺ©", "데이터", "수수료", "운 쀑앙", "운 μƒμœ„5%", "운 μƒμœ„1%"))
out = {}
for code, a in s1["assets"].items():
src = a["src"]
if code == "BTC":
p = os.path.join(os.path.dirname(ROOT), "btc-oracle", "data", "hourly.parquet")
H = pd.read_parquet(p).sort_index()
if "close" not in H.columns:
H = H.rename(columns={"c": "close"})
px = H.close.groupby(H.index.normalize()).last()
else:
d = yf.download(src, period="5y", interval="1d",
progress=False, auto_adjust=True)["Close"]
px = (d.iloc[:, 0] if isinstance(d, pd.DataFrame) else d).dropna()
r = px.pct_change().dropna().values
fee = SC.fee_of(code)
c = announce(r, fee, days)
out[code] = {"p50": float(np.median(c)), "p95": float(np.percentile(c, 95)),
"p99": float(np.percentile(c, 99)), "fee": fee, "days": days}
print(" %-6s %-8s %7.2f%% %10.1f%% %10.1f%% %10.1f%%"
% (code, src, fee * 100, np.median(c) * 100,
np.percentile(c, 95) * 100, np.percentile(c, 99) * 100))
json.dump({"season": 1, "opens": s1["opens"], "closes": s1["closes"], "days": days,
"method": "λΈ”λ‘λΆ€νŠΈμŠ€νŠΈλž© 5일 Β· λ¬΄μž‘μœ„ ν¬μ§€μ…˜ Β· λ ˆλ²„λ¦¬μ§€1 Β· 수수료 반영",
"note": "κ°œλ§‰ κ³΅μ§€μš© μΆ”μ •μΉ˜. μ‹€μ œ 채점은 μ‹œμ¦Œ μ‹€μ œ 경둜둜 맀일 μž¬κ³„μ‚°ν•œλ‹€.",
"ceiling": out},
open(os.path.join(ROOT, "announce_ceiling.json"), "w"),
ensure_ascii=False, indent=1)
print("\n ν™”λ©΄μ˜ '운의 ν•œκ³„μ„ 'μ—λŠ” μƒμœ„5%λ₯Ό μ“΄λ‹€.")
print(" μ €μž₯: finchal/announce_ceiling.json")