거래에서 기계 학습 기술의 적용

저자:리디아, 창작: 2022-12-30 10:53:07, 업데이트: 2023-09-20 09:30:09

img

거래에서 기계 학습 기술의 적용

이 기사의 영감은 FMZ 퀀트 플랫폼에서 데이터 연구를 하는 동안 트랜잭션 문제에 기계 학습 기술을 적용하려고 시도한 후 몇 가지 일반적인 경고와 함정에 대한 관찰에서 왔습니다.

제 이전 기사를 읽지 않았다면, 이 기사를 시작하기 전에 FMZ Quant 플랫폼에서 구축한 자동화된 데이터 연구 환경 가이드와 거래 전략을 수립하기 위한 체계적인 방법을 읽어보시기 바랍니다.

이 두 개의 기사 주소는 여기 있습니다.https://www.fmz.com/digest-topic/9862그리고https://www.fmz.com/digest-topic/9863.

연구환경의 구축에 관한

이 튜토리얼은 모든 기술 수준의 애호가, 엔지니어 및 데이터 과학자를 대상으로 합니다. 업계 리더 또는 프로그래밍 초보자일지라도 필요한 유일한 기술은 파이썬 프로그래밍 언어의 기본 이해와 명령 줄 동작에 대한 충분한 지식입니다.

  • FMZ 퀀트 도커를 설치하고 아나콘다를 설정

FMZ 퀀트 플랫폼FMZ.COM주요 주류 거래소에 대한 고품질의 데이터 소스를 제공 할뿐만 아니라 데이터 분석을 완료한 후 자동 거래를 수행하는 데 도움이되는 풍부한 API 인터페이스의 세트를 제공합니다. 이 인터페이스의 세트에는 계좌 정보를 검색하는 것, 높은, 개방된, 낮은, 수신 가격, 거래량 및 다양한 주류 거래소의 일반적으로 사용되는 기술 분석 지표와 같은 실용적인 도구가 포함됩니다. 특히 실제 거래 과정에서 주요 주류 거래소를 연결하는 공개 API 인터페이스에 대한 강력한 기술 지원을 제공합니다.

위의 모든 기능은 도커와 같은 시스템으로 캡슐화되어 있습니다. 우리가 해야 할 일은 우리의 클라우드 컴퓨팅 서비스를 구매하거나 임대하여 도커 시스템을 배포하는 것입니다.

FMZ 퀀트 플랫폼의 공식 명칭에서 도커 시스템은 도커 시스템이라고 불린다.

도커와 로봇을 어떻게 배포해야 하는지에 대한 제 이전 기사를 참조하시기 바랍니다:https://www.fmz.com/bbs-topic/9864.

독자들이 자신의 클라우드 컴퓨팅 서버를 구입하여 도커를 배포하고 싶다면 이 기사를 참조할 수 있습니다.https://www.fmz.com/digest-topic/5711.

클라우드 컴퓨팅 서버와 도커 시스템을 성공적으로 배포한 후, 다음으로 우리는 파이썬의 현재 가장 큰 유물을 설치합니다: 아나콘다

이 문서에서 요구되는 모든 관련 프로그램 환경 ( 의존성 라이브러리, 버전 관리 등) 을 구현하기 위해 가장 간단한 방법은 아나콘다를 사용하는 것입니다. 그것은 패키지 된 파이썬 데이터 과학 생태계 및 의존성 라이브러리 관리자입니다.

Anaconda를 클라우드 서비스에 설치하기 때문에 클라우드 서버에 Linux 시스템과 Anaconda의 명령 줄 버전을 설치하는 것이 좋습니다.

아나콘다의 설치 방법에 대해서는 아나콘다의 공식 가이드를 참조하십시오.https://www.anaconda.com/distribution/.

만약 당신이 경험이 많은 파이썬 프로그래머이고, 당신이 아나콘다를 사용할 필요가 없다고 느낀다면, 그것은 전혀 문제가 아닙니다. 필요한 의존 환경을 설치할 때 도움이 필요 없다고 가정합니다. 당신은 이 섹션을 직접 건너뛰을 수 있습니다.

거래 전략 개발

거래 전략의 최종 결과물은 다음과 같은 질문에 답해야 합니다.

  • 방향: 자산이 저렴하거나 비싸거나 적정 가치인지 결정합니다.

  • 포지션 개설 조건: 자산이 저렴하거나 비싸다면, 당신은 길게 또는 짧게 가야 합니다.

  • 포지션 거래 종료: 만약 자산의 가격이 합리적이고 우리는 자산에 대한 포지션 (전 구매 또는 판매) 을 가지고 있다면 포지션을 종료해야 합니까?

  • 가격 범위: 포지션이 개설된 가격 (또는 범위).

  • 양: 거래된 화폐의 양 (예를 들어, 디지털 화폐의 양 또는 상품 선물의 롯의 수).

기계 학습은 이 질문들 각각에 답하기 위해 사용될 수 있습니다. 하지만 이 글의 나머지 부분에서 우리는 무역의 방향인 첫 번째 질문에 초점을 맞출 것입니다.

전략적 접근 방식

전략을 구성하는 데는 두 가지 유형의 접근법이 있습니다. 하나는 모델 기반이며 다른 하나는 데이터 마이닝에 기반합니다. 이 두 방법은 기본적으로 서로 반대합니다.

모델 기반 전략 구축에서 우리는 시장 비효율성 모델에서 출발하여 수학 표현식을 (가격과 이익과 같은) 구축하고 장기간에 걸쳐 그 효과를 테스트합니다. 이 모델은 일반적으로 실제 복잡한 모델의 단순화된 버전이며, 장기적인 의미와 안정성을 검증해야합니다. 다음의 일반적인 추세는 평균 회귀 및 중재 전략이이 범주에 속합니다.

다른 한편으로는, 우리는 먼저 가격 패턴을 찾고 데이터 마이닝 방법에서 알고리즘을 사용하려고 노력합니다. 이러한 패턴의 이유는 중요하지 않습니다. 왜냐하면 확인 된 패턴만이 미래에 반복 될 것이기 때문입니다. 이것은 맹인 분석 방법이며, 우리는 무작위 패턴에서 실제 패턴을 식별하기 위해 엄격하게 확인해야합니다. 반복 테스트 방법, k-라인 차트 모델 특징 질량 회귀이이이이 범주에 속합니다.

물론, 기계 학습은 데이터 마이닝 방법에 적용하기가 매우 쉽습니다. 데이터 마이닝을 통해 트랜잭션 신호를 생성하기 위해 기계 학습을 사용하는 방법을 살펴 보겠습니다.

코드 예제는 FMZ 퀀트 플랫폼과 자동화된 트랜잭션 API 인터페이스를 기반으로 한 백테스팅 툴을 사용합니다. 위 섹션에서 도커를 배포하고 아나콘다를 설치한 후, 필요한 데이터 과학 분석 라이브러리와 유명한 기계 학습 모델 scikit-learn을 설치할 필요가 있습니다. 우리는이 섹션을 다시 다루지 않을 것입니다.

pip install -U scikit-learn

머신러닝을 사용하여 거래 전략 신호를 만듭니다.

- 데이터 마이닝 시작하기 전에, 표준 기계 학습 문제 시스템은 다음 그림에서 보여집니다.

img

기계 학습 문제 시스템

우리가 만들 예정인 특징은 어느 정도의 예측 능력을 (X) 가지고 있어야 합니다. 우리는 목표 변수 (Y) 를 예측하고 ML 모델을 훈련시키기 위해 역사적 데이터를 사용하고 실제 값에 가능한 한 가까이 Y를 예측할 수 있습니다. 마지막으로, 우리는 이 모델을 사용하여 Y가 알려지지 않은 새로운 데이터에 대한 예측을 합니다. 이것은 우리를 첫 단계로 이끌고 있습니다:

1단계: 질문 을 설정 하십시오

  • 어떤 예측을 하고 싶으세요? 좋은 예측은 무엇일까요? 예측 결과를 어떻게 평가할까요?

즉, 위의 프레임워크에서 Y는 무엇일까요?

img

무엇을 예측하고 싶으세요?

미래의 가격, 미래의 수익/Pnl, 구매/판매 신호를 예측하고 포트폴리오 할당을 최적화하고 거래를 효율적으로 실행하려고 하고 싶으십니까?

만약 우리가 다음 시간표에 대한 가격을 예측하려고 한다면, 이 경우 Y (t) = 가격 (t+1) 입니다. 이제 우리는 역사적인 데이터를 사용하여 프레임을 완성할 수 있습니다.

Y (t) 는 백테스트에서만 알려져 있지만, 모델을 사용할 때, 우리는 시간 t의 가격 (t+1) 을 알 수 없습니다. 우리는 Y (예측, t) 를 예측하고 시간 t+1에서만 실제 값과 비교하기 위해 모델을 사용합니다. 이것은 예측 모델의 특징으로 Y를 사용할 수 없다는 것을 의미합니다.

목표 Y를 알고 나면 예측을 평가하는 방법도 결정할 수 있다. 이것은 우리가 시도할 데이터의 다른 모델을 구별하는 데 중요합니다. 우리가 해결하는 문제에 따라 모델의 효율성을 측정하기 위해 지표를 선택하십시오. 예를 들어, 가격을 예측하는 경우, 우리는 근 평균 제곱 오류를 지표로 사용할 수 있습니다. 일반적으로 사용되는 일부 지표 (EMA, MACD, 변동 점수 등) 는 FMZ 퀀트 툴박스에서 미리 코딩되었습니다. API 인터페이스를 통해 이러한 지표를 전 세계적으로 호출 할 수 있습니다.

img

미래 가격을 예측하기 위한 ML 프레임워크

예시 목적으로, 우리는 가상의 투자 대상의 예상 미래 기준 (기본) 값을 예측하기 위한 예측 모델을 만들 것입니다.

basis = Price of Stock — Price of Future

basis(t)=S(t)−F(t)

Y(t) = future expected value of basis = Average(basis(t+1),basis(t+2),basis(t+3),basis(t+4),basis(t+5))

이것은 회귀 문제이기 때문에, 우리는 RMSE (근 평균 제곱 오류) 에 대한 모델을 평가할 것입니다. 우리는 또한 전체 Pnl를 평가 기준으로 사용할 것입니다.

참고: RMSE에 대한 관련 수학적 지식을 위해 Baidu 백과사전 참조하십시오.

  • 우리의 목표는 예측된 값이 Y에 가능한 한 가깝도록 하는 모델을 만드는 것입니다.

2단계: 신뢰할 수 있는 데이터를 수집

문제를 해결하는 데 도움이 될 수 있는 데이터를 수집하고 정리하세요.

어떤 데이터를 고려해야 목표 변수 Y를 예측할 수 있습니까? 가격을 예측하는 경우 투자 대상의 가격 데이터, 투자 대상의 거래량 데이터, 관련 투자 대상의 유사한 데이터, 투자 대상의 지수 수준 및 기타 전반적인 시장 지표 및 기타 관련 자산의 가격을 사용할 수 있습니다.

이 데이터에 대한 데이터 액세스 권한을 설정하고 데이터가 정확하고 손실 된 데이터를 해결해야 합니다. 동시에, 모델의 편향을 피하기 위해 데이터가 공정하고 모든 시장 조건 (예를 들어, 같은 수의 이익과 손실 시나리오) 를 완전히 대표하는지 확인하십시오. 배당, 분할 투자 목표, 연장 등을 얻기 위해 데이터를 청소해야 할 수도 있습니다.

FMZ 퀀트 플랫폼 (FMZ.COM) 을 사용하면 구글, 야후, NSE 및 Quandl의 무료 글로벌 데이터; CTP 및 Esunny와 같은 국내 상품 선물의 깊이 데이터; Binance, OKX, Huobi 및 BitMex와 같은 주류 디지털 통화 거래소의 데이터에 액세스 할 수 있습니다. FMZ 퀀트 플랫폼은 또한 투자 목표의 분할 및 심층 시장 데이터와 같은 이러한 데이터를 사전 청소하고 필터링하여 양적 실무자가 쉽게 이해할 수있는 형식으로 전략 개발자에게 제시합니다.

이 문서의 시연을 용이하게 하기 위해, 우리는 가상 투자 목표의 MQK으로 다음 데이터를 사용합니다. 우리는 또한 Auquans Toolbox라고 불리는 매우 편리한 수치 도구를 사용합니다. 자세한 내용은 참조하십시오:https://github.com/Auquan/auquan-toolbox-python.

# Load the data
from backtester.dataSource.quant_quest_data_source import QuantQuestDataSource
cachedFolderName = '/Users/chandinijain/Auquan/qq2solver-data/historicalData/'
dataSetId = 'trainingData1'
instrumentIds = ['MQK']
ds = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
def loadData(ds):
    data = None
    for key in ds.getBookDataByFeature().keys():
        if data is None:
            data = pd.DataFrame(np.nan, index = ds.getBookDataByFeature()[key].index, columns=[])
        data[key] = ds.getBookDataByFeature()[key]
    data['Stock Price'] =  ds.getBookDataByFeature()['stockTopBidPrice'] + ds.getBookDataByFeature()['stockTopAskPrice'] / 2.0
    data['Future Price'] = ds.getBookDataByFeature()['futureTopBidPrice'] + ds.getBookDataByFeature()['futureTopAskPrice'] / 2.0
    data['Y(Target)'] = ds.getBookDataByFeature()['basis'].shift(-5)
    del data['benchmark_score']
    del data['FairValue']
    return data
data = loadData(ds)

위의 코드로 Auquans Toolbox는 데이터 프레임 사전에 데이터를 다운로드하고 로드했습니다. 이제 우리가 원하는 형식으로 데이터를 준비해야합니다. 함수 ds.getBookDataByFeature() 는 각 기능에 하나씩 데이터 프레임의 사전을 반환합니다. 우리는 모든 특성을 가진 주식에 대한 새로운 데이터 프레임을 만듭니다.

단계 3: 데이터를 분할합니다.

  • 훈련 세트를 만들고, 교차 검증을 하고, 데이터에서 이 데이터 세트를 테스트합니다.

이것은 매우 중요한 단계입니다!계속하기 전에, 우리는 데이터를 훈련 데이터 세트로 나누어야 합니다. 모델을 훈련시키기 위해; 테스트 데이터 세트를 모델 성능을 평가하기 위해. 60-70%의 훈련 세트와 30-40%의 테스트 세트로 나누는 것이 좋습니다.

img

데이터를 훈련 세트와 테스트 세트로 나누기

훈련 데이터는 모델 매개 변수를 평가하는 데 사용되기 때문에 모델은 훈련 데이터에 너무 적합할 수 있으며 훈련 데이터는 모델 성능을 오해 할 수 있습니다. 개별 테스트 데이터를 보유하지 않고 모든 데이터를 훈련에 사용할 경우 모델이 새로운 보이지 않는 데이터에서 얼마나 잘 또는 나쁘게 수행하는지 알 수 없습니다. 이것은 실시간 데이터에서 훈련 된 ML 모델의 실패의 주요 이유 중 하나입니다. 사람들은 사용 가능한 모든 데이터를 훈련하고 훈련 데이터 지표에 흥분하지만 모델은 훈련되지 않은 실시간 데이터에 대한 의미있는 예측을 할 수 없습니다.

img

데이터를 훈련 세트, 검증 세트 및 테스트 세트로 나누기

이 방법에는 문제가 있습니다. 만약 우리가 훈련 데이터를 반복적으로 훈련시키고, 테스트 데이터의 성능을 평가하고, 성능에 만족할 때까지 모델을 최적화한다면, 우리는 테스트 데이터를 훈련 데이터의 일부로 암시적으로 받아들이게 됩니다. 결국, 우리의 모델은 훈련과 테스트 데이터의 이 집합에서 잘 수행할 수 있지만, 새로운 데이터를 잘 예측할 수 있다는 것을 보장할 수는 없습니다.

이 문제를 해결하기 위해, 우리는 별도의 검증 데이터 세트를 만들 수 있습니다. 이제, 당신은 데이터를 훈련, 검증 데이터의 성능을 평가, 성능에 만족할 때까지 최적화, 그리고 마지막으로 테스트 데이터를 테스트 할 수 있습니다.

테스트 데이터의 성능을 확인 한 후에는 다시 돌아가서 모델을 더 이상 최적화하려고 하지 마십시오. 모델이 좋은 결과를 주지 않는 것을 발견하면 모델을 완전히 폐기하고 다시 시작하십시오. 훈련 데이터의 60%와 검증 데이터의 20% 및 테스트 데이터의 20%가 분할 될 수 있다고 제안됩니다.

우리의 질문에 대해, 우리는 세 가지 데이터 세트를 사용할 수 있습니다. 우리는 훈련 세트로 하나를, 확인 세트로 두 번째, 그리고 테스트 세트로 세 번째를 사용할 것입니다.

# Training Data
dataSetId =  'trainingData1'
ds_training = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
training_data = loadData(ds_training)
# Validation Data
dataSetId =  'trainingData2'
ds_validation = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
validation_data = loadData(ds_validation)
# Test Data
dataSetId =  'trainingData3'
ds_test = QuantQuestDataSource(cachedFolderName=cachedFolderName,
                                    dataSetId=dataSetId,
                                    instrumentIds=instrumentIds)
out_of_sample_test_data = loadData(ds_test)

각각의 값에 우리는 다음 5개의 기본값의 평균으로 정의된 목표 변수 Y를 더합니다.

def prepareData(data, period):
    data['Y(Target)'] = data['basis'].rolling(period).mean().shift(-period)
    if 'FairValue' in data.columns:
        del data['FairValue']
    data.dropna(inplace=True)
period = 5
prepareData(training_data, period)
prepareData(validation_data, period)
prepareData(out_of_sample_test_data, period)

단계 4: 특징 엔지니어링

데이터 동작을 분석하고 예측 기능을 생성

이제 실제 프로젝트 건설이 시작되었습니다. 기능 선택의 황금 규칙은 예측 능력이 모델이 아닌 기능에서 주로 나온다는 것입니다. 기능 선택이 모델 선택보다 성능에 훨씬 더 큰 영향을 미친다는 것을 알게 될 것입니다. 기능 선택에 대한 몇 가지 고려 사항:

  • 목표 변수와의 관계를 탐구하지 않고는 무작위로 많은 특징을 선택하지 마십시오.

  • 목표 변수와 거의 또는 전혀 관계가 없을 때 과잉 적합으로 이어질 수 있습니다.

  • 선택된 특징은 서로 밀접한 관련이 있을 수 있습니다. 이 경우 소수의 특징이 목표물을 설명할 수도 있습니다.

  • 저는 보통 직관적인 특징을 만들고, 목표 변수와 이 특징들 사이의 상관관계를 확인하고, 어느 것을 사용할지 결정하기 위해 그 사이의 상관관계를 확인합니다.

  • 또한 최대 정보 계수 (MIC) 에 따라 후보 특징을 분류하기 위해 주요 구성 요소 분석 (PCA) 및 다른 방법을 시도 할 수 있습니다.

특징 변환/정상화:

ML 모델은 정규화 측면에서 좋은 성능을 보이는 경향이 있다. 그러나, 미래 데이터 범위가 알려지지 않기 때문에 시간 계열 데이터와 거래할 때 정규화는 어렵다. 데이터는 정규화 범위 밖에 있고, 모델 오류로 이어질 수 있다. 그러나 당신은 여전히 어느 정도의 안정성을 강요할 수 있다:

  • 스케일링: 표준편차 또는 쿼티일 범위로 특징을 나누기

  • 중점화: 현재 값에서 역사적 평균 값을 빼기

  • 정상화: 위의 두 회전 기간 (x - 평균) /stdev.

  • 정규 정규화: 데이터를 -1에서 +1까지 표준화하고 거꾸로 추적 기간 (x-min) / ((max min) 에서 중심을 다시 결정합니다.

참고로 역추적 기간을 초과한 역사적 연속 평균값, 표준편차, 최대 또는 최소값을 사용하기 때문에, 특징의 정상화 표준화 값은 서로 다른 시간에 다른 실제 값을 나타낼 것입니다. 예를 들어, 특징의 현재 값이 5이고, 연속 30 기간 동안의 평균 값이 4.5인 경우, 중점화 후 0.5로 변환됩니다. 그 후, 30 연속 기간의 평균 값이 3이 되면, 3.5 값이 0.5이 될 것입니다. 이것은 잘못된 모델의 원인이 될 수 있습니다. 따라서 정상화는 까다롭고, 모델의 성능을 향상시키는 것을 알아내야 합니다.

첫 번째 반복에서 우리는 혼합 매개 변수를 사용하여 많은 수의 특징을 만들었습니다. 나중에 우리는 특징의 수를 줄일 수 있는지 확인하려고 노력할 것입니다.

def difference(dataDf, period):
    return dataDf.sub(dataDf.shift(period), fill_value=0)
def ewm(dataDf, halflife):
    return dataDf.ewm(halflife=halflife, ignore_na=False,
                      min_periods=0, adjust=True).mean()
def rsi(data, period):
    data_upside = data.sub(data.shift(1), fill_value=0)
    data_downside = data_upside.copy()
    data_downside[data_upside > 0] = 0
    data_upside[data_upside < 0] = 0
    avg_upside = data_upside.rolling(period).mean()
    avg_downside = - data_downside.rolling(period).mean()
    rsi = 100 - (100 * avg_downside / (avg_downside + avg_upside))
    rsi[avg_downside == 0] = 100
    rsi[(avg_downside == 0) & (avg_upside == 0)] = 0
return rsi
def create_features(data):
    basis_X = pd.DataFrame(index = data.index, columns =  [])
    
    basis_X['mom3'] = difference(data['basis'],4)
    basis_X['mom5'] = difference(data['basis'],6)
    basis_X['mom10'] = difference(data['basis'],11)
    
    basis_X['rsi15'] = rsi(data['basis'],15)
    basis_X['rsi10'] = rsi(data['basis'],10)
    
    basis_X['emabasis3'] = ewm(data['basis'],3)
    basis_X['emabasis5'] = ewm(data['basis'],5)
    basis_X['emabasis7'] = ewm(data['basis'],7)
    basis_X['emabasis10'] = ewm(data['basis'],10)
    basis_X['basis'] = data['basis']
    basis_X['vwapbasis'] = data['stockVWAP']-data['futureVWAP']
    
    basis_X['swidth'] = data['stockTopAskPrice'] -
                        data['stockTopBidPrice']
    basis_X['fwidth'] = data['futureTopAskPrice'] -
                        data['futureTopBidPrice']
    
    basis_X['btopask'] = data['stockTopAskPrice'] -
                         data['futureTopAskPrice']
    basis_X['btopbid'] = data['stockTopBidPrice'] -
                         data['futureTopBidPrice']

    basis_X['totalaskvol'] = data['stockTotalAskVol'] -
                             data['futureTotalAskVol']
    basis_X['totalbidvol'] = data['stockTotalBidVol'] -
                             data['futureTotalBidVol']
    
    basis_X['emabasisdi7'] = basis_X['emabasis7'] -
                             basis_X['emabasis5'] + 
                             basis_X['emabasis3']
    
    basis_X = basis_X.fillna(0)
    
    basis_y = data['Y(Target)']
    basis_y.dropna(inplace=True)
    
    print("Any null data in y: %s, X: %s"
            %(basis_y.isnull().values.any(), 
             basis_X.isnull().values.any()))
    print("Length y: %s, X: %s"
            %(len(basis_y.index), len(basis_X.index)))
    
    return basis_X, basis_y
basis_X_train, basis_y_train = create_features(training_data)
basis_X_test, basis_y_test = create_features(validation_data)

단계 5: 모델 선택

선택된 질문에 따라 적절한 통계/ML 모델을 선택합니다.

모델 선택은 문제가 어떻게 형성되는지에 달려 있습니다. 당신은 감독 (기능 행렬의 각 점 X는 목표 변수 Y에 매핑) 또는 감독되지 않은 학습 (특정 매핑없이 모델은 알려지지 않은 패턴을 배우려고합니다) 를 해결합니까? 당신은 회귀 (미래 시간에 실제 가격을 예측) 또는 분류 (미래 시간에 가격 방향을 예측 (증가/감소)) 를 다루고 있습니까?

img

감독 또는 감독을 받지 않은 학습

img

회귀 또는 분류

어떤 일반적인 감독 학습 알고리즘은 시작하는데 도움이 될 수 있습니다.

  • 선형 회귀 (변수, 회귀)

  • 로지스틱 회귀 (파라미터, 분류)

  • 가장 가까운 이웃 (K-Nearest Neighbor, KNN) 알고리즘 (사례 기반, 회귀)

  • SVM, SVR (변수, 분류 및 회귀)

  • 결정 나무

  • 결정 숲

선형 또는 물류 회귀와 같은 간단한 모델로 시작하여 필요에 따라 더 복잡한 모델을 구축하는 것을 제안합니다. 또한 모형을 맹목적으로 블랙 박스처럼 사용하는 대신 모형의 수학을 읽는 것이 좋습니다.

단계 6: 훈련, 검증 및 최적화 (4-6단계 반복)

img

훈련 및 검증 데이터 세트를 사용하여 모델을 훈련하고 최적화하십시오.

이제 당신은 마침내 모델을 만들 준비가 되어 있습니다. 이 단계에서, 당신은 실제로 모델과 모델 매개 변수를 반복합니다. 훈련 데이터에 모델을 훈련시키고, 검증 데이터에 대한 성능을 측정하고, 다시 돌아와 최적화하고, 재 훈련하고 평가합니다. 모델의 성능에 만족하지 않으면 다른 모델을 시도하십시오. 당신은 마침내 만족하는 모델을 얻을 때까지이 단계를 여러 번 반복합니다.

좋아하는 모델만 있으면 다음 단계로 넘어가세요.

우리의 증명 문제에서 간단한 선형 회귀로 시작해 보겠습니다.

from sklearn import linear_model
from sklearn.metrics import mean_squared_error, r2_score
def linear_regression(basis_X_train, basis_y_train,
                      basis_X_test,basis_y_test):
    
    regr = linear_model.LinearRegression()
    # Train the model using the training sets
    regr.fit(basis_X_train, basis_y_train)
    # Make predictions using the testing set
    basis_y_pred = regr.predict(basis_X_test)
    # The coefficients
    print('Coefficients: \n', regr.coef_)
    
    # The mean squared error
    print("Mean squared error: %.2f"
          % mean_squared_error(basis_y_test, basis_y_pred))
    
    # Explained variance score: 1 is perfect prediction
    print('Variance score: %.2f' % r2_score(basis_y_test,
                                            basis_y_pred))
    # Plot outputs
    plt.scatter(basis_y_pred, basis_y_test,  color='black')
    plt.plot(basis_y_test, basis_y_test, color='blue', linewidth=3)
    plt.xlabel('Y(actual)')
    plt.ylabel('Y(Predicted)')
    plt.show()
    
    return regr, basis_y_pred
_, basis_y_pred = linear_regression(basis_X_train, basis_y_train, 
                                    basis_X_test,basis_y_test)

img

정상화 없이 선형 회귀

('Coefficients: \n', array([ -1.0929e+08, 4.1621e+07, 1.4755e+07, 5.6988e+06, -5.656e+01, -6.18e-04, -8.2541e-05,4.3606e-02, -3.0647e-02, 1.8826e+07, 8.3561e-02, 3.723e-03, -6.2637e-03, 1.8826e+07, 1.8826e+07, 6.4277e-02, 5.7254e-02, 3.3435e-03, 1.6376e-02, -7.3588e-03, -8.1531e-04, -3.9095e-02, 3.1418e-02, 3.3321e-03, -1.3262e-06, -1.3433e+07, 3.5821e+07, 2.6764e+07, -8.0394e+06, -2.2388e+06, -1.7096e+07]))
Mean squared error: 0.02
Variance score: 0.96

모델 계수들을 보세요. 어떤 계수들이 중요한지 비교하거나 말할 수 없습니다. 왜냐하면 모든 계수들이 다른 계수들에 속하기 때문입니다.

def normalize(basis_X, basis_y, period):
    basis_X_norm = (basis_X - basis_X.rolling(period).mean())/
                    basis_X.rolling(period).std()
    basis_X_norm.dropna(inplace=True)
    basis_y_norm = (basis_y - 
                    basis_X['basis'].rolling(period).mean())/
                    basis_X['basis'].rolling(period).std()
    basis_y_norm = basis_y_norm[basis_X_norm.index]
    
    return basis_X_norm, basis_y_norm
norm_period = 375
basis_X_norm_test, basis_y_norm_test = normalize(basis_X_test,basis_y_test, norm_period)
basis_X_norm_train, basis_y_norm_train = normalize(basis_X_train, basis_y_train, norm_period)
regr_norm, basis_y_pred = linear_regression(basis_X_norm_train, basis_y_norm_train, basis_X_norm_test, basis_y_norm_test)
basis_y_pred = basis_y_pred * basis_X_test['basis'].rolling(period).std()[basis_y_norm_test.index] + basis_X_test['basis'].rolling(period).mean()[basis_y_norm_test.index]

img

정상화와 함께 선형 회귀

Mean squared error: 0.05
Variance score: 0.90

이 모델은 이전 모델을 개선하지는 않지만 더 나쁘지 않습니다. 이제 우리는 계수를 비교하여 실제로 중요한 것이 무엇인지 볼 수 있습니다.

계수를 살펴봅시다.

for i in range(len(basis_X_train.columns)):
    print('%.4f, %s'%(regr_norm.coef_[i], basis_X_train.columns[i]))

그 결과는 다음과 같습니다.

19.8727, emabasis4
-9.2015, emabasis5
8.8981, emabasis7
-5.5692, emabasis10
-0.0036, rsi15
-0.0146, rsi10
0.0196, mom10
-0.0035, mom5
-7.9138, basis
0.0062, swidth
0.0117, fwidth
2.0883, btopask
2.0311, btopbid
0.0974, bavgask
0.0611, bavgbid
0.0007, topaskvolratio
0.0113, topbidvolratio
-0.0220, totalaskvolratio
0.0231, totalbidvolratio

우리는 분명히 볼 수 있습니다. 어떤 특징들은 다른 것들보다 더 높은 계수를 가지고 있고, 더 강력한 예측 능력을 가지고 있을 수도 있습니다.

서로 다른 특징들 사이의 상관관계를 봅시다.

import seaborn

c = basis_X_train.corr()
plt.figure(figsize=(10,10))
seaborn.heatmap(c, cmap='RdYlGn_r', mask = (np.abs(c) <= 0.8))
plt.show()

img

특징들 사이의 상관관계

어두운 빨간색 영역은 매우 연관된 변수를 나타냅니다. 다시 몇 가지 특징을 만들고 수정하여 모델을 개선하려고 노력하겠습니다.

예를 들어, 저는 emabasisdi7 같은 특징들을 쉽게 폐기할 수 있습니다. 다른 특징들의 선형 조합일 뿐입니다.

def create_features_again(data):
    basis_X = pd.DataFrame(index = data.index, columns =  [])
    basis_X['mom10'] = difference(data['basis'],11)
    basis_X['emabasis2'] = ewm(data['basis'],2)
    basis_X['emabasis5'] = ewm(data['basis'],5)
    basis_X['emabasis10'] = ewm(data['basis'],10)
    basis_X['basis'] = data['basis']
    basis_X['totalaskvolratio'] = (data['stockTotalAskVol']
                                 - data['futureTotalAskVol'])/
                                   100000
    basis_X['totalbidvolratio'] = (data['stockTotalBidVol']
                                 - data['futureTotalBidVol'])/
                                   100000
    basis_X = basis_X.fillna(0)
    
    basis_y = data['Y(Target)']
    basis_y.dropna(inplace=True)
    return basis_X, basis_y
basis_X_test, basis_y_test = create_features_again(validation_data)
basis_X_train, basis_y_train = create_features_again(training_data)
_, basis_y_pred = linear_regression(basis_X_train, basis_y_train, basis_X_test,basis_y_test)
basis_y_regr = basis_y_pred.copy()

img

('Coefficients: ', array([ 0.03246139,
0.49780982, -0.22367172,  0.20275786,  0.50758852,
-0.21510795, 0.17153884]))
Mean squared error: 0.02
Variance score: 0.96

우리의 모델의 성능은 변하지 않았습니다. 우리는 단지 우리의 목표 변수를 설명하기 위해 몇 가지 특성을 필요로 합니다. 우리는 당신이 우리의 모델을 개선할 수 있는 것을 보기 위해 위의 기능의 더 많은 것을 시도하고, 새로운 조합 등을 시도하는 것을 제안합니다.

우리는 또한 더 복잡한 모델을 시도하여 모델의 변화가 성능을 향상시킬 수 있는지 확인할 수 있습니다.

  • 가장 가까운 이웃 (KNN) 알고리즘
from sklearn import neighbors
n_neighbors = 5
model = neighbors.KNeighborsRegressor(n_neighbors, weights='distance')
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_knn = basis_y_pred.copy()

img

  • SVR
from sklearn.svm import SVR
model = SVR(kernel='rbf', C=1e3, gamma=0.1)
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_svr = basis_y_pred.copy()

img

  • 결정 나무
model=ensemble.ExtraTreesRegressor()
model.fit(basis_X_train, basis_y_train)
basis_y_pred = model.predict(basis_X_test)
basis_y_trees = basis_y_pred.copy()

img

단계 7: 테스트 데이터를 백테스트합니다

실제 샘플 데이터의 성능을 확인

img

테스트 데이터 세트 (무해) 에 대한 백테스팅 성능

이것은 중요한 순간입니다. 우리는 테스트 데이터의 마지막 단계에서 최종 최적화 모델을 실행합니다. 우리는 처음부터 그것을 치워두고 지금까지 데이터를 만지지 않았습니다.

이것은 당신이 실시간 거래를 시작할 때 모델이 새롭고 보이지 않는 데이터에서 실행되는 방법에 대한 현실적인 기대를 제공합니다. 따라서 모델을 훈련하거나 검증하는 데 사용되는 깨끗한 데이터 세트가 있는지 확인해야합니다.

테스트 데이터의 백테스트 결과가 마음에 들지 않으면 모델을 폐기하고 다시 시작하십시오. 다시 돌아가거나 모델을 다시 최적화하지 마십시오. 이는 과도한 적합으로 이어질 것입니다! (이 데이터 세트가 오염되었기 때문에 새로운 테스트 데이터 세트를 만드는 것도 좋습니다. 모델을 폐기 할 때 우리는 이미 데이터 세트의 내용을 암시적으로 알고 있습니다.)

여기서 우리는 여전히 Auquan의 도구 상자를 사용할 것입니다.

import backtester
from backtester.features.feature import Feature
from backtester.trading_system import TradingSystem
from backtester.sample_scripts.fair_value_params import FairValueTradingParams
class Problem1Solver():
def getTrainingDataSet(self):
        return "trainingData1"
def getSymbolsToTrade(self):
        return ['MQK']
def getCustomFeatures(self):
        return {'my_custom_feature': MyCustomFeature}
def getFeatureConfigDicts(self):
                            
        expma5dic = {'featureKey': 'emabasis5',
                 'featureId': 'exponential_moving_average',
                 'params': {'period': 5,
                              'featureName': 'basis'}}
        expma10dic = {'featureKey': 'emabasis10',
                 'featureId': 'exponential_moving_average',
                 'params': {'period': 10,
                              'featureName': 'basis'}}                     
        expma2dic = {'featureKey': 'emabasis3',
                 'featureId': 'exponential_moving_average',
                 'params': {'period': 3,
                              'featureName': 'basis'}}
        mom10dic = {'featureKey': 'mom10',
                 'featureId': 'difference',
                 'params': {'period': 11,
                              'featureName': 'basis'}}
        
        return [expma5dic,expma2dic,expma10dic,mom10dic]    
    
    def getFairValue(self, updateNum, time, instrumentManager):
        # holder for all the instrument features
        lbInstF = instrumentManager.getlookbackInstrumentFeatures()
        mom10 = lbInstF.getFeatureDf('mom10').iloc[-1]
        emabasis2 = lbInstF.getFeatureDf('emabasis2').iloc[-1]
        emabasis5 = lbInstF.getFeatureDf('emabasis5').iloc[-1]
        emabasis10 = lbInstF.getFeatureDf('emabasis10').iloc[-1] 
        basis = lbInstF.getFeatureDf('basis').iloc[-1]
        totalaskvol = lbInstF.getFeatureDf('stockTotalAskVol').iloc[-1] - lbInstF.getFeatureDf('futureTotalAskVol').iloc[-1]
        totalbidvol = lbInstF.getFeatureDf('stockTotalBidVol').iloc[-1] - lbInstF.getFeatureDf('futureTotalBidVol').iloc[-1]
        
        coeff = [ 0.03249183, 0.49675487, -0.22289464, 0.2025182, 0.5080227, -0.21557005, 0.17128488]
        newdf['MQK'] = coeff[0] * mom10['MQK'] + coeff[1] * emabasis2['MQK'] +\
                      coeff[2] * emabasis5['MQK'] + coeff[3] * emabasis10['MQK'] +\
                      coeff[4] * basis['MQK'] + coeff[5] * totalaskvol['MQK']+\
                      coeff[6] * totalbidvol['MQK']
                    
        newdf.fillna(emabasis5,inplace=True)
        return newdf
problem1Solver = Problem1Solver()
tsParams = FairValueTradingParams(problem1Solver)
tradingSystem = TradingSystem(tsParams)
tradingSystem.startTrading(onlyAnalyze=False, 
                           shouldPlot=True,
                           makeInstrumentCsvs=False)

img

백테스팅 결과, Pnl는 USD로 계산됩니다 (Pnl는 거래 비용과 다른 수수료에 포함되지 않습니다)

단계 8: 모델을 개선하기 위한 다른 방법

롤링 검증, 세트 학습, 배그 및 부스팅

더 많은 데이터를 수집하거나 더 나은 기능을 만들거나 더 많은 모델을 시도하는 것 외에도 몇 가지 더 개선할 수 있는 점이 있습니다.

1. 롤링 검증

img

롤링 검증

시장 조건은 거의 동일하지 않습니다. 당신이 1 년의 데이터를 가지고 있다고 가정하고 1 월에서 8 월까지의 데이터를 훈련을 위해 사용하고 9 월에서 12 월까지의 데이터를 사용하여 모델을 테스트하십시오. 당신은 궁극적으로 매우 특정 시장 조건에 대해 훈련 할 수 있습니다. 아마도 1 년 반 동안 시장 변동이 없었고 몇 가지 극단적 인 뉴스가 9 월 시장의 급격한 상승으로 이어졌습니다. 모델은이 모델을 배울 수 없으며 쓰레기 예측 결과를 가져올 것입니다.

1월부터 2월까지의 교육, 3월의 검증, 4월부터 5월까지의 재교육, 6월의 검증 등으로 앞으로의 점진적 검증을 시도하는 것이 더 좋을 수 있습니다.

2. 집계 학습

img

집합 학습

일부 모델은 특정 시나리오를 예측하는 데 매우 효과적일 수 있지만, 모델은 다른 시나리오를 예측하는 데 또는 특정 상황에서 매우 과잉 적합 할 수 있습니다. 오류와 과잉 적합성을 줄이는 한 가지 방법은 다양한 모델의 집합을 사용하는 것입니다. 당신의 예측은 많은 모델에 의해 만들어진 예측의 평균이며, 다른 모델의 오류는 상쇄되거나 감소 할 수 있습니다. 일반적인 집합 방법 중 일부는 Bagging 및 Boosting입니다.

img

포지션

img

부흥

간결함을 위해 이 방법들을 건너뛰겠습니다. 하지만 더 많은 정보를 온라인에서 찾을 수 있습니다.

문제를 풀기 위해 집합적인 방법을 시도해보죠.

basis_y_pred_ensemble = (basis_y_trees + basis_y_svr +
                         basis_y_knn + basis_y_regr)/4

img

Mean squared error: 0.02
Variance score: 0.95

지금까지 우리는 많은 지식과 정보를 축적했습니다.

  • 문제를 해결해

  • 신뢰성 있는 데이터를 수집하고 데이터를 정화합니다.

  • 데이터를 훈련, 검증 및 테스트 세트로 분할합니다.

  • 특징을 만들고 그들의 행동을 분석합니다.

  • 행동에 따라 적절한 훈련 모델을 선택합니다.

  • 훈련 데이터를 사용하여 모델을 훈련하고 예측을 합니다.

  • 검증 세트의 성능을 확인하고 다시 최적화합니다.

  • 테스트 세트의 최종 성능을 확인합니다.

여러분들 머리에 들어가지 않았나요? 하지만 아직 끝나지 않았습니다. 여러분은 믿을만한 예측 모델만 가지고 있습니다. 우리가 실제로 전략에서 원했던 것을 기억하세요?

  • 예측 모델에 기반한 신호를 개발하여 거래 방향을 파악합니다.

  • 오픈 포지션과 클로즈 포지션을 식별하기 위한 구체적인 전략을 개발합니다.

  • 위치와 가격을 식별하는 시스템을 실행합니다.

위는 FMZ 퀀트 플랫폼을 사용할 것입니다 (FMZ.COM). FMZ 퀀트 플랫폼에는 고도로 캡슐화되고 완벽한 API 인터페이스가 있으며, 글로벌로 호출 할 수있는 주문 및 거래 기능도 있습니다. 다른 거래소의 API 인터페이스를 하나씩 연결하고 추가 할 필요가 없습니다. FMZ 퀀트 플랫폼의 전략 광장에는이 기사에서 기계 학습 방법과 일치하는 많은 성숙하고 완벽한 대체 전략이 있으며, 특정 전략을 더 강력하게 만들 것입니다. 전략 광장은 다음과 같습니다.https://www.fmz.com/square.

**거래 비용에 대한 중요한 참고: ** 선택된 자산이 언제 긴 또는 짧은 지 모델은 알려줍니다. 그러나 수수료/거래 비용/실용 거래 양/정지 손실 등을 고려하지 않습니다. 거래 비용은 일반적으로 수익성있는 거래를 손실로 전환합니다. 예를 들어, 예상 가격 상승이 0.05 달러인 자산은 구매입니다. 그러나이 거래에 대해 0.10 달러를 지불해야 할 경우 결국 0.05 달러의 순 손실을 얻을 수 있습니다. 브로커의 수수료, 교환 수수료 및 포인트 차이를 고려 한 후 위의 큰 이익 차트는 다음과 같습니다.

img

거래 수수료와 포인트 차이 후 백테스트 결과는 Pnl입니다.

거래 수수료와 가격 차이는 우리 Pnl의 90% 이상을 차지합니다.

마지막으로 몇 가지 일반적인 함정을 살펴보자.

할 일과 하지 말아야 할 일

  • 모든 힘으로 너무 잘 어울리지 말아요!

  • 모든 데이터 포인트 후에 재 훈련하지 마십시오: 이것은 기계 학습 개발에서 사람들이 저지르는 일반적인 실수입니다. 모델이 각 데이터 포인트 후에 재 훈련되어야하는 경우, 그것은 매우 좋은 모델이 아닐 수 있습니다. 즉, 정기적으로 재 훈련이 필요하며 합리적인 빈도로만 훈련되어야합니다. (예를 들어, 하루 내 예측이 이루어지면, 매주 끝에서 재 훈련이 필요합니다.)

  • 편향, 특히 미래 지향적 편향을 피하십시오: 이것은 모델이 작동하지 않는 또 다른 이유이며 미래의 정보를 사용하지 않도록하십시오. 대부분의 경우, 이것은 목표 변수 Y가 모델의 기능으로 사용되지 않는다는 것을 의미합니다. 백테스팅 중에 사용할 수 있지만 모델을 실행할 때 사용할 수 없습니다.

  • 데이터 마이닝 편향에 주의하십시오: 우리는 적절한지 여부를 결정하기 위해 데이터에 일련의 모델링을 수행하려고 할 때 특별한 이유가 없다면, 발생 할 수있는 실제 모드와 무작위 모드를 분리하기 위해 엄격한 테스트를 실행하도록하십시오. 예를 들어, 선형 회귀는 상승 추세 패턴을 잘 설명하지만 더 큰 무작위 방황의 일부가 될 가능성이 있습니다!

너무 많이 꽂지 마세요

이것은 매우 중요합니다. 다시 한 번 언급할 필요가 있다고 생각합니다.

  • 과도한 적합성은 거래 전략에서 가장 위험한 함정입니다.

  • 복잡한 알고리즘은 백테스트에서 매우 잘 수행 할 수 있지만 새로운 보이지 않는 데이터에서 매우 실패합니다. 이 알고리즘은 실제로 데이터의 추세를 밝히지 않으며 실제 예측 능력이 없습니다. 그것은 보는 데이터에 매우 적합합니다.

  • 시스템을 최대한 단순하게 유지하세요. 데이터를 해석하기 위해 복잡한 기능이 필요하다는 것을 발견하면 너무 적합할 수 있습니다.

  • 사용 가능한 데이터를 훈련 데이터와 테스트 데이터로 나누고 실시간 트랜잭션에 모델을 사용하기 전에 항상 실제 샘플 데이터의 성능을 확인하십시오.


관련

더 많은