Spaces:
Running
Running
| # -*- coding: utf-8 -*- | |
| """๊ธฐ์ค์ ๋ด โ ์ฐธ๊ฐ์์ ๊ฐ์ ํ์ ๋์ผ ์ฒ๋. | |
| ODC๊ฐ ์น์ธ์ฝ๊ณผ ๋ถํ์ฑ ํํฉ๋ฌผ์ ๊ฐ์ ํ์ ๋ฃ์ด ์ฒ๋๋ฅผ ๋ณด์ฌ์ฃผ๋ฏ, | |
| ๋ฆฌ๋๋ณด๋์ ๊ธฐ์ค ์ ๋ต์ ํจ๊ป ์ฌ๋ฆฐ๋ค. ์ฒ๋๊ฐ ์๋ ์์ํ๋ ์ฝ์ ์ ์๋ค. | |
| ๐ด ์ฃผ์ต์ธก ๋ชจ๋ธ์ ๋ฃ์ง ์๋๋ค. | |
| ์ด ์ฑ๋ฆฐ์ง์ ๋ช ๋ถ์ "๋๊ฐ ๋ ๋์ ์์ธก ๋ชจ๋ธ์ ๋ง๋๋๊ฐ"์ด์ง | |
| "์ฃผ์ต์๊ฐ ์ ์ผ ์ํ๋ค"๊ฐ ์๋๋ค. ์ฃผ์ต์๊ฐ ์๊ธฐ ๋ชจ๋ธ์ ์ฐธ๊ฐ์ ํ์ ์ฌ๋ฆฌ๋ฉด | |
| ๊ทธ ๋ช ๋ถ์ด ํ๋ ค์ง๋ค. ๊ธฐ์ค์ ์ ์ฒ๋์ด์ง ๊ฒฝ์์๊ฐ ์๋๋ค. | |
| ๊ธฐ์ค์ ์ โ ์ ๋ถ ๊ณต๊ฐ๋ ๊ต๊ณผ์ ๊ธฐ๋ฒ์ด๋ค | |
| BUYHOLD ํญ์ +1. ์ด๊ฑธ ๋ชป ์ด๊ธฐ๋ฉด ์๋ฌด ์๋ฏธ ์๋ค | |
| VOLTARGET ์ง์๊ฐ์ค ๋ณ๋์ฑ์ผ๋ก ํฌ๊ธฐ๋ง ์กฐ์ . ๋ฐฉํฅ์ ๋กฑ ๊ณ ์ | |
| RANDOM ๋งค์ผ -1/0/+1 ๋ฌด์์. ๋ฐ๋ฅ | |
| ๐ VOLTARGET ์ ๋ฐฉํฅ์ ์์ธกํ์ง ์๋๋ค. | |
| ๋ฐฉํฅ ์์ธก์ 60๊ฐ ๋ณ์๋ก ๋ค์ฏ ๋จ๊ณ์ ๊ฑธ์ณ ์ํํ๊ณ ์ ๋ถ ์์ธก๋ ฅ์ด ์์๋ค. | |
| ๊ธฐ์ค์ ์ด ๋ฐฉํฅ์ ๋งํ๋ ์ฒํ๋ฉด, ๊ธฐ๊ฐํ ๊ฒ์ ๊ณต๊ฐ๋ฉด์ ์ฌ๋ฆฌ๋ ์ ์ด ๋๋ค. | |
| ๊ทธ๋์ ์ด ๊ธฐ์ค์ ์ '์ผ๋ง๋ ํฌ๊ฒ ๋ค ๊ฒ์ธ๊ฐ'๋ง ์ ํ๋ค. | |
| """ | |
| import os, sys | |
| import numpy as np, pandas as pd | |
| HERE = os.path.dirname(os.path.abspath(__file__)) | |
| sys.path[:0] = [HERE, os.path.dirname(HERE)] | |
| import ledger as L | |
| ANCHORS = { | |
| "BUYHOLD": "๋จ์ ๋ณด์ ", | |
| "VOLTARGET": "๋ณ๋์ฑ ํ๊ฒํ ", | |
| "RANDOM": "๋ฌด์์", | |
| } | |
| SPAN = 60 | |
| def position(kind, px, seed=0): | |
| """์ค๋ ์ก์ ํฌ์ง์ . -1 ~ +1.""" | |
| if kind == "BUYHOLD": | |
| return 1.0 | |
| if kind == "RANDOM": | |
| # ๋ ์ง๋ฅผ ์๋์ ๋ฃ์ด ๊ฐ์ ๋ ๊ฐ์ ๊ฐ โ ์ฑ์ ์ด ์ฌํ ๊ฐ๋ฅํด์ผ ํ๋ค | |
| g = np.random.default_rng( | |
| int(pd.Timestamp.utcnow().strftime("%Y%m%d")) * 97 + seed) | |
| return float(g.choice([-1.0, 0.0, 1.0])) | |
| s = np.log(px).diff().ewm(span=SPAN).std().dropna() | |
| if len(s) < SPAN * 2: | |
| return 0.0 | |
| # ๋ชฉํ ๋ณ๋์ฑ = ๊ทธ ์ข ๋ชฉ ๊ณผ๊ฑฐ ์ค์๊ฐ. ๋ณ๋์ฑ์ด ๋ฎ์ผ๋ฉด ํฌ๊ฒ, ๋์ผ๋ฉด ์๊ฒ ๋ ๋ค. | |
| target = float(s.iloc[:len(s) // 2].median()) | |
| return float(max(0.0, min(1.0, target / float(s.iloc[-1])))) | |
| def run(assets, prices_fn, dry=False, opens=None): | |
| """๊ธฐ์ค์ ์ ์ ์ค๋ ํฌ์ง์ ์ ๊ณ์ฐํด ์์ฅ์ ๋ฃ๋๋ค. | |
| ๐ด ๊ธฐ์ค์ ์ **์์ฆ ๊ฐ๋ง ์์ ๋ถํฐ** ์กด์ฌํด์ผ ํ๋ค. ์๋ฒ๊ฐ ์ฒ์ ๋ฌ ์๊ฐ์ | |
| ๋ฃ์ผ๋ฉด ๊ทธ ์ ๊ตฌ๊ฐ์ด ๋น์ด, ๋จ์ ๋ณด์ ๊ฐ ์์ฅ์ ๊ทธ๋๋ก ๋ฐ๋ผ๊ฐ์ง ์๋๋ค. | |
| ์ค์ ๋ก ๊ฐ๋ง์ผ์ ์์ฅ โ0.82% ์ธ๋ฐ ๋จ์ ๋ณด์ ๊ฐ +0.12% ๋ก ๋์๋ค โ | |
| ๊ธฐ์ค์ ์ด ํ๋ฝ ๊ตฌ๊ฐ์ ํต์งธ๋ก ๋น์ผ๊ฐ ๊ฒ์ด๋ผ ์ฒ๋ ๊ตฌ์ค์ ๋ชป ํ๋ค. | |
| ๐ ๊ฐ์ด ๊ทธ๋๋ก๋ฉด ๋ค์ ์ฐ์ง ์๋๋ค. ์๋ฒ๊ฐ ์ฌ์์ํ ๋๋ง๋ค ๊ฐ์ ํฌ์ง์ ์ | |
| ์์ผ๋ฉด ์์ฅ์ด ๋ถํ๊ณ , ๋ฌด์์ด ์ค์ ํ๋จ ๋ณ๊ฒฝ์ธ์ง ์ฝ์ ์ ์๊ฒ ๋๋ค. | |
| """ | |
| if not dry: | |
| for name in ANCHORS: | |
| L.register(name, ANCHORS[name], "anchor", list(assets)) | |
| out = [] | |
| for i, a in enumerate(assets): | |
| px = prices_fn(a) | |
| if px is None or len(px) < SPAN * 2: | |
| continue | |
| for name in ANCHORS: | |
| p = round(position(name, px, seed=i), 4) | |
| note = "" | |
| if not dry: | |
| if opens and not L.positions(a, name): | |
| # ๊ฐ๋ง ์์ ์ ๋จผ์ ์ฌ๋๋ค. ๊ทธ๋์ผ ์ฒ๋๊ฐ ์ ๊ตฌ๊ฐ์ ๋ฎ๋๋ค. | |
| L.seed(name, a, p, opens, "anchor-open") | |
| note = "๊ฐ๋ง ์์ ๊ธฐ๋ก" | |
| last = L.latest(a, name) | |
| if last is None or abs(last["p"] - p) > 1e-9: | |
| L.submit(name, a, p, "anchor") | |
| note = note or "๊ฐฑ์ " | |
| else: | |
| note = note or "๋ณ๋ ์์" | |
| out.append({"anchor": name, "asset": a, "position": p, "note": note}) | |
| return out | |
| if __name__ == "__main__": | |
| import main as M | |
| dry = "--dry" in sys.argv | |
| rows = run(list(M.ASSETS), lambda a: M.prices(a, "1d", 1500), dry=dry) | |
| print("๊ธฐ์ค์ ํฌ์ง์ %s\n" % ("(๋ชจ์)" if dry else "์ ์ถ ์๋ฃ")) | |
| by = {} | |
| for r in rows: | |
| by.setdefault(r["asset"], {})[r["anchor"]] = r["position"] | |
| print(" %-6s" % "์ข ๋ชฉ" + "".join("%12s" % k for k in ANCHORS)) | |
| for a, v in by.items(): | |
| print(" %-6s" % a + "".join("%12.3f" % v.get(k, 0) for k in ANCHORS)) | |