
Dans le trading programmatique, il est souvent nécessaire de calculer la moyenne et la variance, comme lors du calcul d’indicateurs tels que les moyennes mobiles et la volatilité. Lorsque nous avons besoin de calculs à haute fréquence et sur de longues périodes, nous devons conserver des données historiques à long terme, ce qui est inutile, prend du temps et consomme beaucoup de ressources. Cet article présente un algorithme de mise à jour en ligne pour le calcul des moyennes et des variances pondérées, ce qui est particulièrement important pour le traitement des flux de données en temps réel et l’ajustement dynamique des stratégies de trading, en particulier les stratégies à haute fréquence. L’article fournit également une implémentation de code Python correspondante pour aider les traders à déployer et à appliquer rapidement cet algorithme dans des transactions réelles.
Si nous utilisons \(\mu_n\) pour représenter la moyenne du \(n\)ième point de données, en supposant que nous avons déjà calculé la moyenne de \(\mu_{n-1}\) du \({n-1}\)ième point de données , maintenant nous Un nouveau point de données \(x_{n}\) est reçu. Nous voulons calculer la nouvelle moyenne \(\mu_{n}\) qui inclut ce nouveau point de données. Ce qui suit est une dérivation détaillée.
\(\mu_n = \frac{1}{n} \sum_{i=1}^{n} x_i\) \(\mu_n = \frac{1}{n} \left( x_n + \sum_{i=1}^{n-1} x_i \right)\) \(\mu_{n-1} = \frac{1}{n-1} \sum_{i=1}^{n-1} x_i\) \((n-1)\mu_{n-1} = \sum_{i=1}^{n-1} x_i\) \(\mu_n = \frac{1}{n} \left( x_n + (n-1)\mu_{n-1} \right)\) \(\mu_n = \mu_{n-1} + \frac{1}{n} (x_n - \mu_{n-1})\)
Le processus de mise à jour des écarts peut être décomposé selon les étapes suivantes :
\(S_n = \sum_{i=1}^{n} (x_i - \mu_n)^2\) \(S_n = \sum_{i=1}^{n} x_i^2 - n\mu_n^2\) \(S_n - S_{n-1} = x_n^2 - n\mu_n^2 + (n - 1)\mu_{n-1}^2\) \(S_n - S_{n-1} = (x_n - \mu_{n-1})(x_n - \mu_n)\) \(S_n = S_{n-1} + (x_n - \mu_{n-1})(x_n - \mu_n)\) \(\sigma_n^2 = \frac{S_n}{n}\)
Comme on peut le voir à partir des deux formules ci-dessus, ce processus nous permet de mettre à jour la nouvelle moyenne et la nouvelle variance lors de la réception de chaque nouveau point de données \(x_n\) en conservant uniquement la moyenne et la variance des données précédentes, sans enregistrer les données historiques. le calcul est plus rapide. Mais le problème est que ce qui est calculé de cette manière est la moyenne et la variance de l’échantillon entier, alors que dans la stratégie réelle, nous devons considérer une certaine période fixe. En observant la mise à jour moyenne ci-dessus, nous pouvons voir que le nouveau montant de mise à jour moyenne est l’écart des nouvelles données par rapport à la moyenne passée multiplié par un ratio. Si ce ratio est fixe, nous obtiendrons la moyenne pondérée exponentiellement dont nous parlerons ensuite.
La moyenne pondérée exponentiellement peut être définie par la relation récursive suivante :
\(\mu_t = \alpha x_t + (1 - \alpha) \mu_{t-1}\)
où \(\mu_t\) est la moyenne pondérée exponentiellement au moment \(t\), \(x_t\) est la valeur observée au moment \(t\), \(\alpha\) est le facteur de pondération et \(\mu_{t-1}\) Il s’agit de la moyenne pondérée exponentiellement du point temporel précédent.
Pour la variance, nous devons calculer la moyenne pondérée exponentiellement des écarts au carré à chaque instant. Ceci peut être réalisé grâce à la relation de récurrence suivante :
\(S_n = \alpha S_{n-1} + (1 - \alpha)(x_n - \mu_n)(x_n - \mu_{n-1})\)
où \(\sigma_t^2\) est la variance pondérée exponentiellement au moment \(t\), et \(\sigma_{t-1}^2\) est la variance pondérée exponentiellement au moment précédent.
Observez la moyenne et la variance pondérées de manière exponentielle. Leurs formes de mise à jour incrémentielle sont conformes à l’intuition. Elles conservent toutes deux une partie de la valeur passée et ajoutent le nouveau changement. Le processus de dérivation spécifique peut être référé à cet article : https://fanf2.user . srcf.net/hermes/doc/antiforgery/stats.pdf
La moyenne simple (également appelée moyenne arithmétique) et la moyenne pondérée exponentielle sont deux mesures statistiques courantes, chacune ayant des caractéristiques et des utilisations différentes. La moyenne simple donne un poids égal à chaque observation et reflète l’emplacement central de l’ensemble de données. La moyenne pondérée exponentiellement est un calcul récursif qui donne plus de poids aux observations plus récentes. Le poids diminue de manière exponentielle à mesure que l’observation s’éloigne de l’heure actuelle.
Bien que la moyenne simple et la moyenne pondérée exponentiellement soient conceptuellement différentes, nous pouvons faire en sorte que la moyenne pondérée exponentiellement se rapproche d’une moyenne simple pour un nombre spécifique d’observations en choisissant une valeur appropriée de \(\alpha\). Cette relation approximative peut être décrite par la taille effective de l’échantillon, qui est une fonction du facteur de pondération \(\alpha\) dans la moyenne pondérée exponentiellement.
La moyenne mobile simple (SMA) est la moyenne arithmétique de tous les prix dans une fenêtre temporelle donnée. Pour une fenêtre temporelle \(N\), le centroïde de la SMA (où se trouve la moyenne) peut être considéré comme étant :
\(\text{Centre de l'AMS} = \frac{1 + N}{2}\)
La moyenne mobile exponentielle (EMA) est une moyenne pondérée dans laquelle les points de données les plus récents ont plus de poids. Le poids de l’EMA diminue de manière exponentielle au fil du temps. Le centroïde de l’EMA peut être obtenu en additionnant les séries suivantes :
\(\text{Centre de l'EMA} = \alpha \times \left[1 + 2(1 - \alpha) + 3(1 - \alpha)^2 + \cdots \right] = \frac{1}{\alpha}\)
Lorsque nous supposons que SMA et EMA ont le même centre de masse, nous obtenons :
\(\frac{1 + N}{2} = \frac{1}{\alpha}\)
En résolvant cette équation, nous pouvons obtenir la relation entre \(\alpha\) et \(N\) :
\(\alpha = \frac{2}{N + 1}\)
Cela signifie que, pour une moyenne mobile en nombre de \(N\) jours donnée, la valeur \(\alpha\) correspondante peut être utilisée pour calculer une moyenne mobile en nombre « équivalente » telle que les deux aient le même centre de masse et que les résultats soient très similaires.
Supposons que nous ayons une EMA qui est mise à jour toutes les secondes avec un facteur de pondération de \(\alpha_1\). Cela signifie que chaque seconde, un nouveau point de données est ajouté à l’EMA avec un poids de \(\alpha_1\), tandis que l’impact des anciens points de données est multiplié par \(1 - \alpha_1\).
Si nous modifions la fréquence de mise à jour, par exemple pour mettre à jour toutes les \(f\) secondes, nous voulons trouver un nouveau facteur de pondération \(\alpha_2\) tel que l’impact total d’un point de données dans les \(f\) secondes soit le même que lors d’une mise à jour toutes les secondes .
Pendant \(f\) secondes, si aucune mise à jour n’est effectuée, l’influence des anciens points de données diminuera continuellement \(f\) fois, chaque fois multipliée par \(1 - \alpha_1\). Par conséquent, le facteur de décroissance total après \(f\) secondes est \((1 - \alpha_1)^f\).
Afin que l’EMA mis à jour toutes les \(f\) secondes ait le même effet de décroissance dans un cycle de mise à jour que l’EMA mis à jour une fois par seconde, nous définissons le facteur de décroissance total après \(f\) secondes pour qu’il soit égal au facteur de décroissance dans une mise à jour faire du vélo:
\((1 - \alpha_1)^f = 1 - \alpha_2\)
En résolvant cette équation, nous obtenons le nouveau facteur de pondération \(\alpha_2\) :
\(\alpha_2 = 1 - (1 - \alpha_1)^f\)
Cette formule donne une approximation du nouveau facteur de pondération \(\alpha_2\) qui maintient l’effet de lissage EMA constant lorsque la fréquence de mise à jour change. Par exemple, nous calculons la moyenne des prix \(\alpha_1\) à 0,001 et mettons à jour le dernier prix toutes les 10 secondes. Si nous la modifions à 1 seconde, l’équivalent \(\alpha_2\) est d’environ 0,01
class ExponentialWeightedStats:
def __init__(self, alpha):
self.alpha = alpha
self.mu = 0
self.S = 0
self.initialized = False
def update(self, x):
if not self.initialized:
self.mu = x
self.S = 0
self.initialized = True
else:
temp = x - self.mu
new_mu = self.mu + self.alpha * temp
self.S = self.alpha * self.S + (1 - self.alpha) * temp * (x - self.mu)
self.mu = new_mu
@property
def mean(self):
return self.mu
@property
def variance(self):
return self.S
# 使用示例
alpha = 0.05 # 权重因子
stats = ExponentialWeightedStats(alpha)
data_stream = [] # 数据流
for data_point in data_stream:
stats.update(data_point)
Dans le trading programmatique à haute fréquence, le traitement rapide des données en temps réel est crucial. Afin d’améliorer l’efficacité informatique et de réduire la consommation de ressources, cet article présente un algorithme de mise à jour en ligne permettant de calculer en continu la moyenne pondérée et la variance d’un flux de données. Le calcul des mises à jour incrémentales en temps réel peut également être utilisé pour calculer diverses données et indicateurs statistiques, tels que la corrélation entre deux prix d’actifs, l’ajustement linéaire, etc., ce qui présente un grand potentiel. Les mises à jour incrémentielles traitent les données comme un système de signaux, ce qui constitue une évolution de la pensée par rapport aux calculs à période fixe. Si votre stratégie comporte également une partie qui enregistre les calculs de données historiques, vous pouvez aussi bien la modifier selon cette idée, enregistrer uniquement l’estimation de l’état du système, et lorsque de nouvelles données arrivent, mettre à jour l’état du système, et ainsi de suite.