<file_sep># 파이썬 모듈은 변수, 함수, 클래스를 포함할 수 있으며, import 예약어를 사용해 다른 모듈에 정의된 변수, 함수, 클래스 등을 자유롭게 불러와서 사용할 수 있다. 
# import 모듈명
# import 패키지명.모듈명
# 다음은 keyword 모듈을 import하여 파이썬 예약어를 확인하는 예제다. 물론 kwlist의 자료형은 리스타다. 

import keyword
print(keyword.kwlist)
print()

# import한 모듈이나 패키지의 실제 파일 위치를 알아보려며, __file__문자열 속성을 확인하면 된다. 
print(keyword.__file__)

# from 예약어를 사용하면, 실제 과정에서 from 다음에 지정한 패키지명이나 모듈명을 생략할 수 있다는 장점이 있다. 
# from 모듈명 import 클래스명, 함수명 등
# from 패키지명 import 모듈명

# 'import 모듈명' 형식으로 calendar 모듈을 임포트 한 경우, calendar 모듈 내의 month() 메서드를 호출하려면 다음 예제의 calendar.month()처럼 모듈명을 먼저 적어주어야 한다.

import calendar
print(calendar.month(2020, 1)) # 모듈명(calendar) 생략 불가

# 하지만 'from 모듈명 import 메서드명' 형식으로 임포트한 경우에는 다음처럼 모듈명 없이 메서드명을 바로 사용할 수 있다. 

from calendar import month
print(month(2020, 2)) # 모듈명(calendar) 생략 가능

# import ~ as ~ 
# as 예약어를 사용하면 이름이 긴 모듈명을 프로그래머가 원하는 별칭(alias)으로 줄여서 사용할 수 있다. 

# import 이름이_긴_모듈명 as 별칭
# from ~ import ~ as 별칭

import datetime
print(datetime.datetime.now()) # 별칭을 사용하지 않은경우

# datetime 모듈의 datetime 타입을 dt라는 별칭으로 지정하면, datetime.datetime.now()대신 dt.now()처럼 코드 길이를 짧게 줄일 수 있다. 
from datetime import datetime as dt
print(dt.now()) # 별칭(dt)를 사용한 경우<file_sep># sort()함수는 리스트를 직접 정렬하고 None을 반환한다. 리스트에서만 사용할 수 있다. 

li = [2, 5, 3, 1, 4]
li.sort()
print(li)

print()

# sorted() 함수는 리스트뿐 아니라 문자열, 튜플, 딕셔너리 등 반복 가능한 자료형에 모두 사용할 수 있다. sorted() 함수는 기존 리스트를 복사해서 새로 만들어 반환하기 때문에 sort() 함수보다는 다소 느리며, 기존의 리스트에는 영향을 주지 않는다. 

li = [4, 3, 1, 2, 5]
print(sorted(li))
print(li)<file_sep>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from Investar import Analyzer

mk = Analyzer.MarketDB()
stocks = ['삼성전자', 'SK하이닉스', '현대자동차', 'NAVER']
df = pd.DataFrame()
for s in stocks:
    df[s] = mk.get_daily_price(s, '2016-01-04', '2018-04-27')['close']

# 시총 상위 4 종목의 수익률을 비교하려면 종가 대신 일간 변동률로 비교를 해야 하기 때문에 데이터프레임에서 제공하는 pct_change() 함수를 사용해 4 종목의 일간 변동률을 구한다. 
daily_ret = df.pct_change()

# 일간 변동률의 평균값에 252를 곱해서 연간 수익률을 구한다. 252는 미국의 1년 평균 개장일로, 우리나라 실정에 맞게 다른 숫자로 바꾸어도 무방하다. 
annual_ret = daily_ret.mean() * 252

# 일간 리스크는 cov() 함수를 사용해 일간 변동률의 공분산으로 구한다.
daily_cov = daily_ret.cov()

# 연간 공분산은 일간 공분산에 252를 곱해 계산한다. 
annual_cov = daily_cov * 252

port_ret = []
# 시총 상위 4종목 비중을 다르게 해 포트폴리오 20,000개를 생성한다. 포트폴리오 수익률, 리스크, 종목 비중을 저장할 각 리스트를 생성한다. 
port_risk = []
port_weights = []

# Display results
print(daily_ret)
print(annual_ret)
print(daily_cov)
print(annual_cov)

# Monte Carlo Simulation
# 포트폴리오 20,000개를 생성하는데 range() 함수와 for in 구문을 사용했다. for in 구문에서 반복횟수를 사용할 일이 없으면 관습적으로 _ 변수에 할당한다. 
for _ in range(20000):
    # 4개의 랜덤 숫자로 구성된 배열을 생성한다. 
    weights = np.random.random(len(stocks))
    
    # 위에서 구한 4개의 랜덤 숫자의 총합으로 나눠 4 종목 비중의 합이 1이 되도록 조정한다. 
    weights /= np.sum(weights)

    # 랜덤하게 생성한 종목별 비중 배열과 종목별 연간 수익률을 곱해 해당 포트폴리오 전체 수익률(returns)을 구한다. 
    returns = np.dot(weights, annual_ret)
    # 종목별 연간 공분산과 종목별 비중 배열을 곱한 뒤 이를 다시 종목별 비중의 전치로 곱한다. 
    risk = np.sqrt(np.dot(weights.T, np.dot(annual_cov, weights)))

    # 포트폴리오 20,000개 수익률, 리스크, 종목별 비중을 각각 리스트에 추가한다. 
    port_ret.append(returns)
    port_risk.append(risk)
    port_weights.append(weights)

portfolio = {'Returns': port_ret, 'Risk': port_risk}
print(portfolio)

# i 값은 0, 1, 2, 3 순으로 변한다. 이때 s값은 삼성전자, sk하이닉스, 현대자동차, Naver 순으로 변한다. 
for i, s in enumerate(stocks):
    # portfolio 딕셔너리에 삼성전자, sk하이닉스, 현대자동차, Naver 키 순서로 비중값을 추가한다.
    portfolio[s] = [weights[i] for weight in port_weights]
df = pd.DataFrame(portfolio)
# 최종 생성된 df 데이터프레임을 출력하면, 시총 상위 4개 종목의 보유 비율에 따라 포트폴리오 20,000개가 각기 다른 리스크와 예상 수익률을 가지는 것을 확인 할 수 있다. 
df = df[['Returns', 'Risk'] + [s for s in stocks]]
print(df)

df.plot.scatter(x='Risk', y='Returns', figsize=(10, 7), grid=True)
plt.title('Efficient Frontier')
plt.xlabel('Risk')
plt.ylabel('Expected Returns')
plt.show()<file_sep># 구분자 변경하기
print('-'.join('2012/01/04'.split('/'))) # 문자열 내부의 '/'를 '-'로 교체
print('2020/08/17'.replace('/', '-'))

# 천 단위 숫자를 쉼표로 구분하기
print(''.join('1,234,567,890'.split(',')))
print('1,122,334,455'.replace(',', ''))

# 반대로 숫자를 표시할 때 천 단위마다 쉼표로 분리해 문자열 형태로 표시하려면 format() 함수를 사용하면 된다. 
print(format(1234567890, ','))<file_sep># RSI(Relative Strength Index)
rsi = 88
if rsi > 70:
  print("RSI", rsi, "means overbought.")
elif rsi < 30:
  print("RSI", rsi, "means oversold.")
else:
  print("...")

# RSI 88 means overbought.<file_sep># 최대 손실 낙폭 - MDD(Maximum Drawdown)은 특정 기간에 발생한 최고점에서 최저점까지 가장 큰 손실을 의미한다. 퀀트 투자에서는 수익률을 높이는 것 보다 MDD를 낮추는 것이 더 낫다고 할 만큼 중요한 지표로서, 특정기간 동안 최대한 얼마늬 손실이 날 수 있는지를 나타낸다. 
# MDD = (최저점 - 최고점) / 최저점

# KOSPI(Korea Composite Stock Price Index, 한국종합주가지수)는 1983년 부터 발표되었으며, 1980년 1월 4일에 상장된 모든 종목의 시가 총액을 기준 지수 100포인트로 집계한다. 따라서 KOSPI 지수 2500은 한국 증시가 1980년 당시보다 25배 올랐음을 나타낸다. 
# KOSPI는 1994년 1145.66 포인트에서 1998년 277.37 포인트까지 4년동안 무려 75.8%가 하락했는데, 이 기간 MDD는 -75.8%이다. 전체 주식 시장이 1/4 토막 난 것이 KOSPI 역사상 최대 손실 낙폭이라고 할 수 있다. 

# 서브프라임 당시의 MDD - 야후 파이넨스로부터 2004년부터 현재까지의 KOSPI 지수 데이터를 다운로드 받아서 KOSPI의 MDD를 구해보자. MDD를 구하려면 기본적으로 rolling() 함수에 대해 알아야 한다. 

# 시리즈.rolling(윈도우 크기 [, min_periods=1]) [.집계 함수()]
# rolling() 함수는 시리즈에서 윈도우 크기에 해당하는 개수만큼 데이터를 추출하여 집계 함수에 해당하는 연산을 실시한다. 집계 함수로는 최댓값 max(), 평균값 mean(), 최솟값 min()을 사용할 수 있다. min_periods를 지정하면 데이터 개수가 윈도우 크기에 못미치더라도 min_periods로 지정한 개수만 만족하면 연산을 수행한다. 

# 다음은 야후 파이넨스에서 KOSPI 지수 데이터를 다운로드 한 뒤 rolling() 함수를 이용하여 1년 동안 최댓값과 최소값을 구하여 MDD를 계산하는 예이다. 

from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
import matplotlib.pyplot as plt

kospi = pdr.get_data_yahoo('^ks11', '2004-01-04') # KOSPI 지수 데이터를 다운로드 한다. KOSPI 지수의 심볼은 ^KS11이다. 

window = 252 # 산정 기간에 해당하는 window 값은 1년 동안 개장일을 252일로 어림잡아 설정했다. 
peak = kospi['Adj Close'].rolling(window, min_periods=1).max() # KOSPI 종가 갈럼에서 1년(거래일 기준) 기간 단위로 최고치 peak를 구한다. 
drawdown = kospi['Adj Close']/peak - 1.0 # drawdown은 최고치(peak) 대비 현재 KOSPI 종가가 얼마나 하락했는지를 구한다. 
max_dd = drawdown.rolling(window, min_periods=1).min() # drawdown에서 1년 기간 단위로 최저치 max__dd를 구한다. 마이너스값이기 때문에 최저치가 바로 최대 손실 낙폭이 된다. 

plt.figure(figsize=(9, 7))
plt.subplot(211) # 2행 1열 중 1행에 그린다. 
kospi['Close'].plot(label='KOSPI', title='KOSPI MDD', grid=True, legend=True)
plt.subplot(212) # 2행 1열 중 2행에 그린다. 
drawdown.plot(c='blue', label='KOSPI DD', grid=True, legend=True)
max_dd.plot(c='red', label='KOSPI MDD', grid=True, legend=True)
plt.show()

# 정확한 MDD는 min()함수로 구한다. 
print(max_dd.min())

# MDD를 기록한 기간을 구하려면 다음과 같이 인덱싱 조건을 적용하면 된다. 
print(max_dd[max_dd==-0.5453665130144085])<file_sep>"""
상속은 클래스가 가지는 모든 속성과 메서드를 다른 클래스에게 물려주는 기법이다. 
자식 클래스는 여러 부모 클래스로부터 상속 받을 수 있는데, 이를 다중 상속이라고 한다.
class 자식 클래스(부모 클래스 1, 부모 클래스 2, ...):
    pass
"""

class A:
    def methodA(self):
        print("Calling A's methodA")
    def method(self):
        print("Calling A's method")

class B:
    def methodB(self):
        print("Calling B's methdB")

class C(A, B):
    def methodC(self):
        print("Calling C's methodC")
    def method(self):
        print("Calling C's overridden method")
        # 부모의 변수나 메소드를 사용할 때는 super() 내장 함수를 호출하면 된다. 
        super().method()

c = C()
print(c.methodA())
print(c.methodB())
print(c.methodC())
print(c.method())

# 위 예제의 method() 메서드처럼 자식 클래스에서 부모 클래스의 메서드 이름과 인수 형식과 동일하게 매서드를 재정의 하는 것을 오버라이딩이라고 한다. <file_sep># 패키지는 여러 모듈(.py 파일)을 특정 디렉터리에 모아놓은 것이다. 패키지명 뒤에 '.'를 붙이고 모듈명을 사용할 수 있다. 예를 들어 A.B로 표기하면 A 패키지의 하위 모듈 B를 명시한 것이다. 이렇게 사용하면 여러 모듈을 사용할 때 모듈명이나 전역변수가 겹치는 문제를 피할 수 있다. 다음은 urllib 패키지의 request 모듈의 자료형을 type() 함수로 확인한 예이다. 

import urllib.request
print(type(urllib.request)) # urllib.request 모듈의 타입은 module이다. 

import urllib
print(type(urllib)) # urllib의 타입은 module로 표시된다. 
print(urllib.__path__) # urllib은 __path__ 속성이 있으므로 패키지다. 
print(urllib.__package__) # urllib이 속한 패키지는 urllib이다. <file_sep>i = 1
while i < 7:
  print(i)
  i += 2

print()

j = 0
while j >= 0:
  j += 1
  if (j % 2) == 0:
    continue
  if j > 5:
    break
  print(j)
else:
  print('Condition is False.')<file_sep>word = "python"
print(len(word))
print(word[0] + word[1] + word[2] + word[3] + word[4] + word[5])
print(word[-6] + word[-5] + word[-4] + word[-3] + word[-2] + word[-1])
<file_sep>class NasdaqStock:
    """Class for NASDAQ stocks""" # 독스트링
    count = 0 # 클래스 변수
    def __init__(self, symbol, price):
        """Constructor for NasdaqStock""" # 독스트링
        self.symbol = symbol # 인스턴스 변수
        self.price = price # 인스턴스 변수
        NasdaqStock.count += 1
        print('Calling __init__({}, {:2f}) > count: {}'.format(self.symbol, self.price, NasdaqStock.count))
    
    def __del__(self):
        """Destructor for NasdaqStock""" # 독스트링
        print("Calling __del__({})".format(self))
    
gg = NasdaqStock("GOOG", 1154.05)
del(gg)
ms = NasdaqStock("MSFT", 102.44)
del(ms)
amz = NasdaqStock("AMZN", 1764.00)
del(amz)<file_sep># 연평균 성장률(CAGR)을 계산하는 함수를 작성해 보자. CAGR은 Compound Annual Growth Rate의 약자로 우리말로는 '복합 연평균 성장률' 또는 '연복리 수익률'이라고도 부른다. 

# CAGR은 1년 동안 얼마 만큼씩 증가하는지를 나타내는 값으로, 주로 투작 수익률을 표시하는데 사용되지만, 판매수량이나 사용자 증가율 등을 나타날 때도 쓴다. 

# CAGR = (L/F)^(1/Y) - 1
# F = 처음 값, L = 마지막 값, Y = 처음 값과 마지막 값 사이의 연(year) 수

# 위의 수학 공식을 파이썬으로 옮겨보면 다음과 같다. 
def getCAGR(first, last, years):
    return (last/first)**(1/years) - 1

# 삼성전자는 1998년 4월 27일 65,300원이던 주가가 액면 분할 직전인 2018년 4월 27일 2,669,000원이 되기까지 정확히 20년 동안이나 무려 4,087%로 상승했다. 이 기간 연평균 성장률을 구하면 20.38%가 나온다. 

cagr = getCAGR(65300, 2669000, 20)
print(" SEC CAGR : {:.2%}".format(cagr))<file_sep>from pywinauto import application
import os, time

# 프로세스 종료 명령 taskkill로 실행 중인 크레온 관련 프로세스(coStarter.exe, CpStart.exe, DibServer.exe)를 종료했다. 인수는 '이미지명이 coStarter로 시작하는 프로세스(/IM coStarter*)를 강제로(/F) 종료하라(/T)'는 뜻이다. 만일 실행 중인 크레온 관련 프로세스가 없으면 해당 프로세스를 찾을 수 없다고 오류 메시지가 발생할 수 있으나, 실행중인 프로세스를 찾아서 종료하는 것이 목적이므로 프로세스를 못 찾는다는 오류 메시지는 무시해도 된다. 
os.system('taskkill /IM coStarter* /F /T')
os.system('taskkill /IM CpStart* /F /T')
os.system('taskkill /IM DibServer* /F /T')
# WMIC(Windows Management Instrumentation Command-line)는 윈도우 시스템 정보를 조회하거나 변경할 때 사용하는 명령이다. 크레온 프로그램은 가제 종료 신호를 받으면 확인 창을 띄우기 때문에 강제로 한 번 더 프로세스를 종료해야 한다. 
os.system('wmic process where "name like \'%coStarter%\'" call terminate')
os.system('wmic process where "name like \'%CpStart%\'" call terminate')
os.system('wmic process where "name like \'%DibServer%\'" call terminate')

time.sleep(5)
app = application.Application()
# 파이윈오토를 이용하여 크레온 프로그램(coStarter.exe)을 크레온 플러스 모드(/prj:cp)로 자동으로 시작한다. 사용자 ID, 암호, 공인인증서 암호를 실행 인수로 지정해 놓으면 로그인 창에 자동으로 입력된다. (* 표시를 자신의 정보로 대체하기 바란다. )
app.start('C:\CREON\STARTER\coStarter.exe /prj:cp/id:**** /pwd:**** /pwdcert:**** /autostart')
time.sleep(60)<file_sep># append() 함수는 넘겨받은 인수의 자료형에 상관 없이 리스트 뒤에 그대로 추가한다. 
L = [1, 2]
L.append([3, 4])
print(L)

# extend() 함수는 넘겨받은 인수가 반복 자료형일 경우, 반복 자료형 내부의 각 원소를 추가한다. 
L2 = [1, 2]
L2.extend([3, 4])
print(L2)<file_sep># 시리즈 생성
import pandas as pd
s = pd.Series([0.0, 3.6, 2.0, 5.8, 4.2, 8.0]) # 리스트로 시리즈 생성
print(s)

# 시리즈의 인덱스 변경
s.index = pd.Index([0.0, 1.2, 1.8, 3.0, 3.6, 4.8]) # 인덱스 변경
s.index.name = 'MY_IDX' # 인덱스명 설정
print(s)

s.name = 'MY_SERIES' # 시리즈명 설정
print(s)

# 데이터 추가
s[5.9] = 5.5
print(s)

ser = pd.Series([6.7, 4.2], index=[6.8, 8.0]) # ser 시리즈 생성
s = s.append(ser) # 기존 s 시리즈에 신규 ser 시리즈를 추가
print(s)

# 데이터 인덱싱
print(s.index[-1]) # -1은 제일 마지막을 의미하므로 제일 마지막 인덱스 값을 출력
print(s.values[-1]) # 인덱스 순서에 해당하는 데이터를 구하려면 values 속성을 사용한다. 
print(s.loc[8.0]) # 인덱스를 이용해서 실제로 가리키는 작업을 수행하는 인덱서를 사용해서 데이터를 표시할 수도 있다. 인덱스값을 사용하는 loc 인덱서와 정수 순서를 사용하는 iloc 인덱서가 있다. 
print(s.loc[8.0]) # 로케이션 인덱서
print(s.iloc[-1]) # 인티저 로케이션 인덱서

# iloc와 values는 인덱스 순서에 해당하는 데이터를 술력한다는 점에서 동일하지만, values는 결과값이 복수개 일 때 배열로 반환하고, iloc는 시리즈로 반환하는 차이점이 있다. 
print(s.values[:])
print(s.iloc[:])

# 데이터 삭제
s = s.drop(8.0) # s.drop(s.index[-1])과 같다. s 시리즈에 변화를 주지 않으려면 대입하지 않으면 됨
print(s)

# 시리즈 정보 보기
print(s.describe())

# 시리즈 출력하기
import pandas as pd
s = pd.Series([0.0, 3.6, 2.0, 5.8, 4.2, 8.0, 5.5, 6.7, 4.2]) # 시리즈 생성
s.index = pd.Index([0.0, 1.2, 1.8, 3.0, 3.6, 4.8, 5.9, 6.8, 8.0]) # 시리즈 인덱스 변경
s.index.name = "MY_IDX" # 시리즈 인덱스명 설정
s.name = 'MY_SERIES' # 시리즈 이름 설정
import matplotlib.pyplot as plt
plt.title("ELLIOTT_WAVE")
plt.plot(s, 'bs--') # 시리즈를 bs--(푸른 사각형과 점선) 형태로 출력
plt.xticks(s.index) # x축의 눈금값을 s 시리즈의 인덱스값으로 설정
plt.yticks(s.values) # y축의 눈금값을 s 시리즈의 데이터값으로 설정
plt.grid(True)
plt.show()<file_sep>try:
  1/0
except Exception as e:
  print('Exception occured :', str(e))
<file_sep># 데이터프레임 생성
import pandas as pd
df = pd.DataFrame({'KOSPI': [1915, 1961, 2026, 24467, 2041], 'KOSDAQ': [542, 682, 631, 798, 675]})
print(df)

# 인덱스 추가
import pandas as pd
df = pd.DataFrame({'KOSPI': [1915, 1961, 2026, 24467, 2041], 'KOSDAQ': [542, 682, 631, 798, 675]}, index=[2014, 2015, 2016, 2017, 2018])
print(df)

# 데이터프레임 객체에 포함된 데이터의 전체적 모습 확인
print(df.describe())

# 데이터프레임의 인덱스 정보, 칼럼 정보, 메모리 사용량 등을 확인
print(df.info())

# 시리즈를 이요한 데이터프레임 생성
kospi = pd.Series([1915, 1961, 2026, 2467, 2041], index=[2014, 2015, 2016, 2017, 2018], name='KOSPI')
print(kospi)
kosdaq = pd.Series([542, 682, 631, 798, 675], index=[2014, 2015, 2016, 2017, 2018], name='KOSDAQ')
print(kosdaq)
df = pd.DataFrame({kospi.name: kospi, kosdaq.name: kosdaq})
print(df)

# 리스트를 이용한 데이터프레임 생성
columns = ['KOSPI', 'KOSDAQ']
index = [2014, 2015, 2016, 2017, 2018]
rows = []
rows.append([1915, 542])
rows.append([1961, 682])
rows.append([2026, 631])
rows.append([2467, 798])
rows.append([2041, 675])
df = pd.DataFrame(rows, columns=columns, index=index)
print(df)

# 데이터프레임 순회 처리
for i in df.index:
    print([i, df['KOSPI'][i], df['KOSDAQ'][i]])

# itertuples() 메서드는 데이터프레임의 각 행을 이름있는 튜플 형태로 반환한다. 
for row in df.itertuples(name='KRX'):
    print(row)

for row in df.itertuples():
    print(row[0], row[1], row[2])

for idx, row in df.iterrows():
    print(idx, row[0], row[1])<file_sep>import matplotlib.pyplot as plt
from Investar import Analyzer

mk = Analyzer.MarketDB()
df = mk.get_daily_price('005930', '2017-07-10', '2018-06-30')

plt.figure(figsize=(9,6))
plt.subplot(2, 1, 1)
plt.title('Samsung Electronics (Investar Data)')
plt.plot(df.index, df['Close'], 'c', label='Close')
plt.legend(loc='best')
plt.subplot(2, 1, 2)
plt.bar(df.index, df['Volume'], color='g', label='Volume')
plt.legend(loc='best')
plt.show()<file_sep># 그래프 출력
# plot(x, y, 마커 형태 [, label='Label'])
from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()
sec = pdr.get_data_yahoo('005930.KS', start='2018-05-04')
msft = pdr.get_data_yahoo('MSFT', start='2018-05-04')

import matplotlib.pyplot as plt

plt.plot(sec.index, sec.Close, 'b', label='Samsung Electronics')
plt.plot(msft.index, msft.Close, 'r--', label="Microsoft")
plt.legend(loc='best') # 범례를 best로 지정하면, 그래프가 표시되지 않는 부분을 찾아서 적절한 위치에 범례를 표시해준다.
plt.show()

# 일간 변동률(daily percent change)로 주가 비교하기
# R(오늘 변동률) = ((R(오늘 종가) - R(어제 종가) / R(어제 종가)) * 100
# 위의 수학식을 파이썬 코드로 옮기는 데 시리즈 모듈에서 제공하는 shift() 함수를 사용한다. 
print(type(sec['Close']))

# 삼성전자 종가 칼럼의 데이터 확인
print(sec['Close'])

# shift() 함수는 데이터를 이동시킬 때 사용하는 함수로, 인수로 n을 줄 경우 전체 데이터가 n행씩 뒤로 이동한다. 
print(sec['Close'].shift(1))

# 위의 수학식을 파이썬 코드로 표현
sec_dpc = (sec['Close'] / sec['Close'].shift(1) - 1) * 100
print(sec_dpc.head())

# 첫 번 째 일간 변동률의 값이 NaN인데, 향후 계산을 위해 NaN을 0으로 변경할 필요가 있다. 
sec_dpc.iloc[0] = 0 # 인티저 로케이션 인덱서를 사용해서 시리즈의 첫 번째 데이터를 0으로 변경한다. 
print(sec_dpc.head())

# 마이크로 소프트 데이터 프레임도 동일하게 처리해준다. 
msft_dpc = (msft['Close'] / msft['Close'].shift(1) - 1) * 100
msft_dpc.iloc[0] = 0
print(msft_dpc.head())

# Histogram은 frequency distribution을 나타내는 그래프로서, 데이터 값 들에 대한 구간별 빈도수를 막대 형태로 나타낸다. 이 때 구간 수를 bins라고 하는데 hist() 함수에서 사용되는 bins의 기본값은 10이다. 빈스에 따라 그래프 모양이 달라지므로 관측한 데이터 특성을 잘 보여주도록 빈스값을 정해야 한다. 삼성전자 주식 종가의 일간 변동률을 히스토그램으로 출력해보자. 맷플롯립에서 히스토그램은 hist() 함수를 사용한다. 삼성전자의 일간 변동률을 18개 구간으로 나누어 빈도수를 표시한다. 

import matplotlib.pyplot as plt
sec_dpc = (sec['Close'] / sec['Close'].shift(1) - 1) * 100
sec_dpc.iloc[0] = 0
plt.hist(sec_dpc, bins=18)
plt.grid(True)
plt.show()
# 출력된 결과를 보면 삼성전자 일간 변동률 분포가 0 bin을 기준으로 좌우 대칭적이다. 정규분포 형태와 비슷하다. 엄밀히 얘기하자면, 주가 수익률은 정규분포보다 중앙 부분이 더 뾰족하고, 양쪽 꼬리는 더 두터운 것으로 알려져 있다. 이를 각각 급첨분포(leptokurtic distribution)와 팻 테일(fat tail)이라 부른다. 

# 주가 수익률이 급첨분포를 나타낸다는 것은 정규분포와 비교했을 때 주가의 움직임이 대부분 매우 작은 범위 안에서 발생한다는 것을 의미한다. 그리고 두꺼운 꼬리를 가리키는 팻 테일은 그래프가 좌우 극단 부분에 해당하는 아주 큰 가격 변동이 정규분포보다 더 많이 발생한다는 의미다. 시리즈의 describe() 매서드를 이용하면 평균과 표준편차를 확인할 수 있다. 
print(sec_dpc.describe())

# 일간 변동률 누적합 구하기
# sec_dpc는 일간 변동률이기 때문에 종목별로 전체적인 변동률을 비교해보려면, 일간 변동률 누적합(Cumulative Sum)을 계산해야 한다. 누적합은 시리즈에서 제공하는 cumsum() 함수를 이용하여 구할 수 있다. 
sec_dpc_cs = sec_dpc.cumsum() # 일간 변동률의 누적합을 구한다. 
print(sec_dpc_cs)
<file_sep># __name__ 속성
# 명령창에서 파이썬 셸을 이용하여 moduleA 모듈을 실행할 수 있다. 함수 정의를 제외한 나머지 부분이 실행되므로 print('MODULE_A :', __name__) 코드에 의해 'MODULE_A : __main__'이 출력된다. 

# moduleA 모듈을 직접 실행하지 않고, 파이썬 셸에서 임포트만 해도 print("MODULE_A :", __name__) 코드가 실행되면서 'MODULE_A : myPackage.moduleA'이 출력된다. 이처럼 __name__속성은 단독으로 실행될 때는 '__main__'문자열이 되고, 임포트할 때는 실제 모듈명('myPackage.moduleA')이 된다. 

<file_sep># 튜플은 리스트처럼 다양한 자료형의 원소를 가지지만, 대괄호 대신 소괄호를 표시하며 원소를 변경할 수 없다. 
# 튜플은 다른 리스트나 내장함수도 원소로 가질 수 있음
myTuple = ('a', 'b', 'c', [10, 20, 30], abs, max)

# 인덱싱을 사용하여 4번째 원소인 리스트를 출력
print(myTuple[3])

# 5번째 원소인 내장함수 abs()에 -100을 파라미터로 전달
print(myTuple[4](-100))

# 6번째 원소인 내장함수 max()에 리스트를 파라미터로 전달
print(myTuple[5](myTuple[3]))

# 원소에 대한 변경이 정말 안 되는지 확인해보자. 첫 번째 원소에 'A'를 대입해 보자. '튜플 객체는 원소할당(item assignment)을 지원하지 않는다.'는 메세지와 함께 타입 에러가 발생할 것이다. 
myTuple[0] = 'A'
<file_sep>from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()

# 2000년 이후의 다우존스 지수 데이터를 야후 파이넨스로 부터 다운로드 한다. 
dow = pdr.get_data_yahoo('^DJI', '2000-01-04')
# 2000년 이후의 KOSPI 데이터를 야후 파이넨스로 부터 다운로드 한다. 
kospi = pdr.get_data_yahoo('^KS11', '2000-01-04')

import matplotlib.pyplot as plt
plt.figure(figsize=(9, 5))

# 다우존스 지수를 붉은 점선으로 출력한다. 
plt.plot(dow.index, dow.Close, 'r--', label='Dow Jones Industrial')
# KOSPI를 푸른 실선으로 출력한다. 
plt.plot(kospi.index, kospi.Close, 'b', label='KOSPI')
plt.grid(True)
plt.legend(loc='best')
plt.show()<file_sep>import pandas as pd
import matplotlib.pyplot as plt
import datetime
from mplfinance.original_flavor import candlestick_ohlc
import matplotlib.dates as mdates
from Investar import Analyzer

mk = Analyzer.MarketDB()
df = mk.get_daily_price('엔씨소프트', '2017-01-01')

# 종가의 12주 지수 이동평균에 해당하는 60일 지수 이동평균을 구한다. 
ema60 = df.close.ewm(span=60).mean()
# 종가의 26주 지수 이동평균에 해당하는 130일 지수 이동평균을 구한다. 
ema130 = df.close.ewm(span=130).mean()
# 12주(60일) 지수 이동평균에서 26주(130일) 지수 이동평균을 빼서 MACD(Moving Average Convergence Divergence)선을 구한다. 
macd = ema60 - ema130
# MACD의 9주(45일) 지수 이동평균을 구해서 신호선으로 저장한다. 
signal = macd.ewm(span=45).mean()
# MACD선에서 신호선을 빼서 MACD 히스토그램을 구한다. 
macdhist = macd - signal
df = df.assign(ema130=ema130, ema60=ema60, macd=macd, signal=signal, macdhist=macdhist).dropna()

# 캔들 차트에 사용할 수 있게 날짜(date)형 인덱스를 숫자형을 변환한다. 
df['number'] = df.index.map(mdates.date2num)
ohlc = df[['number', 'open', 'high', 'low', 'close']]

# 14일 동안의 최댓값을 구한다. min_periods=1을 지정할 경우, 14일 기간에 해당하는 데이터가 모두 누적되지 않았더라도 최소 기간인 1일 이상의 데이터만 존재하면 최댓값을 구하라는 의미다. 
ndays_high = df.high.rolling(window=14, min_periods=1).max()
# 14일 동안의 최솟값을 구한다. min_periods=1로 지정하면, 14일 치 데이터 모두 누적되지 않았더라도 최소 기간인 1일 이상의 데이터만 존재하면 최솟값을 구하라는 의미다. 
ndays_low = df.low.rolling(window=14, min_periods=1).min()

# 빠른 선 %K를 구한다. 
fast_k = (df.close - ndays_low) / (ndays_high - ndays_low) * 100
# 3일 동안 %K의 평균을 구해서 느린 선 %D에 저장한다. 
slow_d = fast_k.rolling(window=3).mean()
# %K와 %D로 데이터프레임을 생성한 뒤 결측치는 제거한다. 
df = df.assign(fast_k=fast_k, slow_d=slow_d).dropna()

plt.figure(figsize=(9, 9))
p1 = plt.subplot(3, 1, 1)
plt.title('Triple Screen Trading (NCSOFT)')
plt.grid(True)
# ohlc의 숫자형 일자, 시가, 고가, 저가, 종가 값을 이용해서 캔들 차트를 그린다. 
candlestick_ohlc(p1, ohlc.values, width=6, colorup='red', colordown='blue')
p1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.plot(df.number, df['ema130'], color='c', label='EMA130')
for i in range(1, len(df.close)):
    # 130일 이동 지수평균이 상승하고 %D가 20 아래로 떨어지면 
    if df.ema130.values[i-1] < df.ema130.values[i] and df.slow_d.values[i-1] >= 20 and df.slow_d.values[i] < 20:
        # 빨간색 삼각형으로 매수 신호를 표시한다. 
        plt.plot(df.number.values[i], 250000, 'r^')
    # 130일 이동 지수평균이 하락하고 %D가 80 위로 상승하면
    elif df.ema130.values[i-1] > df.ema130.values[i] and df.slow_d.values[i-1] <= 80 and df.slow_d.values[i] > 80:
        # 파란색 삼각형으로 매수 신호를 표시한다. 
        plt.plot(df.number.values[i], 250000, 'bv')
plt.legend(loc='best')

p2 = plt.subplot(3, 1, 2)
plt.grid(True)
p2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.bar(df.number, df['macdhist'], color='m', label='MACD-Hist')
plt.plot(df.number, df['macd'], color='b', label='MACD')
plt.plot(df.number, df['signal'], 'g--', label='MACD-Signal')
plt.legend(loc='best')

p3 = plt.subplot(3, 1, 3)
plt.grid(True)
p1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.plot(df.number, df['fast_k'], color='c', label='%K')
plt.plot(df.number, df['slow_d'], color='k', label='%D')
# Y축 눈금을 0, 20, 80, 100으로 설정하여 스토캐스틱의 기준선을 나타낸다.
plt.yticks([0, 20, 80, 100])
plt.legend(loc='best')
plt.show()<file_sep>def functionA():
    print('FUNCTION_A')

def main():
    print('MAIN_A :', __name__)

if __name__ == '__main__':
    main()<file_sep>import pandas as pd
import matplotlib.pyplot as plt
import datetime
from mplfinance.original_flavor import candlestick_ohlc
import matplotlib.dates as mdates
from Investar import Analyzer

mk = Analyzer.MarketDB()
df = mk.get_daily_price('엔씨소프트', '2017-01-01', '2020-12-01')

# 종가의 12주 지수 이동평균에 해당하는 60일 지수 이동평균을 구한다. 
ema60 = df.close.ewm(span=60).mean()
# 종가의 26주 지수 이동평균에 해당하는 130일 지수 이동평균을 구한다. 
ema130 = df.close.ewm(span=130).mean()
# 12주(60일) 지수 이동평균에서 26주(130일) 지수 이동평균을 빼서 MACD(Moving Average Convergence Divergence)선을 구한다. 
macd = ema60 - ema130
# MACD의 9주(45일) 지수 이동평균을 구해서 신호선으로 저장한다. 
signal = macd.ewm(span=45).mean()
# MACD선에서 신호선을 빼서 MACD 히스토그램을 구한다. 
macdhist = macd - signal

df = df.assign(ema130=ema130, ema60=ema60, macd=macd, signal=signal, macdhist=macdhist).dropna()
# 캔들 차트에 사용할 수 있게 날짜(date)형 인덱스를 숫자형을 변환한다. 
df['number'] = df.index.map(mdates.date2num)
ohlc = df[['number', 'open', 'high', 'low', 'close']]

# 14일 동안의 최댓값을 구한다. min_periods=1을 지정할 경우, 14일 기간에 해당하는 데이터가 모두 누적되지 않았더라도 최소 기간인 1일 이상의 데이터만 존재하면 최댓값을 구하라는 의미다. 
ndays_high = df.high.rolling(window=14, min_periods=1).max()
# 14일 동안의 최솟값을 구한다. min_periods=1로 지정하면, 14일 치 데이터 모두 누적되지 않았더라도 최소 기간인 1일 이상의 데이터만 존재하면 최솟값을 구하라는 의미다. 
ndays_low = df.low.rolling(window=14, min_periods=1).min()
# 빠른 선 %K를 구한다. 
fast_k = (df.close - ndays_low) / (ndays_high - ndays_low) * 100
# 3일 동안 %K의 평균을 구해서 느린 선 %D에 저장한다. 
slow_d = fast_k.rolling(window=3).mean()
# %K와 %D로 데이터프레임을 생성한 뒤 결측치는 제거한다. 
df = df.assign(fast_k=fast_k, slow_d=slow_d).dropna()

plt.figure(figsize=(9, 7))
p1 = plt.subplot(2, 1, 1)
plt.title('Triple Screen Trading - Second Screen (NCSOFT)')
plt.grid(True)
# ohlc의 숫자형 일자, 시가, 고가, 저가, 종가 값을 이용해서 캔들 차트를 그린다. 
candlestick_ohlc(p1, ohlc.values, width=6, colorup='red', colordown='blue')
p1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.plot(df.number, df['ema130'], color='c', label='EMA130')
plt.legend(loc='best')

p1 = plt.subplot(2, 1, 2)
plt.grid(True)
p1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.plot(df.number, df['fast_k'], color='c', label='%K')
plt.plot(df.number, df['slow_d'], color='k', label='%D')
# Y축 눈금을 0, 20, 80, 100으로 설정하여 스토캐스틱의 기준선을 나타낸다.
plt.yticks([0, 20, 80, 100])
plt.legend(loc='best')
plt.show()<file_sep>import numpy as np
A = np.array([[1, 2], [3, 4]])
print(A)
print(type(A))
print(A.ndim) # 배열의 차원
print(A.shape) # 배열 크기
print(A.dtype) # 원소 자료형
print(A.max(), A.mean(), A.min(), A.sum())

# 배열의 접근
print(A[0]) # A 배열의 첫 번째 행
print(A[1]) # A 배열의 두 번째 행

# A 배열의 원소에 접근하는 데 A[행 인덱스][열 인덱스] 형식을 사용해도 되고, A[행 인덱스, 열 인덱스] 형식을 사용해도 된다. 
print(A[0][0], A[0][1])
print(A[1][0], A[1][1])
print(A[0, 0], A[0, 1])
print(A[1, 0], A[1, 1])

# 조건에 맞는 원소들만 인덱싱할 수도 있다. 
print(A[A>1])

# Transpose - 배열의 요소를 주대각선을 기준으로 뒤바꾸는 것
print(A)
print(A.T) # A.transpose()와 같다. 

# Flatten - 다차원 배열을 1차원 배열 형태로 바꾸는 것; 평탄화
print(A)
print(A.flatten())

# 배열의 연산 (Array Operations)
# 같은 크기의 행렬끼리는 사칙 연산을 할 수 있다. 두 행렬에서 같은 위치에 있는 원소끼리 연산을 하면 된다. 

print(A + A) # np.add(A, A)와 같다. 
print(A - A) # np.subtract(A, A)와 같다. 
print(A * A) # np.multiply(A, A)와 같다. 
print(A / A) # np.divide(A, A)와 같다. 

# Broadcasting ; 수학에서는 크기가 같은 행렬끼리만 연산을 할 수 있다. 하지만 넘파이에서는 행렬의 크기가 달라도 연산할 수 있게 크기가 작은 행렬을 확장해 주는데, 이를 브로드캐스팅이라고 한다. 다음은 B 행렬을 A 행렬의 크기에 맞게 브로드캐스팅하여 연산한 예이다. 

B = np.array([10, 100])
print(A * B)

# 내적(inner product)구하기
# 넘파이에서 배열끼리 곱셈연산(*)을 할 때 기본적으로 원소별 연산을 수행하기 때문에 두 배열의 내적 곱을 구하려면 dot() 함수를 사용해야 한다. 
print(B.dot(B)) # np.dot(B * B)

print(A.dot(B))<file_sep># set은 중복이 없는 원소 집합을 나타낸다. 
s = {'A', 'P', 'P', 'L', 'E'}
print(s) # {'A', 'P', 'E', 'L'}

# 한 가지 기억할 점은 반드시 우리가 생성한 순서대로 원소가 저장되지는 않는다는 점이다. 
mySet = {'B', 6, 1, 2}
print(mySet)

# Set 내부에 특정 원소가 존재하는지 검사하려면 다음과 같이 if ~ in ~ 비교 구문으로 확인하면 된다. Set은 다른 반복 자료형 보다 훨씬 빨리 원소의 존재 여부를 검사 할 수 있다. 
if 'B' in mySet:
  print("'B' exists in", mySet)

# Set의 원소들은 인덱싱이 불가능한 대신, 원소들의 교집합, 합집합, 차집합을 구할 수 있다. 
setA = {1, 2, 3, 4, 5}
setB = {3, 4, 5, 6, 7}

print(setA & setB) # setA.intersection(setB)
print(setA | setB) # setA.union(setB)
print(setA - setB) # setA.difference(setB)
print(setB - setA) # setB.difference(setA)

# 아쉽게도 셋은 반복 자료형처럼 리터럴로 원소가 없는 생태에서 생성할 수 없다. s = {}으로 생성하면 실제로는 딕셔너리가 생성되니 주의하자. 빈 셋은 s = set()으로 생성해야 한다. 
ls2 = [] # ls = list()와 같은 결과
d = {} # d = dict()와 같은 결과
t = () # t = tuple()과 같은 결과

# 중복 없는 셋의 특징을 이용하면 다음처럼 리스트에서 중복 원소를 간단히 제거할 수 있다. 
ls2 = [1, 3, 5, 2, 2, 3, 4, 2, 1, 1, 1, 1, 5]
print(list(set(ls2)))<file_sep># Get an image file by using request
import requests

url = 'http://bit.ly/2JnsHnT'
r = requests.get(url, stream=True).raw

# Show an image by using pillow
from PIL import Image

img = Image.open(r)
img.show()
img.save('src.png')

# Print file info
print(img.get_format_mimetype)

# copy an image file with 'with ~ as'
BUF_SIZE = 1024
# 1. 원본 이미지 파일(scr.png)을 바이너리 읽기 모드로 열어서 sf 파일 객체를 생성하고, 대상 이미지 파일 (dst.png)을 바이너리 쓰기 모드로 열어서 df 파일 객체를 생성한다. 
with open('src.png', 'rb') as sf, open('dst.png', 'wb') as df:
    while True:
        # 2. sf 파일 객체로 부터 1024 바이트씩 읽는다. 
        data = sf.read(BUF_SIZE) 
        if not data:
            # 3. 읽을 data가 없다면 while 반복문을 빠져나온다.
            break
        # 4. 읽어 온 data를 df 파일 객체에 쓰고 2 부터 다시 반복한다. 
        df.write(data)

# SHA-256으로 파일 복사 검증하기
# 해시는 암호화폐 지갑의 주소처럼 긴 데이터 값을 입력받아서 고정 길이의 고유한 값으로 변환하는 것이 핵심 기능이다.
import hashlib
# 원본 이미지 파일과 사본 이미지 파일에 대한 해시 객체를 각각 생성한다. 
sha_src = hashlib.sha256()
sha_dst = hashlib.sha256()

# 원본 이미지 파일(src.png)을 바이너리 읽기 모드(rb)로 열어서 sf 파일 객체를 생성하고, 사본 이미지 파일(dst.png)을 바이너리 읽기 모드(rb)로 열어서 df 파일 객체를 생성한다.
with open('src.png', 'rb') as sf, open('dst.png', 'rb') as df:
    # sf 파일 객체로부터 전체 내용을 읽어서 원본 이미니제 대한 해시 객체(sha_src)를 업데이트한다. df 파일 객체로부터 전체 내용을 읽어서 사본 이미지에 대한 해시 객체(sha_dst)를 업데이트한다. 
    sha_src.update(sf.read())
    sha_dst.update(df.read())

# 원본 이미지 파일과 사본 이미지 파일을 해시값을 16진수로 각각 출력한다. 
print("src.png's hash : {}".format(sha_src.hexdigest()))
print("dst.png's hash : {}".format(sha_dst.hexdigest()))<file_sep>"""Investar URL Configuration

The `urlpatterns` list routes URLs to views. For more information please see:
    https://docs.djangoproject.com/en/3.1/topics/http/urls/
Examples:
Function views
    1. Add an import:  from my_app import views
    2. Add a URL to urlpatterns:  path('', views.home, name='home')
Class-based views
    1. Add an import:  from other_app.views import Home
    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
Including another URLconf
    1. Import the include() function: from django.urls import include, path
    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from hello import views # hello 앱의 views를 임포트 한 뒤
from django.urls import path, re_path # django.urls로 부터 re_path() 함수를 추가적으로 임포트 한다.
from index import views as index_views # index 모듈 내의 views를 index_views로 임포트한 후
from balance import views as balance_views # balance 모듈 내의 views를 balance_views로 임포트한 후

urlpatterns = [
    path('admin/', admin.site.urls),
    re_path(r'^(?P<name>[A-Z][a-z]*)$', views.sayHello), # urlpatterns 리스트의 마지막에 hello 앱의 URL에 대한 뷰 처리를 추가한다. 
    path('index/', index_views.main_view), # 제일 마지막 라인에 path() 함수를 추가해서 URLConf를 수정한다. URL이 'index/'이면 index 앱 뷰의 main_view() 함수로 매핑하라는 의미다. 
    path('balance/', balance_views.main_view) # URL이 balance인 경우 balance 앱 뷰의 main_view() 함수로 매핑 될 것이다. 
]
<file_sep># Arithmetic Operators
print(1 + 2) # 3
print(3 - 4) # -1
print(5 * 6) # 30
print(2 ** 8) # 2의 8승. pow(2, 8)와 동일함
print(5 / 3) # 1.6666666666666667
print(5 // 3) # 나눗셈 결과의 몫. 1
print(5 % 3) # 나눗셈 결과의 나머지. 2
<file_sep># BlockRequest() 함수를 사용해서 삼성전자(종목코드 005930)의 현재가와 전일대비 가격을 구하는 예는 다음과 같다. 
import win32com.client

# 주식마스터(StockMst) COM 객체를 생성한다. 
obj = win32com.client.Dispatch("DsCbo1.stockMst")
# SetInputValue() 함수로 조회할 데이터를 삼성전자로 지정한다. 
obj.SetInputValue(0, 'A005930')
# BlockRequest() 함수로 삼성전자에 대한 블록 데이터를 요청한다. 
obj.BlockRequest()
sec = {}
# GetHeaderValue() 함수로 현재가 정보(11)를 가져와서 sec 딕셔너리에 넣는다. 
sec['현재가'] = obj.GetHeaderValue(11)
# GetHeaderValue() 함수로 전일대비 가격변동 정보(12)를 가져와서 sec 딕셔너리에 넣는다. 
sec['전일대비'] = obj.GetHeaderValue(12)
<file_sep>def function():
    print('FUNCTION()')

print("MODULE_B:", __name__)<file_sep>import ctypes
import win32com.client
from slacker import Slacker
from datetime import datetime
import slack_config

# CREON Plus 공동 Object
cpStatus = win32com.client.Dispatch('CpUtil.CpCybos')      # 시스템 상태 정보
cpTradeUtil = win32com.client.Dispatch('CpTrade.CpTdUtil')  # 주문 관련 도구

# CREON Plus 시스템 점검 함수
def check_creon_system():
    # 관리자 권한으로 프로세스 실행 여부
    if not ctypes.windll.shell32.IsUserAnAdmin():
        print('check_creon_system() : admin user -> FAILED')
        return False
    
    # 연결 여부 체크
    if (cpStatus.IsConnect == 0):
        print('check_creon_system() : connect to server -> FAILED')
        return False
    
    # 주문 관련 초기화
    if (cpTradeUtil.TradeInit(0) != 0):
        print('check_creon_system() : init trade -> FAILED')
        return False
    
    return True

slack_token = slack_config.token
# 7장 장고 웹 서버 구축 및 자동화에서 발급한 토큰을 입력한다. 
slack = Slacker(slack_token)
def dbgout(message):
    # datetime.now() 함수로 현재 시간을 구한 후 [월/일 시:분:초] 형식으로 출력한 후 한 칸 띄우고 함수 호출 시 인수로 받은 message 문자열을 출력한다. 
    print(datetime.now().strftime('[%m/%d %H:%M:%S]'), message)
    strbuf = datetime.now().strftime('[%m/%d %H:%M:%S]') + message
    # etf-algo-trading 채널로 메세지를 보내려면 워크스페이스에 etf-algo-trading 채널을 미리 만들어 둬야 한다. 별도의 채널을 만들기 싫다면 #etf-algo-trading 대신 #general을 인수로 주어 일반 채널로 메시지를 보내도 된다. 
    slack.chat.post_message('#etf-algo-trading', strbuf)

cpStock = win32com.client.Dispatch("DsCbo1.StockMst") # 주식 종목별 정보

def get_current_price(code):
    cpStock.SetInputValue(0, code) # 종목코드에 대한 가격 정보
    cpStock.BlockRequest()

    item = {}
    item['cur_price'] = cpStock.GetHeaderValue(11) # 현재가
    item['ask'] = cpStock.GetHeaderValue(16) # 매수호가
    item['bid'] = cpStock.GetHeaderValue(17) # 매도호가

    return item['cur_price'], item['ask'], item['bid']<file_sep># 앞에서 셋이 다른 반복자료형 보다 검색 시간이 훨씬 빠르다고 했는데, 이 말이 사실인지 실제로 확인해 보자. 일반적으로 파이썬 프로그램 성능 측정에 표준 라이브러리 timeit을 사용한다. 
#timeit(테스트 구분, setup=테스트 준비 구문, number=테스트 반복 횟수)

# 순회 속도 비교하기
# setup 구문에서 0부터 9999까지 정수 1만개를 원소로 갖는 리스트, 튜플, 셋을 생성한 후, 각 반복 자료형 별로 모든 원소를 처음부터 끝까지 순회(for ~ in ~)하는 동작을 1000번 반복하는 데 걸릴 총 시간을 비교 해 보자. 
import timeit

iteration_test = """
for i in itr :
  pass
"""
# list
print(timeit.timeit(iteration_test, setup='itr = list(range(10000))', number=10000))
# tuple
print(timeit.timeit(iteration_test, setup='itr = tuple(range(10000))', number=10000))
# set
print(timeit.timeit(iteration_test, setup='itr = set(range(10000))', number=10000))

# 검색속도 비교하기
# 다음은 표준 라이브러리인 random 모듈의 randint() 함수를 이용하여 0 이상 9999 이하의 임의의 난수를 생성한 후, 0부터 9999까지 정수 1만 개로 구성된 반복 자료형에 존재하는지 검색(if ~ in ~)하는 코드다.

search_test = """
import random
x = random.randint(0, len(itr)-1)
if x in itr:
  pass
"""
#임의의 난수를 검색하는 작업을 1000 번씩 반복해서 수행한 결과, 셋의 검색 속도가 리스트나 튜플보다 월등히 빠른 것으로 나타났다.

# set
print(timeit.timeit(search_test, setup='itr = set(range(10000))', number=10000))
# list
print(timeit.timeit(search_test, setup='itr = list(range(10000))', number=10000))
#tuple
print(timeit.timeit(search_test, setup='itr = tuple(range(10000))', number=10000))<file_sep># 고가, 저가, 종가의 합을 3으로 나눠서 중심 가격 TP(Typical Price)를 구한다.
df['TP'] = (df['high'] + df['low'] + df['close']) / 3
df['PMF'] = 0
df['NMF'] = 0
# range 함수는 마지막 값을 포함하지 않으므로 0부터 종가 개수 -2까지 반복한다. 
for i in range(len(df.close)-1):
    
    # i번째 중심 가격보다 i+1번째 중심 가격이 높으면
    if df.TP.values[i] < df.TP.values[i+1]:
        # i+1번째 중심 가격과 i+1번째 거래량의 곱을 i+1번째 긍정적 현금 흐름 PMF(Positive Money Flow)에 저장한다.
        df.PMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
        # i+1번째 부정적 현금 흐름 NMF(Negative Money Flow)값은 0으로 저장한다. 
        df.NMF.values[i+1] = 0
    else:
        df.NMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
        df.PMF.values[i+1] =0
# 10일 동안의 긍정적 현금 흐름의 합을 10일 동안의 부정적 현금 흐름의 합으로 나눈 결과를 현금 흐름 비율 MFR(Money Flow Ratio) 칼럼에 저장한다.
df['MFR'] = df.PMF.rolling(window=10).sum() / df.NMF.rolling(window=10).sum()
# 10일 기준으로 현금흐름지수를 계산한 결과를 MFI10(Money Flow Index 10)칼럼에 저장한다. 
df['MFI10'] = 100 - 100 / (1 + df['MFR'])<file_sep># 최근 유전자 가위 기술(CRISPR)로 각광을 받는 미국 대표 기업들을 딕셔너리로 나타내면 다음과 같다. 
crispr = {'EDIT': 'Editas Medicine', 'NTLA': 'Intellia Therapeutics'}

# 순서가 없으므로 시퀀스 자료형들처럼 인덱스로 값에 접근하는 것은 불가능하다. 만일 다음처럼 인덱스 숫자로 원소에 접근하려고 하면, 인터프리터는 이를 키로 처리하기 때문에 키 에러가 발생한다. 

# print(crispr[1]) # 순서가 없으므로 인덱스 숫자로 접근할 수 없다. 

print(crispr['NTLA'])

# 원소를 추가하고 싶다면, 다음과 같이 키와 값을 함께 지정하여야 한다. 다음은 CRSP 키에 'CRISPR Therapeutics'를 값으로 넣는 예제다. 
crispr['CRSP'] = 'CRISPR Therapeutics'
print(crispr)

# CRSP 종목을 추가했으므로 총 원소 개수는 3개다. 
print(len(crispr))<file_sep># 리스트 복사
# 리스트는 문자열과 마찬가지로 인텍싱과 슬라이싱이 가능하며, len() 함수를 비롯한 여러 내장 함수를 사용할 수 있다. [:]를 사용하면 리스트를 복사할 수 있다. 
myList = ['Thoughts', 'become', 'things.']
newList = myList[:] # 전체 영역 슬라이싱 [:]은 새로운 리스트를 반환한다. 
print(newList)

# [:]를 사용하여 리스트를 복사한 경우, 새로운 리스트를 변경하더라도 기존 리스트는 변경되지 않는다. 
newList[-1] = 'actions.'
print(newList)
print(myList)

# 리스트 내포(comprehension)
# 내포 기능은 파이썬이 다른 언어들에 비해 얼마나 더 고수준 언어 인지를 보여준다. 내포 기능을 사용하면 리스트, 딕셔너리, 셋 같은 열거형 객체의 전체 또는 일부 원소를 변경하여 새로운 열거형 객체를 생성할 수 있다. 보통 다른 언어에서는 이런 작업을 for 반복문으로 처리하는데, 파이썬의 내포 기능을 사용하면 훨씬 간결하게 처리할 수 있다. 
# 다음은 for 반복문을 사용해서 리스트의 모든 원소에 대하여 제곱을 구하는 예제다. 
nums = [1, 2, 3, 4, 5]
squares = []
for x in nums:
  squares.append(x ** 2)
print(squares)

# 위의 코드는 리스트 내포를 사용하여 더 간단히 바꿀 수 있다. 
nums2 = [1, 2, 3, 4, 5]
squares2 = [x ** 2 for x in nums2]
print(squares2)

# 특히, 리스트에서 조건에 맞는 원소만 골라서 가공한 뒤 새로운 리스트로 생성할 때 편리하게 사용할 수 있으므로 익혀두자. 다음은 결과가 짝수일 때만 원소로 저장하는 코드다. 
nums3 = [1, 2, 3, 4, 5]
squares3 = [x ** 2 for x in nums3 if x % 2 == 0]
print(squares3)<file_sep>import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from Investar import Analyzer

mk = Analyzer.MarketDB()
stocks = ['삼성전자', 'SK하이닉스', '현대자동차', 'NAVER']
df = pd.DataFrame()
for s in stocks:
    df[s] = mk.get_daily_price(s, '2016-01-04', '2018-04-27')['close']

daily_ret = df.pct_change()
annual_ret = daily_ret.mean() * 252
daily_cov = daily_ret.cov()
annual_cov = daily_cov * 252

port_ret = []
port_risk = []
port_weights = []
sharpe_ratio = []

for _ in range(20000):
    weights = np.random.random(len(stocks))
    weights /= np.sum(weights)

    returns = np.dot(weights, annual_ret)
    risk = np.sqrt(np.dot(weights.T, np.dot(annual_cov, weights)))

    port_ret.append(returns)
    port_risk.append(risk)
    port_weights.append(weights)
    # 포트폴리오의 수익률을 리스크로 나눈 값을 샤프 지수 리스트에 추가한다.
    sharpe_ratio.append(returns/risk)

portfolio = {'Returns': port_ret, 'Risk': port_risk, 'Sharpe':sharpe_ratio}
for i, s in enumerate(stocks):
    portfolio[s] = [weights[i] for weight in port_weights]
df = pd.DataFrame(portfolio)
# 샤프 지수 칼럼을 데이터프레임에 추가한다. 생성된 데이터프레임은 다음과 같다. 
df = df[['Returns', 'Risk', 'Sharpe'] + [s for s in stocks]]

# 샤프 지수 칼럼에서 샤프 지숫값이 제일 큰 행을 max_sharpe로 정한다. 
max_sharpe = df.loc[df['Sharpe'] == df['Sharpe'].max()]
# 리스크 칼럼에서 리스크 값이 제일 작은 행을 min_risk로 정한다.
min_risk = df.loc[df['Risk'] == df['Risk'].min()]

# Return result looks wrong. Will need to be fixed. 
print(max_sharpe)
print(min_risk)

# 포트폴리오의 샤프 지수에 따라 컬러맵을 'viridis'로 표시하고 테두리는 검정(k)으로 표시한다. 
df.plot.scatter(x='Risk', y='Returns', c='Sharpe', cmap='viridis', edgecolors='k', figsize=(11, 7), grid=True)
# 샤프 지수가 가장 큰 포트폴리오를 300 크기의 붉은 별표(*)로 표시한다.
plt.scatter(x=max_sharpe['Risk'], y=max_sharpe['Returns'], c='r', marker='*', s=300)
# 리스크가 제일 작은 포트폴리오를 200 크기의 붉은 엑스표(x)로 표시한다. 
plt.scatter(x=min_risk['Risk'], y=min_risk['Returns'], c='r', marker='X', s=200)
plt.title('Portfolio Optimization')
plt.xlabel('Risk')
plt.ylabel('Expected Returns')
plt.show()

<file_sep>import matplotlib.pyplot as plt
from Investar import Analyzer

mk = Analyzer.MarketDB()
df = mk.get_daily_price('NAVER', '2019-01-02')

# 20개 종가를 이용해서 평균을 구한다.
df['MA20'] = df['close'].rolling(window=20).mean()
# 20개 종가를 이용해서 표준편차를 구한 뒤 stdde 칼럼으로 df에 추가한다. 
df['stddev'] = df['close'].rolling(window=20).std()
# 중간 볼린저 밴드 + (2 x 표준편차)를 상단 볼린저 밴드로 계산한다. 
df['upper'] = df['MA20'] + (df['stddev'] * 2)
# 중간 볼린저 밴드 - (2 x 표준편차)를 하단 볼린저 밴드로 계산한다. 
df['lower'] = df['MA20'] - (df['stddev'] * 2)
# (종가 - 하단 밴드) / (상단 밴드 - 하단 밴드)를 구해 %B 칼럼을 생성한다.
df['PB'] = (df['close'] - df['lower']) / (df['upper'] - df['lower'])
# (상단 밴드 - 하단 밴드) / 중간 밴드 X 100을 구해 bandwidth(밴드폭) 칼럼을 생성한다. 
# df['bandwidth'] = (df['upper'] - df['lower']) / df['MA20'] * 100

# 고가, 저가, 종가의 합을 3으로 나눠서 중심 가격 TP(Typical Price)를 구한다.
df['TP'] = (df['high'] + df['low'] + df['close']) / 3
df['PMF'] = 0
df['NMF'] = 0
# range 함수는 마지막 값을 포함하지 않으므로 0부터 종가 개수 -2까지 반복한다. 
for i in range(len(df.close)-1):
    
    # i번째 중심 가격보다 i+1번째 중심 가격이 높으면
    if df.TP.values[i] < df.TP.values[i+1]:
        # i+1번째 중심 가격과 i+1번째 거래량의 곱을 i+1번째 긍정적 현금 흐름 PMF(Positive Money Flow)에 저장한다.
        df.PMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
        # i+1번째 부정적 현금 흐름 NMF(Negative Money Flow)값은 0으로 저장한다. 
        df.NMF.values[i+1] = 0
    else:
        df.NMF.values[i+1] = df.TP.values[i+1] * df.volume.values[i+1]
        df.PMF.values[i+1] =0
# 10일 동안의 긍정적 현금 흐름의 합을 10일 동안의 부정적 현금 흐름의 합으로 나눈 결과를 현금 흐름 비율 MFR(Money Flow Ratio) 칼럼에 저장한다.
df['MFR'] = df.PMF.rolling(window=10).sum() / df.NMF.rolling(window=10).sum()
# 10일 기준으로 현금흐름지수를 계산한 결과를 MFI10(Money Flow Index 10)칼럼에 저장한다. 
df['MFI10'] = 100 - 100 / (1 + df['MFR'])
# 위는 19번째 행까지 NaN 이므로 값이 있는 20번째 행부터 사용한다.
df = df[19:]

plt.figure(figsize=(9, 8))
# 기존의 볼린저 밴드 차트를 2행 1열의 그리드에서 1열에 배치한다. 
plt.subplot(2, 1, 1)
# x좌표 df.index에 해당하는 종가를 y좌표로 설정해 파란색(#0000ff) 실선으로 표시한다.
plt.plot(df.index, df['close'], color='#0000ff', label='Close')
# x좌표 df.index에 해당하는 상단 볼린저 밴드값을 y좌표로 설정해 붉은 실선(r--)으로 표시한다.
plt.plot(df.index, df['upper'], 'r--', label='Upper band')
plt.plot(df.index, df['MA20'], 'k--', label='Moving average 20')
plt.plot(df.index, df['lower'], 'c--', label='Lower band')
# 상단 볼린저 밴드와 하단 볼린저 밴드 사이를 회색으로 칠한다.
plt.fill_between(df.index, df['upper'], df['lower'], color='0.9')
# plt.title('NAVER Bollinger Band (20 day, 2 std)')

for i in range(len(df.close)):
    # %b가 0.8 보다 크고 10일 기준 MFI가 80보다 크면
    if df.PB.values[i] > 0.8 and df.MFI10.values[i] > 80:
        # 매수 시점을 나타내기 위해 첫 번째 그래프가 종가 위치에 빨간색 삼각형을 표시한다.
        plt.plot(df.index.values[i], df.close.values[i], 'r^')
    # %b가 0.2보다 작고 10일 기준 MFI가 20보다 작으면
    elif df.PB.values[i] < 0.2 and df.MFI10.values[i] < 20:
        # 매도 시점을 나타내기 위해 첫 번째 그래프의 종가 위치에 파란색 삼각형을 표시한다. 
        plt.plot(df.index.values[i], df.close.values[i], 'bv')
plt.legend(loc='best')

# %B 차트를 2행 1열의 그리드에서 2열에 배치한다.
plt.subplot(2, 1, 2)
# x좌표 df.index에 해당하는 %b 값을 y좌표로 설정해 파란(b)실선으로 표시한다. 
# plt.plot(df.index, df['PB'], color='b', label='%B')
# x좌표 df.index에 해당하는 bandwidth값을 y좌표로 설정해 자홍(magenta) 실선으로 표시한다. 
# plt.plot(df.index, df['bandwidth'], color='m', label='Bandwidth') 

# MFI와 비교할 수 있게 %b를 그대로 표시하지 않고 100을 곱해서 푸른색 실선으로 표시한다. 
plt.plot(df.index, df['PB'] * 100, 'b', label='%B X 100')
# 10일 기준 MFI를 녹색의 점선으로 표시한다. 
plt.plot(df.index, df['MFI10'], 'g--', label='MFI(10 day')
# y축 눈금을 -20부터 120까지 20단위로 표시한다. 
plt.yticks([-20, 0, 20, 40, 60, 80, 100, 120])
for i in range(len(df.close)):
    if df.PB.values[i] > 0.8 and df.MFI10.values[i] > 80:
        plt.plot(df.index.values[i], 0, 'r^')
    elif df.PB.values[i] < 0.2 and df.MFI10.values[i] < 20:
        plt.plot(df.index.values[i], 0, 'bv')
        
plt.grid(True)
plt.legend(loc='best')
plt.show()
<file_sep>from datetime import datetime
import backtrader as bt

# bt.Strategy 클래스를 상속받아서 MyStrategy 클래스를 작성한다. 
class MyStrategy(bt.Strategy):
    def __init__(self):
        # RSI 지표를 사용하려면 MyStrategy 클래스 생성자에서 RSI 지표로 사용할 변수를 저장한다. 
        self.rsi = bt.indicators.RSI(self.data.close)
    # next() 메서드는 주어진 데이터와 지표(indicator)를 만족시키는 최소 주기마다 자동으로 호출된다. 시장에 참여하고 있지 않을 때 RSI가 30 미망니면 매수하고, 시장에 참여하고 있을 때 RSI가 70을 초과하면 매도하도록 구현한다. 
    def next(self):
        if not self.position:
            if self.rsi < 30:
                self.order = self.buy()
        else:
            if self.rsi > 70:
                self.order = self.sell()

# Cerebro 클래스는 백트레이더의 핵심 클래스로서, 데이터를 취합하고 백테스트 또는 라이브 트레이딩을 실행한 뒤 그 결과를 출력하는 기능을 담당한다. 
cerebro = bt.Cerebro()
cerebro.addstrategy(MyStrategy)
# 엔씨소프트(036570.KS)의 종가 데이터는 야후 파이낸스 데이터를 이용해서 취합한다. 
data = bt.feeds.YahooFinanceData(dataname='036570.KS', fromdate=datetime(2017, 1, 1), todate=datetime(2019, 12, 1))
cerebro.adddata(data)
# 초기 투자 자금을 천만 원으로 설정한다. 
cerebro.broker.setcash(10000000)
# 엔씨소프트 주식의 매매 단위는 30주로 설정한다. 보유한 현금에 비해 매수하려는 주식의 총 매수 금액(주가 * 매매 단위)이 크면 매수가 이루어지지 않음에 유의하자. 
cerebro.addsizer(bt.sizers.SizerFix, stake=30)

print(f'Initial Portfolio Value : {cerebro.broker.getvalue():,.0f} KRW')
# Cerebro 클래스로 백테스트를 실행한다. 
cerebro.run()
print(f'Final Portfolio Value : {cerebro.broker.getvalue():,.0f} KRW')
# 백테스트 결과를 차트로 출력한다. 
cerebro.plot()<file_sep># lambda는 이름 없는 간단한 함수를 만들 때 사용한다. 'lambda 인수 : 표현식'형태로 사용하며, 아래에 선언된 함수 객체와 비슷하게 동작한다. 
"""
def lambda ( 인수 ):
    return 표현식
"""

# 람다로 천 단위 숫자에 쉼표 삽입해 보자. 
insertComma = lambda x : format(x, ',')
print(insertComma(1234567890))
<file_sep>import matplotlib.pyplot as plt
import matplotlib.image as mpimg

dst_img = mpimg.imread('dst.png')
print(dst_img)

pseudo_img = dst_img [:, :, 0] # RGB 채널 중 첫 번째 채널(0)만 슬라이싱해서 저장한 관계로 부동소수점 데이터가 2차원으로 변경되었다.
print(pseudo_img)

# subplot() 함수로 두 이미지를 나란히 표시하자. 
plt.suptitle('Image Processing', fontsize=18)
plt.subplot(1, 2, 1) # 인수를 1, 2, 1 처럼 쉼표로 구분해 넘겨주면 1행 2열의 행렬에서 첫 번째(1) 그림을 설정하는 것이다. 
plt.title('Original Image')
plt.imshow(mpimg.imread('src.png'))

plt.subplot(122) # 인수를 쉼표 구분 없이 모두 붙여서 전달할 수도 있다. 예를 들어 1행 2열의 행렬에서 두 번째 그림을 설정할 때는 인수를 122처럼 숫자를 모두 붙여 넘겨줄 수도 있다. 

plt.title('Pseudocolor Image')
dst_img = mpimg.imread('dst.png')
pseudo_img = dst_img [:, :, 0]
plt.imshow(pseudo_img)
plt.show()<file_sep># 파이썬은 함수 호출이 끝나고 결과값을 반환할 때, 여러 결과값을 한꺼번에 반환할 수 있다. 여거 결과값은 기본적으로 튜플 객체로 변환되어 반환된다. 

def myFunc():
    var1 = 'a'
    var2 = [1, 2, 3]
    var3 = max
    return var1, var2, var3 # 여러 개의 결과값은 기본적으로 튜플 타입으로 반환된다. 

print(myFunc())

# 함수 결과값을 튜플 객체 하나로 받지 않고, 함수에서 반환한 순서대로 여러 객체로 나누어 받으려면 변수를 쉼표로 구분하여 받으면 된다. 
s, l, f = myFunc()
print(s)
print(l)
print(f)<file_sep># 함수를 정의할 때 반환값을 지정하지 않으면 None을 반환한다. 따라서 아래 세 함수는 모두 None을 반환한다. 참고로 다음처럼 세미콜론;을 사용하면 여러 줄 명령을 한 줄에 작성해 실행할 수 있다. 

def func1():
    pass

def func2():
    return

def func3():
    return None

print(func1()); print(func2()); print(func3())

# None은 NoneType 클래스의 객체이며, None이 반환되었는지 확인하려면 == 또는 is 연산자를 사용한다. 

print(type(None))
print(func1() == None)
print(func1() is None)<file_sep>from pandas_datareader import data as pdr
import yfinance as yf
yf.pdr_override()

sec = pdr.get_data_yahoo('005930.KS', start='2018-05-04')
sec_dpc = (sec['Close']-sec['Close'].shift(1)) / sec['Close'].shift(1) * 100
sec_dpc.iloc[0] = 0 # 일간 변동률의 첫 번째 값인 Nan을 0으로 변경한다. 
sec_dpc_cs = sec_dpc.cumsum() # 일간 변동률의 누적합을 구한다. 
print(sec_dpc_cs)

msft = pdr.get_data_yahoo('MSFT', start='2018-05-04')
msft_dpc= (msft['Close']-msft['Close'].shift(1)) / msft['Close'].shift(1) * 100
msft_dpc.iloc[0] = 0
msft_dpc_cs = msft_dpc.cumsum()
print(msft_dpc_cs)

import matplotlib.pyplot as plt
plt.plot(sec.index, sec_dpc_cs, 'b', label='Samsung Electronics')
plt.plot(msft.index, msft_dpc_cs, 'r--', label='Microsoft')
plt.ylabel('Change %')
plt.grid(True)
plt.legend(loc='best')
plt.show()<file_sep># Python 문자열의 인덱스
#  P  y  t  h  o  n
#  0  1  2  3  4  5
# -6 -5 -4 -3 -2 -1

word = "python"
print(word[0:6])
print(word[0:])
print(word[:6]) # 5번째 인덱스에 해당하는 문자(n)까지 표시된다. 
print(word[:-1]) # -2번째 인덱스에 해당하는 문자(o)까지 표시된다. <file_sep>import pandas as pd
import matplotlib.pyplot as plt
import datetime
from mplfinance.original_flavor import candlestick_ohlc
import matplotlib.dates as mdates
from Investar import Analyzer

mk = Analyzer.MarketDB()
df = mk.get_daily_price('엔씨소프트', '2017-01-01', '2020-12-01')

# 종가의 12주 지수 이동평균에 해당하는 60일 지수 이동평균을 구한다. 
ema60 = df.close.ewm(span=60).mean()
# 종가의 26주 지수 이동평균에 해당하는 130일 지수 이동평균을 구한다. 
ema130 = df.close.ewm(span=130).mean()
# 12주(60일) 지수 이동평균에서 26주(130일) 지수 이동평균을 빼서 MACD(Moving Average Convergence Divergence)선을 구한다. 
macd = ema60 - ema130
# MACD의 9주(45일) 지수 이동평균을 구해서 신호선으로 저장한다. 
signal = macd.ewm(span=45).mean()
# MACD선에서 신호선을 빼서 MACD 히스토그램을 구한다. 
macdhist = macd - signal

df = df.assign(ema130=ema130, ema60=ema60, macd=macd, signal=signal, macdhist=macdhist).dropna()
# 캔들 차트에 사용할 수 있게 날짜(date)형 인덱스를 숫자형을 변환한다. 
df['number'] = df.index.map(mdates.date2num)
ohlc = df[['number', 'open', 'high', 'low', 'close']]

plt.figure(figsize=(9, 7))
p1 = plt.subplot(2, 1, 1)
plt.title('Triple Screen Trading - First Screen (NCSOFT)')
plt.grid(True)
# ohlc의 숫자형 일자, 시가, 고가, 저가, 종가 값을 이용해서 캔들 차트를 그린다. 
candlestick_ohlc(p1, ohlc.values, width=6, colorup='red', colordown='blue')
p1.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.plot(df.number, df['ema130'], color='c', label='EMA130')
plt.legend(loc='best')

p2 = plt.subplot(2, 1, 2)
plt.grid(True)
p2.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.bar(df.number, df['macdhist'], color='m', label='MACD-Hist')
plt.plot(df.number, df['macd'], color='b', label='MACD')
plt.plot(df.number, df['signal'], 'g--', label='MACD-Signal')
plt.legend(loc='best')
plt.show()<file_sep>crispr = {'EDIT': 'Editas Medicine', 'NTLA': 'Intellia Therapeutics', 'CRSP': 'CRISPR Therapeutics'}
# % 기호 방식
for x in crispr:
  print('%s : %s' % (x, crispr[x])) # old school format

# {} 기호 방식
for x in crispr:
  print('{} : {}'.format(x, crispr[x])) # new school format

# f-string 방식
for x in crispr:
  print(f'{x} : {crispr[x]}') # brand new school format
  <file_sep>from slacker import Slacker
import slack_config

slack_token = slack_config.token
slack = Slacker(slack_token)

markdown_text = '''
This message is plain.
*This message is bold.*
'This message is code.'
_This message is italic._
~This message is strike.~
'''

attach_dict = {
    'color'         : '#ff0000',
    'author_name'   : 'INVESTAR',
    'author_link'   : 'github.com/yeonhodev',
    'title'         : '오늘의 증시 KOSPI',
    'title_link'    : 'http://finance.naver.com/sise/sise_index.nhn?code=KOSPI',
    'text'          : '2,326.13 △11.89 (+0.51%)',
    'image_url'     : 'https://ssl.pstatic.net/imgstock/chart3/day/KOSPI.png'
}

attach_list = [attach_dict]
slack.chat.post_message(channel="#general", text=markdown_text, attachments=attach_list)<file_sep>for i in [1, 2, 3]:
  print(i)

# range(시작값, 멈춤값, 증가값) : 멈춤값으로 주어진 수는 반복할 범위에 포함되지 않는다.
for i in range(1, 7, 2):
  print(i)

# enumerate([반복자료형], 인덱스의_시작값) : 각각의 반복과정에서 아이템 인덱스를 구할수 있어서 편리하다. 시작값을 생략하면 첫번째 인덱스는 0부터 시작한다.
FAANG = ['FB', 'AMZN', 'AAPL', 'NFLX', 'GOOGL']
for idx, symbol in enumerate(FAANG, 1):
  print(idx, symbol)<file_sep>import backtrader as bt
from datetime import datetime

class MyStrategy(bt.Strategy):
    def __init__(self):
        self.dataclose = self.datas[0].close
        self.order = None
        self.buyprice = None
        self.buycomm = None
        self.rsi = bt.indicators.RSI_SMA(self.data.close, period=21)
    
    # 기존 코드에 비해서 가장 큰 변화는 MyStrategy 클래스에 notify_order() 메서드가 추가되었다는 점이다. 이 메서드는 주문(order) 상태에 변화가 있을 때마다 자동으로 실행된다. 인수로 주문(order)를 객체를 넘겨 받는다. 주문 상태는 완료(Completed). 취소(Cancelled) 
    def notify_order(self, order):
        if order.status in [order.Submitted, order.Accepted]:
            return
        # 주문 상태가 완료(Completed)이면 매수인지 매도인지 확인하여 상세 주문 정보를 출력한다. 주문 처리 관련 코드는 기존과 같다. 단지 주문 상태를 출력해주는 기능을 추가했다. 
        if order.status in[order.Completed]:
            if order.isbuy():
                self.log(f'BUY : 주가 {order.executed.price:,.0f}, 수량 {order.executed.size:,.0f}, 수수료 {order.executed.comm:,.0f}, 자산 {cerebro.broker.getvalue():,.0f}')
                self.buyprice = order.executed.price
                self.buycomm = order.executed.comm
            else:
                self.log(f'SELL : 주가 {order.executed.price:,.0f}, 수량 {order.executed.size:,.0f}, 수수료 {order.executed.comm:,.0f}, 자산 {cerebro.broker.getvalue():,.0f}')
            self.bar_executed = len(self)
        elif order.status in [order.Cancelled]:
            self.log('ORDER CANCELLED')
        elif order.status in [order.Margin]:
            self.log('ORDER MARGIN')
        elif order.status in [order.Rejected]:
            self.log('ORDER REJECTED')
        self.order = None
    
    def next(self):
        if not self.position:
            if self.rsi < 30:
                self.order = self.buy()
        else:
            if self.rsi > 70:
                self.order = self.sell()

    # log() 메서드는 텍스트 메세지를 인수로 받아서 셀 화면에 주문 일자와 함께 출력하는 역할을 한다. 
    def log(self, txt, dt=None):
        dt = self.datas[0].datetime.date(0)
        print(f'[{dt.isoformat()}] {txt}')

cerebro = bt.Cerebro()
cerebro.addstrategy(MyStrategy)
data = bt.feeds.YahooFinanceData(dataname='036570.KS', fromdate=datetime(2017, 1, 1), todate=datetime(2019, 12, 1))
cerebro.adddata(data)
cerebro.broker.setcash(10000000)
# 수수료(commission)는 매수나 매도가 발생할 때마다 차감된다. 우리나라는 주식을 매도할 때 0.25%를 증권거래세로 내야 하고, 증권회사별로 다르긴 하지만 주식을 매수하거나 매도할 때 일반적으로 0.015%를 증권거래수수료로 내야 한다. 즉, 주식을 한 번 거래(매수/매도)할 때 대략 0.28% 비용이 소요된다. 백트레이더에서는 매수와 매도 시점마다 수수료가 동일 비율로 두 번 차감되므로, 0.28%를 2로 나누어 수수료를 0.14%로 설정했다. 
cerebro.broker.setcommission(commission=0.0014)
# 사이즈(size)는 매매 주문을 적용할 주식수를 나타내며, 특별히 지정하지 않으면 1이다. PercentSizer를 사용하면 포트폴리오 자산에 대한 퍼센트로 지정할 수 있는데, 100으로 지정하면 수수료를 낼 수 없어서 ORDER MARGIN이 발생하므로, 수수료를 차감한 퍼센트로 저장해야 한다. 
cerebro.addsizer(bt.sizers.PercentSizer, percents=90)

print(f'Initial Portfolio Value : {cerebro.broker.getvalue():,.0f} KRW')
cerebro.run()
print(f'Final Portfolio Value : {cerebro.broker.getvalue():,.0f} KRW')
# 주가를 표시할 때 캔들스틱 차트로 표시한다. 
cerebro.plot(style='candlestick')
