Skip to content

Commit e8058ac

Browse files
new version
1 parent 5ec11c8 commit e8058ac

11 files changed

Lines changed: 163 additions & 115 deletions

File tree

exit/exit_fibo.py

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Module that will try to exit the market when we are in """
2+
13
import entry.entry_fibo as ef
24
import sys
35
import operator as op
@@ -6,24 +8,12 @@
68
import pandas as pd
79

810
class ExitFibo(ef.EntFibo):
11+
""" Class that uses Fibonnacci strategy to exit the market.
912
13+
It first uses the `EntFibo()` class in `entry_fibo.py` to enter in the market.
14+
"""
1015

1116
def __init__(self,init_):
12-
"""
13-
Class that uses Fibonnacci strategy to exit the market.
14-
15-
It first uses the EntFibo to enter the market (inheritance)
16-
Then it tries to exit the market with Fibonacci retracement and extension. 1 type at the moment:
17-
1- Extension from a previous wave (largest one in the last trend)
18-
19-
Notes
20-
-----
21-
Need to use also Fibonnacci retracements to exit the market
22-
23-
No slippage included in `try_exit()`. If the price reached the desired level, we just exit at either the
24-
current price or the next desired price
25-
26-
"""
2717

2818
new_obj = init_
2919
self.__dict__.update(new_obj.__dict__) #replacing self object with Initialise object
@@ -32,15 +22,25 @@ def __init__(self,init_):
3222
self.trade_return])
3323

3424
def __call__(self,curr_row,buy_signal=False,sell_signal=False):
35-
25+
""" Method that will first try to enter the market with `self.ent_fibo` then it will try to exit with
26+
`self.try_exit()` whenever we have a position
27+
"""
3628
super().__init__()
3729
self.ent_fibo(curr_row=curr_row, buy_signal=buy_signal, sell_signal=sell_signal)
3830
return self.try_exit()
39-
4031

4132
def try_exit(self):
4233
"""
43-
Method which try to exit the market.
34+
This is the ethod that tries to exit the market when wr have a position with `entry_fibo.py`
35+
36+
Then it tries to exit
37+
the market using Fibonacci retracement and extension. 1 type at the moment:
38+
1- Largest extension `self.largest_extension_` from the current trend
39+
40+
There is no slippage included in `try_exit()`. If the price reached the desired level, we just exit at
41+
either the current price or the next desired price
42+
43+
4444
4545
This method will make the system exit the market when a close or a stop loss signal is triggered
4646

indicator.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,20 @@ def calcul_indicator(self):
2525
The function iterate through the indicators in `self.indicator` and through the range of `self.series`,defined
2626
in `init_operations.py` and function `init_series()`. Then it calculates the value of the indicator using
2727
the subseries `self.sous_series`.
28+
29+
30+
Parameters
31+
----------
32+
`self.series` : pandas Dataframe
33+
It contains the series used to build the model.
34+
`self.indicator` : dictionary
35+
It contains the indicator we are using for the strategy.
36+
37+
Return
38+
------
39+
The function doesn't return anything in itself, but it calculates and stores the value of the desired indicator
40+
in `self.indicator` with new columns in `self.series` (pandas Dataframe)
41+
2842
"""
2943

3044
super().__call__()

indicators/regression/linear_regression.py

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,22 @@
1+
"""Module that evaluates the slope and r_square of a serie"""
2+
13
from scipy import stats
24
from initialize import Initialize
35
from manip_data import ManipData as md
46
from init_operations import InitOp as io
57
import copy
68

79
class RegressionSlopeStrenght(Initialize):
8-
"""
9-
Indicateur qui évalue si la "slope" est différente de 0 pour une régression linéaire
10-
Valeurs retournées sont 1 (pente positive), -1 (pente négative) et 0 (neutre)
11-
Prendre en considération que cette technique viole des principes statistiques, ie l'autocorrélation des données qui fait
12-
que les erreurs ne suivent pas une loi normale
13-
+ la trend (saisonnalité aussi dans certains cas) qui font que les données ne sont pas indépendantes
14-
"""
10+
"""Class that evaluates the slope and r2 value of a serie
1511
12+
Take into consideration that we use r2 to see if one variable can explain movement in the other.
13+
We are not trying to forecast using the r2 value.
14+
15+
Parameters
16+
----------
17+
`self.sous_series` : pandas Dataframe
18+
Contains the subseries on which we calculate the slope and r2 value
19+
"""
1620

1721
def __init__(self,series_,self_):
1822
super().__init__()
@@ -21,21 +25,16 @@ def __init__(self,series_,self_):
2125
self.__dict__.update(new_obj.__dict__)
2226
io.init_series(self)
2327
del new_obj, self_
24-
25-
#init_ = init.Initialize()
2628
self.sous_series=md.sous_series_(series_,self.nb_data)
2729

2830
def __store_stat(self):
29-
30-
"""
31-
Function to return stat in a list
32-
"""
31+
"""Function that returns stat in a list"""
3332

3433
return stats.linregress(self.sous_series[self.date_ordinal_name],
3534
self.sous_series[self.default_data])
3635

3736
def slope(self):
38-
"""
37+
""" Function that return the slope of a serie.
3938
La pente est la 1ième valeur retournée dans cette stats.linregress, d'où le [0]
4039
"""
4140

indicators/regression/mann_kendall.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@
1313
from init_operations import InitOp as io
1414
import copy
1515

16-
1716
class MannKendall(Initialize):
1817
"""
19-
Mann-Kendall is a non-parametric test to determine if a trend is present over time (using monotonic function)
20-
18+
Mann-Kendall is a non-parametric test to determine if there is a trend in a time-series
2119
"""
2220

2321
def __init__(self,series_,self_,alpha=0.01,iteration=True):
@@ -41,6 +39,10 @@ def mk(self):
4139
4240
https://github.com/mps9506/Mann-Kendall-Trend/blob/master/mk_test.py
4341
42+
The goal here is to calculate the Mann Kendall value at data point, so to
43+
save time, we just substract the first value and add the last value when
44+
we go to a new data point.
45+
4446
This function is derived from code originally posted by Sat Kumar Tomer
4547
(satkumartomer@gmail.com)
4648
See also: http://vsp.pnnl.gov/help/Vsample/Design_Trend_Mann_Kendall.htm

init_operations.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,21 @@ def __init__(self):
1313
def __call__(self):
1414
self.reset_value()
1515

16-
1716
def reset_value(self):
18-
"""Function to reset the dictionary that contains the trading journal (entry, exit, return).
17+
"""Function to reset the dictionary that contains the trading journal (entry, exit, return) in
18+
`self.trades_track`
19+
20+
We need to do that when we optimize, ie when `self.is_walkfoward` is `True`
21+
22+
"""
1923

20-
We need to do that when we optimize, ie when `self.is_walkfoward` is `True` """
2124
self.trades_track = pd.DataFrame(columns=[self.entry_row, self.entry_level, self.exit_row, self.exit_level, \
2225
self.trade_return])
2326

2427
def init_series(self):
25-
"""Function that extract data from csv to a pandas Dataframe"""
28+
"""Function that extract the data from csv to a pandas Dataframe `self.series`
29+
30+
It actually is the data that we are using for the strategy """
2631

2732
self.series = md.csv_to_pandas(self.date_name, self.start_date, self.end_date, self.name, self.directory,
2833
self.asset, ordinal_name=self.date_ordinal_name, is_fx=self.is_fx, dup_col = self.dup_col)

initialize.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,13 +97,13 @@ def __init__(self):
9797
self.is_fx = True
9898
self.asset = "EURUSD"
9999
self.start_date = datetime.strptime('2015-10-15', "%Y-%m-%d")
100-
self.end_date = datetime.strptime('2020-10-18', "%Y-%m-%d")
100+
self.end_date = datetime.strptime('2016-02-18', "%Y-%m-%d")
101101

102102
self.is_walkfoward = True
103103
self.training_name_ = '_training'
104104
self.test_name_ = '_test'
105-
self.training_ = 18 #Lenght in months of training period (put 18)
106-
self.test_ = 9 #Lenght in months of testing period (put 9)
105+
self.training_ = 2 #Lenght in months of training period (put 18)
106+
self.test_ = 1 #Lenght in months of testing period (put 9)
107107
self.dict_name_ = {self.training_name_:self.training_,self.test_name_:self.test_}
108108
self.train_param= [] #Optimized training parameters used for the test period
109109

manip_data.py

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
from functools import wraps
66
from statsmodels.tsa.stattools import adfuller
77
import os.path
8+
import numpy as np
89

910

1011
def ordinal_date(function):
@@ -94,7 +95,7 @@ def sous_series_(cls,series_,nb_data,point_data=0):
9495

9596
cls.sous_series=series_.iloc[point_data:point_data + nb_data,:]
9697
if nb_data > len(series_):
97-
raise Exception("Number of necessary data to calculate the indicator lower than available data")
98+
raise Exception("Not enough necessary data to calculate the indicator")
9899
return cls.sous_series
99100

100101
@classmethod
@@ -109,4 +110,27 @@ def de_trend(cls, series, period, p_value, date_name, date_ordinal_name, default
109110
series_diff[date_ordinal_name] = series[date_ordinal_name]
110111
if adfuller(series_diff[default_data])[1] > p_value:
111112
raise Exception("The differentiated series is not stationary")
112-
return series_diff
113+
return series_diff
114+
115+
@classmethod
116+
def nan_list(cls,list_):
117+
"""Check if a list has one empty value
118+
119+
Return
120+
------
121+
Bool : `True` or `False`
122+
Return `True` if at least one value in the list is `nan` and `False otherwise
123+
"""
124+
125+
return True if True in np.isnan(list_) else False
126+
127+
@classmethod
128+
def pd_tolist(cls,pd_, row_name):
129+
"""Transform a pandas column to a list. It makes sure it is an integer"""
130+
pd__ = pd_.loc[:, row_name].tolist()
131+
try:
132+
t = [int(i) for i in pd__]
133+
except:
134+
raise Exception("Mistake happened in pd_tolist")
135+
else :
136+
return [int(i) for i in pd__]

math_op.py

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Basic math operations
2+
Module for mathematical operation support
33
"""
44
from scipy.signal import argrelextrema
55
import matplotlib.pyplot as plt
@@ -33,16 +33,13 @@ def local_extremum(cls,start_point,end_point,window = 6,min_= 'min',max_='max',i
3333
max_ : str
3434
Name given to max data column
3535
36-
3736
Return
3837
------
3938
DataFrame list : Return a pandas dataframe `cls.series` with the none empty min or max value
4039
(if both are empty, nothing is returned. If one of
4140
them has a value, return the local min or max with index no)
4241
"""
4342

44-
45-
4643
cls.series=cls.series.loc[start_point:end_point,cls.default_col]
4744
cls.series=pd.DataFrame({cls.default_col: cls.series})
4845

@@ -66,27 +63,4 @@ def local_extremum(cls,start_point,end_point,window = 6,min_= 'min',max_='max',i
6663
#Filter nan value for min or max out
6764
cls.series=cls.series.loc[(cls.series[min_].isna())==False | (cls.series[max_].isna() == False)]
6865

69-
return cls.series
70-
71-
@classmethod
72-
def nan_list(cls,list_):
73-
"""Check if a list has one empty value
74-
75-
Return
76-
------
77-
Bool : `True` or `False`
78-
Return `True` if at least one value in the list is `nan` and `False otherwise
79-
"""
80-
81-
return True if True in np.isnan(list_) else False
82-
83-
@classmethod
84-
def pd_tolist(cls,pd_, row_name):
85-
"""Transform a pandas column to a list. It makes sure it is an integer"""
86-
pd__ = pd_.loc[:, row_name].tolist()
87-
try:
88-
t = [int(i) for i in pd__]
89-
except:
90-
raise Exception("Mistake happened in pd_tolist")
91-
else :
92-
return [int(i) for i in pd__]
66+
return cls.series

optimize_.py

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,52 @@
1-
"""Module to run the program - use optimization tool if asked"""
1+
"""Module to run the program - use optimization tools if desired"""
2+
23
from pnl import PnL
34
from manip_data import ManipData as md
45
from date_manip import DateManip as dm
56
from optimize.genetic_algorithm import GenAlgo as ga
6-
from indicator import Indicator
77

88
class Optimize(PnL):
99

1010
def __init__(self):
11+
"""Function that initializes stuff.
12+
13+
It resets the `self.series` with `self.init_series()` and dictionary that track the pnl
14+
`self.trades_track` with `reset_value()` when we optimize.
15+
"""
1116
super().__init__()
1217
self.init_series()
1318
self.reset_value()
1419
self.params = {}
1520

1621
def __call__(self):
22+
"""Function do different things dependent if we optimize or not"""
1723

18-
# If we optimize
1924
if self.is_walkfoward:
2025
self.walk_foward()
2126
else :
22-
Indicator.calcul_indicator(self)
27+
self.calcul_indicator()
2328
self.pnl_()
24-
self.first_write = md.write_csv_(self.dir_output, self.name_out, self.first_write, add_doc="",
25-
is_walkfoward=self.is_walkfoward, **self.pnl_dict)
29+
md.write_csv_(self.dir_output, self.name_out, add_doc="", is_walkfoward=self.is_walkfoward, **self.pnl_dict)
2630

27-
def execute_(self,add_doc=""):
28-
""" Just runs the whole program without optimization
31+
def walk_foward(self):
32+
"""Function that do the walk-foward analysis (optimization).
2933
30-
Load data (and clean), calculate indicators, check for signal, calculate pnl + write results to file
31-
"""
34+
First it runs through the divided period (1 period interval for training and testing). We have to choose
35+
properly `self.start_date` and `self.end_date` as they set the numbers of period.
3236
37+
Then the program runs through each training and testing period (in `self.dict_name_`). The program optimizes
38+
only in the training period `self.training_name_`. The results are store in the folder results and
39+
results_training for the training period and results_test for the testing period.
40+
41+
Parameters
42+
----------
43+
`self.start_date` : datetime object
44+
Set in `initialize.py`. Beginning date of training and testing.
45+
`self.end_date` : datetime object
46+
Set in `initialize.py`. End date of training and testing.
47+
48+
"""
3349

34-
def walk_foward(self):
3550
md_ = md
3651

3752
_first_time = True
@@ -48,7 +63,7 @@ def walk_foward(self):
4863
if _first_time :
4964
md_(self.dir_output,self.name_out,extension = key_).erase_content()
5065
self.init_series()
51-
Indicator.calcul_indicator(self)
66+
self.calcul_indicator()
5267
if key_ == self.training_name_: #we only optimize for the training period
5368
self.optimize_param()
5469
self.pnl_dict,self.params = ga(self).__call__()
@@ -64,11 +79,13 @@ def walk_foward(self):
6479
_first_time = False
6580

6681
def assign_value(self):
67-
""" Function to assign the value to each optimized parameters obtained in the optimization function"""
82+
""" Function to assign the value to each optimized parameters obtained in the optimization module.
83+
84+
The `genetic_algorithm.py` return the dictionary with the value and when we test in `r_square_tr.py` they are
85+
in a different format"""
6886

6987
for item in range(len(self.op_param)):
7088
if len(self.op_param[item]) > 1:
7189
self.op_param[item][0][self.op_param[item][1]] = self.params[self.op_param[item][1]]
7290
else:
73-
setattr(self, self.op_param[item][0], self.params[self.op_param[item][0]])
74-
t =5
91+
setattr(self, self.op_param[item][0], self.params[self.op_param[item][0]])

0 commit comments

Comments
 (0)