J'ai un ensemble de données de prix OHLC, que j'ai analysé de CSV dans un fichier de données Pandas et rééchantillonné à 15 minutes):
<class 'pandas.core.frame.DataFrame'>
DatetimeIndex: 500047 entries, 1998-05-04 04:45:00 to 2012-08-07 00:15:00
Freq: 15T
Data columns:
Close 363152 non-null values
High 363152 non-null values
Low 363152 non-null values
Open 363152 non-null values
dtypes: float64(4)
J'aimerais ajouter diverses colonnes calculées, en commençant par des colonnes simples telles que period Range (H-L), puis des booléens pour indiquer la présence de modèles de prix que je définirai, par exemple. un motif de bougie de marteau, pour lequel un exemple de définition:
def closed_in_top_half_of_range(h,l,c):
return c > l + (h-1)/2
def lower_wick(o,l,c):
return min(o,c)-l
def real_body(o,c):
return abs(c-o)
def lower_wick_at_least_twice_real_body(o,l,c):
return lower_wick(o,l,c) >= 2 * real_body(o,c)
def is_hammer(row):
return lower_wick_at_least_twice_real_body(row["Open"],row["Low"],row["Close"]) \
and closed_in_top_half_of_range(row["High"],row["Low"],row["Close"])
Problème de base: comment mapper la fonction à la colonne, en particulier dans laquelle je voudrais référencer plus d’une autre colonne ou toute la ligne ou quoi que ce soit?
This post traite de l'ajout de deux colonnes calculées d'une seule colonne source, ce qui est proche, mais pas tout à fait.
Et légèrement plus avancé: pour les modèles de prix déterminés en fonction de plusieurs barres (T), comment puis-je référencer différentes lignes (par exemple, T-1, T-2, etc.) dans la définition de fonction?
Le code exact variera pour chacune des colonnes que vous voulez faire, mais vous voudrez probablement utiliser les fonctions map
et apply
. Dans certains cas, vous pouvez simplement utiliser directement les colonnes existantes, car elles sont des objets Pandas Series, qui fonctionnent également comme des tableaux Numpy, qui fonctionnent automatiquement au niveau des éléments pour les opérations mathématiques habituelles.
>>> d
A B C
0 11 13 5
1 6 7 4
2 8 3 6
3 4 8 7
4 0 1 7
>>> (d.A + d.B) / d.C
0 4.800000
1 3.250000
2 1.833333
3 1.714286
4 0.142857
>>> d.A > d.C
0 True
1 True
2 True
3 False
4 False
Si vous devez utiliser des opérations telles que max et min dans une ligne, vous pouvez utiliser apply
avec axis=1
Pour appliquer la fonction de votre choix à chaque ligne. Voici un exemple qui calcule min(A, B)-C
, ce qui semble être votre "mèche inférieure":
>>> d.apply(lambda row: min([row['A'], row['B']])-row['C'], axis=1)
0 6
1 2
2 -3
3 -3
4 -7
J'espère que cela vous donne une idée de la façon de procéder.
Edition: pour comparer les lignes aux lignes voisines, la méthode la plus simple consiste à découper les colonnes à comparer, en laissant de côté le début/la fin, puis à comparer les tranches obtenues. Par exemple, cela vous indiquera pour quelles lignes l'élément de la colonne A est inférieur à celui de la ligne suivante de la colonne C:
d['A'][:-1] < d['C'][1:]
et cela le fait dans l'autre sens, en vous indiquant quelles lignes ont un moins que le précédent: C:
d['A'][1:] < d['C'][:-1]
Faire ['A"][:-1]
Couper le dernier élément de la colonne A et ['C'][1:]
Couper le premier élément de la colonne C, de sorte que lorsque vous les alignez et les comparez, vous comparez chaque élément en A avec le C de la rangée suivante.
Tu aurais pu is_hammer
en terme de row["Open"]
etc. comme suit
def is_hammer(rOpen,rLow,rClose,rHigh):
return lower_wick_at_least_twice_real_body(rOpen,rLow,rClose) \
and closed_in_top_half_of_range(rHigh,rLow,rClose)
Ensuite, vous pouvez utiliser la carte:
df["isHammer"] = map(is_hammer, df["Open"], df["Low"], df["Close"], df["High"])
Pour la deuxième partie de votre question, vous pouvez également utiliser shift
, par exemple:
df['t-1'] = df['t'].shift(1)
t-1
contiendrait alors les valeurs de t une ligne plus haut.
http://pandas.pydata.org/pandas-docs/stable/generated/pandas.DataFrame.shift.html
Les quatre premières fonctions que vous listez fonctionneront également sur les vecteurs, à la différence que lower_wick doit être adapté. Quelque chose comme ça,
def lower_wick_vec(o, l, c):
min_oc = numpy.where(o > c, c, o)
return min_oc - l
où o, l et c sont des vecteurs. Vous pouvez le faire de cette façon plutôt que de prendre le df en entrée et d’éviter d’utiliser numpy, bien que ce soit beaucoup plus lent:
def lower_wick_df(df):
min_oc = df[['Open', 'Close']].min(axis=1)
return min_oc - l
Les trois autres travailleront sur des colonnes ou des vecteurs tels quels. Ensuite, vous pouvez finir avec
def is_hammer(df):
lw = lower_wick_at_least_twice_real_body(df["Open"], df["Low"], df["Close"])
cl = closed_in_top_half_of_range(df["High"], df["Low"], df["Close"])
return cl & lw
Les opérateurs de bits peuvent effectuer la logique de définition sur des vecteurs booléens, &
pour and
, |
pour or
etc. Cela suffit pour vectoriser complètement les exemples de calculs que vous avez donnés et devrait être relativement rapide. Vous pourriez probablement accélérer encore davantage en travaillant temporairement avec les tableaux numpy sous-jacents aux données lors de ces calculs.
Pour la deuxième partie, je recommanderais d’introduire une colonne indiquant le motif de chaque ligne et d’écrire une famille de fonctions qui traitent chaque motif. Puis groupez le motif et appliquez la fonction appropriée à chaque groupe.