Modèle de facteur de monnaie numérique

Auteur:Je ne sais pas., Créé: 2022-10-24 17:37:50, Mis à jour: 2023-09-15 20:59:38

img

Cadre du modèle facteur

Les rapports de recherche sur le modèle multifactoriel du marché boursier sont volumineux, avec de riches théories et pratiques. Peu importe le nombre de devises, la valeur totale du marché, le volume des transactions, le marché des dérivés, etc. sur le marché de la monnaie numérique, il suffit de mener une recherche factuelle. Ce document est principalement destiné aux débutants des stratégies quantitatives et n'impliquera pas de principes mathématiques complexes et d'analyse statistique. Il utilisera le marché du futur perpétuel de Binance comme source de données pour construire un cadre simple pour la recherche des facteurs, ce qui est pratique pour évaluer les indicateurs facteurs.

Le facteur peut être considéré comme un indicateur et une expression peut être écrite. Le facteur change constamment, reflétant les informations sur les revenus futurs. Généralement, le facteur représente une logique d'investissement.

Par exemple, l'hypothèse derrière le facteur de prix de clôture est que le prix de l'action peut prédire les gains futurs, et plus le prix de l'action est élevé, plus les gains futurs seront élevés (ou peuvent être inférieurs). En fait, la construction d'un portefeuille basé sur ce facteur est un modèle / stratégie d'investissement pour acheter des actions à prix élevé dans un tour régulier.

Le marché boursier et le marché de la monnaie numérique sont tous deux des systèmes complexes. Il n'y a pas de facteur qui puisse prédire complètement les profits futurs, mais ils ont toujours une certaine prévisibilité. L'alpha efficace (mode d'investissement) deviendra progressivement invalide avec plus d'apport en capital. Cependant, ce processus produira d'autres modèles sur le marché, donnant ainsi naissance à un nouvel alpha. Le facteur de valeur de marché était autrefois une stratégie très efficace sur le marché des actions A. Il suffit d'acheter 10 actions avec la valeur de marché la plus basse et de les ajuster une fois par jour. Depuis 2007, le backtest de 10 ans rapportera plus de 400 fois les profits, dépassant de loin le marché global. Cependant, le marché boursier du cheval blanc en 2017 reflète l'échec du petit facteur de valeur de marché, et le facteur de valeur est devenu populaire.

Les facteurs recherchés sont à la base de l'établissement de stratégies. Une meilleure stratégie peut être construite en combinant plusieurs facteurs efficaces non liés.

import requests
from datetime import date,datetime
import time
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import requests, zipfile, io
%matplotlib inline

Ressource de données

Jusqu'à présent, les données horaires de la ligne K des contrats à terme perpétuels Binance USDT depuis le début de 2022 ont dépassé 150 devises. Comme nous l'avons mentionné précédemment, le modèle de facteur est un modèle de sélection de devises, qui est orienté vers toutes les devises plutôt qu'une certaine devise. Les données de la ligne K incluent les prix d'ouverture et de clôture élevés et bas, le volume des transactions, le nombre de transactions, le volume d'achat des preneurs et d'autres données. Ces données ne sont certainement pas la source de tous les facteurs, tels que l'indice boursier américain, l'expectative d'augmentation des taux d'intérêt, la rentabilité, les données sur la chaîne, la popularité des médias sociaux, etc. Des sources de données inhabituelles peuvent également trouver un alpha efficace, mais le volume de prix de base est également suffisant.

## Current trading pair
Info = requests.get('https://fapi.binance.com/fapi/v1/exchangeInfo')
symbols = [s['symbol'] for s in Info.json()['symbols']]
symbols = list(filter(lambda x: x[-4:] == 'USDT', [s.split('_')[0] for s in symbols]))
print(symbols)

À l' extérieur:

['BTCUSDT', 'ETHUSDT', 'BCHUSDT', 'XRPUSDT', 'EOSUSDT', 'LTCUSDT', 'TRXUSDT', 'ETCUSDT', 'LINKUSDT',
'XLMUSDT', 'ADAUSDT', 'XMRUSDT', 'DASHUSDT', 'ZECUSDT', 'XTZUSDT', 'BNBUSDT', 'ATOMUSDT', 'ONTUSDT',
'IOTAUSDT', 'BATUSDT', 'VETUSDT', 'NEOUSDT', 'QTUMUSDT', 'IOSTUSDT', 'THETAUSDT', 'ALGOUSDT', 'ZILUSDT',
'KNCUSDT', 'ZRXUSDT', 'COMPUSDT', 'OMGUSDT', 'DOGEUSDT', 'SXPUSDT', 'KAVAUSDT', 'BANDUSDT', 'RLCUSDT',
'WAVESUSDT', 'MKRUSDT', 'SNXUSDT', 'DOTUSDT', 'DEFIUSDT', 'YFIUSDT', 'BALUSDT', 'CRVUSDT', 'TRBUSDT',
'RUNEUSDT', 'SUSHIUSDT', 'SRMUSDT', 'EGLDUSDT', 'SOLUSDT', 'ICXUSDT', 'STORJUSDT', 'BLZUSDT', 'UNIUSDT',
'AVAXUSDT', 'FTMUSDT', 'HNTUSDT', 'ENJUSDT', 'FLMUSDT', 'TOMOUSDT', 'RENUSDT', 'KSMUSDT', 'NEARUSDT',
'AAVEUSDT', 'FILUSDT', 'RSRUSDT', 'LRCUSDT', 'MATICUSDT', 'OCEANUSDT', 'CVCUSDT', 'BELUSDT', 'CTKUSDT',
'AXSUSDT', 'ALPHAUSDT', 'ZENUSDT', 'SKLUSDT', 'GRTUSDT', '1INCHUSDT', 'CHZUSDT', 'SANDUSDT', 'ANKRUSDT',
'BTSUSDT', 'LITUSDT', 'UNFIUSDT', 'REEFUSDT', 'RVNUSDT', 'SFPUSDT', 'XEMUSDT', 'BTCSTUSDT', 'COTIUSDT',
'CHRUSDT', 'MANAUSDT', 'ALICEUSDT', 'HBARUSDT', 'ONEUSDT', 'LINAUSDT', 'STMXUSDT', 'DENTUSDT', 'CELRUSDT',
'HOTUSDT', 'MTLUSDT', 'OGNUSDT', 'NKNUSDT', 'SCUSDT', 'DGBUSDT', '1000SHIBUSDT', 'ICPUSDT', 'BAKEUSDT',
'GTCUSDT', 'BTCDOMUSDT', 'TLMUSDT', 'IOTXUSDT', 'AUDIOUSDT', 'RAYUSDT', 'C98USDT', 'MASKUSDT', 'ATAUSDT',
'DYDXUSDT', '1000XECUSDT', 'GALAUSDT', 'CELOUSDT', 'ARUSDT', 'KLAYUSDT', 'ARPAUSDT', 'CTSIUSDT', 'LPTUSDT',
'ENSUSDT', 'PEOPLEUSDT', 'ANTUSDT', 'ROSEUSDT', 'DUSKUSDT', 'FLOWUSDT', 'IMXUSDT', 'API3USDT', 'GMTUSDT',
'APEUSDT', 'BNXUSDT', 'WOOUSDT', 'FTTUSDT', 'JASMYUSDT', 'DARUSDT', 'GALUSDT', 'OPUSDT', 'BTCUSDT',
'ETHUSDT', 'INJUSDT', 'STGUSDT', 'FOOTBALLUSDT', 'SPELLUSDT', '1000LUNCUSDT', 'LUNA2USDT', 'LDOUSDT',
'CVXUSDT']

print(len(symbols))

À l' extérieur:

153

#Function to obtain any period of K-line
def GetKlines(symbol='BTCUSDT',start='2020-8-10',end='2021-8-10',period='1h',base='fapi',v = 'v1'):
    Klines = []
    start_time = int(time.mktime(datetime.strptime(start, "%Y-%m-%d").timetuple()))*1000 + 8*60*60*1000
    end_time =  min(int(time.mktime(datetime.strptime(end, "%Y-%m-%d").timetuple()))*1000 + 8*60*60*1000,time.time()*1000)
    intervel_map = {'m':60*1000,'h':60*60*1000,'d':24*60*60*1000}
    while start_time < end_time:
        mid_time = start_time+1000*int(period[:-1])*intervel_map[period[-1]]
        url = 'https://'+base+'.binance.com/'+base+'/'+v+'/klines?symbol=%s&interval=%s&startTime=%s&endTime=%s&limit=1000'%(symbol,period,start_time,mid_time)
        res = requests.get(url)
        res_list = res.json()
        if type(res_list) == list and len(res_list) > 0:
            start_time = res_list[-1][0]+int(period[:-1])*intervel_map[period[-1]]
            Klines += res_list
        if type(res_list) == list and len(res_list) == 0:
            start_time = start_time+1000*int(period[:-1])*intervel_map[period[-1]]
        if mid_time >= end_time:
            break
    df = pd.DataFrame(Klines,columns=['time','open','high','low','close','amount','end_time','volume','count','buy_amount','buy_volume','null']).astype('float')
    df.index = pd.to_datetime(df.time,unit='ms')
    return df
start_date = '2022-1-1'
end_date = '2022-09-14'
period = '1h'
df_dict = {}
for symbol in symbols:
    df_s = GetKlines(symbol=symbol,start=start_date,end=end_date,period=period,base='fapi',v='v1')
    if not df_s.empty:
        df_dict[symbol] = df_s
symbols = list(df_dict.keys())
print(df_s.columns)

À l' extérieur:

Index(['time', 'open', 'high', 'low', 'close', 'amount', 'end_time', 'volume',
       'count', 'buy_amount', 'buy_volume', 'null'],
      dtype='object')

Les données qui nous intéressent: prix de clôture, prix d'ouverture, volume des transactions, nombre de transactions et proportion d'achat des preneurs sont d'abord extraites des données de la ligne K. Sur la base de ces données, les facteurs requis sont traités.

df_close = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq=period),columns=df_dict.keys())
df_open = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq=period),columns=df_dict.keys())
df_volume = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq=period),columns=df_dict.keys())
df_buy_ratio = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq=period),columns=df_dict.keys())
df_count = pd.DataFrame(index=pd.date_range(start=start_date, end=end_date, freq=period),columns=df_dict.keys())
for symbol in df_dict.keys():
    df_s = df_dict[symbol]
    df_close[symbol] = df_s.close
    df_open[symbol] = df_s.open
    df_volume[symbol] = df_s.volume
    df_count[symbol] = df_s['count']
    df_buy_ratio[symbol] = df_s.buy_amount/df_s.amount
df_close = df_close.dropna(how='all')
df_open = df_open.dropna(how='all')
df_volume = df_volume.dropna(how='all')
df_count = df_count.dropna(how='all')
df_buy_ratio = df_buy_ratio.dropna(how='all')

La performance globale de l'indice de marché est sombre, ayant chuté de 60% depuis le début de l'année jusqu'à ces derniers jours.

df_norm = df_close/df_close.fillna(method='bfill').iloc[0] #normalization
df_norm.mean(axis=1).plot(figsize=(15,6),grid=True);
#Final index profit chart

img

Jugement de validité des facteurs

  • Méthode de régression Le rendement de la période suivante est la variable dépendante, et le facteur à tester est la variable indépendante. Le coefficient obtenu par régression est également le rendement du facteur. Après la construction de l'équation de régression, la validité et la volatilité des facteurs sont généralement considérées par référence à la valeur moyenne absolue de la valeur du coefficient t, à la proportion de la séquence de valeur absolue de la valeur du coefficient t supérieure à 2, au rendement du facteur annualisé, à la volatilité du profit du facteur annualisé et au rapport Sharpe du profit du facteur. Plusieurs facteurs peuvent être régressés à la fois. Veuillez vous référer au document barra pour plus de détails.

  • Indicateur de la fréquence IC, IR et autres indicateurs Le soi-disant IC est le coefficient de corrélation entre le facteur et le taux de rendement de la période suivante. Maintenant, RANK_ IC est également utilisé généralement, c'est le coefficient de corrélation entre le classement des facteurs et le taux de rendement de la prochaine action. IR est généralement la valeur moyenne de la séquence IC / l'écart type de la séquence IC.

  • Méthode de régression stratifiée Dans cet article, nous utiliserons cette méthode, qui consiste à trier les monnaies en fonction des facteurs à tester, à les diviser en N groupes pour le backtesting de groupe et à utiliser une période fixe pour l'ajustement de la position. Si la situation est idéale, le taux de rendement des monnaies du groupe N montrera une bonne monotonie, augmentant ou diminuant de manière monotone, et l'écart de revenu de chaque groupe est grand. Ces facteurs se reflètent dans une bonne discrimination. Si le premier groupe a les plus hauts profits et le dernier groupe a les plus bas profits, puis allez long dans le premier groupe et allez court dans le dernier groupe pour obtenir le rendement final, qui est l'indicateur de référence du ratio Sharp.

Opération réelle de backtest

Les pièces à sélectionner sont divisées en 3 groupes en fonction du classement des facteurs du plus petit au plus grand. Chaque groupe de devises représente environ 1/3 du total. Si un facteur est efficace, plus le nombre de points dans chaque groupe est petit, plus le taux de rendement sera élevé, mais cela signifie également que chaque devise a relativement plus de fonds alloués. Si les positions longues et courtes sont respectivement double effet de levier, et le premier groupe et le dernier groupe sont respectivement 10 devises, alors une devise représente 10% du total. Si une devise qui est raccourcie est doublée, alors 20% sont retirés; Si le nombre de groupes est de 50, alors 4% seront retirés. La diversification des devises peut réduire le risque de cygnes noirs. Allez long le premier groupe (facteur de valeur minimale), allez au troisième groupe. Le plus grand facteur est, et le nombre de revenus est plus élevé, vous pouvez simplement inverser le facteur et la position courte ou courte en un revenu réciproque ou négatif.

En général, la capacité de prédiction des facteurs peut être évaluée grossièrement en fonction du taux de retour et du rapport Sharpe du backtest final. En outre, il doit également se référer à la simpleur de l'expression des facteurs, à la sensibilité à la taille du groupe, à l'intervalle d'ajustement de position et au temps initial du backtest.

En ce qui concerne la fréquence d'ajustement des positions, le marché boursier a généralement une période de 5 jours, 10 jours et un mois. Cependant, pour le marché de la monnaie numérique, une telle période est sans aucun doute trop longue, et le marché dans le bot réel est surveillé en temps réel. Il n'est pas nécessaire de s'en tenir à une période spécifique pour ajuster à nouveau les positions. Par conséquent, dans le bot réel, nous ajustons les positions en temps réel ou dans un court laps de temps.

En ce qui concerne la façon de fermer la position, selon la méthode traditionnelle, la position peut être fermée si elle n'est pas dans le groupe lors du tri la prochaine fois. Cependant, dans le cas de l'ajustement de la position en temps réel, certaines devises peuvent être juste à la limite, ce qui peut entraîner une fermeture de position aller-retour. Par conséquent, cette stratégie adopte la méthode d'attendre les changements de regroupement, puis de fermer la position lorsque la position dans la direction opposée doit être ouverte. Par exemple, le premier groupe va long. Lorsque les devises dans le statut de position longue sont divisées en troisième groupe, alors fermez la position et allez court. Si la position est fermée dans une période fixe, comme tous les jours ou toutes les 8 heures, vous pouvez également fermer la position sans être dans un groupe. Essayez autant que vous le pouvez.

#Backtest engine
class Exchange:
    
    def __init__(self, trade_symbols, fee=0.0004, initial_balance=10000):
        self.initial_balance = initial_balance #Initial assets
        self.fee = fee
        self.trade_symbols = trade_symbols
        self.account = {'USDT':{'realised_profit':0, 'unrealised_profit':0, 'total':initial_balance, 'fee':0, 'leverage':0, 'hold':0}}
        for symbol in trade_symbols:
            self.account[symbol] = {'amount':0, 'hold_price':0, 'value':0, 'price':0, 'realised_profit':0,'unrealised_profit':0,'fee':0}
            
    def Trade(self, symbol, direction, price, amount):
        
        cover_amount = 0 if direction*self.account[symbol]['amount'] >=0 else min(abs(self.account[symbol]['amount']), amount)
        open_amount = amount - cover_amount
        self.account['USDT']['realised_profit'] -= price*amount*self.fee #Net of fees
        self.account['USDT']['fee'] += price*amount*self.fee
        self.account[symbol]['fee'] += price*amount*self.fee

        if cover_amount > 0: #Close position first
            self.account['USDT']['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount  #Profits
            self.account[symbol]['realised_profit'] += -direction*(price - self.account[symbol]['hold_price'])*cover_amount
            
            self.account[symbol]['amount'] -= -direction*cover_amount
            self.account[symbol]['hold_price'] = 0 if self.account[symbol]['amount'] == 0 else self.account[symbol]['hold_price']
            
        if open_amount > 0:
            total_cost = self.account[symbol]['hold_price']*direction*self.account[symbol]['amount'] + price*open_amount
            total_amount = direction*self.account[symbol]['amount']+open_amount
            
            self.account[symbol]['hold_price'] = total_cost/total_amount
            self.account[symbol]['amount'] += direction*open_amount
                    
    
    def Buy(self, symbol, price, amount):
        self.Trade(symbol, 1, price, amount)
        
    def Sell(self, symbol, price, amount):
        self.Trade(symbol, -1, price, amount)
        
    def Update(self, close_price): #Update assets
        self.account['USDT']['unrealised_profit'] = 0
        self.account['USDT']['hold'] = 0
        for symbol in self.trade_symbols:
            if not np.isnan(close_price[symbol]):
                self.account[symbol]['unrealised_profit'] = (close_price[symbol] - self.account[symbol]['hold_price'])*self.account[symbol]['amount']
                self.account[symbol]['price'] = close_price[symbol]
                self.account[symbol]['value'] = abs(self.account[symbol]['amount'])*close_price[symbol]
                self.account['USDT']['hold'] += self.account[symbol]['value']
                self.account['USDT']['unrealised_profit'] += self.account[symbol]['unrealised_profit']
        self.account['USDT']['total'] = round(self.account['USDT']['realised_profit'] + self.initial_balance + self.account['USDT']['unrealised_profit'],6)
        self.account['USDT']['leverage'] = round(self.account['USDT']['hold']/self.account['USDT']['total'],3)

#Function of test factor
def Test(factor, symbols, period=1, N=40, value=300):
    e = Exchange(symbols, fee=0.0002, initial_balance=10000)
    res_list = []
    index_list = []
    factor = factor.dropna(how='all')
    for idx, row in factor.iterrows():
        if idx.hour % period == 0:
            buy_symbols =  row.sort_values().dropna()[0:N].index
            sell_symbols = row.sort_values().dropna()[-N:].index
            prices = df_close.loc[idx,]
            index_list.append(idx)
            for symbol in symbols:
                if symbol in buy_symbols and e.account[symbol]['amount'] <= 0:
                    e.Buy(symbol,prices[symbol],value/prices[symbol]-e.account[symbol]['amount'])
                if symbol in sell_symbols and e.account[symbol]['amount'] >= 0:
                    e.Sell(symbol,prices[symbol], value/prices[symbol]+e.account[symbol]['amount'])
            e.Update(prices)
            res_list.append([e.account['USDT']['total'],e.account['USDT']['hold']])
    return pd.DataFrame(data=res_list, columns=['total','hold'],index = index_list)

Test de facteur simple

Facteur de volume de négociation: les monnaies longues simples avec un faible volume de négociation et les monnaies courtes avec un volume de négociation élevé, qui se comportent très bien, ce qui indique que les monnaies populaires ont tendance à baisser.

Facteur de prix de négociation: l'effet d'une monnaie longue avec des prix bas et d'une monnaie courte avec des prix élevés est à la fois ordinaire.

Facteur de nombre de transactions: La performance est très similaire au volume de transactions. Il est évident que la corrélation entre le facteur de volume de transactions et le facteur de nombre de transactions est très élevée. En fait, la corrélation moyenne entre eux dans différentes devises a atteint 0,97, ce qui indique que les deux facteurs sont très similaires. Ce facteur doit être pris en compte lors de la synthèse de plusieurs facteurs.

3h facteur de momentum: (df_close - df_close. shift (3)) /df_ close. shift(3). C'est-à-dire la hausse de 3 heures du facteur. Les résultats du backtest montrent que la hausse de 3 heures a des caractéristiques de régression évidentes, c'est-à-dire que la hausse est plus facile à tomber plus tard. La performance globale est correcte, mais il y a aussi une longue période de retrait et d'oscillation.

Facteur de momentum de 24h: le résultat de la période d'ajustement de position de 24h est bon, le rendement est similaire à celui de la momentum de 3h et le retrait est plus petit.

Facteur de changement du volume des transactions: df_ volume.rolling ((24).mean() /df_ volume. rolling (96). mean(), c'est-à-dire le rapport entre le volume des transactions au cours de la dernière journée et le volume des transactions au cours des trois derniers jours. La position est ajustée toutes les 8 heures. Les résultats du backtesting étaient bons et le retrait était également relativement faible, ce qui indique que ceux qui avaient un volume de transactions actif étaient plus enclins à diminuer.

Facteur de changement du nombre de transactions: df_ count.rolling ((24).mean() /df_ count.rolling ((96). mean (), c'est-à-dire le ratio du nombre de transactions du dernier jour par rapport au nombre de transactions des trois derniers jours. La position est ajustée toutes les 8h. Les résultats du backtesting étaient bons et le retrait est également relativement faible, ce qui indique que ceux dont le volume de transactions actives étaient plus enclins à diminuer.

Facteur de variation de la valeur d'une seule transaction: - ((df_volume.rolling(24).mean()/df_count.rolling(24.mean())/(df_volume.rolling(24.mean()/df_count.rolling(96.mean()) , c'est-à-dire le rapport entre la valeur de la transaction du dernier jour et la valeur de la transaction des trois derniers jours, et la position sera ajustée toutes les 8h. Ce facteur est également fortement corrélé avec le facteur de volume de transaction.

Facteur de changement du preneur par proportion de transaction: df_buy_ratio.rolling ((24).mean() /df_buy_ratio.rolling ((96).mean(), c'est-à-dire le rapport du preneur par volume au volume total de transaction du dernier jour à la valeur de transaction des trois derniers jours, et la position sera ajustée toutes les 8 heures. Ce facteur fonctionne assez bien et n'a guère de corrélation avec le facteur de volume de transaction.

Facteur de volatilité: (df_close/df_open).rolling(24).std(), aller long les devises avec une faible volatilité, il a un certain effet.

Facteur de corrélation entre le volume des transactions et le prix de clôture: df_close.rolling ((96).corr ((df_volume), le prix de clôture au cours des quatre derniers jours a un facteur de corrélation du volume des transactions, qui s'est bien comporté dans l'ensemble.

Les facteurs énumérés ici sont basés sur le volume des prix. En fait, la combinaison de formules de facteurs peut être très complexe sans logique évidente. Vous pouvez vous référer à la célèbre méthode de construction de facteurs ALPHA101:https://github.com/STHSF/alpha101.

#transaction volume
factor_volume = df_volume
factor_volume_res = Test(factor_volume, symbols, period=4)
factor_volume_res.total.plot(figsize=(15,6),grid=True);

img

#transaction price
factor_close = df_close
factor_close_res = Test(factor_close, symbols, period=8)
factor_close_res.total.plot(figsize=(15,6),grid=True);

img

#transaction count
factor_count = df_count
factor_count_res = Test(factor_count, symbols, period=8)
factor_count_res.total.plot(figsize=(15,6),grid=True);

img

print(df_count.corrwith(df_volume).mean())

0.9671246744996017

#3h momentum factor
factor_1 =  (df_close - df_close.shift(3))/df_close.shift(3)
factor_1_res = Test(factor_1,symbols,period=1)
factor_1_res.total.plot(figsize=(15,6),grid=True);

img

#24h momentum factor
factor_2 =  (df_close - df_close.shift(24))/df_close.shift(24)
factor_2_res = Test(factor_2,symbols,period=24)
tamenxuanfactor_2_res.total.plot(figsize=(15,6),grid=True);

img

#factor of transaction volume
factor_3 = df_volume.rolling(24).mean()/df_volume.rolling(96).mean()
factor_3_res = Test(factor_3, symbols, period=8)
factor_3_res.total.plot(figsize=(15,6),grid=True);

img

#factor of transaction number
factor_4 = df_count.rolling(24).mean()/df_count.rolling(96).mean()
factor_4_res = Test(factor_4, symbols, period=8)
factor_4_res.total.plot(figsize=(15,6),grid=True);

img

#factor correlation
print(factor_4.corrwith(factor_3).mean())

0.9707239580854841

#single transaction value factor
factor_5 = -(df_volume.rolling(24).mean()/df_count.rolling(24).mean())/(df_volume.rolling(24).mean()/df_count.rolling(96).mean())
factor_5_res = Test(factor_5, symbols, period=8)
factor_5_res.total.plot(figsize=(15,6),grid=True);

img

print(factor_4.corrwith(factor_5).mean())

0.861206620552479

#proportion factor of taker by transaction
factor_6 = df_buy_ratio.rolling(24).mean()/df_buy_ratio.rolling(96).mean()
factor_6_res = Test(factor_6, symbols, period=4)
factor_6_res.total.plot(figsize=(15,6),grid=True);

img

print(factor_3.corrwith(factor_6).mean())

0.1534572192503726

#volatility factor
factor_7 = (df_close/df_open).rolling(24).std()
factor_7_res = Test(factor_7, symbols, period=2)
factor_7_res.total.plot(figsize=(15,6),grid=True);

img

#correlation factor between transaction volume and closing price
factor_8 = df_close.rolling(96).corr(df_volume)
factor_8_res = Test(factor_8, symbols, period=4)
factor_8_res.total.plot(figsize=(15,6),grid=True);

img

Synthèse multifactorielle

Il est certainement la partie la plus importante du processus de construction de stratégie pour découvrir de nouveaux facteurs efficaces constamment, mais sans une bonne méthode de synthèse de facteurs, un excellent facteur alpha unique ne peut pas jouer son rôle maximal.

Méthode de poids égal: tous les facteurs à synthétiser sont additionnés avec des poids égaux pour obtenir de nouveaux facteurs après la synthèse.

Méthode de pondération du taux de rendement des facteurs historiques: tous les facteurs à combiner sont ajoutés selon la moyenne arithmétique du taux de rendement des facteurs historiques dans la dernière période en tant que poids pour obtenir un nouveau facteur après synthèse.

Maximiser la pondération IC_IR: la valeur moyenne IC du facteur composite sur une période d'histoire est utilisée comme estimation de la valeur IC du facteur composite dans la période suivante, et la matrice de covariance de la valeur IC historique est utilisée comme estimation de la volatilité du facteur composite dans la période suivante.

L'analyse des composants principaux (PCA) est une méthode courante pour la réduction de la dimensionnalité des données, et la corrélation entre les facteurs peut être élevée.

Ce document fera référence à l'attribution manuelle de la validité des facteurs.ae933a8c-5a94-4d92-8f33-d92b70c36119.pdf

Lorsque l'on teste des facteurs simples, le tri est fixe, mais la synthèse multifactorielle doit combiner des données complètement différentes, donc tous les facteurs doivent être standardisés, et la valeur extrême et la valeur manquante doivent être généralement supprimées.

#standardize functions, remove missing values and extreme values, and standardize
def norm_factor(factor):
    factor = factor.dropna(how='all')
    factor_clip = factor.apply(lambda x:x.clip(x.quantile(0.2), x.quantile(0.8)),axis=1)
    factor_norm = factor_clip.add(-factor_clip.mean(axis=1),axis ='index').div(factor_clip.std(axis=1),axis ='index')
    return factor_norm


df_volume_norm = norm_factor(df_volume)
factor_1_norm = norm_factor(factor_1)
factor_6_norm = norm_factor(factor_6)
factor_7_norm = norm_factor(factor_7)
factor_8_norm = norm_factor(factor_8)
factor_total = 0.6*df_volume_norm + 0.4*factor_1_norm + 0.2*factor_6_norm + 0.3*factor_7_norm + 0.4*factor_8_norm
factor_total_res = Test(factor_total, symbols, period=8)
factor_total_res.total.plot(figsize=(15,6),grid=True);

img

Résumé

Ce document introduit la méthode de test du facteur unique et teste les facteurs simples communs, et introduit initialement la méthode de synthèse de facteurs multiples. Cependant, il existe de nombreux contenus de recherche de facteurs multiples. Chaque point mentionné dans le document peut être développé davantage. Il s'agit d'un moyen réalisable de transformer la recherche sur diverses stratégies en exploration du facteur alpha. L'utilisation de la méthodologie des facteurs peut grandement accélérer la vérification des idées de trading, et il existe de nombreux matériaux de référence.

Le vrai robot de:https://www.fmz.com/robot/486605


Relationnée

Plus de