Stock Fraud Detection Using Peer Group Analysis

Stock Fraud Detection Using Peer Group Analysis

2020, Nov 23    
peer_group_analysis

금융공학 네트워킹 논문리딩 발표자료 - Stock fraud detection using peer group analysis

금융공학 네트워킹 논문리딩에서 발표한 자료와 코드 입니다. 코드는 해당 논문을 보면서 직접 만들어 보았습니다.



Import Modules

In [1]:
import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline

plt.rcParams["font.family"] = 'GULIM'
plt.rcParams["axes.grid"] = True
plt.rcParams["figure.figsize"] = (12,6)
plt.rcParams["axes.formatter.useoffset"] = False
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams["axes.formatter.limits"] = -10000, 10000

Read Data

# 데이터가 없다면 FinanceDataReader 설치하여 아래 코드를 통해 다운로드 가능
import FinanceDataReader as fdr
stocks = fdr.StockListing('KOSDAQ') # 코스닥
name_code = stocks[['Name','Symbol']]
for name, code in name_code.head().values:
    print(name,code)
df_list = [fdr.DataReader(code, '2016-05-01', '2019-09-30')['Close'] for name, code in name_code.values]
df = pd.concat(df_list, axis=1)
df.columns = name_code['Name']
df = df.dropna(axis=1) # drop nan
In [2]:
df = pd.read_pickle('kosdaq.pkl')
In [3]:
df.head()
Out[3]:
Name 3S APS홀딩스 AP위성 CJ ENM CJ프레시웨이 CMG제약 CNH CS CSA 코스믹 DMS ... 휴맥스 휴맥스홀딩스 휴메딕스 휴비츠 휴온스글로벌 휴젤 흥구석유 흥국 흥국에프엔비 희림
Date
2016-05-02 4240 14044 9050 196400 56500 4684 1495 1690 4992 9050 ... 13200 7380 44198 17500 55424 102816 3255 2348 5143 6010
2016-05-03 4330 14007 9100 196400 57300 4739 1510 1690 5012 9280 ... 13200 7380 47273 17800 55424 110045 3420 2400 5683 6760
2016-05-04 4115 14268 9090 196000 56100 4657 1485 1700 4894 9130 ... 13450 7370 47713 17350 55424 108442 3355 2390 5653 5800
2016-05-09 4085 14119 9010 198000 55600 4639 1455 1720 4649 9160 ... 13550 7300 47713 17200 55424 108374 3320 2363 5663 5560
2016-05-10 4130 14342 9010 199100 57100 4748 1475 1750 4742 9200 ... 13750 7320 48680 18000 55424 108920 3445 2363 5843 5690

5 rows × 1098 columns

Preprocessing

논문에서 분석 기간의 종가의 최솟값이 5천원 이상인 종목들을 필터링 하였는데 사실 왜 했는지 모르겠다.... 시가 총액도 아니고 종가를 기준으로?? 일단 비슷하게 만원 이상인 종목들을 날려보자

classify a company as a global outlier if the minimum daily closing price since 2004 is greater than m=5000

In [4]:
(df.min() >= 10000).sum()
Out[4]:
128
In [5]:
df = df[df.columns[df.min() < 10000]]
df.head()
Out[5]:
Name 3S APS홀딩스 AP위성 CMG제약 CNH CS CSA 코스믹 DMS EG EMW ... 휘닉스소재 휴림로봇 휴마시스 휴맥스 휴맥스홀딩스 휴비츠 흥구석유 흥국 흥국에프엔비 희림
Date
2016-05-02 4240 14044 9050 4684 1495 1690 4992 9050 10300 2780 ... 1545 1065 2519 13200 7380 17500 3255 2348 5143 6010
2016-05-03 4330 14007 9100 4739 1510 1690 5012 9280 10350 2810 ... 1570 1095 2468 13200 7380 17800 3420 2400 5683 6760
2016-05-04 4115 14268 9090 4657 1485 1700 4894 9130 10350 2830 ... 1660 1063 2475 13450 7370 17350 3355 2390 5653 5800
2016-05-09 4085 14119 9010 4639 1455 1720 4649 9160 10200 2800 ... 1725 1019 2475 13550 7300 17200 3320 2363 5663 5560
2016-05-10 4130 14342 9010 4748 1475 1750 4742 9200 10250 2925 ... 1665 1023 2475 13750 7320 18000 3445 2363 5843 5690

5 rows × 970 columns

Train, Test 분리

논문과 같이 40개월 코스닥 데이터를 사용했으며 18개월(2016-05-01 ~ 2017-10-31)을 트레이닝 셋으로 사용

In [6]:
train_df = df[:'2017-10-31']
len(train_df)
Out[6]:
368
In [7]:
train_df = train_df.iloc[3:]
len(train_df)
Out[7]:
365

11월 부터 테스트 셋

In [8]:
test_df = df['2017-10-31':]
len(test_df)
Out[8]:
470

Smoothing the dataFrame

5일씩 Non overlap 하게 짤라서 평균을 내어 smoothing

In [9]:
def smooth_df(df):
    D = len(df) # 365
    N = int(D/5) # 73
    window_length = int(D/N) # 5
    print(D, N, window_length)
    df_smoothed = pd.DataFrame(columns = df.columns, index=range(N))
    for i in range(N):
        start_iloc = i * window_length 
        end_iloc = start_iloc + window_length
        y_i = df.iloc[start_iloc:end_iloc].mean(axis=0)
        df_smoothed.iloc[i] = y_i
    return df_smoothed
In [10]:
train_df
Out[10]:
Name 3S APS홀딩스 AP위성 CMG제약 CNH CS CSA 코스믹 DMS EG EMW ... 휘닉스소재 휴림로봇 휴마시스 휴맥스 휴맥스홀딩스 휴비츠 흥구석유 흥국 흥국에프엔비 희림
Date
2016-05-09 4085 14119 9010 4639 1455 1720 4649 9160 10200 2800 ... 1725 1019 2475 13550 7300 17200 3320 2363 5663 5560
2016-05-10 4130 14342 9010 4748 1475 1750 4742 9200 10250 2925 ... 1665 1023 2475 13750 7320 18000 3445 2363 5843 5690
2016-05-11 4220 14305 8980 4676 1485 1735 4796 9220 10200 2900 ... 1720 1035 2475 13550 7270 17900 3455 2350 5843 5610
2016-05-12 4175 14939 9020 4684 1470 1910 4757 9690 10200 2915 ... 1750 1035 2475 14000 7280 17650 3410 2328 5763 5370
2016-05-13 4040 14752 8950 4540 1495 1885 4674 10300 10300 3200 ... 1800 1069 2475 13400 7180 17650 3435 2360 5713 5170
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
2017-10-25 1945 10150 8030 3252 1390 1420 3923 8480 10000 2940 ... 924 2075 1640 9340 4695 13400 2790 3650 1959 4655
2017-10-26 1935 9820 8000 3228 1385 1405 4021 8310 9900 2800 ... 919 2015 1640 9290 4670 13300 2800 3650 1951 4575
2017-10-27 1955 10100 7910 3290 1380 1405 4115 8480 10000 2795 ... 942 2025 1630 9260 4685 13700 2810 3675 1959 4615
2017-10-30 1915 10100 8020 3261 1380 1435 4286 8710 10400 2805 ... 956 2080 1620 9360 4730 13700 2815 3695 1961 4610
2017-10-31 1915 9970 8030 3276 1390 1495 4497 8760 10800 2910 ... 966 2085 1620 9420 4700 13650 2820 3625 1965 4605

365 rows × 970 columns

In [11]:
train_df_smoothed = smooth_df(train_df)
train_df_smoothed
365 73 5
Out[11]:
Name 3S APS홀딩스 AP위성 CMG제약 CNH CS CSA 코스믹 DMS EG EMW ... 휘닉스소재 휴림로봇 휴마시스 휴맥스 휴맥스홀딩스 휴비츠 흥구석유 흥국 흥국에프엔비 희림
0 4130 14491.4 8994 4657.4 1476 1800 4723.6 9514 10230 2948 ... 1732 1036.2 2475 13650 7270 17680 3413 2352.8 5765 5480
1 3755 13895.2 7584 5003.4 1439 1769 4478.6 10050 10058 3098 ... 1830 1044 2481 12740 7190 16670 3368 2289.2 5305 5134
2 3616 14007.2 7482 6183.6 1373 1885 4309.8 9900 9578 3256 ... 1606 961.6 2480.8 12230 7088 16460 3248 2311.2 4855 5112
3 3516 14551.2 7280 7133.2 1422 1852 4312.6 9984 9714 3359 ... 1466 944.8 2462 12680 7122 16490 3233 2324.2 4843 5228
4 3690 13850.6 7288 7086.6 1396 1986 4509.8 9586 9754 3283 ... 1433 978 2458.4 12820 7132 16890 3221 2297.2 4929 5250
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
68 1943 9580 8140 3317.4 1414 1324 3336.6 8398 12030 2231 ... 998.2 2086 2086 9708 4732 12900 2669 3631 1975.4 4773
69 1906 9534 7994 3165.4 1406 1292 3599.6 8324 11080 2371 ... 957 1890 1884 9284 4564 12860 2649 3481 1939.4 4595
70 1950 9552 8004 3183.2 1399 1442 4076.2 8150 10640 2910 ... 927.8 2067 1834 9512 4699 13010 2749 3518 1913.8 4546
71 1914 9398 7996 3156.4 1391 1437 4014.6 8110 10190 2748 ... 923 2047 1623 9450 4643 13410 2720 3546 1917.8 4541
72 1933 10028 7998 3261.4 1385 1432 4168.4 8548 10220 2850 ... 941.4 2056 1630 9334 4696 13550 2807 3659 1959 4612

73 rows × 970 columns

Normalize

In [12]:
def normalize_df(df):
    return (df - df.mean())/df.std()
In [13]:
train_df_normalized = normalize_df(train_df_smoothed)
train_df_normalized
Out[13]:
Name 3S APS홀딩스 AP위성 CMG제약 CNH CS CSA 코스믹 DMS EG EMW ... 휘닉스소재 휴림로봇 휴마시스 휴맥스 휴맥스홀딩스 휴비츠 흥구석유 흥국 흥국에프엔비 희림
0 2.37126 -0.223349 0.721973 1.02083 -0.30483 -0.458186 0.530776 0.0613452 0.137598 0.221694 ... 1.18643 -1.11287 -0.02492 0.841845 0.846127 2.79417 2.05376 -1.54273 3.32221 0.867475
1 1.72743 -0.382308 -1.5657 1.38439 -0.716091 -0.554522 0.168019 0.589583 0.065545 0.697778 ... 1.53267 -1.10042 -0.000303491 0.230202 0.753371 2.05632 1.78518 -1.61336 2.77254 -0.107894
2 1.48878 -0.352446 -1.73119 2.62448 -1.44969 -0.194036 -0.0819141 0.441755 -0.135532 1.19925 ... 0.741256 -1.23199 -0.00112404 -0.112587 0.635108 1.90291 1.06895 -1.58893 2.23482 -0.169911
3 1.31709 -0.207405 -2.05892 3.62227 -0.905049 -0.296588 -0.0777683 0.524539 -0.0785599 1.52616 ... 0.246621 -1.25882 -0.0782558 0.189874 0.674529 1.92483 0.979418 -1.57449 2.22048 0.157091
4 1.61583 -0.394199 -2.04594 3.57331 -1.19404 0.119836 0.214215 0.132302 -0.0618035 1.28495 ... 0.130028 -1.2058 -0.0930258 0.283973 0.686123 2.21704 0.907796 -1.60448 2.32325 0.219108
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
68 -1.38358 -1.53282 -0.663608 -0.387173 -0.993971 -1.93742 -1.52288 -1.03849 0.891635 -2.05399 ... -1.40617 0.563401 -1.62089 -1.80771 -2.09655 -0.697817 -2.38686 -0.12324 -1.20612 -1.12555
69 -1.4471 -1.54509 -0.900488 -0.546887 -1.08289 -2.03687 -1.13347 -1.11142 0.493671 -1.60964 ... -1.55173 0.250437 -2.44965 -2.0927 -2.29134 -0.727039 -2.50623 -0.289821 -1.24914 -1.62733
70 -1.37156 -1.54029 -0.884263 -0.528184 -1.1607 -1.57072 -0.427792 -1.2829 0.309351 0.101086 ... -1.6549 0.533063 -2.65479 -1.93945 -2.13481 -0.617458 -1.90937 -0.248731 -1.27973 -1.76546
71 -1.43337 -1.58135 -0.897243 -0.556344 -1.24962 -1.58626 -0.519 -1.32232 0.120841 -0.413084 ... -1.67186 0.501128 -3.52047 -1.98112 -2.19974 -0.325241 -2.08246 -0.217636 -1.27495 -1.77955
72 -1.40075 -1.41338 -0.893998 -0.446015 -1.31631 -1.6018 -0.291277 -0.890666 0.133408 -0.0893472 ... -1.60685 0.515498 -3.49175 -2.05909 -2.13829 -0.222965 -1.56319 -0.0921445 -1.22572 -1.5794

73 rows × 970 columns

Target 선정

target1 : 네이처셀
test period에 주가 조작 이슈 있었음
https://www.hankyung.com/finance/article/2018061279391

In [14]:
df['네이처셀'].plot(title='네이처셀 종가'); # target1
span_start = test_df.index[0]
span_end = test_df.index[-1]
plt.axvspan(span_start, span_end, facecolor='gray', alpha=0.3);

target2 : 제일제강
test period 에 보물선 관련 주가조작 이슈 있었음
https://news.joins.com/article/22919304

In [15]:
df['제일제강'].plot(title='제일제강 종가'); # target2
span_start = test_df.index[0]
span_end = test_df.index[-1]
plt.axvspan(span_start, span_end, facecolor='gray', alpha=0.3);

네이처셀로 진행

In [16]:
target_series = train_df_normalized['네이처셀'] # X_i

Train 기간 동안 Peer Group 선정

In [17]:
def calc_euc_dist(series): # Euclidean Distance
    return np.sqrt(((target_series - series) ** 2).sum())
In [18]:
dist = train_df_normalized.apply(lambda x : calc_euc_dist(x), axis=0)
dist.head()
Out[18]:
Name
3S        11.700914
APS홀딩스    15.735540
AP위성      14.869663
CMG제약      8.075605
CNH       13.190733
dtype: float64
In [19]:
dist_sorted = dist.sort_values()[1:] # 자기 자신 제외
dist_sorted
Out[19]:
Name
엔에스         5.345096
SM C&C      5.412384
스킨앤스킨       5.456172
경남제약        5.711724
시노펙스        5.830698
             ...    
케이피엠테크     15.989271
에스에이티      16.016126
코디엠        16.024772
유니온커뮤니티    16.080493
넥스턴        16.278828
Length: 969, dtype: float64
In [20]:
dist_sorted.describe()
Out[20]:
count    969.000000
mean      11.942634
std        2.230288
min        5.345096
25%       10.311152
50%       12.077649
75%       13.682500
max       16.278828
dtype: float64

거리가 가까운 k(=50)개의 peer 선택

In [21]:
k = 50
top_k_peers_distance = dist_sorted.head(k)
top_k_peers_distance
Out[21]:
Name
엔에스           5.345096
SM C&C        5.412384
스킨앤스킨         5.456172
경남제약          5.711724
시노펙스          5.830698
SBI핀테크솔루션즈    5.933813
디티앤씨          6.337951
신성델타테크        6.443471
에프앤리퍼블릭       6.488208
ISC           6.502550
케이엘넷          6.553080
엠케이전자         6.553721
KMH           6.643468
갤럭시아머니트리      6.737118
양지사           6.811462
티사이언티픽        6.858392
피에스텍          6.981878
성창오토텍         6.993433
와이지-원         7.071417
피앤이솔루션        7.086791
엘아이에스         7.117580
넥슨지티          7.223210
테크윙           7.233212
모다이노칩         7.237018
NHN한국사이버결제    7.262490
코웰패션          7.315591
바이오톡스텍        7.338416
매커스           7.350338
원익            7.373758
기가레인          7.376317
엘오티베큠         7.382457
드림시큐리티        7.383900
신라섬유          7.429271
내츄럴엔도텍        7.438572
웰크론한텍         7.560494
알엔투테크놀로지      7.602668
아이톡시          7.615776
아이진           7.623052
유니슨           7.742869
아이에스이커머스      7.787082
멕아이씨에스        7.843576
아이씨케이         7.848960
티에스이          7.854217
에코프로          7.883812
상아프론테크        7.899212
서울제약          7.902802
드림어스컴퍼니       7.929727
나노스           7.932388
이엠텍           7.940515
덕산하이메탈        7.959457
dtype: float64
In [22]:
# target
target_series.plot();
In [23]:
# Peer Group
top_k_peers = top_k_peers_distance.index
train_df_normalized[top_k_peers].plot(figsize=(12,7));

Peer Group Summary 계산

  • Simple Mean : 단순 평균
  • Weighted Mean : 논문에서 새로 고안한 weight를 부여

In [24]:
top_k_values = train_df_normalized[top_k_peers] # y_i_pi(j)
top_k_values
Out[24]:
Name 엔에스 SM C&C 스킨앤스킨 경남제약 시노펙스 SBI핀테크솔루션즈 디티앤씨 신성델타테크 에프앤리퍼블릭 ISC ... 멕아이씨에스 아이씨케이 티에스이 에코프로 상아프론테크 서울제약 드림어스컴퍼니 나노스 이엠텍 덕산하이메탈
0 1.1638 0.499601 2.33647 0.606659 -0.0447967 0.566178 1.87953 0.708815 2.01406 0.47472 ... 0.919971 0.207765 -0.529407 -0.258113 0.428211 2.15948 0.0278291 1.09631 -0.612013 0.843518
1 0.746059 0.491901 1.75618 0.678126 0.168268 0.298105 1.86334 0.200475 1.49508 0.581346 ... 0.456027 -0.160845 -0.582433 -0.338121 0.303216 1.61916 -0.21691 0.582353 -0.289835 0.628112
2 0.735348 0.4688 1.29988 0.842801 0.238655 0.138854 2.26279 0.140671 1.12927 0.532779 ... 0.331757 0.143191 -0.635459 -0.403717 0.460352 1.71422 0.0305893 -0.484939 -0.0345244 0.680332
3 0.925472 0.535535 2.24719 0.7246 0.187292 -0.159742 2.40314 -0.0213003 1.07871 0.769456 ... 0.547159 1.44274 -0.530967 -0.338121 0.528207 1.70922 0.0131079 -0.512314 -0.156101 0.755398
4 0.992417 0.584303 2.00416 0.822233 0.0712474 0.067191 2.02528 -0.111007 1.2066 0.943379 ... 1.24515 1.90014 -0.503674 -0.352534 0.228219 1.80927 -0.0899403 -0.512314 -0.271599 0.314794
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
68 1.43693 1.01552 0.922936 0.557712 3.54686 1.8163 0.71355 2.74218 0.913652 1.17304 ... -0.612699 2.20687 2.19754 2.42985 1.53531 -0.171881 1.42174 2.62497 1.14477 0.301739
69 1.00045 1.19262 1.00229 0.83473 1.75674 1.63316 0.400462 1.48628 0.637063 1.04183 ... -0.894379 2.13153 2.12346 2.31981 1.14247 -0.557106 1.05003 1.90217 1.72833 0.0504314
70 0.949572 1.38256 0.466638 1.62842 1.7225 1.89593 0.146753 1.44391 0.641524 0.774403 ... -0.873667 2.13153 1.51912 1.94271 0.856764 -0.617141 0.902819 1.91139 1.96541 0.347431
71 1.45836 1.14642 0.759264 2.07467 1.48661 1.76587 0.0441902 1.53861 0.651934 0.217648 ... -1.25891 2.50821 1.46843 1.84341 0.531778 -0.417024 0.678321 2.04058 2.12346 0.758661
72 1.14505 1.36459 0.511276 1.9553 1.90322 1.66236 -0.128548 1.99462 1.15307 0.0135343 ... -1.36868 3.35575 1.88952 1.46631 0.546063 -0.311963 0.860495 1.89601 2.25111 0.569365

73 rows × 50 columns

In [25]:
gamma = 10
prox = np.exp(-gamma * top_k_peers_distance) 
weight = prox / prox.sum()
weight
Out[25]:
Name
엔에스           5.331122e-01
SM C&C        2.720142e-01
스킨앤스킨         1.755595e-01
경남제약          1.363243e-02
시노펙스          4.148387e-03
SBI핀테크솔루션즈    1.479296e-03
디티앤씨          2.599598e-05
신성델타테크        9.049787e-06
에프앤리퍼블릭       5.785590e-06
ISC           5.012600e-06
케이엘넷          3.024225e-06
엠케이전자         3.004889e-06
KMH           1.224785e-06
갤럭시아머니트리      4.801143e-07
양지사           2.282832e-07
티사이언티픽        1.427776e-07
피에스텍          4.153041e-08
성창오토텍         3.699843e-08
와이지-원         1.696295e-08
피앤이솔루션        1.454563e-08
엘아이에스         1.069106e-08
넥슨지티          3.717686e-09
테크윙           3.363853e-09
모다이노칩         3.238234e-09
NHN한국사이버결제    2.510049e-09
코웰패션          1.475934e-09
바이오톡스텍        1.174742e-09
매커스           1.042711e-09
원익            8.249954e-10
기가레인          8.041529e-10
엘오티베큠         7.562650e-10
드림시큐리티        7.454261e-10
신라섬유          4.735449e-10
내츄럴엔도텍        4.314878e-10
웰크론한텍         1.274881e-10
알엔투테크놀로지      8.361954e-11
아이톡시          7.334699e-11
아이진           6.819975e-11
유니슨           2.057899e-11
아이에스이커머스      1.322538e-11
멕아이씨에스        7.517253e-12
아이씨케이         7.123198e-12
티에스이          6.758446e-12
에코프로          5.027085e-12
상아프론테크        4.309571e-12
서울제약          4.157614e-12
드림어스컴퍼니       3.176232e-12
나노스           3.092814e-12
이엠텍           2.851411e-12
덕산하이메탈        2.359368e-12
dtype: float64
In [26]:
p_i_simplemean = top_k_values.apply(lambda x : x.mean(), axis=1) # simple mean
In [27]:
p_i_weighted = top_k_values.apply(lambda x : x.dot(weight.T), axis=1) # weighted mean
p_i_weighted
Out[27]:
0     1.175515
1     0.850294
2     0.760501
3     1.044063
4     1.051533
        ...   
68    1.229379
69    1.054846
70    0.996407
71    1.259691
72    1.108425
Length: 73, dtype: float64

Peer group summary plot

In [28]:
p_i_weighted.plot(label='weighted');
p_i_simplemean.plot(label='simple');
target_series.plot();
plt.legend();

Peer group updates

In [29]:
weight
Out[29]:
Name
엔에스           5.331122e-01
SM C&C        2.720142e-01
스킨앤스킨         1.755595e-01
경남제약          1.363243e-02
시노펙스          4.148387e-03
SBI핀테크솔루션즈    1.479296e-03
디티앤씨          2.599598e-05
신성델타테크        9.049787e-06
에프앤리퍼블릭       5.785590e-06
ISC           5.012600e-06
케이엘넷          3.024225e-06
엠케이전자         3.004889e-06
KMH           1.224785e-06
갤럭시아머니트리      4.801143e-07
양지사           2.282832e-07
티사이언티픽        1.427776e-07
피에스텍          4.153041e-08
성창오토텍         3.699843e-08
와이지-원         1.696295e-08
피앤이솔루션        1.454563e-08
엘아이에스         1.069106e-08
넥슨지티          3.717686e-09
테크윙           3.363853e-09
모다이노칩         3.238234e-09
NHN한국사이버결제    2.510049e-09
코웰패션          1.475934e-09
바이오톡스텍        1.174742e-09
매커스           1.042711e-09
원익            8.249954e-10
기가레인          8.041529e-10
엘오티베큠         7.562650e-10
드림시큐리티        7.454261e-10
신라섬유          4.735449e-10
내츄럴엔도텍        4.314878e-10
웰크론한텍         1.274881e-10
알엔투테크놀로지      8.361954e-11
아이톡시          7.334699e-11
아이진           6.819975e-11
유니슨           2.057899e-11
아이에스이커머스      1.322538e-11
멕아이씨에스        7.517253e-12
아이씨케이         7.123198e-12
티에스이          6.758446e-12
에코프로          5.027085e-12
상아프론테크        4.309571e-12
서울제약          4.157614e-12
드림어스컴퍼니       3.176232e-12
나노스           3.092814e-12
이엠텍           2.851411e-12
덕산하이메탈        2.359368e-12
dtype: float64

Peer Group Summary Update

테스트 기간을 돌면서 대푯값을 업데이트 해줘야 함

Test Set Smoothing and Normalizing

In [30]:
len(test_df)
Out[30]:
470
In [31]:
test_df_smoothed = smooth_df(test_df) # smoothing
test_df_smoothed
470 94 5
Out[31]:
Name 3S APS홀딩스 AP위성 CMG제약 CNH CS CSA 코스믹 DMS EG EMW ... 휘닉스소재 휴림로봇 휴마시스 휴맥스 휴맥스홀딩스 휴비츠 흥구석유 흥국 흥국에프엔비 희림
0 1952 10030 7996 3722.2 1418 1450 4589.2 8626 10830 2765 ... 970.6 2107 1718 9304 4668 13400 2824 3624 1934.6 4620
1 2010 10310 7856 5292.6 1413 1439 4627.6 8428 10600 2652 ... 959.8 2161 1828 9268 4639 13940 2829 3567 1988.2 4654
2 1934 9800 7640 6162.4 1393 1498 4091 8308 12270 2633 ... 992.4 2057 2085 9246 4693 13920 2759 3631 2037 4825
3 1909 9060 7632 6469.4 1384 1706 3981.2 8160 13890 2882 ... 959.8 2015 2511 9064 4846 13770 2751 3718 2057 4769
4 1876 8818 7740 7760.6 1376 1740 4131.2 7216 12780 2881 ... 926.4 2193 2405 9340 4885 14030 2740 3604 2008.2 4673
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
89 3089 8936 7202 2741 1386 3013 4090 4776 9302 2780 ... 625.6 846.4 1421 5230 3306 9238 4923 4491 1823.8 4243
90 3125 9890 7116 2882 1442 3395 4090 5012 9218 2780 ... 649 880.2 1479 5694 3587 9358 5068 4705 1830.6 4377
91 3071 11060 7084 2876 1475 3507 4090 5208 9608 2780 ... 693.6 892.4 1479 5544 3532 9510 5313 4921 1763.4 4464
92 3444 10230 7018 2953 1541 3522 4090 5324 9782 2780 ... 716.4 926.6 1537 5526 3502 9516 7444 5032 1752.2 4601
93 3620 9778 6704 2809 1495 3361 4090 5354 9628 2780 ... 718.2 910 1486 5350 3388 9196 6558 4910 1725.4 4511

94 rows × 970 columns

In [32]:
# train set 의 mean 과 std 사용하여 normalize
mean = train_df_smoothed.mean()
std = train_df_smoothed.std()
In [33]:
test_df_normalized = (test_df_smoothed - mean)/std
test_df_normalized
Out[33]:
Name 3S APS홀딩스 AP위성 CMG제약 CNH CS CSA 코스믹 DMS EG EMW ... 휘닉스소재 휴림로봇 휴마시스 휴맥스 휴맥스홀딩스 휴비츠 흥구석유 흥국 흥국에프엔비 희림
0 -1.36813 -1.41284 -0.897243 0.0381695 -0.94951 -1.54586 0.331778 -0.813795 0.388943 -0.359128 ... -1.50368 0.596933 -3.13071 -2.07926 -2.17076 -0.332546 -1.46173 -0.131013 -1.25487 -1.55685
1 -1.26855 -1.33819 -1.12439 1.68826 -1.00509 -1.58004 0.388635 -1.00893 0.292594 -0.717778 ... -1.54184 0.683158 -2.6794 -2.10345 -2.20438 0.0619459 -1.43189 -0.194314 -1.19083 -1.46101
2 -1.39903 -1.47417 -1.47484 2.60221 -1.22739 -1.39669 -0.405879 -1.12719 0.992173 -0.778082 ... -1.42666 0.517095 -1.62499 -2.11824 -2.14177 0.0473351 -1.84969 -0.12324 -1.13251 -0.97896
3 -1.44195 -1.67146 -1.48782 2.92478 -1.32743 -0.750304 -0.568454 -1.27305 1.67081 0.0122173 ... -1.54184 0.450031 0.122779 -2.24057 -1.96438 -0.0622461 -1.89743 -0.0266226 -1.10861 -1.13682
4 -1.49861 -1.73599 -1.31259 4.28151 -1.41635 -0.644644 -0.346357 -2.20338 1.20582 0.00904342 ... -1.65985 0.734254 -0.312113 -2.05506 -1.91916 0.127695 -1.96309 -0.153224 -1.16693 -1.40745
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
89 0.583978 -1.70452 -2.18548 -0.992825 -1.3052 3.31138 -0.40736 -4.60804 -0.251151 -0.31152 ... -2.72261 -1.41594 -4.34922 -4.81753 -3.74993 -3.37306 11.0663 0.831826 -1.38727 -2.61961
90 0.645786 -1.45017 -2.32501 -0.844669 -0.682746 4.4985 -0.40736 -4.37546 -0.286339 -0.31152 ... -2.63993 -1.36197 -4.11126 -4.50566 -3.42412 -3.28539 11.9318 1.06948 -1.37915 -2.24187
91 0.553074 -1.13823 -2.37693 -0.850974 -0.315945 4.84656 -0.40736 -4.1823 -0.122964 -0.31152 ... -2.48235 -1.34249 -4.11126 -4.60648 -3.48789 -3.17435 13.3941 1.30936 -1.45945 -1.99661
92 1.19347 -1.35952 -2.48401 -0.770066 0.417656 4.89317 -0.40736 -4.06798 -0.0500741 -0.31152 ... -2.4018 -1.28788 -3.8733 -4.61858 -3.52267 -3.16997 26.1131 1.43263 -1.47283 -1.61041
93 1.49565 -1.48003 -2.99346 -0.921374 -0.0936415 4.39284 -0.40736 -4.03841 -0.114586 -0.31152 ... -2.39544 -1.31438 -4.08255 -4.73688 -3.65485 -3.40374 20.8249 1.29714 -1.50485 -1.86412

94 rows × 970 columns

In [34]:
test_target_series = test_df_normalized[target_series.name]
test_target_series
Out[34]:
0     2.60303
1     3.82391
2     7.63144
3      10.348
4      15.176
       ...   
89    2.38505
90    2.10734
91      2.799
92    2.81996
93    2.59726
Name: 네이처셀, Length: 94, dtype: object
In [35]:
test_df_normalized[top_k_peers]
Out[35]:
Name 엔에스 SM C&C 스킨앤스킨 경남제약 시노펙스 SBI핀테크솔루션즈 디티앤씨 신성델타테크 에프앤리퍼블릭 ISC ... 멕아이씨에스 아이씨케이 티에스이 에코프로 상아프론테크 서울제약 드림어스컴퍼니 나노스 이엠텍 덕산하이메탈
0 1.19325 1.31839 0.42696 1.85884 2.25136 2.10826 -0.268898 2.14662 1.58728 -0.239348 ... -1.23198 4.31763 2.17415 1.67298 0.64963 -0.171881 1.01691 1.95138 2.49427 0.647695
1 1.51191 1.23626 0.397202 2.51155 2.39974 2.88329 -0.342311 2.53037 2.3427 -0.450832 ... -1.25062 2.71001 2.10786 2.04202 0.696057 0.248364 0.985625 2.11747 2.04443 1.37877
2 1.53066 2.05248 0.377363 2.34127 3.05415 2.72138 -0.51289 2.797 1.96499 -0.307403 ... -1.37903 3.98131 2.90326 2.35335 0.828194 0.638592 0.992986 2.38199 3.13862 0.918584
3 1.48513 1.90361 0.144254 2.53433 3.96538 4.30858 -0.56903 4.53133 1.57985 -0.0278639 ... -1.58201 4.997 2.90716 2.68214 1.0889 0.438475 1.3739 2.29279 4.13555 0.308266
4 1.43426 1.64694 -0.212848 2.90898 4.80052 4.31124 -0.727733 4.92006 1.849 -0.266106 ... -1.6835 3.84678 2.25603 2.61504 0.796052 0.253367 1.34077 2.05595 3.73435 0.0895962
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
89 -1.50705 -3.0271 -4.33937 5.90605 2.49676 7.81742 -1.40249 -1.00808 -1.88466 -3.08805 ... -3.37047 -2.76533 -1.3287 0.325642 1.42103 -2.68635 0.472224 4.90104 -1.38524 -4.55796
90 -1.18089 -2.94497 -4.08543 5.90605 2.16575 8.33499 -1.18981 -0.514689 -1.77789 -2.98758 ... -3.25552 -1.62183 -1.24604 0.475943 1.53174 -2.61531 0.807131 5.13788 -1.04847 -4.01292
91 -0.646401 -2.85257 -4.15982 5.90605 2.21902 7.88378 -1.2006 -0.786302 -1.67825 -2.90883 ... -3.25241 0.261576 -1.20315 0.449103 1.75315 -2.53126 0.759287 5.07329 -1.11169 -3.68002
92 -0.515188 -2.63439 -4.11023 5.90605 2.21902 8.45443 -1.10128 -0.661709 -1.67974 -2.77706 ... -3.23274 -0.915554 -1.09398 0.475943 1.81744 -2.41719 0.900978 5.33934 -1.21503 -3.5103
93 -0.628191 -2.54712 -4.22331 5.90605 2.13151 8.3748 -1.25674 -0.885977 -1.80317 -2.7917 ... -3.14989 -1.73618 -1.12673 0.314907 1.33532 -2.58029 1.02979 5.1348 -1.2746 -3.88563

94 rows × 50 columns

Update Weight

In [36]:
param_lambda = 0.2
T = len(test_df_normalized)
weight_df = pd.DataFrame(columns=top_k_peers, index=range(T))
weight_df
Out[36]:
Name 엔에스 SM C&C 스킨앤스킨 경남제약 시노펙스 SBI핀테크솔루션즈 디티앤씨 신성델타테크 에프앤리퍼블릭 ISC ... 멕아이씨에스 아이씨케이 티에스이 에코프로 상아프론테크 서울제약 드림어스컴퍼니 나노스 이엠텍 덕산하이메탈
0 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
1 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
2 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
3 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
4 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ... ...
89 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
90 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
91 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
92 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN
93 NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN ... NaN NaN NaN NaN NaN NaN NaN NaN NaN NaN

94 rows × 50 columns

In [37]:
for t in range(T):
    sum_prox_j = 0
    x = test_target_series[t]
    prox_j = {}
    for j in top_k_peers: # j : company name
        x_pi = test_df_normalized[j][t]
        d_pi = np.abs(x - x_pi)
        prox_j[j] = np.exp(-gamma * d_pi)
        sum_prox_j += prox_j[j]
    for j in top_k_peers:
        weight[j] = (1-param_lambda) * weight[j] + param_lambda * (prox_j[j] / sum_prox_j)
    weight_df.iloc[t] = weight

Weight Plot

최근 기간의 움직임이 유사한 peer에는 큰 weight가 반대로 유사하지 않은 peer에는 작은 값이 부여되도록 조종됨

In [38]:
weight_df.plot();
plt.legend().set_visible(False);

Update Peer Group Summary

계산된 변동 weight를 통해 대푯값 업데이트

In [39]:
test_p_i = (weight_df * test_df_normalized[top_k_peers]).sum(axis=1)
test_p_i
Out[39]:
0     1.387050
1     2.011508
2     2.546743
3     3.166244
4     3.094556
        ...   
89    3.430631
90    3.217312
91    3.295193
92    3.257558
93    2.969756
Length: 94, dtype: float64
In [40]:
# 종가
test_df_smoothed[target_series.name].plot();
In [41]:
test_target_series.plot(label = 'target');
test_p_i.plot(label = 'centroid');
plt.title(target_series.name)
plt.legend();

Evaluation

그룹의 대푯값 (centroid)를 threshold 범위 이상 벗어날 시 fraud한 종목으로 인식

논문에서는 Fraud한 종목에 대한 레이블된 데이터가 있어서 모델 평가를 진행하고 각종 parameter에 대한 sensitivity 분석 등을 진행 (저는 그런 데이터가 없어요...)

다른 종목에 돌린 결과