first commit
This commit is contained in:
commit
e28ff3afa4
5
.gitignore
vendored
Normal file
5
.gitignore
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
stock/**/*
|
||||||
|
.vscode/**/*
|
||||||
|
__pycache__/**/*
|
||||||
|
dist/**/*
|
||||||
|
stock.db
|
20
LICENSE
Normal file
20
LICENSE
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
Copyright (c) 2022 monoid
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
12
app.py
Normal file
12
app.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
from distutils.log import debug
|
||||||
|
import flask
|
||||||
|
|
||||||
|
app = flask.Flask(__name__)
|
||||||
|
|
||||||
|
@app.route("/<m>")
|
||||||
|
def index(m:str):
|
||||||
|
return flask.send_from_directory("dist", m)
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
app.run(host='0.0.0.0', port=12001, debug=True)
|
||||||
|
|
196
db.py
Normal file
196
db.py
Normal file
@ -0,0 +1,196 @@
|
|||||||
|
import sqlite3
|
||||||
|
import datetime
|
||||||
|
import pandas as pd
|
||||||
|
import krx
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import enum
|
||||||
|
import json
|
||||||
|
import tqdm
|
||||||
|
|
||||||
|
CREATE_STOCK_STATEMENT = """
|
||||||
|
CREATE TABLE "STOCK" (
|
||||||
|
"Code" TEXT,
|
||||||
|
"Date" TEXT,
|
||||||
|
"Close" INTEGER NOT NULL,
|
||||||
|
"Diff" INTEGER NOT NULL,
|
||||||
|
"Open" INTEGER NOT NULL,
|
||||||
|
"High" INTEGER NOT NULL,
|
||||||
|
"Low" INTEGER NOT NULL,
|
||||||
|
"Volume" INTEGER NOT NULL,
|
||||||
|
PRIMARY KEY("Date","Code")
|
||||||
|
);"""
|
||||||
|
|
||||||
|
@enum.unique
|
||||||
|
class STOCK_INDEX(enum.IntEnum):
|
||||||
|
CODE = 0
|
||||||
|
DATE = 1
|
||||||
|
CLOSE = 2
|
||||||
|
DIFF = 3
|
||||||
|
OPEN = 4
|
||||||
|
HIGH = 5
|
||||||
|
LOW = 6
|
||||||
|
VOLUME = 7
|
||||||
|
|
||||||
|
|
||||||
|
CREATE_KRXCorp_STATEMENT = """
|
||||||
|
CREATE TABLE "KRXCorp" (
|
||||||
|
"Name" TEXT,
|
||||||
|
"Code" TEXT,
|
||||||
|
"Sector" TEXT,
|
||||||
|
"Product" TEXT,
|
||||||
|
"ListingDay" TEXT,
|
||||||
|
"ClosingMonth" TEXT,
|
||||||
|
"Representative" TEXT,
|
||||||
|
"Homepage" TEXT,
|
||||||
|
"AddressArea" TEXT,
|
||||||
|
"LastUpdate" TEXT,
|
||||||
|
PRIMARY KEY("Code")
|
||||||
|
);
|
||||||
|
"""
|
||||||
|
CREATE_UNIQUE_Stock_INDEX_Statement = """
|
||||||
|
CREATE UNIQUE INDEX "STOCK_INDEX" ON "STOCK" (
|
||||||
|
"Code",
|
||||||
|
"Date"
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
def create_table(nday:int = 90):
|
||||||
|
"""
|
||||||
|
Create table for stock data
|
||||||
|
|
||||||
|
:param nday: determine lastest crolling day.
|
||||||
|
"""
|
||||||
|
print("initialize table...")
|
||||||
|
with sqlite3.connect("stock.db") as db:
|
||||||
|
db.execute(CREATE_STOCK_STATEMENT)
|
||||||
|
db.execute(CREATE_KRXCorp_STATEMENT)
|
||||||
|
db.execute(CREATE_UNIQUE_Stock_INDEX_Statement)
|
||||||
|
code_df: pd.DataFrame = krx.load_from_krx_server()
|
||||||
|
code_df.to_csv("krx.csv")
|
||||||
|
print("compelete to download from krx")
|
||||||
|
code_df = code_df.rename(columns={'회사명': 'name', '종목코드': 'code'})
|
||||||
|
code_df_size = len(code_df)
|
||||||
|
last_updated = (datetime.date.today() - datetime.timedelta(nday)).isoformat()
|
||||||
|
for i,row in code_df.iterrows():
|
||||||
|
code = row["code"]
|
||||||
|
print(f"{code_df_size}/{i+1} : code { code }",end="\r")
|
||||||
|
db.execute("""
|
||||||
|
INSERT INTO "KRXCorp" (Name,Code,Sector,Product,ListingDay,ClosingMonth,Representative,Homepage,AddressArea,LastUpdate) VALUES (
|
||||||
|
?,?,?,?,?,?,?,?,?,?)
|
||||||
|
""",(row["name"],row["code"],row["업종"],row["주요제품"],row["상장일"],row["결산월"],row["대표자명"],row["홈페이지"],row["지역"]
|
||||||
|
, last_updated))
|
||||||
|
print("\nComplete!")
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
def update_krx(nday:int = 90):
|
||||||
|
print("update krx...")
|
||||||
|
with sqlite3.connect("stock.db") as db:
|
||||||
|
if os.path.exists("krx.csv"):
|
||||||
|
code_df = pd.read_csv("krx.csv", index_col=0, dtype=str)
|
||||||
|
else:
|
||||||
|
code_df: pd.DataFrame = krx.load_from_krx_server()
|
||||||
|
code_df.to_csv("krx.csv")
|
||||||
|
print("compelete to download from krx")
|
||||||
|
code_df = code_df.rename(columns={'회사명': 'name', '종목코드': 'code'})
|
||||||
|
pbar = tqdm.tqdm(code_df.iterrows(), total=len(code_df))
|
||||||
|
for _,row in pbar:
|
||||||
|
code:str = row["code"]
|
||||||
|
name:str = row["name"]
|
||||||
|
pbar.set_description(f"{ code }")
|
||||||
|
q = db.execute("SELECT COUNT(*) FROM KRXCorp Where Code = ?",[code])
|
||||||
|
a = q.fetchone()[0]
|
||||||
|
lastUpdate = (datetime.date.today() - datetime.timedelta(nday)).isoformat()
|
||||||
|
if a > 0:
|
||||||
|
db.execute("""
|
||||||
|
UPDATE "KRXCorp" Set Name = ?,
|
||||||
|
Sector = ?,
|
||||||
|
Product = ?,
|
||||||
|
ListingDay = ?,
|
||||||
|
ClosingMonth = ?,
|
||||||
|
Representative = ?,
|
||||||
|
Homepage = ?,
|
||||||
|
AddressArea = ?,
|
||||||
|
WHERE Code = ?;
|
||||||
|
""",(row["name"],row["업종"],row["주요제품"],row["상장일"],row["결산월"],row["대표자명"],row["홈페이지"],row["지역"],code
|
||||||
|
))
|
||||||
|
else:
|
||||||
|
db.execute("""
|
||||||
|
INSERT INTO "KRXCorp" (Name,Code,Sector,Product,ListingDay,ClosingMonth,Representative,Homepage,AddressArea,LastUpdate) VALUES (
|
||||||
|
?,?,?,?,?,?,?,?,?,?)
|
||||||
|
""",(row["name"],row["code"],row["업종"],row["주요제품"],row["상장일"],row["결산월"],row["대표자명"],row["홈페이지"],row["지역"],lastUpdate
|
||||||
|
))
|
||||||
|
print("\nComplete!")
|
||||||
|
db.commit()
|
||||||
|
|
||||||
|
def get_data_from_krx(db,stock_code):
|
||||||
|
cursor = db.execute("""SELECT * FROM KRXCorp WHERE code = ?""", [stock_code])
|
||||||
|
return cursor.fetchone()
|
||||||
|
|
||||||
|
|
||||||
|
class KRXCorp:
|
||||||
|
__slots__ = ("Name","Code","Sector","Product","ListingDay",
|
||||||
|
"ClosingMonth","Representative","Homepage","AddressArea","LastUpdate")
|
||||||
|
|
||||||
|
def __init__(self,**kwargs):
|
||||||
|
super().__init__()
|
||||||
|
self.Name = kwargs["Name"]
|
||||||
|
self.Code = kwargs["Code"]
|
||||||
|
self.Sector = kwargs["Sector"]
|
||||||
|
self.Product = kwargs["Product"]
|
||||||
|
self.ListingDay = kwargs["ListingDay"]
|
||||||
|
self.ClosingMonth = kwargs["ClosingMonth"]
|
||||||
|
self.Representative = kwargs["Representative"]
|
||||||
|
self.Homepage = kwargs["Homepage"]
|
||||||
|
self.AddressArea = kwargs["AddressArea"]
|
||||||
|
self.LastUpdate = kwargs["LastUpdate"]
|
||||||
|
@classmethod
|
||||||
|
def from_db(cls,arr):
|
||||||
|
return cls(Name=arr[0], Code = arr[1],Sector =arr[2],Product =arr[3],
|
||||||
|
ListingDay =arr[4],ClosingMonth =arr[5],Representative= arr[6],
|
||||||
|
Homepage = arr[7],AddressArea = arr[8],LastUpdate = arr[9])
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return f"{{{self.Name}: {self.Code}}}"
|
||||||
|
|
||||||
|
def toDict(self):
|
||||||
|
return {
|
||||||
|
"Name": self.Name,
|
||||||
|
"Code": self.Code,
|
||||||
|
"Sector": self.Sector,
|
||||||
|
"Product": self.Product,
|
||||||
|
"ListingDay": self.ListingDay,
|
||||||
|
"ClosingMonth": self.ClosingMonth,
|
||||||
|
"Representative": self.Representative,
|
||||||
|
"Homepage": self.Homepage,
|
||||||
|
"AddressArea": self.AddressArea,
|
||||||
|
"LastUpdate": self.LastUpdate
|
||||||
|
}
|
||||||
|
|
||||||
|
def GetAllKRXCorp(db):
|
||||||
|
return [KRXCorp.from_db(c) for c in db.execute("""SELECT * From KRXCorp""")]
|
||||||
|
|
||||||
|
def GetAllStockCode(db):
|
||||||
|
return [c[0] for c in db.execute("""SELECT Code From KRXCorp""")]
|
||||||
|
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Stock DataBase")
|
||||||
|
parser.add_argument("--create",action="store_true",help="create table")
|
||||||
|
parser.add_argument("--update",action="store_true",help="update table")
|
||||||
|
parser.add_argument("--getAll",action="store_true",help="get data from krx")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.create:
|
||||||
|
if os.path.exists("stock.db"):
|
||||||
|
print("db file already exists!")
|
||||||
|
else:
|
||||||
|
create_table()
|
||||||
|
|
||||||
|
if args.update:
|
||||||
|
update_krx()
|
||||||
|
|
||||||
|
if args.getAll:
|
||||||
|
with sqlite3.connect("stock.db") as db:
|
||||||
|
print(GetAllKRXCorp(db))
|
227
gen.py
Normal file
227
gen.py
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import sqlite3
|
||||||
|
from typing import Dict, List
|
||||||
|
from render import *
|
||||||
|
import db as database
|
||||||
|
from jinja2 import Environment, PackageLoader, select_autoescape
|
||||||
|
import pandas as pd
|
||||||
|
import tqdm
|
||||||
|
|
||||||
|
class DataStore:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.db = sqlite3.connect("stock.db")
|
||||||
|
self.pricesCache: Dict[str,] = {}
|
||||||
|
|
||||||
|
def getAllKRXCorp(self) -> List[database.KRXCorp]:
|
||||||
|
return database.GetAllKRXCorp(self.db)
|
||||||
|
|
||||||
|
def getStockPrice(self,code,length) -> pd.DataFrame:
|
||||||
|
if code in self.pricesCache and len(self.pricesCache[code]) >= length:
|
||||||
|
return self.pricesCache[code]
|
||||||
|
else:
|
||||||
|
s = GetStockPriceFrom(self.db,code,length)
|
||||||
|
s = pd.DataFrame(s,
|
||||||
|
columns=[s for s in database.STOCK_INDEX.__members__.keys()])
|
||||||
|
s.set_index("DATE", inplace=True)
|
||||||
|
self.pricesCache[code] = s
|
||||||
|
return self.pricesCache[code]
|
||||||
|
|
||||||
|
def clearCache(self) -> None:
|
||||||
|
self.pricesCache = {}
|
||||||
|
|
||||||
|
def __del__(self) -> None:
|
||||||
|
self.db.close()
|
||||||
|
|
||||||
|
|
||||||
|
class OutputCollectorElement:
|
||||||
|
def __init__(self, name: str, description: str) -> None:
|
||||||
|
self.name = name
|
||||||
|
self.description = description
|
||||||
|
self.corpListByDate:Dict[str,database.KRXCorp] = {}
|
||||||
|
|
||||||
|
def __str__(self) -> str:
|
||||||
|
return f"OutputCollectorElement:{self.name}"
|
||||||
|
|
||||||
|
def addCorp(self, date, corp):
|
||||||
|
self.corpListByDate.setdefault(date, []).append(corp)
|
||||||
|
|
||||||
|
def toDict(self) -> Dict:
|
||||||
|
return {
|
||||||
|
"name": self.name,
|
||||||
|
"description": self.description,
|
||||||
|
"corpListByDate": {k:[d.toDict() for d in v]
|
||||||
|
for k,v in self.corpListByDate.items()}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OutputCollector:
|
||||||
|
def __init__(self) -> None:
|
||||||
|
self.data: Dict[str,OutputCollectorElement] = {}
|
||||||
|
|
||||||
|
def addResult(self, key, help = ""):
|
||||||
|
"""
|
||||||
|
add output category to collect
|
||||||
|
"""
|
||||||
|
self.data[key] = OutputCollectorElement(key, help)
|
||||||
|
|
||||||
|
def collect(self, key, corp, date):
|
||||||
|
self.data[key].addCorp(date, corp)
|
||||||
|
|
||||||
|
def isVolumeNTimes(stock: pd.DataFrame, mul: float, nday:int, order=1) -> bool:
|
||||||
|
return stock.iloc[nday]['VOLUME'] > stock.iloc[nday+order]['VOLUME'] * mul
|
||||||
|
|
||||||
|
def isVolumeMulPriceGreaterThan(stock: pd.DataFrame, threshold: int, nday: int) -> bool:
|
||||||
|
return stock.iloc[nday]['VOLUME'] * stock.iloc[nday]['CLOSE'] > threshold
|
||||||
|
|
||||||
|
def isMACDCrossSignal(signal: pd.Series, macd: pd.Series, nday: int, order=1) -> bool:
|
||||||
|
return (signal.iloc[nday] < macd.iloc[nday] and
|
||||||
|
signal.iloc[nday+order] > macd.iloc[nday+order])
|
||||||
|
|
||||||
|
def isRelativeDiffLessThan(a:pd.Series,b:pd.Series, threshold: float,nday:int) -> bool:
|
||||||
|
return (a.iloc[nday] - b.iloc[nday]) / b.iloc[nday] < threshold
|
||||||
|
|
||||||
|
def isDiffGreaterThan(a:pd.Series,b:pd.Series, nday:int) -> bool:
|
||||||
|
"""a is bigger than b"""
|
||||||
|
return (a.iloc[nday] > b.iloc[nday])
|
||||||
|
|
||||||
|
def prepareCollector(collector: OutputCollector) -> None:
|
||||||
|
collector.addResult("cross 2", """\
|
||||||
|
5일선과 20일선이 서로 만나는 시점 즉 상대 오차가 1% 이하이고
|
||||||
|
5일선과 60일선이 서로 만나는 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("cross 3", """\
|
||||||
|
cross 2의 조건에서 더해서 거래량이 이전 날짜보다 3배 증가하고
|
||||||
|
100000 이상인 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("cross 4", """\
|
||||||
|
20일선과 60일선이 서로 만나는 시점 즉 상대 오차가 1% 이하이고
|
||||||
|
거래량이 1000000 이상인 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("d20d5", """\
|
||||||
|
5일선이 20선보다 큰 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("d20d5VolumeX5", """\
|
||||||
|
d20d5의 조건에서 더해서 거래량이 이전 날짜보다 5배 증가한 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("DiffDistance", """\
|
||||||
|
5일선과 20일선이 서로 만나는 시점 즉 상대 오차가 3% 이하이고
|
||||||
|
5일선과 60일선이 서로 만나고 거래량이 이전 날짜보다 3배 증가한
|
||||||
|
시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("volume", """\
|
||||||
|
거래량이 이전 날짜보다 3배 증가한 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("volume5", """\
|
||||||
|
거래량과 가격의 곱이 50,000,000,000 이상인 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("volumeX5", """\
|
||||||
|
거래량이 이전 날짜보다 5배 증가한 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
collector.addResult("macd", """\
|
||||||
|
signal과 macd가 서로 교차한 시점을 찾습니다. 즉 signal이 아래로 떨어지고
|
||||||
|
macd가 위로 올라가는 시점을 찾습니다.
|
||||||
|
""")
|
||||||
|
|
||||||
|
def collect(data: DataStore, collector: OutputCollector, corp: database.KRXCorp
|
||||||
|
, nday: int) -> None:
|
||||||
|
stock = data.getStockPrice(corp.Code,70)
|
||||||
|
if len(stock) < 70:
|
||||||
|
return
|
||||||
|
|
||||||
|
d5 = stock["CLOSE"].loc[::-1].rolling(window=5
|
||||||
|
).mean().dropna().loc[::-1]
|
||||||
|
d20 = stock["CLOSE"].loc[::-1].rolling(window=20
|
||||||
|
).mean().dropna().loc[::-1]
|
||||||
|
d60 = stock["CLOSE"].loc[::-1].rolling(window=60
|
||||||
|
).mean().dropna().loc[::-1]
|
||||||
|
|
||||||
|
if (isRelativeDiffLessThan(d5, d20, 0.01, nday) and
|
||||||
|
isRelativeDiffLessThan(d5, d60, 0.01, nday)):
|
||||||
|
collector.collect("cross 2", corp, stock.index[nday])
|
||||||
|
if (isVolumeNTimes(stock, 3, 0) and
|
||||||
|
isVolumeMulPriceGreaterThan(stock, 100000, nday)):
|
||||||
|
collector.collect("cross 3", corp, stock.index[nday])
|
||||||
|
|
||||||
|
if (isRelativeDiffLessThan(d20, d60, 0.01, nday) and
|
||||||
|
isVolumeMulPriceGreaterThan(stock, 1000000, nday)):
|
||||||
|
collector.collect("cross 4", corp, stock.index[nday])
|
||||||
|
|
||||||
|
if (isDiffGreaterThan(d5, d20, nday)):
|
||||||
|
collector.collect("d20d5", corp, stock.index[nday])
|
||||||
|
if (isVolumeNTimes(stock, 5, nday)):
|
||||||
|
collector.collect("d20d5VolumeX5", corp, stock.index[nday])
|
||||||
|
|
||||||
|
if (isRelativeDiffLessThan(d5, d20, 0.03, nday) and
|
||||||
|
isRelativeDiffLessThan(d5, d60, 0.03, nday) and
|
||||||
|
isVolumeNTimes(stock, 3, nday)):
|
||||||
|
collector.collect("DiffDistance", corp, stock.index[nday])
|
||||||
|
|
||||||
|
if (isVolumeNTimes(stock, 3, nday)):
|
||||||
|
collector.collect("volume", corp, stock.index[nday])
|
||||||
|
|
||||||
|
if (isVolumeMulPriceGreaterThan(stock, 50000000, nday)):
|
||||||
|
collector.collect("volume5", corp, stock.index[nday])
|
||||||
|
|
||||||
|
if (isVolumeNTimes(stock, 5, nday)):
|
||||||
|
collector.collect("volumeX5", corp, stock.index[nday])
|
||||||
|
|
||||||
|
ewm12 = stock["CLOSE"].loc[::-1].ewm(span=12).mean().loc[::-1]
|
||||||
|
ewm26 = stock["CLOSE"].loc[::-1].ewm(span=26).mean().loc[::-1]
|
||||||
|
macd = (ewm12 - ewm26)
|
||||||
|
signal = macd.ewm(span=9).mean()
|
||||||
|
|
||||||
|
if (isMACDCrossSignal(macd, signal, nday)):
|
||||||
|
collector.collect("macd", corp, stock.index[nday])
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="주식 검색 정보를 출력합니다.")
|
||||||
|
parser.add_argument("--format", "-f", choices=["json", "html"], default="html",
|
||||||
|
help="출력 포맷을 지정합니다. 기본값은 html입니다.")
|
||||||
|
parser.add_argument("--dir", "-d", default=".", help="출력할 폴더를 지정합니다.")
|
||||||
|
parser.add_argument("--corp", "-c", help="주식 코드를 지정합니다. 지정하지 않으면 모든 주식을 검색합니다.")
|
||||||
|
parser.add_argument("--printStdout", action="store_true", help="출력한 결과를 표준 출력으로 출력합니다.")
|
||||||
|
parser.add_argument("--version", "-v", action="version", version="%(prog)s 1.0")
|
||||||
|
parser.add_argument("--verbose", "-V", action="store_true", help="출력할 내용을 자세히 표시합니다.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parser.parse_args()
|
||||||
|
dataStore = DataStore()
|
||||||
|
krx_corps = dataStore.getAllKRXCorp()
|
||||||
|
if args.corp:
|
||||||
|
krx_corps = [corp for corp in krx_corps if corp.Code == args.corp]
|
||||||
|
|
||||||
|
env = Environment(
|
||||||
|
loader=PackageLoader('render', 'templates'),
|
||||||
|
autoescape=select_autoescape(['html', 'xml'])
|
||||||
|
)
|
||||||
|
collector = OutputCollector()
|
||||||
|
prepareCollector(collector)
|
||||||
|
|
||||||
|
for corp in tqdm.tqdm(krx_corps):
|
||||||
|
for nday in range(0, 5):
|
||||||
|
collect(dataStore, collector, corp, nday)
|
||||||
|
dataStore.clearCache()
|
||||||
|
|
||||||
|
for k,v in collector.data.items():
|
||||||
|
if args.format == "json":
|
||||||
|
data = json.dumps(v.toDict(), indent=4, ensure_ascii=False)
|
||||||
|
if args.printStdout:
|
||||||
|
print(k)
|
||||||
|
print(data)
|
||||||
|
else:
|
||||||
|
with open(os.path.join(args.dir, k + ".json", encoding="UTF-8"), "w") as f:
|
||||||
|
f.write(data)
|
||||||
|
else:
|
||||||
|
template = env.get_template("Lists.html")
|
||||||
|
|
||||||
|
days = v.corpListByDate.keys()
|
||||||
|
days = list(days)
|
||||||
|
days.sort(reverse=True)
|
||||||
|
days = days[:5]
|
||||||
|
|
||||||
|
html = template.render(collected=v, title=k, days=days)
|
||||||
|
if args.printStdout:
|
||||||
|
print(html)
|
||||||
|
else:
|
||||||
|
with open(os.path.join(args.dir, k + ".html"), "w", encoding="UTF-8") as f:
|
||||||
|
f.write(html)
|
18
krx.py
Normal file
18
krx.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import pandas as pd
|
||||||
|
|
||||||
|
def load_from_krx_server():
|
||||||
|
code_df = pd.read_html('http://kind.krx.co.kr/corpgeneral/corpList.do?method=download&searchType=13', header=0)[0]
|
||||||
|
code_df.종목코드 = code_df.종목코드.map('{:06d}'.format)
|
||||||
|
return code_df
|
||||||
|
|
||||||
|
def load_stock_codes():
|
||||||
|
code_df = pd.read_csv('krx.csv',index_col=0)
|
||||||
|
|
||||||
|
code_df.종목코드 = code_df.종목코드.map('{:06d}'.format)
|
||||||
|
code_df = code_df[['회사명', '종목코드']]
|
||||||
|
code_df = code_df.rename(columns={'회사명': 'name', '종목코드': 'code'})
|
||||||
|
|
||||||
|
stock_codes = code_df["code"]
|
||||||
|
return stock_codes
|
||||||
|
|
||||||
|
|
54
print_json.py
Normal file
54
print_json.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import sqlite3
|
||||||
|
from render import *
|
||||||
|
import db as database
|
||||||
|
import json
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="print corp data to json")
|
||||||
|
|
||||||
|
parser.add_argument("--krx", action="store_true" , default=False,
|
||||||
|
help="print krx corp data")
|
||||||
|
|
||||||
|
def printKRXCorpJson(corp):
|
||||||
|
print("{", end="")
|
||||||
|
i = 0
|
||||||
|
for attr in corp.__slots__:
|
||||||
|
if i > 0:
|
||||||
|
print(",",end="")
|
||||||
|
print("\"",attr,"\":\"",corp.__getattribute__(attr),"\"",sep="",end="")
|
||||||
|
i += 1
|
||||||
|
print("}",end="")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
args = parser.parse_args()
|
||||||
|
db = sqlite3.connect("stock.db")
|
||||||
|
if args.krx:
|
||||||
|
print("[")
|
||||||
|
corp_list = database.GetAllKRXCorp(db)
|
||||||
|
j = 0
|
||||||
|
for corp in corp_list:
|
||||||
|
if j > 0:
|
||||||
|
print(",")
|
||||||
|
printKRXCorpJson(corp)
|
||||||
|
j += 1
|
||||||
|
#print(json.dumps(corp_list,ensure_ascii=False,indent=2))
|
||||||
|
print("]")
|
||||||
|
else:
|
||||||
|
stock_list = GetAllStockPrice(db,60)
|
||||||
|
i = 0
|
||||||
|
print("[")
|
||||||
|
for stock in stock_list:
|
||||||
|
if i > 0:
|
||||||
|
print(",")
|
||||||
|
s = [*map(str,stock)][2:]
|
||||||
|
code = f'"{stock[0]}"'
|
||||||
|
date = str(datetime.datetime.fromisoformat(stock[1]).timestamp())
|
||||||
|
print("[",code,",",date,",".join(s),"]",sep="",end="")
|
||||||
|
i += 1
|
||||||
|
print("]")
|
||||||
|
db.close()
|
||||||
|
|
||||||
|
|
||||||
|
|
97
render.py
Normal file
97
render.py
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import re
|
||||||
|
import datetime
|
||||||
|
import sqlite3
|
||||||
|
|
||||||
|
import csv
|
||||||
|
import sqlite3
|
||||||
|
import datetime
|
||||||
|
from db import GetAllStockCode
|
||||||
|
|
||||||
|
def GetMovingAverageAt(db,code: str,day = 5,date = datetime.date.today()):
|
||||||
|
if isinstance(day,list):
|
||||||
|
listday = day
|
||||||
|
day = max(day)
|
||||||
|
else:
|
||||||
|
listday = [day]
|
||||||
|
last = date - datetime.timedelta(days=day)
|
||||||
|
l = [row for row in db.execute("SELECT Date,Close FROM STOCK WHERE Code = ? AND Date <= ? ORDER BY Date DESC LIMIT ?",
|
||||||
|
[code,last.isoformat(),day])]
|
||||||
|
return [sum(map(lambda x: x[1],l[0:ad]))/ad for ad in listday]
|
||||||
|
|
||||||
|
def GetStockPriceFrom(db,code: str,n,date = datetime.date.today()):
|
||||||
|
"""
|
||||||
|
return (code,date,close,diff,open,high,low,volume)[]
|
||||||
|
"""
|
||||||
|
last = date - datetime.timedelta(days=n)
|
||||||
|
return [row for row in db.execute(f"SELECT * FROM STOCK WHERE Code = ? AND Date <= ? ORDER BY Date DESC LIMIT ?",
|
||||||
|
[code,date.isoformat(),n])]
|
||||||
|
|
||||||
|
def GetAllStockPrice(db,n,date = datetime.date.today()):
|
||||||
|
"""
|
||||||
|
return cursor
|
||||||
|
"""
|
||||||
|
last = date - datetime.timedelta(days=n)
|
||||||
|
return db.execute(f"SELECT * FROM STOCK WHERE Date > ? ORDER BY Date DESC",
|
||||||
|
[last.isoformat()])
|
||||||
|
|
||||||
|
#lastest is front
|
||||||
|
def makeMovingAveragePoint(n,arr,reversed = False):
|
||||||
|
if len(arr) < n:
|
||||||
|
raise IndexError
|
||||||
|
if not reversed:
|
||||||
|
start = sum(arr[:n])
|
||||||
|
ret = [start/n]
|
||||||
|
for i in range(0,len(arr)-n):
|
||||||
|
nex = start-arr[i]+arr[i+n]
|
||||||
|
ret.append(nex/n)
|
||||||
|
start = nex
|
||||||
|
else:
|
||||||
|
start = sum(arr[-n:])
|
||||||
|
ret = [start/n]
|
||||||
|
for i in range(0,len(arr)-n):
|
||||||
|
nex = start-arr[-i-1]+arr[-i-1-n]
|
||||||
|
ret.append(nex/n)
|
||||||
|
start = nex
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def detectTF(d5,d20)-> bool:
|
||||||
|
series = [*map(lambda x: x[0] > x[1],zip(d5,d20))]
|
||||||
|
return series[0] and (not series[1])
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
print("start")
|
||||||
|
db = sqlite3.connect("stock.db")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#stock_codes = GetAllStockCode(db)
|
||||||
|
#
|
||||||
|
#for stock_code in stock_codes:
|
||||||
|
# arr = GetStockPriceFrom(db,stock_code,25)
|
||||||
|
# if len(arr) < 25:
|
||||||
|
# print(f"stock_code {stock_code} : lack of data")
|
||||||
|
# continue
|
||||||
|
# #print([*map(lambda x: x[2],arr)])
|
||||||
|
# d5 = makeMovingAveragePoint(5,[*map(lambda x: x[2],arr)])
|
||||||
|
# d20 = makeMovingAveragePoint(20,[*map(lambda x: x[2],arr)])
|
||||||
|
# higher = detectTF(d5[:5],d20[:5])
|
||||||
|
# if higher:
|
||||||
|
# print(f"stock_code {stock_code} : {higher} {d5} {d20}")
|
||||||
|
|
||||||
|
#print(GetMovingAverageAt(db,"155660",day=[5,20]))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#arr = GetStockPriceFrom(db,"155660",25)
|
||||||
|
#arr.sort(key=lambda x:x[2])
|
||||||
|
#print([*map(lambda x: x[2],arr)])
|
||||||
|
#print(makeMovingAveragePoint(2,[*map(lambda x: x[2],arr)]))
|
||||||
|
#print(makeMovingAveragePoint(2,[*map(lambda x: x[2],arr)],reversed=True))
|
||||||
|
#d5 = makeMovingAveragePoint(5,[*map(lambda x: x[2],arr)])
|
||||||
|
#d20 = makeMovingAveragePoint(20,[*map(lambda x: x[2],arr)])
|
||||||
|
#print(d5)
|
||||||
|
#print(d20)
|
||||||
|
#print(detectTF(d5,d20))
|
||||||
|
#print(GetAllStockCode(db))
|
||||||
|
db.close()
|
BIN
requirements.txt
Normal file
BIN
requirements.txt
Normal file
Binary file not shown.
9
static/index.html
Normal file
9
static/index.html
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ko">
|
||||||
|
<head>
|
||||||
|
<title></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Hello</h1>
|
||||||
|
</body>
|
||||||
|
</html>
|
91
stock_price.py
Normal file
91
stock_price.py
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import bs4
|
||||||
|
import requests
|
||||||
|
import re
|
||||||
|
import multiprocessing as mp
|
||||||
|
import sqlite3
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
def get_naver_finance_price(code,page=1):
|
||||||
|
#url = (f'https://finance.naver.com/item/sise_day.nhn?code={code}&page={page}')
|
||||||
|
url = 'https://finance.naver.com/item/sise_day.nhn'
|
||||||
|
headers = {"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36"}
|
||||||
|
# print(url)
|
||||||
|
html = requests.get(url,params={'code':code,'page':page},headers=headers)
|
||||||
|
if html.status_code != 200:
|
||||||
|
raise UserWarning(html.status_code)
|
||||||
|
return html.text
|
||||||
|
|
||||||
|
stock_h = ['날짜','종가','전일비','시가','고가','저가','거래량']
|
||||||
|
def get_data(soup,date):
|
||||||
|
nums = soup.select(".tah")
|
||||||
|
i = 0
|
||||||
|
ret=[pd.DataFrame(columns=stock_h)]
|
||||||
|
nums = [*map(lambda x:x.text.replace(',','').strip(),nums)]
|
||||||
|
while True:
|
||||||
|
m = nums[i:(i+7)]
|
||||||
|
if not m:
|
||||||
|
break
|
||||||
|
#for ISO 8601
|
||||||
|
m[0] = m[0].replace(".","-")
|
||||||
|
#date
|
||||||
|
if m[0] <= date:
|
||||||
|
return pd.concat(ret,ignore_index=True),True
|
||||||
|
|
||||||
|
ret.append(pd.DataFrame([m],columns=stock_h))
|
||||||
|
i += 7
|
||||||
|
return pd.concat(ret,ignore_index=True),False
|
||||||
|
def get_last_page(soup):
|
||||||
|
a = soup.select_one('.pgRR a')
|
||||||
|
if a is None:
|
||||||
|
index_list = soup.select('td a')
|
||||||
|
return len(index_list)
|
||||||
|
href = a.attrs['href']
|
||||||
|
p = re.compile(r"page=(\d*)")
|
||||||
|
g = p.search(href)
|
||||||
|
return g.groups()[0]
|
||||||
|
|
||||||
|
def croll_naver_page(code,page,date):
|
||||||
|
html_text = get_naver_finance_price(code,page)
|
||||||
|
soup = bs4.BeautifulSoup(html_text,'html.parser')
|
||||||
|
return get_data(soup,date)
|
||||||
|
|
||||||
|
def croll_naver_page_all(code,date) -> pd.DataFrame:
|
||||||
|
html_text = get_naver_finance_price(code)
|
||||||
|
#print(html_text)
|
||||||
|
s = bs4.BeautifulSoup(html_text,'html.parser')
|
||||||
|
last = int(get_last_page(s))
|
||||||
|
r = [(code,i) for i in range(1,last+1)]
|
||||||
|
retdata = []
|
||||||
|
for c,pagenum in r:
|
||||||
|
d,is_end = croll_naver_page(c,pagenum,date)
|
||||||
|
if is_end:
|
||||||
|
retdata.append(d)
|
||||||
|
break
|
||||||
|
retdata.append(d)
|
||||||
|
if len(retdata) == 0:
|
||||||
|
return []
|
||||||
|
return pd.concat(retdata,ignore_index=True)
|
||||||
|
#with mp.Pool(CPU_COUNT) as pl:
|
||||||
|
# dl = pl.starmap(croll_naver_page,r)
|
||||||
|
# return pd.concat(dl,ignore_index=True)
|
||||||
|
|
||||||
|
def toSqlPos(x,code):
|
||||||
|
return (code,x["날짜"],x["종가"],x["전일비"],x["시가"],x["고가"],x["저가"],x["거래량"])
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
db = sqlite3.connect("stock.db")
|
||||||
|
today = datetime.date.today()
|
||||||
|
|
||||||
|
krx_stock_rows = [(i,code,last_update) for i,(code,last_update) in enumerate(db.execute("""SELECT Code,LastUpdate From KRXCorp"""))]
|
||||||
|
total = len(krx_stock_rows)
|
||||||
|
for i,code,last_update in krx_stock_rows:
|
||||||
|
print(f"{total}/{i}: code {code} : {last_update}")
|
||||||
|
if last_update == today.isoformat():
|
||||||
|
continue
|
||||||
|
d = croll_naver_page_all(code,last_update)
|
||||||
|
cursor = db.cursor()
|
||||||
|
if len(d)> 0:
|
||||||
|
cursor.executemany("INSERT INTO STOCK (Code,Date,Close,Diff,Open,High,Low,Volume) VALUES (?,?,?,?,?,?,?,?)",[toSqlPos(x,code) for i,x in d.iterrows() ])
|
||||||
|
cursor.execute("""UPDATE KRXCorp Set LastUpdate = ? WHERE Code = ?""",(today.isoformat(),code))
|
||||||
|
db.commit()
|
59
templates/Lists.html
Normal file
59
templates/Lists.html
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Stock</title>
|
||||||
|
<style>
|
||||||
|
body{
|
||||||
|
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||||
|
background: linear-gradient(to right, #2b2b2b, #3d1844);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
.table_item:nth-child(2n){
|
||||||
|
background: #a7a7a7;
|
||||||
|
}
|
||||||
|
.table_item:nth-child(2n+1){
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.table_item:hover{
|
||||||
|
background: #8d8d8d;
|
||||||
|
}
|
||||||
|
.container{
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: 24px auto;
|
||||||
|
background: #f0f0f0;
|
||||||
|
color: black;
|
||||||
|
box-shadow: 0px 0px 5px 0px white;
|
||||||
|
text-decoration: none;
|
||||||
|
grid-template-columns: repeat({{ 5 }}, 1fr);
|
||||||
|
}
|
||||||
|
.container a:link, a:visited{
|
||||||
|
text-decoration: none;
|
||||||
|
color: black;
|
||||||
|
}
|
||||||
|
.data_header{
|
||||||
|
border-bottom: 1px solid #a7a7a7;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div style="margin: auto; max-width: 750px;">
|
||||||
|
<h1>{{title}} Stock List</h1>
|
||||||
|
<section class="description">
|
||||||
|
{{collected.description}}
|
||||||
|
</section>
|
||||||
|
<section class="container">
|
||||||
|
{% for day in days|reverse %}
|
||||||
|
<div class="data_header">{{ day }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% for day in days|reverse %}
|
||||||
|
{% set corplist = collected.corpListByDate[day] %}
|
||||||
|
<div>{% for item in corplist %}
|
||||||
|
<div class="table_item"><a href="https://stockplus.com/m/stocks/KOREA-A{{ item.Code }}">{{ item.Name }}({{item.Code}})</a></div>{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
328
test.ipynb
Normal file
328
test.ipynb
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
{
|
||||||
|
"cells": [
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 1,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import sqlite3\n",
|
||||||
|
"from typing import Dict\n",
|
||||||
|
"from render import * \n",
|
||||||
|
"import db as database\n",
|
||||||
|
"from jinja2 import Environment, PackageLoader, select_autoescape\n",
|
||||||
|
"import pandas as pd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 8,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"import importlib"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 14,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"<module 'db' from 'c:\\\\Users\\\\Monoid\\\\Desktop\\\\stock\\\\db.py'>"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 14,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"importlib.reload(database)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 3,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"db = sqlite3.connect(\"stock.db\")\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 16,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"krx = database.GetAllKRXCorp(db)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 20,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"krxDf = pd.DataFrame([corp.toDict() for corp in krx])"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 22,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"data = GetStockPriceFrom(db,\"155660\", 61)\n"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 32,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"s = pd.DataFrame(data, columns=[s for s in database.STOCK_INDEX.__members__.keys()])"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 56,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"s.set_index(\"DATE\", inplace=True)"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 91,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"stock = s"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 92,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"d5 = stock[\"CLOSE\"].loc[::-1].rolling(window=5\n",
|
||||||
|
" ).mean().dropna().loc[::-1]\n",
|
||||||
|
"d20 = stock[\"CLOSE\"].loc[::-1].rolling(window=20\n",
|
||||||
|
" ).mean().dropna().loc[::-1]\n",
|
||||||
|
"d60 = stock[\"CLOSE\"].loc[::-1].rolling(window=60\n",
|
||||||
|
" ).mean().dropna().loc[::-1]"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 100,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": [
|
||||||
|
"ewm12 = stock[\"CLOSE\"].loc[::-1].ewm(span=12).mean().loc[::-1]\n",
|
||||||
|
"ewm26 = stock[\"CLOSE\"].loc[::-1].ewm(span=26).mean().loc[::-1]\n",
|
||||||
|
"macd = (ewm12 - ewm26)\n",
|
||||||
|
"signal = macd.ewm(span=9).mean()"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 101,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"DATE\n",
|
||||||
|
"2022-05-20 148.895069\n",
|
||||||
|
"2022-05-19 152.584580\n",
|
||||||
|
"2022-05-18 122.762721\n",
|
||||||
|
"2022-05-17 97.031260\n",
|
||||||
|
"2022-05-16 50.671176\n",
|
||||||
|
" ... \n",
|
||||||
|
"2022-02-28 7.956286\n",
|
||||||
|
"2022-02-25 1.291958\n",
|
||||||
|
"2022-02-24 -0.770309\n",
|
||||||
|
"2022-02-23 4.262821\n",
|
||||||
|
"2022-02-22 0.000000\n",
|
||||||
|
"Name: CLOSE, Length: 61, dtype: float64"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 101,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": [
|
||||||
|
"macd"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 105,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 115,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/plain": [
|
||||||
|
"['2022-05-16', '2022-05-15']"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 115,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 142,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/html": [
|
||||||
|
"<div>\n",
|
||||||
|
"<style scoped>\n",
|
||||||
|
" .dataframe tbody tr th:only-of-type {\n",
|
||||||
|
" vertical-align: middle;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe tbody tr th {\n",
|
||||||
|
" vertical-align: top;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe thead th {\n",
|
||||||
|
" text-align: right;\n",
|
||||||
|
" }\n",
|
||||||
|
"</style>\n",
|
||||||
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||||||
|
" <thead>\n",
|
||||||
|
" <tr style=\"text-align: right;\">\n",
|
||||||
|
" <th></th>\n",
|
||||||
|
" <th>a</th>\n",
|
||||||
|
" <th>b</th>\n",
|
||||||
|
" <th>c</th>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </thead>\n",
|
||||||
|
" <tbody>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>0</th>\n",
|
||||||
|
" <td>1</td>\n",
|
||||||
|
" <td>2</td>\n",
|
||||||
|
" <td>3</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" <tr>\n",
|
||||||
|
" <th>1</th>\n",
|
||||||
|
" <td>4</td>\n",
|
||||||
|
" <td>5</td>\n",
|
||||||
|
" <td>6</td>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </tbody>\n",
|
||||||
|
"</table>\n",
|
||||||
|
"</div>"
|
||||||
|
],
|
||||||
|
"text/plain": [
|
||||||
|
" a b c\n",
|
||||||
|
"0 1 2 3\n",
|
||||||
|
"1 4 5 6"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 142,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"cell_type": "code",
|
||||||
|
"execution_count": 132,
|
||||||
|
"metadata": {},
|
||||||
|
"outputs": [
|
||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"text/html": [
|
||||||
|
"<div>\n",
|
||||||
|
"<style scoped>\n",
|
||||||
|
" .dataframe tbody tr th:only-of-type {\n",
|
||||||
|
" vertical-align: middle;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe tbody tr th {\n",
|
||||||
|
" vertical-align: top;\n",
|
||||||
|
" }\n",
|
||||||
|
"\n",
|
||||||
|
" .dataframe thead th {\n",
|
||||||
|
" text-align: right;\n",
|
||||||
|
" }\n",
|
||||||
|
"</style>\n",
|
||||||
|
"<table border=\"1\" class=\"dataframe\">\n",
|
||||||
|
" <thead>\n",
|
||||||
|
" <tr style=\"text-align: right;\">\n",
|
||||||
|
" <th></th>\n",
|
||||||
|
" <th>a</th>\n",
|
||||||
|
" <th>b</th>\n",
|
||||||
|
" <th>c</th>\n",
|
||||||
|
" </tr>\n",
|
||||||
|
" </thead>\n",
|
||||||
|
" <tbody>\n",
|
||||||
|
" </tbody>\n",
|
||||||
|
"</table>\n",
|
||||||
|
"</div>"
|
||||||
|
],
|
||||||
|
"text/plain": [
|
||||||
|
"Empty DataFrame\n",
|
||||||
|
"Columns: [a, b, c]\n",
|
||||||
|
"Index: []"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"execution_count": 132,
|
||||||
|
"metadata": {},
|
||||||
|
"output_type": "execute_result"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"source": []
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"metadata": {
|
||||||
|
"interpreter": {
|
||||||
|
"hash": "4958a03c5ef93b3c628112f436609f44fba8a7f6eb1fb9f266a15f7204ae796a"
|
||||||
|
},
|
||||||
|
"kernelspec": {
|
||||||
|
"display_name": "Python 3.10.2 ('stock': venv)",
|
||||||
|
"language": "python",
|
||||||
|
"name": "python3"
|
||||||
|
},
|
||||||
|
"language_info": {
|
||||||
|
"codemirror_mode": {
|
||||||
|
"name": "ipython",
|
||||||
|
"version": 3
|
||||||
|
},
|
||||||
|
"file_extension": ".py",
|
||||||
|
"mimetype": "text/x-python",
|
||||||
|
"name": "python",
|
||||||
|
"nbconvert_exporter": "python",
|
||||||
|
"pygments_lexer": "ipython3",
|
||||||
|
"version": "3.10.2"
|
||||||
|
},
|
||||||
|
"orig_nbformat": 4
|
||||||
|
},
|
||||||
|
"nbformat": 4,
|
||||||
|
"nbformat_minor": 2
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user