The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── Awesome Oscillator backtest.py
├── Bollinger Bands Pattern Recognition backtest.py
├── Dual Thrust backtest.py
├── Heikin-Ashi backtest.py
├── LICENSE
├── London Breakout backtest.py
├── MACD Oscillator backtest.py
├── Monte Carlo project
    ├── Monte Carlo backtest.py
    ├── README.md
    └── preview
    │   ├── ge accuracy.png
    │   ├── ge simulation.png
    │   ├── ge simulation2.png
    │   ├── ge versus.png
    │   ├── nvda simulation.png
    │   ├── nvda versus.png
    │   └── xkcd_curve_fitting.png
├── Oil Money project
    ├── Oil Money CAD.py
    ├── Oil Money COP.py
    ├── Oil Money NOK.py
    ├── Oil Money RUB.py
    ├── Oil Money Trading backtest.py
    ├── README.md
    ├── data
    │   ├── brent crude nokjpy.csv
    │   ├── urals crude rubaud.csv
    │   ├── vas crude copaud.csv
    │   └── wcs crude cadaud.csv
    ├── oil production
    │   ├── oil production choropleth.csv
    │   ├── oil production choropleth.py
    │   ├── oil production cost curve.csv
    │   ├── oil production cost curve.py
    │   └── worldmapshape.json
    └── preview
    │   ├── cad after.png
    │   ├── cad before.png
    │   ├── cad crude.png
    │   ├── cad currency.png
    │   ├── cad elbow.png
    │   ├── cad groups.png
    │   ├── cad kmeans.gif
    │   ├── cad model.png
    │   ├── cad silhouette.png
    │   ├── cad wcs in aud.png
    │   ├── cad wcs in usd.png
    │   ├── cop after 2017.png
    │   ├── cop before 2017.png
    │   ├── cop groups.png
    │   ├── cop model.png
    │   ├── cop profit distribution.png
    │   ├── cop profit heatmap.png
    │   ├── cop trading asset.png
    │   ├── cop trading positions.png
    │   ├── cop vs brl.png
    │   ├── cop vs crude.png
    │   ├── cop vs gold.png
    │   ├── cop vs mxn.png
    │   ├── cop vs usd vs mxn.png
    │   ├── cop vs usd.png
    │   ├── cop vs vas.png
    │   ├── nok EG failed.png
    │   ├── nok asset value.png
    │   ├── nok brent crude.png
    │   ├── nok correlation.png
    │   ├── nok fitted vs actual.png
    │   ├── nok model summary.png
    │   ├── nok oil money positions.png
    │   ├── nok profit distribution.png
    │   ├── nok profit heatmap.png
    │   ├── nok trading asset.png
    │   ├── nok trading positions.png
    │   ├── nok trend.png
    │   ├── nok vs brent.png
    │   ├── nok vs gdp.png
    │   ├── nok vs ir.png
    │   ├── oil production bubble map.png
    │   ├── oil production choropleth.PNG
    │   ├── oil production cost curve.png
    │   ├── rub 2015 positions.png
    │   ├── rub 2016 positions.png
    │   ├── rub 2017 positions.png
    │   ├── rub 2017- trend.png
    │   ├── rub 2018 positions.png
    │   ├── rub model -2016.png
    │   ├── rub model 2017-.png
    │   ├── rub ols -2016.PNG
    │   ├── rub ols 2017-.PNG
    │   ├── rub stepwise1.png
    │   └── rub stepwise2.png
├── Options Straddle backtest.py
├── Ore Money project
    ├── README.md
    ├── iron ore audeur.csv
    ├── iron ore brlaud.csv
    ├── iron ore production
    │   ├── iron ore production bubble map.csv
    │   └── iron ore production bubble map.py
    ├── iron ore uahusd.csv
    └── preview
    │   └── iron ore production bubble map.png
├── Pair trading backtest.py
├── Parabolic SAR backtest.py
├── README.md
├── RSI Pattern Recognition backtest.py
├── Shooting Star backtest.py
├── Smart Farmers project
    ├── README.md
    ├── check consistency.py
    ├── cleanse data.py
    ├── country selection.py
    ├── data
    │   ├── capita.csv
    │   ├── cme.csv
    │   ├── forecast.csv
    │   ├── grand.csv
    │   ├── malay_gdp.csv
    │   ├── malay_land.csv
    │   ├── malay_pop.csv
    │   ├── malay_prix.csv
    │   ├── malay_prod.csv
    │   ├── mapping.csv
    │   ├── palm.csv
    │   └── tres_grand.csv
    ├── estimate demand.py
    ├── forecast.py
    └── preview
    │   ├── cabbage price.png
    │   ├── cabbage production.png
    │   ├── cabbage regression.png
    │   ├── cocoa price.png
    │   ├── cocoa production.png
    │   ├── cocoa regression.png
    │   ├── coconut price.png
    │   ├── coconut production.png
    │   ├── coconut regression.png
    │   ├── demand model.PNG
    │   ├── mango price.png
    │   ├── mango production.png
    │   ├── mango regression.png
    │   ├── naive model matrix.PNG
    │   ├── naive model.PNG
    │   ├── oil palm price.png
    │   ├── oil palm production.png
    │   ├── oil palm regression.png
    │   ├── oil palm vs palm oil.png
    │   ├── overall production.png
    │   ├── price change derivation.PNG
    │   ├── pricing mechanism.PNG
    │   ├── ricardian model.PNG
    │   ├── rubber price.png
    │   ├── rubber production.png
    │   ├── rubber regression.png
    │   └── workflow.png
├── VIX Calculator.py
├── data
    ├── bitcoin.csv
    ├── cme holidays.csv
    ├── gbpusd.csv
    ├── henry hub european options.csv
    ├── stoxx50.xlsx
    └── treasury yield curve rates.csv
└── preview
    ├── awesome asset.png
    ├── awesome ma.png
    ├── awesome oscillator.png
    ├── awesome positions.png
    ├── bollinger bands bottom w pattern.png
    ├── bollinger bands positions.png
    ├── dual thrust positions.png
    ├── heikin-ashi asset value.png
    ├── heikin-ashi positions.png
    ├── london breakout positions.png
    ├── london breakout thresholds.png
    ├── macd oscillator.png
    ├── macd positions.png
    ├── options straddle payoff diagram.png
    ├── pair trading asset.png
    ├── pair trading eg two step.PNG
    ├── pair trading positions.png
    ├── parabolic sar positions.png
    ├── quantitative-trading-profile.png
    ├── rsi head-shoulder pattern.png
    ├── rsi oscillator.png
    ├── rsi pattern oscillator.png
    ├── rsi pattern positions.png
    ├── rsi positions.png
    ├── shooting star positions.png
    └── vix calculator.PNG


/Awesome Oscillator backtest.py:
--------------------------------------------------------------------------------
  1 | # coding: utf-8
  2 | 
  3 | #details of awesome oscillator can be found here
  4 | # https://www.tradingview.com/wiki/Awesome_Oscillator_(AO)
  5 | #basically i use awesome oscillator to compare with macd oscillator
  6 | #lets see which one makes more money
  7 | #there is not much difference between two of em
  8 | #this time i use exponential smoothing on macd
  9 | #for awesome oscillator, i use simple moving average instead
 10 | #the rules are quite simple
 11 | #these two are momentum trading strategy
 12 | #they compare the short moving average with long moving average
 13 | #if the difference is positive
 14 | #we long the asset, vice versa
 15 | #awesome oscillator has slightly more conditions for signals
 16 | #we will see about it later
 17 | #for more details about macd
 18 | # https://github.com/je-suis-tm/quant-trading/blob/master/MACD%20oscillator%20backtest.py
 19 | 
 20 | 
 21 | # In[1]:
 22 | #need to get fix yahoo finance package first
 23 | import matplotlib.pyplot as plt
 24 | import numpy as np
 25 | import pandas as pd
 26 | import fix_yahoo_finance as yf
 27 | 
 28 | 
 29 | # In[2]:
 30 | 
 31 | #this part is macd
 32 | #i will not go into details as i have another session called macd
 33 | #the only difference is that i use ewma function to apply exponential smoothing technique
 34 | def ewmacd(signals,ma1,ma2):
 35 |     
 36 |     signals['macd ma1']=signals['Close'].ewm(span=ma1).mean()    
 37 |     signals['macd ma2']=signals['Close'].ewm(span=ma2).mean()   
 38 |     
 39 |     return signals
 40 |     
 41 | def signal_generation(df,method,ma1,ma2):
 42 |     
 43 |     signals=method(df,ma1,ma2)
 44 |     signals['macd positions']=0
 45 |     signals['macd positions'][ma1:]=np.where(signals['macd ma1'][ma1:]>=signals['macd ma2'][ma1:],1,0)
 46 |     signals['macd signals']=signals['macd positions'].diff()
 47 |     signals['macd oscillator']=signals['macd ma1']-signals['macd ma2']
 48 |     
 49 |     return signals
 50 | 
 51 | 
 52 | # In[3]:
 53 |     
 54 | #for awesome oscillator
 55 | #moving average is based on the mean of high and low instead of close price
 56 | def awesome_ma(signals):
 57 |     
 58 |     signals['awesome ma1'],signals['awesome ma2']=0,0
 59 |     signals['awesome ma1']=((signals['High']+signals['Low'])/2).rolling(window=5).mean()
 60 |     signals['awesome ma2']=((signals['High']+signals['Low'])/2).rolling(window=34).mean()
 61 |     
 62 |     return signals
 63 | 
 64 | 
 65 | #awesome signal generation,AWESOME!
 66 | def awesome_signal_generation(df,method):
 67 |     
 68 |     signals=method(df)
 69 |     signals.reset_index(inplace=True)
 70 |     signals['awesome signals']=0
 71 |     signals['awesome oscillator']=signals['awesome ma1']-signals['awesome ma2']  
 72 |     signals['cumsum']=0
 73 | 
 74 | 
 75 |     for i in range(2,len(signals)):
 76 | 
 77 |         #awesome oscillator has an extra way to generate signals
 78 |         #its called saucer
 79 |         #A Bearish Saucer setup occurs when the AO is below the Zero Line
 80 |         #in another word, awesome oscillator is negative
 81 |         #A Bearish Saucer entails two consecutive green bars (with the second bar being higher than the first bar) being followed by a red bar.
 82 |         #in another word, green bar refers to open price is higher than close price
 83 |     
 84 |         if (signals['Open'][i]>signals['Close'][i] and 
 85 |         signals['Open'][i-1]<signals['Close'][i-1] and 
 86 |         signals['Open'][i-2]<signals['Close'][i-2] and
 87 |         signals['awesome oscillator'][i-1]>signals['awesome oscillator'][i-2] and
 88 |         signals['awesome oscillator'][i-1]<0 and 
 89 |         signals['awesome oscillator'][i]<0):
 90 |             signals.at[i,'awesome signals']=1
 91 | 
 92 | 
 93 |         #this is bullish saucer
 94 |         #vice versa
 95 |         
 96 |         if (signals['Open'][i]<signals['Close'][i] and 
 97 |         signals['Open'][i-1]>signals['Close'][i-1] and 
 98 |         signals['Open'][i-2]>signals['Close'][i-2] and
 99 |         signals['awesome oscillator'][i-1]<signals['awesome oscillator'][i-2] and
100 |         signals['awesome oscillator'][i-1]>0 and
101 |         signals['awesome oscillator'][i]>0):
102 |             signals.at[i,'awesome signals']=-1
103 | 
104 | 
105 |         #this part is the same as macd signal generation
106 |         #nevertheless, we have extra rules to get signals ahead of moving average
107 |         #if we get signals before moving average generate any signal
108 |         #we will ignore signals generated by moving average then
109 |         #as it is delayed and probably deliver fewer profit than previous signals
110 |         #we use cumulated sum to see if there has been created any open positions
111 |         #if so, we will take a pass
112 |         
113 |         if signals['awesome ma1'][i]>signals['awesome ma2'][i]:
114 |             signals.at[i,'awesome signals']=1
115 |             signals['cumsum']=signals['awesome signals'].cumsum()
116 |             if signals['cumsum'][i]>1:
117 |                 signals.at[i,'awesome signals']=0
118 |             
119 |         if signals['awesome ma1'][i]<signals['awesome ma2'][i]:
120 |             signals.at[i,'awesome signals']=-1
121 |             signals['cumsum']=signals['awesome signals'].cumsum()
122 |             if signals['cumsum'][i]<0:
123 |                 signals.at[i,'awesome signals']=0
124 |     
125 |     signals['cumsum']=signals['awesome signals'].cumsum()
126 |     
127 |     return signals
128 | 
129 | 
130 | # In[4]:
131 |     
132 | #we plot the results to compare
133 | #basically the same as macd
134 | #im not gonna explain much
135 | def plot(new,ticker):
136 |     
137 |     #positions
138 |     fig=plt.figure()
139 |     ax=fig.add_subplot(211)
140 | 
141 |     new['Close'].plot(label=ticker)
142 |     ax.plot(new.loc[new['awesome signals']==1].index,new['Close'][new['awesome signals']==1],label='AWESOME LONG',lw=0,marker='^',c='g')
143 |     ax.plot(new.loc[new['awesome signals']==-1].index,new['Close'][new['awesome signals']==-1],label='AWESOME SHORT',lw=0,marker='v',c='r')
144 | 
145 |     plt.legend(loc='best')
146 |     plt.grid(True)
147 |     plt.title('Positions')
148 | 
149 |     bx=fig.add_subplot(212,sharex=ax)
150 |     new['Close'].plot(label=ticker)
151 |     bx.plot(new.loc[new['macd signals']==1].index,new['Close'][new['macd signals']==1],label='MACD LONG',lw=0,marker='^',c='g')
152 |     bx.plot(new.loc[new['macd signals']==-1].index,new['Close'][new['macd signals']==-1],label='MACD SHORT',lw=0,marker='v',c='r')
153 | 
154 |     plt.legend(loc='best')
155 |     plt.grid(True)
156 |     plt.show()
157 | 
158 |     
159 |     #oscillator
160 |     fig=plt.figure()
161 |     cx=fig.add_subplot(211)
162 | 
163 |     c=np.where(new['Open']>new['Close'],'r','g')
164 |     cx.bar(range(len(new)),new['awesome oscillator'],color=c,label='awesome oscillator')
165 | 
166 |     plt.grid(True)
167 |     plt.legend(loc='best')
168 |     plt.title('Oscillator')
169 | 
170 |     dx=fig.add_subplot(212,sharex=cx)
171 | 
172 |     new['macd oscillator'].plot(kind='bar',label='macd oscillator')
173 | 
174 |     plt.grid(True)
175 |     plt.legend(loc='best')
176 |     plt.xlabel('')
177 |     plt.xticks([])
178 |     plt.show()
179 | 
180 | 
181 | 
182 |     #moving average
183 |     fig=plt.figure()
184 |     ex=fig.add_subplot(211)
185 | 
186 |     new['awesome ma1'].plot(label='awesome ma1')
187 |     new['awesome ma2'].plot(label='awesome ma2',linestyle=':')
188 | 
189 |     plt.legend(loc='best')
190 |     plt.grid(True)
191 |     plt.xticks([])
192 |     plt.xlabel('')
193 |     plt.title('Moving Average')
194 | 
195 |     fig=plt.figure()
196 |     fx=fig.add_subplot(212,sharex=bx)
197 |     
198 |     new['macd ma1'].plot(label='macd ma1')
199 |     new['macd ma2'].plot(label='macd ma2',linestyle=':')
200 | 
201 |     plt.legend(loc='best')
202 |     plt.grid(True)
203 |     plt.show()
204 | 
205 | 
206 | # In[5]:
207 |     
208 | #normally i dont include backtesting stats
209 | #for the comparison, i am willing to make an exception
210 | #capital0 is intial capital
211 | #positions defines how much shares we buy for every single trade
212 | def portfolio(signals):
213 |         
214 |     capital0=5000
215 |     positions=100
216 | 
217 |     portfolio=pd.DataFrame()
218 |     portfolio['Close']=signals['Close']
219 |     
220 |     #cumsum is used to calculate the change of value while holding shares
221 |     portfolio['awesome holding']=signals['cumsum']*portfolio['Close']*positions
222 |     portfolio['macd holding']=signals['macd positions']*portfolio['Close']*positions
223 | 
224 |     #basically cash is initial capital minus the profit we make from every trade
225 |     #note that we have to use cumulated sum to add every profit into our cash
226 |     portfolio['awesome cash']=capital0-(signals['awesome signals']*portfolio['Close']*positions).cumsum()
227 |     portfolio['macd cash']=capital0-(signals['macd signals']*portfolio['Close']*positions).cumsum()
228 | 
229 |     portfolio['awesome asset']=portfolio['awesome holding']+portfolio['awesome cash']
230 |     portfolio['macd asset']=portfolio['macd holding']+portfolio['macd cash']
231 | 
232 |     portfolio['awesome return']=portfolio['awesome asset'].pct_change()
233 |     portfolio['macd return']=portfolio['macd asset'].pct_change()
234 |     
235 |     return portfolio
236 | 
237 | 
238 | # In[6]:
239 | 
240 | #lets plot how two strategies increase our asset value
241 | def profit(portfolio):
242 |         
243 |     gx=plt.figure()
244 |     gx.add_subplot(111)
245 | 
246 |     portfolio['awesome asset'].plot()
247 |     portfolio['macd asset'].plot()
248 | 
249 |     plt.legend(loc='best')
250 |     plt.grid(True)
251 |     plt.title('Awesome VS MACD')
252 |     plt.show()
253 | 
254 | 
255 | # In[7]:
256 | 
257 | #i use a function to calculate maximum drawdown
258 | #the idea is simple
259 | #for every day, we take the current asset value
260 | #to compare with the previous highest asset value
261 | #we get our daily drawdown
262 | #it is supposed to be negative if it is not the maximum for this period so far
263 | #we implement a temporary variable to store the minimum value
264 | #which is called maximum drawdown
265 | #for each daily drawdown that is smaller than our temporary value
266 | #we update the temp until we finish our traversal
267 | #in the end we return the maximum drawdown
268 | def mdd(series):
269 | 
270 |     temp=0
271 |     for i in range(1,len(series)):
272 |         if temp>(series[i]/max(series[:i])-1):
273 |             temp=(series[i]/max(series[:i])-1)
274 | 
275 |     return temp
276 | 
277 | 
278 | def stats(portfolio):
279 |     
280 |     stats=pd.DataFrame([0])
281 | 
282 |     #lets calculate some sharpe ratios
283 |     #note that i set risk free return at 0 for simplicity
284 |     #alternatively we can use snp500 as a benchmark
285 |     stats['awesome sharpe']=(portfolio['awesome asset'].iloc[-1]/5000-1)/np.std(portfolio['awesome return'])
286 |     stats['macd sharpe']=(portfolio['macd asset'].iloc[-1]/5000-1)/np.std(portfolio['macd return'])
287 | 
288 |     stats['awesome mdd']=mdd(portfolio['awesome asset'])
289 |     stats['macd mdd']=mdd(portfolio['macd asset'])
290 | 
291 |     #ta-da!
292 |     print(stats)
293 | 
294 | 
295 | # In[8]:   
296 | 
297 | def main():
298 |     
299 |     #awesome oscillator uses 5 lags as short ma
300 |     #34 lags as long ma
301 |     #for the consistent comparison
302 |     #i apply the same to macd oscillator
303 |     ma1=5
304 |     ma2=34
305 | 
306 |     #downloading
307 |     stdate=input('start date in format yyyy-mm-dd:')
308 |     eddate=input('end date in format yyyy-mm-dd:')
309 |     ticker=input('ticker:')
310 |     df=yf.download(ticker,start=stdate,end=eddate)
311 | 
312 |     #slicing the downloaded dataset
313 |     #if the dataset is too large
314 |     #backtesting plot would look messy
315 |     slicer=int(input('slicing:'))
316 |     signals=signal_generation(df,ewmacd,ma1,ma2)
317 |     sig=awesome_signal_generation(signals,awesome_ma)
318 |     new=sig[slicer:]
319 |     plot(new,ticker)
320 |     
321 |     portfo=portfolio(sig)
322 |     profit(portfo)
323 |     
324 |     stats(portfo)
325 |     
326 |     #from my tests
327 |     #macd has demonstrated a higher sharpe ratio
328 |     #it executes fewer trades but brings more profits
329 |     #however its maximum drawdown is higher than awesome oscillator
330 |     #which one is better?
331 |     #it depends on your risk averse level
332 | 
333 | if __name__ == '__main__':
334 |     main()
335 | 


--------------------------------------------------------------------------------
/Bollinger Bands Pattern Recognition backtest.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | #bollinger bands is a simple indicator
  7 | #just moving average plus moving standard deviation
  8 | #but pattern recognition is a differenct case
  9 | #visualization is easy for human to identify the pattern
 10 | #but for the machines, we gotta find a different approach
 11 | #when we talk about pattern recognition these days
 12 | #people always respond with machine learning
 13 | #why machine learning when u can use arithmetic approach 
 14 | #which is much faster and simpler?
 15 | 
 16 | #there are many patterns for recognition
 17 | #top m, bottom w, head-shoulder top, head-shoulder bottom, elliott waves
 18 | #in this content, we only discuss bottom w
 19 | #top m is just the reverse of bottom w
 20 | #rules of bollinger bands and bottom w can be found in the following link:
 21 | # https://www.tradingview.com/wiki/Bollinger_Bands_(BB)
 22 | 
 23 | import os
 24 | import pandas as pd
 25 | import matplotlib.pyplot as plt
 26 | import copy
 27 | import numpy as np
 28 | 
 29 | 
 30 | # In[2]:
 31 | os.chdir('d:/')
 32 | 
 33 | 
 34 | # In[3]:
 35 | 
 36 | #first step is to calculate moving average and moving standard deviation
 37 | #we plus/minus two standard deviations on moving average
 38 | #we get our upper, mid, lower bands
 39 | def bollinger_bands(df):
 40 |     
 41 |     data=copy.deepcopy(df)
 42 |     data['std']=data['price'].rolling(window=20,min_periods=20).std()
 43 |     data['mid band']=data['price'].rolling(window=20,min_periods=20).mean()
 44 |     data['upper band']=data['mid band']+2*data['std']
 45 |     data['lower band']=data['mid band']-2*data['std']
 46 |     
 47 |     return data
 48 | 
 49 | 
 50 | # In[4]:
 51 | 
 52 | 
 53 | #the signal generation is a bit tricky
 54 | #there are four conditions to satisfy
 55 | #for the shape of w, there are five nodes
 56 | #from left to right, top to bottom, l,k,j,m,i
 57 | #when we generate signals
 58 | #the iteration node is the top right node i, condition 4
 59 | #first, we find the middle node j, condition 2
 60 | #next, we identify the first bottom node k, condition 1
 61 | #after that, we point out the first top node l
 62 | #l is not any of those four conditions
 63 | #we just use it for pattern visualization
 64 | #finally, we locate the second bottom node m, condition 3
 65 | #plz refer to the following link for my poor visualization
 66 | # https://github.com/je-suis-tm/quant-trading/blob/master/preview/bollinger%20bands%20bottom%20w%20pattern.png
 67 | def signal_generation(data,method):
 68 |     
 69 |     #according to investopedia
 70 |     #for a double bottom pattern
 71 |     #we should use 3-month horizon which is 75
 72 |     period=75
 73 |     
 74 |     #alpha denotes the difference between price and bollinger bands
 75 |     #if alpha is too small, its unlikely to trigger a signal
 76 |     #if alpha is too large, its too easy to trigger a signal
 77 |     #which gives us a higher probability to lose money
 78 |     #beta denotes the scale of bandwidth
 79 |     #when bandwidth is larger than beta, it is expansion period
 80 |     #when bandwidth is smaller than beta, it is contraction period
 81 |     alpha=0.0001
 82 |     beta=0.0001
 83 |     
 84 |     df=method(data)
 85 |     df['signals']=0
 86 |     
 87 |     #as usual, cumsum denotes the holding position
 88 |     #coordinates store five nodes of w shape
 89 |     #later we would use these coordinates to draw a w shape
 90 |     df['cumsum']=0
 91 |     df['coordinates']=''
 92 |     
 93 |     for i in range(period,len(df)):
 94 |         
 95 |         #moveon is a process control
 96 |         #if moveon==true, we move on to verify the next condition
 97 |         #if false, we move on to the next iteration
 98 |         #threshold denotes the value of node k
 99 |         #we would use it for the comparison with node m
100 |         #plz refer to condition 3
101 |         moveon=False
102 |         threshold=0.0
103 |         
104 |         #bottom w pattern recognition
105 |         #there is another signal generation method called walking the bands
106 |         #i personally think its too late for following the trend
107 |         #after confirmation of several breakthroughs
108 |         #maybe its good for stop and reverse
109 |         #condition 4
110 |         if (df['price'][i]>df['upper band'][i]) and \
111 |         (df['cumsum'][i]==0):
112 |             
113 |             for j in range(i,i-period,-1):                
114 |                 
115 |                 #condition 2
116 |                 if (np.abs(df['mid band'][j]-df['price'][j])<alpha) and \
117 |                 (np.abs(df['mid band'][j]-df['upper band'][i])<alpha):
118 |                     moveon=True
119 |                     break
120 |             
121 |             if moveon==True:
122 |                 moveon=False
123 |                 for k in range(j,i-period,-1):
124 |                     
125 |                     #condition 1
126 |                     if (np.abs(df['lower band'][k]-df['price'][k])<alpha):
127 |                         threshold=df['price'][k]
128 |                         moveon=True
129 |                         break
130 |                         
131 |             if moveon==True:
132 |                 moveon=False
133 |                 for l in range(k,i-period,-1):
134 |                     
135 |                     #this one is for plotting w shape
136 |                     if (df['mid band'][l]<df['price'][l]):
137 |                         moveon=True
138 |                         break
139 |                     
140 |             if moveon==True:
141 |                 moveon=False        
142 |                 for m in range(i,j,-1):
143 |                     
144 |                     #condition 3
145 |                     if (df['price'][m]-df['lower band'][m]<alpha) and \
146 |                     (df['price'][m]>df['lower band'][m]) and \
147 |                     (df['price'][m]<threshold):
148 |                         df.at[i,'signals']=1
149 |                         df.at[i,'coordinates']='%s,%s,%s,%s,%s'%(l,k,j,m,i)
150 |                         df['cumsum']=df['signals'].cumsum()
151 |                         moveon=True
152 |                         break
153 |         
154 |         #clear our positions when there is contraction on bollinger bands
155 |         #contraction on the bandwidth is easy to understand
156 |         #when price momentum exists, the price would move dramatically for either direction
157 |         #which greatly increases the standard deviation
158 |         #when the momentum vanishes, we clear our positions
159 |         
160 |         #note that we put moveon in the condition
161 |         #just in case our signal generation time is contraction period
162 |         #but we dont wanna clear positions right now
163 |         if (df['cumsum'][i]!=0) and \
164 |         (df['std'][i]<beta) and \
165 |         (moveon==False):
166 |             df.at[i,'signals']=-1
167 |             df['cumsum']=df['signals'].cumsum()
168 |             
169 |     return df
170 | 
171 | 
172 | # In[5]:
173 | 
174 | #visualization
175 | def plot(new):
176 |     
177 |     #as usual we could cut the dataframe into a small slice
178 |     #for a tight and neat figure
179 |     #a and b denotes entry and exit of a trade
180 |     a,b=list(new[new['signals']!=0].iloc[:2].index)
181 |     
182 |     newbie=new[a-85:b+30]
183 |     newbie.set_index(pd.to_datetime(newbie['date'],format='%Y-%m-%d %H:%M:%S'),inplace=True)
184 | 
185 |    
186 |     fig=plt.figure(figsize=(10,5))
187 |     ax=fig.add_subplot(111)
188 |     
189 |     #plotting positions on price series and bollinger bands
190 |     ax.plot(newbie['price'],label='price')
191 |     ax.fill_between(newbie.index,newbie['lower band'],newbie['upper band'],alpha=0.2,color='#45ADA8')
192 |     ax.plot(newbie['mid band'],linestyle='--',label='moving average',c='#132226')
193 |     ax.plot(newbie['price'][newbie['signals']==1],marker='^',markersize=12, \
194 |             lw=0,c='g',label='LONG')
195 |     ax.plot(newbie['price'][newbie['signals']==-1],marker='v',markersize=12, \
196 |             lw=0,c='r',label='SHORT')
197 |     
198 |     #plotting w shape
199 |     #we locate the coordinates then find the exact date as index
200 |     temp=newbie['coordinates'][newbie['signals']==1]
201 |     indexlist=list(map(int,temp[temp.index[0]].split(',')))
202 |     ax.plot(newbie['price'][pd.to_datetime(new['date'].iloc[indexlist])], \
203 |             lw=5,alpha=0.7,c='#FE4365',label='double bottom pattern')
204 |     
205 |     #add some captions
206 |     plt.text((newbie.loc[newbie['signals']==1].index[0]), \
207 |              newbie['lower band'][newbie['signals']==1],'Expansion',fontsize=15,color='#563838')
208 |     plt.text((newbie.loc[newbie['signals']==-1].index[0]), \
209 |              newbie['lower band'][newbie['signals']==-1],'Contraction',fontsize=15,color='#563838')
210 |     
211 |     plt.legend(loc='best')
212 |     plt.title('Bollinger Bands Pattern Recognition')
213 |     plt.ylabel('price')
214 |     plt.grid(True)
215 |     plt.show()
216 | 
217 | 
218 | # In[6]:
219 | 
220 | #ta-da
221 | def main():
222 |     
223 |     #again, i download data from histdata.com
224 |     #and i take the average of bid and ask price
225 |     df=pd.read_csv('gbpusd.csv')
226 |     
227 |     signals=signal_generation(df,bollinger_bands)
228 | 
229 |     new=copy.deepcopy(signals)
230 |     plot(new)
231 | 
232 | #how to calculate stats could be found from my other code called Heikin-Ashi
233 | # https://github.com/je-suis-tm/quant-trading/blob/master/heikin%20ashi%20backtest.py
234 | 
235 | 
236 | if __name__ == '__main__':
237 |     main()
238 | 


--------------------------------------------------------------------------------
/Dual Thrust backtest.py:
--------------------------------------------------------------------------------
  1 | # -*- coding: utf-8 -*-
  2 | """
  3 | Created on Mon Mar 19 15:22:38 2018
  4 | @author: Administrator
  5 | 
  6 | """
  7 | # In[1]:
  8 | 
  9 | #dual thrust is an opening range breakout strategy
 10 | #it is very similar to London Breakout
 11 | #please check London Breakout if u have any questions
 12 | # https://github.com/je-suis-tm/quant-trading/blob/master/London%20Breakout%20backtest.py
 13 | #Initially we set up upper and lower thresholds based on previous days open, close, high and low 
 14 | #When the market opens and the price exceeds thresholds, we would take long/short positions prior to upper/lower thresholds 
 15 | #However, there is no stop long/short position in this strategy
 16 | #We clear all positions at the end of the day
 17 | #rules of dual thrust can be found in the following link
 18 | # https://www.quantconnect.com/tutorials/dual-thrust-trading-algorithm/
 19 | 
 20 | import os
 21 | import matplotlib.pyplot as plt
 22 | import numpy as np
 23 | import pandas as pd
 24 | 
 25 | # In[2]:
 26 | 
 27 | os.chdir('D:/')
 28 | 
 29 | 
 30 | # In[3]:
 31 | 
 32 | 
 33 | #data frequency convertion from minute to intra daily
 34 | #as we are doing backtesting, we have already got all the datasets we need
 35 | #we can create a table to store all open, close, high and low prices
 36 | #and calculate the range before we get to signal generation
 37 | #otherwise, we would have to put this part inside the loop
 38 | #it would greatly increase the time complexity
 39 | #however, in real time trading, we do not have futures price
 40 | #we have to store all past information in sql db
 41 | #we have to calculate the range from db before the market opens
 42 | 
 43 | def min2day(df,column,year,month,rg):
 44 |     
 45 |     #lets create a dictionary 
 46 |     #we use keys to classify different info we need
 47 |     memo={'date':[],'open':[],'close':[],'high':[],'low':[]}
 48 |     
 49 |     #no matter which month
 50 |     #the maximum we can get is 31 days
 51 |     #thus, we only need to run a traversal on 31 days
 52 |     #nevertheless, not everyday is a workday
 53 |     #assuming our raw data doesnt contain weekend prices
 54 |     #we use try function to make sure we get the info of workdays without errors
 55 |     #note that i put date at the end of the loop
 56 |     #the date appendix doesnt depend on our raw data
 57 |     #it only relies on the range function above
 58 |     #we could accidentally append weekend date if we put it at the beginning of try function
 59 |     #not until the program cant find price in raw data will the program stop
 60 |     #by that time, we have already appended weekend date
 61 |     #we wanna make sure the length of all lists in dictionary are the same
 62 |     #so that we can construct a structured table in the next step
 63 |     for i in range(1,32):
 64 |     
 65 |         try:
 66 |             temp=df['%s-%s-%s 3:00:00'%(year,month,i):'%s-%s-%s 12:00:00'%(year,month,i)][column]
 67 | 
 68 |             memo['open'].append(temp[0])
 69 |             memo['close'].append(temp[-1])
 70 |             memo['high'].append(max(temp))
 71 |             memo['low'].append(min(temp))
 72 |             memo['date'].append('%s-%s-%s'%(year,month,i))
 73 |        
 74 | 
 75 |         except Exception:
 76 |             pass
 77 |         
 78 |     intraday=pd.DataFrame(memo)
 79 |     intraday.set_index(pd.to_datetime(intraday['date']),inplace=True)
 80 |     
 81 |     
 82 |     #preparation
 83 |     intraday['range1']=intraday['high'].rolling(rg).max()-intraday['close'].rolling(rg).min()
 84 |     intraday['range2']=intraday['close'].rolling(rg).max()-intraday['low'].rolling(rg).min()
 85 |     intraday['range']=np.where(intraday['range1']>intraday['range2'],intraday['range1'],intraday['range2'])
 86 |     
 87 |     return intraday
 88 | 
 89 | 
 90 | #signal generation
 91 | #even replace assignment with pandas.at
 92 | #it still takes a while for us to get the result
 93 | #any optimization suggestion besides using numpy array?
 94 | def signal_generation(df,intraday,param,column,rg):
 95 |     
 96 |     #as the lags of days have been set to 5  
 97 |     #we should start our backtesting after 4 workdays of current month
 98 |     #cumsum is to control the holding of underlying asset
 99 |     #sigup and siglo are the variables to store the upper/lower threshold  
100 |     #upper and lower are for the purpose of tracking sigup and siglo
101 |     signals=df[df.index>=intraday['date'].iloc[rg-1]]
102 |     signals['signals']=0
103 |     signals['cumsum']=0
104 |     signals['upper']=0.0
105 |     signals['lower']=0.0
106 |     sigup=float(0)
107 |     siglo=float(0)
108 |     
109 |     #for traversal on time series
110 |     #the tricky part is the slicing
111 |     #we have to either use [i:i] or pd.Series
112 |     #first we set up thresholds at the beginning of london market
113 |     #which is est 3am
114 |     #if the price exceeds either threshold
115 |     #we will take long/short positions  
116 |     
117 |     for i in signals.index:
118 |         
119 |         #note that intraday and dataframe have different frequencies
120 |         #obviously different metrics for indexes
121 |         #we use variable date for index convertion
122 |         date='%s-%s-%s'%(i.year,i.month,i.day)
123 |         
124 |         
125 |         #market opening
126 |         #set up thresholds
127 |         if (i.hour==3 and i.minute==0):
128 |             sigup=float(param*intraday['range'][date]+pd.Series(signals[column])[i])
129 |             siglo=float(-(1-param)*intraday['range'][date]+pd.Series(signals[column])[i])
130 | 
131 |         #thresholds got breached
132 |         #signals generating
133 |         if (sigup!=0 and pd.Series(signals[column])[i]>sigup):
134 |             signals.at[i,'signals']=1
135 |         if (siglo!=0 and pd.Series(signals[column])[i]<siglo):
136 |             signals.at[i,'signals']=-1
137 | 
138 | 
139 |         #check if signal has been generated
140 |         #if so, use cumsum to verify that we only generate one signal for each situation
141 |         if pd.Series(signals['signals'])[i]!=0:
142 |             signals['cumsum']=signals['signals'].cumsum()        
143 |             if (pd.Series(signals['cumsum'])[i]>1 or pd.Series(signals['cumsum'])[i]<-1):
144 |                 signals.at[i,'signals']=0
145 |                
146 |             #if the price goes from below the lower threshold to above the upper threshold during the day
147 |             #we reverse our positions from short to long
148 |             if (pd.Series(signals['cumsum'])[i]==0):
149 |                 if (pd.Series(signals[column])[i]>sigup):
150 |                     signals.at[i,'signals']=2
151 |                 if (pd.Series(signals[column])[i]<siglo):
152 |                     signals.at[i,'signals']=-2
153 |                     
154 |         #by the end of london market, which is est 12pm
155 |         #we clear all opening positions
156 |         #the whole part is very similar to London Breakout strategy
157 |         if i.hour==12 and i.minute==0:
158 |             sigup,siglo=float(0),float(0)
159 |             signals['cumsum']=signals['signals'].cumsum()
160 |             signals.at[i,'signals']=-signals['cumsum'][i:i]
161 |             
162 |         #keep track of trigger levels
163 |         signals.at[i,'upper']=sigup
164 |         signals.at[i,'lower']=siglo
165 | 
166 |     return signals
167 | 
168 | #plotting the positions
169 | def plot(signals,intraday,column):
170 |         
171 |     #we have to do a lil bit slicing to make sure we can see the plot clearly
172 |     #the only reason i go to -3 is that day we execute a trade    
173 |     #give one hour before and after market trading hour for as x axis  
174 |     date=pd.to_datetime(intraday['date']).iloc[-3]      
175 |     signew=signals['%s-%s-%s 02:00:00'%(date.year,date.month,date.day):'%s-%s-%s 13:00:00'%(date.year,date.month,date.day)]
176 |     
177 |     fig=plt.figure(figsize=(10,5))
178 |     ax=fig.add_subplot(111)    
179 |     
180 |     #mostly the same as other py files
181 |     #the only difference is to create an interval for signal generation
182 |     ax.plot(signew.index,signew[column],label=column)
183 |     ax.fill_between(signew.loc[signew['upper']!=0].index,signew['upper'][signew['upper']!=0],signew['lower'][signew['upper']!=0],alpha=0.2,color='#355c7d')
184 |     ax.plot(signew.loc[signew['signals']==1].index,signew[column][signew['signals']==1],lw=0,marker='^',markersize=10,c='g',label='LONG')
185 |     ax.plot(signew.loc[signew['signals']==-1].index,signew[column][signew['signals']==-1],lw=0,marker='v',markersize=10,c='r',label='SHORT')
186 | 
187 |     #change legend text color
188 |     lgd=plt.legend(loc='best').get_texts()
189 |     for text in lgd:
190 |         text.set_color('#6C5B7B')
191 | 
192 |     #add some captions
193 |     plt.text('%s-%s-%s 03:00:00'%(date.year,date.month,date.day),signew['upper']['%s-%s-%s 03:00:00'%(date.year,date.month,date.day)],'Upper Bound',color='#C06C84')
194 |     plt.text('%s-%s-%s 03:00:00'%(date.year,date.month,date.day),signew['lower']['%s-%s-%s 03:00:00'%(date.year,date.month,date.day)],'Lower Bound',color='#C06C84')
195 |     
196 |     plt.ylabel(column)
197 |     plt.xlabel('Date')
198 |     plt.title('Dual Thrust')
199 |     plt.grid(True)
200 |     plt.show()
201 | 
202 | 
203 | 
204 | # In[4]:
205 | def main():
206 |     
207 |     #similar to London Breakout
208 |     #my raw data comes from the same website
209 |     # http://www.histdata.com/download-free-forex-data/?/excel/1-minute-bar-quotes
210 |     #just take the mid price of whatever currency pair you want
211 | 
212 |     df=pd.read_csv('gbpusd.csv')
213 |     df.set_index(pd.to_datetime(df['date']),inplace=True)
214 | 
215 |     #rg is the lags of days
216 |     #param is the parameter of trigger range, it should be smaller than one
217 |     #normally ppl use 0.5 to give long and short 50/50 chance to trigger
218 |     rg=5
219 |     param=0.5
220 | 
221 |     #these three variables are for the frequency convertion from minute to intra daily
222 |     year=df.index[0].year
223 |     month=df.index[0].month
224 |     column='price'
225 |     
226 |     intraday=min2day(df,column,year,month,rg)
227 |     signals=signal_generation(df,intraday,param,column,rg)
228 |     plot(signals,intraday,column)
229 | 
230 | #how to calculate stats could be found from my other code called Heikin-Ashi
231 | # https://github.com/je-suis-tm/quant-trading/blob/master/heikin%20ashi%20backtest.py
232 | 
233 | if __name__ == '__main__':
234 |     main()
235 | 


--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
  1 |                                  Apache License
  2 |                            Version 2.0, January 2004
  3 |                         http://www.apache.org/licenses/
  4 | 
  5 |    TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
  6 | 
  7 |    1. Definitions.
  8 | 
  9 |       "License" shall mean the terms and conditions for use, reproduction,
 10 |       and distribution as defined by Sections 1 through 9 of this document.
 11 | 
 12 |       "Licensor" shall mean the copyright owner or entity authorized by
 13 |       the copyright owner that is granting the License.
 14 | 
 15 |       "Legal Entity" shall mean the union of the acting entity and all
 16 |       other entities that control, are controlled by, or are under common
 17 |       control with that entity. For the purposes of this definition,
 18 |       "control" means (i) the power, direct or indirect, to cause the
 19 |       direction or management of such entity, whether by contract or
 20 |       otherwise, or (ii) ownership of fifty percent (50%) or more of the
 21 |       outstanding shares, or (iii) beneficial ownership of such entity.
 22 | 
 23 |       "You" (or "Your") shall mean an individual or Legal Entity
 24 |       exercising permissions granted by this License.
 25 | 
 26 |       "Source" form shall mean the preferred form for making modifications,
 27 |       including but not limited to software source code, documentation
 28 |       source, and configuration files.
 29 | 
 30 |       "Object" form shall mean any form resulting from mechanical
 31 |       transformation or translation of a Source form, including but
 32 |       not limited to compiled object code, generated documentation,
 33 |       and conversions to other media types.
 34 | 
 35 |       "Work" shall mean the work of authorship, whether in Source or
 36 |       Object form, made available under the License, as indicated by a
 37 |       copyright notice that is included in or attached to the work
 38 |       (an example is provided in the Appendix below).
 39 | 
 40 |       "Derivative Works" shall mean any work, whether in Source or Object
 41 |       form, that is based on (or derived from) the Work and for which the
 42 |       editorial revisions, annotations, elaborations, or other modifications
 43 |       represent, as a whole, an original work of authorship. For the purposes
 44 |       of this License, Derivative Works shall not include works that remain
 45 |       separable from, or merely link (or bind by name) to the interfaces of,
 46 |       the Work and Derivative Works thereof.
 47 | 
 48 |       "Contribution" shall mean any work of authorship, including
 49 |       the original version of the Work and any modifications or additions
 50 |       to that Work or Derivative Works thereof, that is intentionally
 51 |       submitted to Licensor for inclusion in the Work by the copyright owner
 52 |       or by an individual or Legal Entity authorized to submit on behalf of
 53 |       the copyright owner. For the purposes of this definition, "submitted"
 54 |       means any form of electronic, verbal, or written communication sent
 55 |       to the Licensor or its representatives, including but not limited to
 56 |       communication on electronic mailing lists, source code control systems,
 57 |       and issue tracking systems that are managed by, or on behalf of, the
 58 |       Licensor for the purpose of discussing and improving the Work, but
 59 |       excluding communication that is conspicuously marked or otherwise
 60 |       designated in writing by the copyright owner as "Not a Contribution."
 61 | 
 62 |       "Contributor" shall mean Licensor and any individual or Legal Entity
 63 |       on behalf of whom a Contribution has been received by Licensor and
 64 |       subsequently incorporated within the Work.
 65 | 
 66 |    2. Grant of Copyright License. Subject to the terms and conditions of
 67 |       this License, each Contributor hereby grants to You a perpetual,
 68 |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 69 |       copyright license to reproduce, prepare Derivative Works of,
 70 |       publicly display, publicly perform, sublicense, and distribute the
 71 |       Work and such Derivative Works in Source or Object form.
 72 | 
 73 |    3. Grant of Patent License. Subject to the terms and conditions of
 74 |       this License, each Contributor hereby grants to You a perpetual,
 75 |       worldwide, non-exclusive, no-charge, royalty-free, irrevocable
 76 |       (except as stated in this section) patent license to make, have made,
 77 |       use, offer to sell, sell, import, and otherwise transfer the Work,
 78 |       where such license applies only to those patent claims licensable
 79 |       by such Contributor that are necessarily infringed by their
 80 |       Contribution(s) alone or by combination of their Contribution(s)
 81 |       with the Work to which such Contribution(s) was submitted. If You
 82 |       institute patent litigation against any entity (including a
 83 |       cross-claim or counterclaim in a lawsuit) alleging that the Work
 84 |       or a Contribution incorporated within the Work constitutes direct
 85 |       or contributory patent infringement, then any patent licenses
 86 |       granted to You under this License for that Work shall terminate
 87 |       as of the date such litigation is filed.
 88 | 
 89 |    4. Redistribution. You may reproduce and distribute copies of the
 90 |       Work or Derivative Works thereof in any medium, with or without
 91 |       modifications, and in Source or Object form, provided that You
 92 |       meet the following conditions:
 93 | 
 94 |       (a) You must give any other recipients of the Work or
 95 |           Derivative Works a copy of this License; and
 96 | 
 97 |       (b) You must cause any modified files to carry prominent notices
 98 |           stating that You changed the files; and
 99 | 
100 |       (c) You must retain, in the Source form of any Derivative Works
101 |           that You distribute, all copyright, patent, trademark, and
102 |           attribution notices from the Source form of the Work,
103 |           excluding those notices that do not pertain to any part of
104 |           the Derivative Works; and
105 | 
106 |       (d) If the Work includes a "NOTICE" text file as part of its
107 |           distribution, then any Derivative Works that You distribute must
108 |           include a readable copy of the attribution notices contained
109 |           within such NOTICE file, excluding those notices that do not
110 |           pertain to any part of the Derivative Works, in at least one
111 |           of the following places: within a NOTICE text file distributed
112 |           as part of the Derivative Works; within the Source form or
113 |           documentation, if provided along with the Derivative Works; or,
114 |           within a display generated by the Derivative Works, if and
115 |           wherever such third-party notices normally appear. The contents
116 |           of the NOTICE file are for informational purposes only and
117 |           do not modify the License. You may add Your own attribution
118 |           notices within Derivative Works that You distribute, alongside
119 |           or as an addendum to the NOTICE text from the Work, provided
120 |           that such additional attribution notices cannot be construed
121 |           as modifying the License.
122 | 
123 |       You may add Your own copyright statement to Your modifications and
124 |       may provide additional or different license terms and conditions
125 |       for use, reproduction, or distribution of Your modifications, or
126 |       for any such Derivative Works as a whole, provided Your use,
127 |       reproduction, and distribution of the Work otherwise complies with
128 |       the conditions stated in this License.
129 | 
130 |    5. Submission of Contributions. Unless You explicitly state otherwise,
131 |       any Contribution intentionally submitted for inclusion in the Work
132 |       by You to the Licensor shall be under the terms and conditions of
133 |       this License, without any additional terms or conditions.
134 |       Notwithstanding the above, nothing herein shall supersede or modify
135 |       the terms of any separate license agreement you may have executed
136 |       with Licensor regarding such Contributions.
137 | 
138 |    6. Trademarks. This License does not grant permission to use the trade
139 |       names, trademarks, service marks, or product names of the Licensor,
140 |       except as required for reasonable and customary use in describing the
141 |       origin of the Work and reproducing the content of the NOTICE file.
142 | 
143 |    7. Disclaimer of Warranty. Unless required by applicable law or
144 |       agreed to in writing, Licensor provides the Work (and each
145 |       Contributor provides its Contributions) on an "AS IS" BASIS,
146 |       WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 |       implied, including, without limitation, any warranties or conditions
148 |       of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 |       PARTICULAR PURPOSE. You are solely responsible for determining the
150 |       appropriateness of using or redistributing the Work and assume any
151 |       risks associated with Your exercise of permissions under this License.
152 | 
153 |    8. Limitation of Liability. In no event and under no legal theory,
154 |       whether in tort (including negligence), contract, or otherwise,
155 |       unless required by applicable law (such as deliberate and grossly
156 |       negligent acts) or agreed to in writing, shall any Contributor be
157 |       liable to You for damages, including any direct, indirect, special,
158 |       incidental, or consequential damages of any character arising as a
159 |       result of this License or out of the use or inability to use the
160 |       Work (including but not limited to damages for loss of goodwill,
161 |       work stoppage, computer failure or malfunction, or any and all
162 |       other commercial damages or losses), even if such Contributor
163 |       has been advised of the possibility of such damages.
164 | 
165 |    9. Accepting Warranty or Additional Liability. While redistributing
166 |       the Work or Derivative Works thereof, You may choose to offer,
167 |       and charge a fee for, acceptance of support, warranty, indemnity,
168 |       or other liability obligations and/or rights consistent with this
169 |       License. However, in accepting such obligations, You may act only
170 |       on Your own behalf and on Your sole responsibility, not on behalf
171 |       of any other Contributor, and only if You agree to indemnify,
172 |       defend, and hold each Contributor harmless for any liability
173 |       incurred by, or claims asserted against, such Contributor by reason
174 |       of your accepting any such warranty or additional liability.
175 | 
176 |    END OF TERMS AND CONDITIONS
177 | 
178 |    APPENDIX: How to apply the Apache License to your work.
179 | 
180 |       To apply the Apache License to your work, attach the following
181 |       boilerplate notice, with the fields enclosed by brackets "[]"
182 |       replaced with your own identifying information. (Don't include
183 |       the brackets!)  The text should be enclosed in the appropriate
184 |       comment syntax for the file format. We also recommend that a
185 |       file or class name and description of purpose be included on the
186 |       same "printed page" as the copyright notice for easier
187 |       identification within third-party archives.
188 | 
189 |    Copyright [yyyy] [name of copyright owner]
190 | 
191 |    Licensed under the Apache License, Version 2.0 (the "License");
192 |    you may not use this file except in compliance with the License.
193 |    You may obtain a copy of the License at
194 | 
195 |        http://www.apache.org/licenses/LICENSE-2.0
196 | 
197 |    Unless required by applicable law or agreed to in writing, software
198 |    distributed under the License is distributed on an "AS IS" BASIS,
199 |    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 |    See the License for the specific language governing permissions and
201 |    limitations under the License.
202 | 


--------------------------------------------------------------------------------
/London Breakout backtest.py:
--------------------------------------------------------------------------------
  1 | # coding: utf-8
  2 | 
  3 | # In[1]:
  4 | 
  5 | #this is to London, the greatest city in the world
  6 | #i was a Londoner, proud of being Londoner, how i love the city!
  7 | #to St Paul, Tate Modern, Millennium Bridge and so much more!
  8 | 
  9 | #okay, lets get down to business
 10 | #the idea of london break out strategy is to take advantage of fx trading hour
 11 | #basically fx trading is 24 hour non stop for weekdays
 12 | #u got tokyo, before tokyo closes, u got london
 13 | #in the afternoon, u got new york, when new york closes, its sydney
 14 | #and several hours later, tokyo starts again
 15 | #however, among these three major players
 16 | #london is where the majority trades are executed
 17 | #not sure if it will stay the same after brexit actually takes place
 18 | #what we intend to do is look at the last trading hour before london starts
 19 | #we set up our thresholds based on that hours high and low
 20 | #when london market opens, we examine the first 30 minutes
 21 | #if it goes way above or below thresholds
 22 | #we long or short certain currency pairs
 23 | #and we clear our positions based on target and stop loss we set
 24 | #if they havent reach the trigger condition by the end of trading hour
 25 | #we exit our trades and close all positions
 26 | 
 27 | #it sounds easy in practise
 28 | #just a simple prediction of london fx market based on tokyo market
 29 | #but the code of london breakout is extremely time consuming
 30 | #first, we need to get one minute frequency dataset for backtest
 31 | #i would recommend this website
 32 | # http://www.histdata.com/download-free-forex-data/?/excel/1-minute-bar-quotes
 33 | #we can get as many as free datasets of various currency pairs we want
 34 | #before our backtesting, we should cleanse the raw data
 35 | #what we get from the website is one minute frequency bid-ask price
 36 | #i take the average of em and add a header called price
 37 | #i save it on local disk then read it via python
 38 | #please note that this website uses new york time zone utc -5
 39 | #for non summer daylight saving time
 40 | #london market starts at gmt 8 am
 41 | #which is est 3 am
 42 | #daylight saving time is another story
 43 | #what a stupid idea it is
 44 | import os
 45 | os.chdir('d:/')
 46 | import matplotlib.pyplot as plt
 47 | import pandas as pd
 48 | 
 49 | # In[2]:
 50 | 
 51 | def london_breakout(df):
 52 |     
 53 |     df['signals']=0
 54 | 
 55 |     #cumsum is the cumulated sum of signals
 56 |     #later we would use it to control our positions
 57 |     df['cumsum']=0
 58 | 
 59 |     #upper and lower are our thresholds
 60 |     df['upper']=0.0
 61 |     df['lower']=0.0
 62 | 
 63 |     return df
 64 | 
 65 | 
 66 | def signal_generation(df,method):
 67 |     
 68 |     #tokyo_price is a list to store average price of
 69 |     #the last trading hour of tokyo market
 70 |     #we use max, min to define the real threshold later
 71 |     tokyo_price=[]
 72 | 
 73 |     #risky_stop is a parameter set by us
 74 |     #it is to reduce the risk exposure to volatility
 75 |     #i am using 100 basis points
 76 |     #for instance, we have defined our upper and lower thresholds
 77 |     #however, when london market opens
 78 |     #the price goes skyrocketing 
 79 |     #say 200 basis points above upper threshold
 80 |     #i personally wouldnt get in the market as its too risky
 81 |     #also, my stop loss and target is 50 basis points
 82 |     #just half of my risk interval
 83 |     #i will use this variable for later stop loss set up
 84 |     risky_stop=0.01
 85 | 
 86 |     #this is another parameter set by us
 87 |     #it is about how long opening volatility would wear off
 88 |     #for me, 30 minutes after the market opening is the boundary
 89 |     #as long as its under 30 minutes after the market opening
 90 |     #if the price reaches threshold level, i will trade on signals
 91 |     open_minutes=30
 92 | 
 93 |     #this is the price when we execute a trade
 94 |     #we need to save it to set up the stop loss
 95 |     executed_price=float(0)
 96 |     
 97 |     signals=method(df)
 98 |     signals['date']=pd.to_datetime(signals['date'])
 99 |     
100 |     #this is the core part
101 |     #the time complexity for this part is extremely high
102 |     #as there are too many constraints
103 |     #if u have a better idea to optimize it
104 |     #plz let me know
105 | 
106 |     for i in range(len(signals)):
107 |         
108 |         #as mentioned before
109 |         #the dataset use eastern standard time
110 |         #so est 2am is the last hour before london starts
111 |         #we try to append all the price into the list called threshold
112 |         if signals['date'][i].hour==2:
113 |             tokyo_price.append(signals['price'][i])
114 |             
115 |         #est 3am which is gmt 8am
116 |         #thats when london market starts
117 |         #good morning city of london and canary wharf!
118 |         #right at this moment
119 |         #we get max and min of the price of tokyo trading hour
120 |         #we set up the threshold as the way it is
121 |         #alternatively, we can put 10 basis points above and below thresholds
122 |         #we also use upper and lower list to keep track of our thresholds
123 |         #and now we clear the list called threshold
124 |         elif signals['date'][i].hour==3 and signals['date'][i].minute==0:
125 | 
126 |             upper=max(tokyo_price)
127 |             lower=min(tokyo_price)
128 | 
129 |             signals.at[i,'upper']=upper
130 |             signals.at[i,'lower']=lower
131 | 
132 |             tokyo_price=[]
133 |             
134 |         #prior to 30 minutes i have mentioned before
135 |         #as long as its under 30 minutes after market opening
136 |         #signals will be generated once conditions are met
137 |         #this is a relatively risky way
138 |         #alternatively, we can set the signal generation time at a fixed point
139 |         #when its gmt 8 30 am, we check the conditions to see if there is any signal
140 |         elif signals['date'][i].hour==3 and signals['date'][i].minute<open_minutes:
141 | 
142 |             #again, we wanna keep track of thresholds during signal generation periods
143 |             signals.at[i,'upper']=upper
144 |             signals.at[i,'lower']=lower
145 |             
146 |             #this is the condition of signals generation
147 |             #when the price is above upper threshold
148 |             #we set signals to 1 which implies long
149 |             if signals['price'][i]-upper>0:
150 |                 signals.at[i,'signals']=1
151 | 
152 |                 #we use cumsum to check the cumulated sum of signals
153 |                 #we wanna make sure that
154 |                 #only the first price above upper threshold triggers the signal
155 |                 #also, if it goes skyrocketing
156 |                 #say 200 basis points above, which is 100 above our risk tolerance
157 |                 #we set it as a false alarm
158 |                 signals['cumsum']=signals['signals'].cumsum()
159 | 
160 |                 if signals['price'][i]-upper>risky_stop:
161 |                     signals.at[i,'signals']=0
162 | 
163 |                 elif signals['cumsum'][i]>1:
164 |                     signals.at[i,'signals']=0
165 | 
166 |                 else:
167 | 
168 |                     #we also need to store the price when we execute a trade
169 |                     #its for stop loss calculation
170 |                     executed_price=signals['price'][i]
171 | 
172 |             #vice versa    
173 |             if signals['price'][i]-lower<0:
174 |                 signals.at[i,'signals']=-1
175 | 
176 |                 signals['cumsum']=signals['signals'].cumsum()
177 | 
178 |                 if lower-signals['price'][i]>risky_stop:
179 |                     signals.at[i,'signals']=0
180 | 
181 |                 elif signals['cumsum'][i]<-1:
182 |                     signals.at[i,'signals']=0
183 | 
184 |                 else:
185 |                     executed_price=signals['price'][i]
186 |                     
187 |         #when its gmt 5 pm, london market closes
188 |         #we use cumsum to see if there is any position left open
189 |         #we take -cumsum as a signal
190 |         #if there is no open position, -0 is still 0
191 |         elif signals['date'][i].hour==12:
192 |             signals['cumsum']=signals['signals'].cumsum()
193 |             signals.at[i,'signals']=-signals['cumsum'][i]
194 |             
195 |         #during london trading hour after opening but before closing
196 |         #we still use cumsum to check our open positions
197 |         #if there is any open position
198 |         #we set our condition at original executed price +/- half of the risk interval
199 |         #when it goes above or below our risk tolerance
200 |         #we clear positions to claim profit or loss
201 |         else:
202 |             signals['cumsum']=signals['signals'].cumsum()
203 |             
204 |             if signals['cumsum'][i]!=0:
205 |                 if signals['price'][i]>executed_price+risky_stop/2:
206 |                     signals.at[i,'signals']=-signals['cumsum'][i]
207 |                     
208 |                 if signals['price'][i]<executed_price-risky_stop/2:
209 |                     signals.at[i,'signals']=-signals['cumsum'][i]
210 |     
211 |     return signals
212 | 
213 | 
214 | 
215 | def plot(new):
216 |     
217 |     #the first plot is price with LONG/SHORT positions
218 |     fig=plt.figure()
219 |     ax=fig.add_subplot(111)
220 | 
221 |     new['price'].plot(label='price')
222 | 
223 |     ax.plot(new.loc[new['signals']==1].index,new['price'][new['signals']==1],lw=0,marker='^',c='g',label='LONG')
224 |     ax.plot(new.loc[new['signals']==-1].index,new['price'][new['signals']==-1],lw=0,marker='v',c='r',label='SHORT')
225 |       
226 |     #this is the part where i add some vertical line to indicate market beginning and ending
227 |     date=new.index[0].strftime('%Y-%m-%d')
228 |     plt.axvline('%s 03:00:00'%(date),linestyle=':',c='k')
229 |     plt.axvline('%s 12:00:00'%(date),linestyle=':',c='k')
230 | 
231 | 
232 |     plt.legend(loc='best')
233 |     plt.title('London Breakout')
234 |     plt.ylabel('price')
235 |     plt.xlabel('Date')
236 |     plt.grid(True)
237 |     plt.show()
238 | 
239 | 
240 |     #lets look at the market opening and break it down into 110 minutes
241 |     #we wanna observe how the price goes beyond the threshold
242 | 
243 |     f='%s 02:50:00'%(date)
244 |     l='%s 03:30:00'%(date)
245 |     news=signals[f:l]
246 |     fig=plt.figure()
247 |     bx=fig.add_subplot(111)
248 | 
249 |     bx.plot(news.loc[news['signals']==1].index,news['price'][news['signals']==1],lw=0,marker='^',markersize=10,c='g',label='LONG')
250 |     bx.plot(news.loc[news['signals']==-1].index,news['price'][news['signals']==-1],lw=0,marker='v',markersize=10,c='r',label='SHORT')
251 | 
252 |     #i only need to plot non zero thresholds
253 |     #zero is the value outta market opening period 
254 |     bx.plot(news.loc[news['upper']!=0].index,news['upper'][news['upper']!=0],lw=0,marker='.',markersize=7,c='#BC8F8F',label='upper threshold')
255 |     bx.plot(news.loc[news['lower']!=0].index,news['lower'][news['lower']!=0],lw=0,marker='.',markersize=5,c='#FF4500',label='lower threshold')
256 |     bx.plot(news['price'],label='price')
257 | 
258 | 
259 |     plt.grid(True)
260 |     plt.ylabel('price')
261 |     plt.xlabel('time interval')
262 |     plt.xticks([])
263 |     plt.title('%s Market Opening'%date)
264 |     plt.legend(loc='best')
265 |     plt.show()
266 |     
267 |     
268 | # In[3]:
269 | def main():
270 |     
271 |     df=pd.read_csv('gbpusd.csv')
272 | 
273 |     signals=signal_generation(df,london_breakout)
274 | 
275 |     new=signals
276 |     new.set_index(pd.to_datetime(signals['date']),inplace=True)
277 |     date=new.index[0].strftime('%Y-%m-%d')
278 |     new=new['%s'%date]
279 | 
280 |     plot(new)
281 | 
282 | #how to calculate stats could be found from my other code called Heikin-Ashi
283 | # https://github.com/je-suis-tm/quant-trading/blob/master/heikin%20ashi%20backtest.py
284 | 
285 | if __name__ == '__main__':
286 |     main()
287 | 


--------------------------------------------------------------------------------
/MACD Oscillator backtest.py:
--------------------------------------------------------------------------------
  1 | # -*- coding: utf-8 -*-
  2 | """
  3 | Created on Tue Feb  6 11:57:46 2018
  4 | 
  5 | @author: Administrator
  6 | """
  7 | 
  8 | # In[1]:
  9 | 
 10 | #need to get fix yahoo finance package first
 11 | 
 12 | import matplotlib.pyplot as plt
 13 | import numpy as np
 14 | import pandas as pd
 15 | import fix_yahoo_finance as yf
 16 | 
 17 | 
 18 | 
 19 | # In[2]:
 20 | 
 21 | #simple moving average
 22 | def macd(signals):
 23 |     
 24 |     
 25 |     signals['ma1']=signals['Close'].rolling(window=ma1,min_periods=1,center=False).mean()
 26 |     signals['ma2']=signals['Close'].rolling(window=ma2,min_periods=1,center=False).mean()
 27 |     
 28 |     return signals
 29 | 
 30 | 
 31 | 
 32 | # In[3]:
 33 | 
 34 | #signal generation
 35 | #when the short moving average is larger than long moving average, we long and hold
 36 | #when the short moving average is smaller than long moving average, we clear positions
 37 | #the logic behind this is that the momentum has more impact on short moving average
 38 | #we can subtract short moving average from long moving average
 39 | #the difference between is sometimes positive, it sometimes becomes negative
 40 | #thats why it is named as moving average converge/diverge oscillator
 41 | def signal_generation(df,method):
 42 |     
 43 |     signals=method(df)
 44 |     signals['positions']=0
 45 | 
 46 |     #positions becomes and stays one once the short moving average is above long moving average
 47 |     signals['positions'][ma1:]=np.where(signals['ma1'][ma1:]>=signals['ma2'][ma1:],1,0)
 48 | 
 49 |     #as positions only imply the holding
 50 |     #we take the difference to generate real trade signal
 51 |     signals['signals']=signals['positions'].diff()
 52 | 
 53 |     #oscillator is the difference between two moving average
 54 |     #when it is positive, we long, vice versa
 55 |     signals['oscillator']=signals['ma1']-signals['ma2']
 56 | 
 57 |     return signals
 58 | 
 59 | 
 60 | 
 61 | # In[4]:
 62 | 
 63 | #plotting the backtesting result
 64 | def plot(new, ticker):
 65 |     
 66 |     #the first plot is the actual close price with long/short positions
 67 |     fig=plt.figure()
 68 |     ax=fig.add_subplot(111)
 69 |     
 70 |     new['Close'].plot(label=ticker)
 71 |     ax.plot(new.loc[new['signals']==1].index,new['Close'][new['signals']==1],label='LONG',lw=0,marker='^',c='g')
 72 |     ax.plot(new.loc[new['signals']==-1].index,new['Close'][new['signals']==-1],label='SHORT',lw=0,marker='v',c='r')
 73 | 
 74 |     plt.legend(loc='best')
 75 |     plt.grid(True)
 76 |     plt.title('Positions')
 77 |     
 78 |     plt.show()
 79 |     
 80 |     #the second plot is long/short moving average with oscillator
 81 |     #note that i use bar chart for oscillator
 82 |     fig=plt.figure()
 83 |     cx=fig.add_subplot(211)
 84 | 
 85 |     new['oscillator'].plot(kind='bar',color='r')
 86 | 
 87 |     plt.legend(loc='best')
 88 |     plt.grid(True)
 89 |     plt.xticks([])
 90 |     plt.xlabel('')
 91 |     plt.title('MACD Oscillator')
 92 | 
 93 |     bx=fig.add_subplot(212)
 94 | 
 95 |     new['ma1'].plot(label='ma1')
 96 |     new['ma2'].plot(label='ma2',linestyle=':')
 97 |     
 98 |     plt.legend(loc='best')
 99 |     plt.grid(True)
100 |     plt.show()
101 | 
102 |     
103 | # In[5]:
104 | 
105 | def main():
106 |     
107 |     #input the long moving average and short moving average period
108 |     #for the classic MACD, it is 12 and 26
109 |     #once a upon a time you got six trading days in a week
110 |     #so it is two week moving average versus one month moving average
111 |     #for now, the ideal choice would be 10 and 21
112 |     
113 |     global ma1,ma2,stdate,eddate,ticker,slicer
114 | 
115 |     #macd is easy and effective
116 |     #there is just one issue
117 |     #entry signal is always late
118 |     #watch out for downward EMA spirals!
119 |     ma1=int(input('ma1:'))
120 |     ma2=int(input('ma2:'))
121 |     stdate=input('start date in format yyyy-mm-dd:')
122 |     eddate=input('end date in format yyyy-mm-dd:')
123 |     ticker=input('ticker:')
124 | 
125 |     #slicing the downloaded dataset
126 |     #if the dataset is too large, backtesting plot would look messy
127 |     #you get too many markers cluster together
128 |     slicer=int(input('slicing:'))
129 | 
130 |     #downloading data
131 |     df=yf.download(ticker,start=stdate,end=eddate)
132 |     
133 |     new=signal_generation(df,macd)
134 |     new=new[slicer:]
135 |     plot(new, ticker)
136 | 
137 | 
138 | #how to calculate stats could be found from my other code called Heikin-Ashi
139 | # https://github.com/je-suis-tm/quant-trading/blob/master/heikin%20ashi%20backtest.py
140 | 
141 | 
142 | if __name__ == '__main__':
143 |     main()
144 | 


--------------------------------------------------------------------------------
/Monte Carlo project/README.md:
--------------------------------------------------------------------------------
 1 | ## Monte Carlo simulation in trading is nothing but house of cards
 2 | 
 3 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/xkcd_curve_fitting.png)
 4 | 
 5 | Why do I put this picture (see <a href=https://www.explainxkcd.com/wiki/index.php/2048:_Curve-Fitting>here</a> for explanation) right in front of everything? Have I caught your attention? Hopefully I have. These days, blogs on data science topic are dime a dozen. Some blogs teach you how to use decision tree/random forrest to predict the stock price movement, others illustrate the possibility of back-propagation neural network to forecast a bond price. The brutal truth is, most of them are nothing but house of cards. So far, I have not heard any quant shops hit the jackpot via machine learning. Most applications of machine learning in trading are leaned towards analytics rather than prediction or execution. One quant estimates the failure rate of machine learning in live tests of trading is at about <a href=https://www.bloomberg.com/news/articles/2019-07-11/why-machine-learning-hasn-t-made-investors-smarter-quicktake>90%</a>. 
 6 | 
 7 | Monte Carlo, my first thought on these two words is the grand casino, where you dress up in tuxedo, meet Famke Janssen after car chase  and introduce yourself in a deep sexy voice, 'Bond, James Bond'. Indeed, the simulation is named after the infamous casino in Monaco near Côte d'Azur. It actually refers to the computer simulation of massive amount of random events. This unconventional mathematical method is extremely powerful in the study of stochastic process. Here comes the argument on Linkedin that caught my eyes the other day. "Stock price can be seemed as a <a href=https://en.wikipedia.org/wiki/Wiener_process>Wiener Process</a>. Hence, we can use Monte Carlo simulation to predict the stock price." said a data science blog. Well, in order to be a Wiener Process, we have to assume the stock price is continuous in time. In reality, the market closes. The overnight volatility exists. But that is not the biggest issue here. The biggest issue is, can we really use Monte Carlo simulation to predict the stock price, even a range or its direction?
 8 | 
 9 | The author offered a quite interesting idea. As he suggested, the first step was to run as many simulations as possible on <a href=https://en.wikipedia.org/wiki/Stochastic_differential_equation#Use_in_probability_and_mathematical_finance>stochastic differential equations</a> to predict stock price. The goal of simulations was to pick a best fitted curve (in translation, the smallest standard deviation) compared to the historical data. There might exist a hidden pattern in the historical log return. The best fitted curve had the potential to replicate the hidden pattern and reflect it in the future forecast. The idea sounds neat, doesn't it? Inspired by his idea, we can build up the simulation accordingly. To fully unlock the potential of Monte Carlo simulation on fat tail events, the ticker we pick is General Electric, one of the worst performing stock in 2018. The share price of GE plunged 57.9% in 2018 thanks to its long history of M&A failures. The time horizon of the data series is 2016/1/15-2019/1/15. We split the series into halves, the first half as training horizon and the second half as testing horizon. Monte Carlo simulation will only justify its power if it can predict an extreme event like this.
10 | 
11 | Let's take a look at the figure below. Wow, what a great fit! The best fit is almost like running a cool linear regression with valid input. As you can see, it smoothly fits the curve in training horizon.
12 | 
13 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/ge%20simulation.png)
14 | 
15 | If we extend it to testing horizon...
16 | 
17 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/ge%20versus.png)
18 | 
19 | Oops, house of cards collapses. The real momentum is completely the opposite to the forecast, let alone the actual price. The forecast looks quite okay for the first 50 days of testing horizon. After that, the real momentum falls like a stone down through the deep sea while the forecast is gently climbing up. You may argue the number of the iterations is too small so we cannot make a case. Not exactly, let's look at the figure below.
20 | 
21 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/ge%20accuracy.png)
22 | 
23 | We start from 500 times of simulation to 1500 times of simulation. Each round we increase the number by 50. We don't look at the actual price forecast, just the direction. If the end price of testing horizon is larger than the end price of training horizon, we define it as gain. Vice versa. Only when both actual and forecast directions align, we consider the forecast is accurate. As the result shows, the prediction accuracy is irrelavant to the numbers of simulation. The accuracy is more sort of tossing a coin to guess heads or tails regardless of the times of simulation. If you think 1500 is still not large enough, you can definitely try 150000, be my guest. We don't have so much computing power as an individual user (frankly no patience is the real reason) but I can assure you the result is gonna stay the same. <a href=https://en.wikipedia.org/wiki/Law_of_large_numbers>Law of Large Numbers</a> theorem would not work here.
24 | 
25 | Now that the prophet of Monte Carlo turns out to be a con artist. Does Monte Carlo simulation have any application in risk management? Unless you are drinking the Kool-Aid. Let's extend the first figure a little bit longer to the end of testing horizon.
26 | 
27 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/ge%20simulation2.png)
28 | 
29 | Obviously, out of 500 times of simulation, none of them successfully predict the scale of the downslide. The lowest price of 500 predictions is 10.99 per share. It is still way higher than the real bottom price at 6.71 per share (no wonder GE got its ass kicked out of DJIA). The so-called fat tail event simulation is merely a mirage. If you think GE in 2018 is an extreme case, you'd be so wrong. The next ticker we test is Nvidia from 2006/1/15 to 2009/1/15. In 2008, the share price of Nvidia dropped 75.6%!! The financial crisis is the true playground for risk quants. By default, we continue to split the time horizon of NVDA into two parts by 50:50. The result is in the figure below.
30 | 
31 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/nvda%20versus.png)
32 | 
33 | Undoubtedly, the best forecast fails the expectation again. It cannot accurately predict the direction of the momentum. For the extreme event, Monte Carlo simulation cannot predict the scale of downslide, again! 6.086 per share is the lowest price we achieve from our 500 times of simulation, yet the actual lowest is at 5.9 per share.
34 | 
35 | ![alt text](https://raw.githubusercontent.com/je-suis-tm/quant-trading/master/Monte%20Carlo%20project/preview/nvda%20simulation.png)
36 | 
37 | Still thinking about Monte Carlo for your next big project?
38 | 
39 | 


--------------------------------------------------------------------------------
/Monte Carlo project/preview/ge accuracy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/ge accuracy.png


--------------------------------------------------------------------------------
/Monte Carlo project/preview/ge simulation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/ge simulation.png


--------------------------------------------------------------------------------
/Monte Carlo project/preview/ge simulation2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/ge simulation2.png


--------------------------------------------------------------------------------
/Monte Carlo project/preview/ge versus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/ge versus.png


--------------------------------------------------------------------------------
/Monte Carlo project/preview/nvda simulation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/nvda simulation.png


--------------------------------------------------------------------------------
/Monte Carlo project/preview/nvda versus.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/nvda versus.png


--------------------------------------------------------------------------------
/Monte Carlo project/preview/xkcd_curve_fitting.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Monte Carlo project/preview/xkcd_curve_fitting.png


--------------------------------------------------------------------------------
/Oil Money project/Oil Money CAD.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | import pandas as pd
  8 | import os
  9 | import matplotlib.pyplot as plt
 10 | import copy
 11 | import matplotlib.patches as mpatches
 12 | from mpl_toolkits import mplot3d
 13 | import matplotlib.cm as cm
 14 | import statsmodels.api as sm
 15 | import numpy as np
 16 | from sklearn.cluster import KMeans
 17 | from sklearn.metrics import silhouette_score,silhouette_samples
 18 | from sklearn.model_selection import train_test_split
 19 | os.chdir('d:/')
 20 | 
 21 | 
 22 | # In[2]:
 23 | 
 24 | #plot two curves on same x axis, different y axis
 25 | def dual_axis_plot(xaxis,data1,data2,fst_color='r',
 26 |                     sec_color='b',fig_size=(10,5),
 27 |                    x_label='',y_label1='',y_label2='',
 28 |                    legend1='',legend2='',grid=False,title=''):
 29 |     
 30 |     fig=plt.figure(figsize=fig_size)
 31 |     ax=fig.add_subplot(111)
 32 |     
 33 | 
 34 |     ax.set_xlabel(x_label)
 35 |     ax.set_ylabel(y_label1, color=fst_color)
 36 |     ax.plot(xaxis, data1, color=fst_color,label=legend1)
 37 |     ax.tick_params(axis='y',labelcolor=fst_color)
 38 |     ax.yaxis.labelpad=15
 39 | 
 40 |     plt.legend(loc=3)
 41 |     ax2=ax.twinx()
 42 | 
 43 |     ax2.set_ylabel(y_label2, color=sec_color,rotation=270)
 44 |     ax2.plot(xaxis, data2, color=sec_color,label=legend2)
 45 |     ax2.tick_params(axis='y',labelcolor=sec_color)
 46 |     ax2.yaxis.labelpad=15
 47 | 
 48 |     fig.tight_layout()
 49 |     plt.legend(loc=4)
 50 |     plt.grid(grid)
 51 |     plt.title(title)
 52 |     plt.show()
 53 | 
 54 | #get distance between a point and a line
 55 | def get_distance(x,y,a,b):
 56 |     
 57 |     temp1=y-x*a-b
 58 |     temp2=(a**2+1)**0.5
 59 |     
 60 |     return np.abs(temp1/temp2)
 61 | 
 62 | #create line equation from two points
 63 | def get_line_params(x1,y1,x2,y2):
 64 |     
 65 |     a=(y1-y2)/(x1-x2)
 66 |     b=y1-a*x1
 67 |     
 68 |     return a,b
 69 | 
 70 | 
 71 | # In[3]:
 72 | 
 73 | 
 74 | df=pd.read_csv('wcs crude cadaud.csv',encoding='utf-8')
 75 | df.set_index('date',inplace=True)
 76 | 
 77 | 
 78 | # In[4]:
 79 | 
 80 | 
 81 | df.index=pd.to_datetime(df.index,format='%m/%d/%Y')
 82 | 
 83 | 
 84 | # In[5]:
 85 | 
 86 | 
 87 | df=df.reindex(columns=
 88 | ['cny',
 89 |  'gbp',
 90 |  'usd',
 91 |  'eur',
 92 |  'krw',
 93 |  'mxn',
 94 |  'gas',
 95 |  'wcs',
 96 |  'edmonton',
 97 |  'wti',
 98 |  'gold',
 99 |  'jpy',
100 |  'cad'])
101 | 
102 | 
103 | # In[6]:
104 | 
105 | #create r squared bar charts
106 | 
107 | var=locals()
108 | 
109 | for i in df.columns:
110 |     if i!='cad':
111 |             x=sm.add_constant(df[i])
112 |             y=df['cad']
113 |             m=sm.OLS(y,x).fit()
114 |             var[str(i)]=m.rsquared
115 |      
116 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
117 | ax.spines['top'].set_visible(False)
118 | ax.spines['right'].set_visible(False)
119 | 
120 | width=0.7
121 | colorlist=['#9499a6','#9499a6','#9499a6','#9499a6',
122 |            '#9499a6','#9499a6','#9499a6','#582a20',
123 |            '#be7052','#f2c083','#9499a6','#9499a6']
124 | 
125 | temp=list(df.columns)
126 | for i in temp:
127 |     if i!='cad':
128 |         plt.bar(temp.index(i)+width,            
129 |             var[str(i)],width=width,label=i,
130 |                color=colorlist[temp.index(i)])
131 |  
132 | plt.title('Regressions on Loonie')
133 | plt.ylabel('R Squared\n')
134 | plt.xlabel('\nRegressors')
135 | plt.xticks(np.arange(len(temp))+width,
136 |            ['Yuan', 'Sterling', 'Dollar', 'Euro', 'KRW',
137 |              'MXN', 'Gas', 'WCS', 'Edmonton',
138 |              'WTI', 'Gold', 'Yen'],fontsize=10)
139 | plt.show()
140 | 
141 | 
142 | # In[7]:
143 | 
144 | #normalized value of loonie,yuan and sterling
145 | 
146 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
147 | ax.spines['top'].set_visible(False)
148 | ax.spines['right'].set_visible(False)
149 | 
150 | (df['cny']/df['cny'].iloc[0]).plot(c='#77c9d4',label='Yuan')
151 | (df['gbp']/df['gbp'].iloc[0]).plot(c='#57bc90',label='Sterling')
152 | (df['cad']/df['cad'].iloc[0]).plot(c='#015249',label='Loonie')
153 | 
154 | plt.legend(loc=0)
155 | plt.xlabel('Date')
156 | plt.ylabel('Normalized Value by 100')
157 | plt.title('Loonie vs Yuan vs Sterling')
158 | plt.show()
159 | 
160 | 
161 | # In[8]:
162 | 
163 | #normalized value of wti,wcs and edmonton
164 | 
165 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
166 | ax.spines['top'].set_visible(False)
167 | ax.spines['right'].set_visible(False)
168 | 
169 | (df['wti']/df['wti'].iloc[0]).plot(c='#2a78b2',
170 |                                    label='WTI',alpha=0.5)
171 | (df['wcs']/df['wcs'].iloc[0]).plot(c='#7b68ee',
172 |                                    label='WCS',alpha=0.5)
173 | (df['edmonton']/df['edmonton'].iloc[0]).plot(c='#110b3c',
174 |                                              label='Edmonton',alpha=0.5)
175 | plt.legend(loc=0)
176 | plt.xlabel('Date')
177 | plt.ylabel('Normalized Value by 100')
178 | plt.title('Crude Oil Blends')
179 | plt.show()
180 | 
181 | 
182 | # In[9]:
183 | 
184 | 
185 | dual_axis_plot(df.index,df['cad'],df['wcs'],
186 |                x_label='Date',y_label1='Canadian Dollar',
187 |                y_label2='Western Canadian Select',
188 |                legend1='Loonie',
189 |                legend2='WCS',
190 |                title='Loonie VS WCS in AUD',
191 |                fst_color='#a5a77f',sec_color='#d8dc2c')
192 | 
193 | dual_axis_plot(df.index,
194 |                np.divide(df['cad'],df['usd']),
195 |                np.divide(df['wcs'],df['usd']),
196 |                x_label='Date',y_label1='Canadian Dollar',
197 |                y_label2='Western Canadian Select',
198 |                legend1='Loonie',
199 |                legend2='WCS',
200 |                title='Loonie VS WCS in USD',
201 |                fst_color='#eb712f',sec_color='#91371b')
202 | 
203 | 
204 | # In[10]:
205 | 
206 | #using elbow method to find optimal number of clusters
207 | 
208 | df['date']=[i for i in range(len(df.index))]
209 | 
210 | x=df[['cad','wcs','date']].reset_index(drop=True)
211 | 
212 | sse=[]
213 | for i in range(1, 8):
214 |     kmeans = KMeans(n_clusters = i)
215 |     kmeans.fit(x)
216 |     sse.append(kmeans.inertia_/10000)
217 | 
218 | a,b=get_line_params(0,sse[0],len(sse)-1,sse[-1])
219 | 
220 | distance=[]
221 | for i in range(len(sse)):    
222 |     distance.append(get_distance(i,sse[i],a,b))
223 | 
224 | dual_axis_plot(np.arange(1,len(distance)+1),sse,distance,
225 |                x_label='Numbers of Cluster',y_label1='Sum of Squared Error',
226 |                y_label2='Perpendicular Distance',legend1='SSE',legend2='Distance',
227 |                title='Elbow Method for K Means',fst_color='#116466',sec_color='#e85a4f')
228 |   
229 |   
230 |   
231 | # In[11]:
232 | 
233 | #using silhouette score to find optimal number of clusters
234 | 
235 | sil=[]
236 | for n in range(2,8):
237 |     
238 |     clf=KMeans(n).fit(x)
239 |     projection=clf.predict(x)
240 |         
241 |     sil.append(silhouette_score(x,projection))
242 |     
243 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
244 | ax.spines['top'].set_visible(False)
245 | ax.spines['right'].set_visible(False)
246 | 
247 | ax.plot(np.arange(2,len(sil)+2),sil,
248 |        label='Silhouette',c='#5c0811',
249 |        drawstyle='steps-mid')
250 | ax.plot(sil.index(max(sil))+2,max(sil),
251 |         marker='*',markersize=20,lw=0,
252 |        label='Max Score',c='#d94330')
253 | 
254 | plt.ylabel('Silhouette Score')
255 | plt.xlabel('Numbers of Cluster')
256 | plt.title('Silhouette Analysis for K Means')
257 | plt.legend(loc=0)
258 | plt.show()
259 | 
260 | 
261 | # In[12]:
262 | 
263 | #k means
264 | 
265 | clf=KMeans(n_clusters=2).fit(x)
266 | df['class']=clf.predict(x)
267 | threshold=df[df['class']==0].index[-1]
268 | 
269 | 
270 | # In[13]:
271 | 
272 | #plot clusters in 3d figure
273 | 
274 | ax=plt.figure(figsize=(10,7)).add_subplot(111, projection='3d')
275 | 
276 | 
277 | xdata=df['wcs']
278 | ydata=df['cad']
279 | zdata=df['date']
280 | ax.scatter3D(xdata[df['class']==0],ydata[df['class']==0],
281 |              zdata[df['class']==0],c='#faed26',s=10,alpha=0.5,
282 |              label='Before {}'.format(threshold.strftime('%Y-%m-%d')))
283 | ax.scatter3D(xdata[df['class']==1],ydata[df['class']==1],
284 |              zdata[df['class']==1],c='#46344e',s=10,alpha=0.5,
285 |              label='After {}'.format(threshold.strftime('%Y-%m-%d')))
286 | ax.grid(False)
287 | for i in ax.w_xaxis, ax.w_yaxis, ax.w_zaxis:
288 |     i.pane.set_visible(False)  
289 |     
290 | ax.set_xlabel('WCS')
291 | ax.set_ylabel('Loonie')
292 | ax.set_zlabel('Date')
293 | ax.set_title('K Means on Loonie')
294 | ax.legend(loc=6,bbox_to_anchor=(0.12, -0.1), ncol=4)
295 | 
296 | plt.show()
297 | 
298 | #to generate gif, u can use the following code
299 | #it generates 3d figures from different angles
300 | #use imageio to concatenate 
301 | """
302 | for ii in range(0,360,10):
303 |     ax.view_init(elev=10., azim=ii)
304 |     plt.savefig("cad kmeans%d.png" % (ii))
305 | 
306 | import imageio
307 | 
308 | filenames=["movie%d.png" % (ii) for ii in range(0,360,10)] 
309 | 
310 | images=list(map(lambda filename:imageio.imread(filename),
311 |                 filenames))
312 | 
313 | imageio.mimsave('cad kmeans.gif',images,duration = 0.2)
314 | """
315 | 
316 | # In[14]:
317 | 
318 | #create before/after regression comparison
319 | #the threshold is based upon the finding of k means
320 | 
321 | m=sm.OLS(df['cad'][df['class']==0],sm.add_constant(df['wcs'][df['class']==0])).fit()
322 | before=m.rsquared
323 | m=sm.OLS(df['cad'][df['class']==1],sm.add_constant(df['wcs'][df['class']==1])).fit()
324 | after=m.rsquared
325 | 
326 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
327 | ax.spines['top'].set_visible(False)
328 | ax.spines['right'].set_visible(False)
329 | plt.bar(['Before {}'.format(threshold.strftime('%Y-%m-%d')),
330 |          'After {}'.format(threshold.strftime('%Y-%m-%d'))],
331 |         [before,after],color=['#f172a1','#a1c3d1'])
332 | plt.ylabel('R Squared')
333 | plt.title('Cluster + Regression')
334 | plt.show()
335 | 
336 | 
337 | 
338 | # In[15]:
339 | 
340 | #create 1 std, 2 std band
341 | 
342 | for i in range(2):
343 |     
344 |     x_train,x_test,y_train,y_test=train_test_split(
345 |         sm.add_constant(df['wcs'][df['class']==i]),
346 |         df['cad'][df['class']==i],test_size=0.5,shuffle=False)
347 |     
348 |     m=sm.OLS(y_test,x_test).fit()
349 |     
350 |     forecast=m.predict(x_test)
351 |     
352 |     ax=plt.figure(figsize=(10,5)).add_subplot(111)
353 |     ax.spines['top'].set_visible(False)
354 |     ax.spines['right'].set_visible(False)
355 |     forecast.plot(label='Fitted',c='#ab987a')
356 |     y_test.plot(label='Actual',c='#ff533d')
357 |     ax.fill_between(y_test.index,
358 |                     forecast+np.std(m.resid),
359 |                     forecast-np.std(m.resid),
360 |                     color='#0f1626', \
361 |                     alpha=0.6, \
362 |                     label='1 Sigma')
363 |     
364 |     ax.fill_between(y_test.index,
365 |                     forecast+2*np.std(m.resid),
366 |                     forecast-2*np.std(m.resid),
367 |                     color='#0f1626', \
368 |                     alpha=0.8, \
369 |                     label='2 Sigma')
370 |     
371 |     plt.legend(loc=0)
372 |     title='Before '+threshold.strftime('%Y-%m-%d') if i==0 else 'After '+threshold.strftime('%Y-%m-%d')
373 |     plt.title(f'{title}\nCanadian Dollar Positions\nR Squared {round(m.rsquared*100,2)}%\n')
374 |     plt.xlabel('\nDate')
375 |     plt.ylabel('CADAUD')
376 |     plt.show()
377 | 
378 | 
379 | 
380 | 
381 | 


--------------------------------------------------------------------------------
/Oil Money project/Oil Money COP.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | import pandas as pd
  8 | import os
  9 | import matplotlib.pyplot as plt
 10 | import statsmodels.api as sm
 11 | import seaborn as sns
 12 | import numpy as np
 13 | from sklearn.model_selection import train_test_split
 14 | os.chdir('k:/')
 15 | 
 16 | 
 17 | # In[2]:
 18 | 
 19 | #plot two curves on same x axis, different y axis
 20 | def dual_axis_plot(xaxis,data1,data2,fst_color='r',
 21 |                     sec_color='b',fig_size=(10,5),
 22 |                    x_label='',y_label1='',y_label2='',
 23 |                    legend1='',legend2='',grid=False,title=''):
 24 |     
 25 |     fig=plt.figure(figsize=fig_size)
 26 |     ax=fig.add_subplot(111)
 27 |     
 28 | 
 29 |     ax.set_xlabel(x_label)
 30 |     ax.set_ylabel(y_label1, color=fst_color)
 31 |     ax.plot(xaxis, data1, color=fst_color,label=legend1)
 32 |     ax.tick_params(axis='y',labelcolor=fst_color)
 33 |     ax.yaxis.labelpad=15
 34 | 
 35 |     plt.legend(loc=3)
 36 |     ax2=ax.twinx()
 37 | 
 38 |     ax2.set_ylabel(y_label2, color=sec_color,rotation=270)
 39 |     ax2.plot(xaxis, data2, color=sec_color,label=legend2)
 40 |     ax2.tick_params(axis='y',labelcolor=sec_color)
 41 |     ax2.yaxis.labelpad=15
 42 | 
 43 |     fig.tight_layout()
 44 |     plt.legend(loc=4)
 45 |     plt.grid(grid)
 46 |     plt.title(title)
 47 |     plt.show()
 48 | 
 49 | 
 50 | 
 51 | # In[3]:
 52 | 
 53 | #read dataframe    
 54 | df=pd.read_csv('vas crude copaud.csv',encoding='utf-8')
 55 | df.set_index('date',inplace=True)
 56 | df.index=pd.to_datetime(df.index)
 57 | 
 58 | 
 59 | # In[4]:
 60 | 
 61 | #run regression on each input
 62 | 
 63 | D={}
 64 | 
 65 | for i in df.columns:
 66 |     if i!='cop':
 67 |             x=sm.add_constant(df[i])
 68 |             y=df['cop']
 69 |             m=sm.OLS(y,x).fit()
 70 |             D[i]=m.rsquared
 71 |             
 72 | D=dict(sorted(D.items(),key=lambda x:x[1],reverse=True))
 73 | 
 74 | 
 75 | # In[5]:
 76 | 
 77 | #create r squared bar charts
 78 | 
 79 | colorlist=[]
 80 | 
 81 | for i in D:
 82 |     if i =='wti':
 83 |         colorlist.append('#447294')
 84 |     elif i=='brent':
 85 |         colorlist.append('#8fbcdb')
 86 |     elif i=='vasconia':
 87 |         colorlist.append('#f4d6bc')
 88 |     else:
 89 |         colorlist.append('#cdc8c8')
 90 |         
 91 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
 92 | ax.spines['top'].set_visible(False)
 93 | ax.spines['right'].set_visible(False)
 94 | 
 95 | width=0.7
 96 | 
 97 | for i in D:
 98 |     plt.bar(list(D.keys()).index(i)+width,            
 99 |             D[i],width=width,label=i,
100 |             color=colorlist[list(D.keys()).index(i)])
101 |  
102 | plt.title('Regressions on COP')
103 | plt.ylabel('R Squared\n')
104 | plt.xlabel('\nRegressors')
105 | plt.xticks(np.arange(len(D))+width,
106 |            [i.upper() for i in D.keys()],fontsize=8)
107 | plt.show()
108 | 
109 | 
110 | # In[6]:
111 | 
112 | #normalized value of wti,brent and vasconia
113 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
114 | ax.spines['top'].set_visible(False)
115 | ax.spines['right'].set_visible(False)
116 | 
117 | (df['vasconia']/df['vasconia'].iloc[0]).plot(c='#6f6ff4',
118 |                                              label='Vasconia',alpha=0.5)
119 | (df['brent']/df['brent'].iloc[0]).plot(c='#e264c0',
120 |                                    label='Brent',alpha=0.5)
121 | (df['wti']/df['wti'].iloc[0]).plot(c='#fb6630',
122 |                                    label='WTI',alpha=0.5)
123 | plt.legend(loc=0)
124 | plt.xlabel('Date')
125 | plt.ylabel('Normalized Value by 100')
126 | plt.title('Crude Oil Blends')
127 | plt.show()
128 | 
129 | 
130 | # In[7]:
131 | 
132 | #cop vs gold
133 | dual_axis_plot(df.index,df['cop'],df['gold'],
134 |                x_label='Date',y_label1='Colombian Peso',
135 |                y_label2='Gold LBMA',
136 |                legend1='COP',
137 |                legend2='Gold',
138 |                title='COP VS Gold',
139 |                fst_color='#96CEB4',sec_color='#FFA633')
140 | 
141 | #cop vs usd
142 | dual_axis_plot(df.index,df['cop'],df['usd'],
143 |                x_label='Date',y_label1='Colombian Peso',
144 |                y_label2='US Dollar',
145 |                legend1='COP',
146 |                legend2='USD',
147 |                title='COP VS USD',
148 |                fst_color='#9DE0AD',sec_color='#5C4E5F')
149 | 
150 | 
151 | # In[8]:
152 | 
153 | #cop vs brl
154 | dual_axis_plot(df.index,df['cop'],df['brl'],
155 |                x_label='Date',y_label1='Colombian Peso',
156 |                y_label2='Brazilian Real',
157 |                legend1='COP',
158 |                legend2='BRL',
159 |                title='COP VS BRL',
160 |                fst_color='#a4c100',sec_color='#f7db4f')
161 | 
162 | #usd vs mxn
163 | dual_axis_plot(df.index,df['usd'],df['mxn'],
164 |                x_label='Date',y_label1='US Dollar',
165 |                y_label2='Mexican Peso',
166 |                legend1='USD',
167 |                legend2='MXN',
168 |                title='USD VS MXN',
169 |                fst_color='#F4A688',sec_color='#A2836E')
170 | 
171 | #cop vs mxn
172 | dual_axis_plot(df.index,df['cop'],df['mxn'],
173 |                x_label='Date',y_label1='Colombian Peso',
174 |                y_label2='Mexican Peso',
175 |                legend1='COP',
176 |                legend2='MXN',
177 |                title='COP VS MXN',
178 |                fst_color='#F26B38',sec_color='#B2AD7F')
179 | 
180 | 
181 | # In[9]:
182 | 
183 | #cop vs vasconia
184 | dual_axis_plot(df.index,df['cop'],df['vasconia'],
185 |                x_label='Date',y_label1='Colombian Peso',
186 |                y_label2='Vasconia Crude',
187 |                legend1='COP',
188 |                legend2='Vasconia',
189 |                title='COP VS Vasconia',
190 |                fst_color='#346830',sec_color='#BBAB9B')
191 | 
192 | 
193 | # In[10]:
194 | 
195 | #create before/after regression comparison
196 | m=sm.OLS(df['cop'][:'2016'],sm.add_constant(df['vasconia'][:'2016'])).fit()
197 | before=m.rsquared
198 | m=sm.OLS(df['cop']['2017':],sm.add_constant(df['vasconia']['2017':])).fit()
199 | after=m.rsquared
200 | 
201 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
202 | ax.spines['top'].set_visible(False)
203 | ax.spines['right'].set_visible(False)
204 | plt.bar(['Before 2017',
205 |          'After 2017'],
206 |         [before,after],color=['#82b74b', '#5DD39E'])
207 | plt.ylabel('R Squared')
208 | plt.title('Before/After Regression')
209 | plt.show()
210 | 
211 | 
212 | # In[11]:
213 | 
214 | 
215 | #create 1 std, 2 std band before 2017
216 | x_train,x_test,y_train,y_test=train_test_split(
217 |         sm.add_constant(df['vasconia'][:'2016']),
218 |         df['cop'][:'2016'],test_size=0.5,shuffle=False)
219 |     
220 | m=sm.OLS(y_test,x_test).fit()
221 |     
222 | forecast=m.predict(x_test)
223 |     
224 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
225 | ax.spines['top'].set_visible(False)
226 | ax.spines['right'].set_visible(False)
227 | forecast.plot(label='Fitted',c='#FEFBD8')
228 | y_test.plot(label='Actual',c='#ffd604')
229 | ax.fill_between(y_test.index,
230 |                     forecast+np.std(m.resid),
231 |                     forecast-np.std(m.resid),
232 |                     color='#F4A688', 
233 |                     alpha=0.6, 
234 |                     label='1 Sigma')
235 |     
236 | ax.fill_between(y_test.index,
237 |                     forecast+2*np.std(m.resid),
238 |                     forecast-2*np.std(m.resid),
239 |                     color='#8c7544', 
240 |                     alpha=0.8, 
241 |                     label='2 Sigma')
242 |     
243 | plt.legend(loc=0)
244 | plt.title(f'Colombian Peso Positions\nR Squared {round(m.rsquared*100,2)}%\n')
245 | plt.xlabel('\nDate')
246 | plt.ylabel('COPAUD')
247 | plt.show()
248 | 
249 | 
250 | # In[12]:
251 | 
252 | 
253 | #create 1 std, 2 std band after 2017
254 | x_train,x_test,y_train,y_test=train_test_split(
255 |         sm.add_constant(df['vasconia']['2017':]),
256 |         df['cop']['2017':],test_size=0.5,shuffle=False)
257 |     
258 | m=sm.OLS(y_test,x_test).fit()
259 |     
260 | forecast=m.predict(x_test)
261 |     
262 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
263 | ax.spines['top'].set_visible(False)
264 | ax.spines['right'].set_visible(False)
265 | forecast.plot(label='Fitted',c='#FEFBD8')
266 | y_test.plot(label='Actual',c='#ffd604')
267 | ax.fill_between(y_test.index,
268 |                     forecast+np.std(m.resid),
269 |                     forecast-np.std(m.resid),
270 |                     color='#F4A688', 
271 |                     alpha=0.6, 
272 |                     label='1 Sigma')
273 |     
274 | ax.fill_between(y_test.index,
275 |                     forecast+2*np.std(m.resid),
276 |                     forecast-2*np.std(m.resid),
277 |                     color='#8c7544', \
278 |                     alpha=0.8, \
279 |                     label='2 Sigma')
280 |     
281 | plt.legend(loc=0)
282 | plt.title(f'Colombian Peso Positions\nR Squared {round(m.rsquared*100,2)}%\n')
283 | plt.xlabel('\nDate')
284 | plt.ylabel('COPAUD')
285 | plt.show()
286 | 
287 | 
288 | # In[13]:
289 | 
290 | #shrink data size for better viz
291 | dataset=df['2016':]
292 | dataset.reset_index(inplace=True)
293 | 
294 | 
295 | # In[14]:
296 | 
297 | #import the strategy script as this is a script for analytics and visualization
298 | #the official trading strategy script is in the following link
299 | # https://github.com/je-suis-tm/quant-trading/blob/master/Oil%20Money%20project/Oil%20Money%20Trading%20backtest.py
300 | import oil_money_trading_backtest as om
301 | 
302 | #generate signals,monitor portfolio performance
303 | #plot positions and total asset
304 | signals=om.signal_generation(dataset,'vasconia','cop',om.oil_money,stop=0.001)
305 | p=om.portfolio(signals,'cop')
306 | om.plot(signals,'cop')
307 | om.profit(p,'cop')
308 | 
309 | 
310 | # In[15]:
311 | 
312 | 
313 | #try different holding period and stop loss/profit point
314 | dic={}
315 | for holdingt in range(5,20):
316 |     for stopp in np.arange(0.001,0.005,0.0005):
317 |         signals=om.signal_generation(dataset,'vasconia','cop',om.oil_money,
318 |                                      holding_threshold=holdingt,
319 |                                      stop=round(stopp,4))
320 |         
321 |         p=om.portfolio(signals,'cop')
322 |         dic[holdingt,round(stopp,4)]=p['asset'].iloc[-1]/p['asset'].iloc[0]-1
323 |      
324 | profile=pd.DataFrame({'params':list(dic.keys()),'return':list(dic.values())})
325 | 
326 | 
327 | # In[16]:
328 | 
329 | 
330 | #plotting the distribution of return
331 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
332 | ax.spines['top'].set_visible(False)
333 | ax.spines['right'].set_visible(False)
334 | profile['return'].apply(lambda x:x*100).hist(histtype='bar',
335 |                                              color='#b2660e',
336 |                                              width=0.45,bins=20)
337 | plt.title('Distribution of Return on COP Trading')
338 | plt.grid(False)
339 | plt.ylabel('Frequency')
340 | plt.xlabel('Return (%)')
341 | plt.show()
342 | 
343 | 
344 | # In[17]:
345 | 
346 | 
347 | #plotting the heatmap of return under different parameters
348 | #try to find the optimal parameters to maximize the return
349 | 
350 | #convert the dataframe into a matrix format first
351 | matrix=pd.DataFrame(columns=[round(i,4) for i in np.arange(0.001,0.005,0.0005)])
352 | 
353 | matrix['index']=np.arange(5,20)
354 | matrix.set_index('index',inplace=True)
355 | 
356 | for i,j in profile['params']:
357 |     matrix.at[i,round(j,4)]=     profile['return'][profile['params']==(i,j)].item()*100
358 | 
359 | for i in matrix.columns:
360 |     matrix[i]=matrix[i].apply(float)
361 | 
362 | 
363 | #plotting
364 | fig=plt.figure(figsize=(10,5))
365 | ax=fig.add_subplot(111)
366 | sns.heatmap(matrix,cmap=plt.cm.viridis,
367 |             xticklabels=3,yticklabels=3)
368 | ax.collections[0].colorbar.set_label('Return(%)\n',
369 |                                       rotation=270)
370 | plt.xlabel('\nStop Loss/Profit (points)')
371 | plt.ylabel('Position Holding Period (days)\n')
372 | plt.title('Profit Heatmap\n',fontsize=10)
373 | plt.style.use('default')
374 | 
375 | #it seems like the return doesnt depend on the stop profit/loss point
376 | #it is correlated with the length of holding period
377 | #the ideal one should be 17 trading days
378 | #as for stop loss/profit point could range from 0.002 to 0.005
379 | 
380 | 


--------------------------------------------------------------------------------
/Oil Money project/Oil Money RUB.py:
--------------------------------------------------------------------------------
  1 | # coding: utf-8
  2 | 
  3 | # In[1]:
  4 | 
  5 | 
  6 | import os
  7 | import pandas as pd
  8 | import numpy as np
  9 | import statsmodels.api as sm
 10 | import matplotlib.pyplot as plt
 11 | import re
 12 | 
 13 | os.chdir('d:/')
 14 | df=pd.read_csv('urals crude rubaud.csv')
 15 | 
 16 | 
 17 | # In[2]:
 18 | 
 19 | 
 20 | df.set_index('date',inplace=True)
 21 | df.index=pd.to_datetime(df.index)
 22 | df.dropna(inplace=True)
 23 | 
 24 | 
 25 | # In[3]:
 26 | 
 27 | #this is the part to create r squared of different regressors 
 28 | #in different years for stepwise regression
 29 | #we can use locals to create lists for different currency
 30 | #each list contains r squared of different years
 31 | year=df.index.year.drop_duplicates().tolist()
 32 | var=locals()
 33 | for i in df.columns:
 34 |     if i!='rub':
 35 |         var[i]=[]
 36 |         for j in year:
 37 |             x=sm.add_constant(df[i][str(j):str(j)])
 38 |             y=df['rub'][str(j):str(j)]
 39 |             m=sm.OLS(y,x).fit()
 40 |             var[i].append(m.rsquared)
 41 |            
 42 | 
 43 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
 44 | ax.spines['top'].set_visible(False)
 45 | ax.spines['right'].set_visible(False)
 46 | 
 47 | #to save you from the hassle
 48 | #just use these codes to generate the bar chart
 49 | width=0.3
 50 | colorlist=['#c0334d','#d6618f','#f3d4a0','#f1931b']
 51 | bar=locals()
 52 | for j in range(len(year)):
 53 |     bar[j]=[var[i][j] for i in df.columns if i!='rub']
 54 |     plt.bar(np.arange(1,len([i for i in df.columns if i!='rub'])*2+1,2)+j*width,            
 55 |             bar[j],width=width,label=year[j],color=colorlist[j])
 56 |     
 57 | plt.legend(loc=0)
 58 | plt.title('Stepwise Regression Year by Year')
 59 | plt.ylabel('R Squared')
 60 | plt.xlabel('Regressors')
 61 | plt.xticks(np.arange(1,len([i for i in df.columns if i!='rub'])*2+1,2)+(len(year)-1)*width/2,            
 62 |            ['Urals Crude', 'Japanese\nYen',             
 63 |             'Euro', 'Henry Hub', 'Chinese\nYuan',             
 64 |             'Korean\nWon', 'Ukrainian\nHryvnia'],fontsize=10)
 65 | plt.show()
 66 | 
 67 | 
 68 | # In[4]:
 69 | 
 70 | #this is similar to In[3]
 71 | #In[3] is r squared of each regressor in each year
 72 | #In[4] is r squared of each regressor of years cumulated
 73 | var=locals()
 74 | for i in df.columns:
 75 |     if i!='rub':
 76 |         var[i]=[]
 77 |         for j in year:
 78 |             x=sm.add_constant(df[i][:str(j)])
 79 |             y=df['rub'][:str(j)]
 80 |             m=sm.OLS(y,x).fit()
 81 |             var[i].append(m.rsquared)
 82 |            
 83 | 
 84 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
 85 | ax.spines['top'].set_visible(False)
 86 | ax.spines['right'].set_visible(False)
 87 | 
 88 | width=0.3
 89 | colorlist=['#04060f','#03353e','#0294a5','#a79c93']
 90 | bar=locals()
 91 | for j in range(len(year)):
 92 |     bar[j]=[var[i][j] for i in df.columns if i!='rub']
 93 |     plt.bar(np.arange(1,len([i for i in df.columns if i!='rub'])*2+1,2)+j*width,            
 94 |             bar[j],width=width,label=year[j],color=colorlist[j])
 95 |     
 96 | plt.legend(loc=0)
 97 | plt.title('Stepwise Regression Year Cumulated')
 98 | plt.ylabel('R Squared')
 99 | plt.xlabel('Regressors')
100 | plt.xticks(np.arange(1,len([i for i in df.columns if i!='rub'])*2+1,2)+(len(year)-1)*width/2,            
101 |            ['Urals Crude', 'Japanese\nYen',             
102 |             'Euro', 'Henry Hub', 'Chinese\nYuan',             
103 |             'Korean\nWon', 'Ukrainian\nHryvnia'],fontsize=10)
104 | plt.show()
105 | 
106 | 
107 | # In[5]:
108 | 
109 | #print model summary and actual vs fitted line chart
110 | x=sm.add_constant(pd.concat([df['urals']],axis=1))
111 | y=df['rub']
112 | m=sm.OLS(y['2017':'2018'],x['2017':'2018']).fit()
113 | print(m.summary())
114 | 
115 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
116 | ax.spines['top'].set_visible(False)
117 | ax.spines['right'].set_visible(False)
118 | ax.plot(df.loc['2017':'2018'].index,m.predict(), \
119 |         c='#0abda0',label='Fitted')
120 | ax.plot(df.loc['2017':'2018'].index, \
121 |         df['rub']['2017':'2018'],c='#132226',label='Actual')
122 | plt.legend(loc=0)
123 | plt.title('Russian Ruble 2017-2018')
124 | plt.ylabel('RUBAUD')
125 | plt.xlabel('Date')
126 | plt.show()
127 | 
128 | 
129 | # In[6]:
130 | 
131 | #print model summary and actual vs fitted line chart
132 | x=sm.add_constant(pd.concat([df['urals'],df['eur']],axis=1))
133 | y=df['rub']
134 | m=sm.OLS(y[:'2016'],x[:'2016']).fit()
135 | print(m.summary())
136 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
137 | ax.spines['top'].set_visible(False)
138 | ax.spines['right'].set_visible(False)
139 | plt.plot(df.loc[:'2016'].index,m.predict(), \
140 |          c='#c05640',label='Fitted')
141 | plt.plot(df.loc[:'2016'].index,df['rub'][:'2016'], \
142 |          c='#edd170',label='Actual')
143 | plt.legend(loc=0)
144 | plt.title('Russian Ruble Before 2017')
145 | plt.ylabel('RUBAUD')
146 | plt.xlabel('Date')
147 | plt.show()
148 | 
149 | 
150 | # In[7]:
151 | 
152 | #normalize different regressors by 100 as the initial value
153 | #so that we can observe the trend of different regressors in the same scale
154 | ax=plt.figure(figsize=(10,5)).add_subplot(111)
155 | ax.spines['top'].set_visible(False)
156 | ax.spines['right'].set_visible(False)
157 | 
158 | (df['urals']['2017':'2018']/df['urals']['2017':'2018'].iloc[0]*100).plot(label='Urals Crude',c='#728ca3',alpha=0.5)
159 | (df['jpy']['2017':'2018']/df['jpy']['2017':'2018'].iloc[0]*100).plot(label='Japanese Yen',c='#99bfaa')
160 | (df['eur']['2017':'2018']/df['eur']['2017':'2018'].iloc[0]*100).plot(label='Euro',c='#5c868d')
161 | (df['rub']['2017':'2018']/df['rub']['2017':'2018'].iloc[0]*100).plot(label='Russian Ruble',c='#000000')
162 | plt.legend(loc=0)
163 | plt.title('2017-2018 Trend')
164 | plt.ylabel('Normalized Value by 100')
165 | plt.xlabel('Date')
166 | plt.show()
167 | 
168 | 
169 | # In[8]:
170 | 
171 | #plot actual vs fitted line chart for each year
172 | #including one sigma and two sigma confidence interval
173 | for i in df.index.year.drop_duplicates():
174 |     
175 |     temp=df.loc[str(i):str(i)]
176 |     train=temp.iloc[:int(len(temp)/3)]
177 |     test=temp.iloc[int(len(temp)/3):]
178 |     x=sm.add_constant(train['urals'])
179 |     y=train['rub']
180 |     m=sm.OLS(y,x).fit()
181 |     forecast=m.predict(sm.add_constant(test['urals']))
182 |     resid=np.std(train['rub']-m.predict())
183 |     
184 |     ax=plt.figure(figsize=(10,5)).add_subplot(111)
185 |     ax.spines['top'].set_visible(False)
186 |     ax.spines['right'].set_visible(False)
187 |     
188 |     ax.plot(test.index, \
189 |             forecast, \
190 |             label='Fitted',c='#f5ca99')
191 |     test['rub'].plot(label='Actual',c='#ed5752')
192 |     
193 |     
194 |     ax.fill_between(test.index, \
195 |                     forecast+resid, \
196 |                     forecast-resid, \
197 |                     color='#1e1f26', \
198 |                     alpha=0.8, \
199 |                     label='1 Sigma')
200 |     
201 |     ax.fill_between(test.index, \
202 |                     forecast+2*resid, \
203 |                     forecast-2*resid, \
204 |                     color='#d0e1f9', \
205 |                      alpha=0.7, \
206 |                      label='2 Sigma')
207 |     
208 |     plt.legend(loc='best')
209 |     plt.title(f'{i} Russian Ruble Positions\nR Squared {round(m.rsquared*100,2)}%\n')
210 |     plt.ylabel('RUBAUD')
211 |     plt.xlabel('Date')
212 |     plt.legend()
213 |     plt.show()
214 | 
215 | 


--------------------------------------------------------------------------------
/Oil Money project/Oil Money Trading backtest.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | #here is the official trading strategy script for this lil project
  8 | #the details could be found in the readme of the repo, section norwegian krone and brent crude
  9 | # https://github.com/je-suis-tm/quant-trading/blob/master/Oil%20Money%20project/README.md
 10 | import statsmodels.api as sm
 11 | import copy
 12 | import pandas as pd
 13 | import numpy as np
 14 | import matplotlib.pyplot as plt
 15 | import os
 16 | os.chdir('d:/')
 17 | 
 18 | 
 19 | # In[2]:
 20 | 
 21 | #theoratically we only need two sigma to trigger the trading signals
 22 | #i only add one sigma to make it look better in visualization
 23 | def oil_money(dataset):
 24 |     
 25 |     df=copy.deepcopy(dataset)
 26 |     
 27 |     df['signals']=0
 28 |     df['pos2 sigma']=0.0
 29 |     df['neg2 sigma']=0.0
 30 |     df['pos1 sigma']=0.0
 31 |     df['neg1 sigma']=0.0
 32 |     df['forecast']=0.0
 33 |     
 34 |     return df
 35 | 
 36 | 
 37 | # In[3]:
 38 | 
 39 | #the trading idea is straight forward
 40 | #we run regression on nok and brent of the past 50 data points by default
 41 | #if the rsquared exceeds 0.7 by default
 42 | #the regression model is deemed valid
 43 | #we calculate the standard deviation of the residual
 44 | #and use +/- two sigma as the threshold to trigger the trading signals
 45 | #once the trade is executed
 46 | #we would start a counter to count the period of position holding
 47 | #if the holding period exceeds 10 days by default
 48 | #we clear our positions
 49 | #meanwhile, if the spread between current price and entry price exceeds stop limit
 50 | #which is 0.5 points by default in both ways
 51 | #we clear our positions to claim profit/loss
 52 | #once our positions are cleared
 53 | #we recalibrate our regression model based on the latest 50 data points
 54 | #we keep doing this on and on
 55 | def signal_generation(dataset,x,y,method, \
 56 |                       holding_threshold=10, \
 57 |                       stop=0.5,rsquared_threshold=0.7, \
 58 |                       train_len=50):
 59 |     
 60 |     df=method(dataset)
 61 |     
 62 |     #variable holding takes 3 values, -1,0,1
 63 |     #0 implies no holding positions
 64 |     #1 implies long, -1 implies short
 65 |     #when we wanna clear our positions
 66 |     #we just reverse the sign of holding
 67 |     #which is quite convenient
 68 |     holding=0
 69 |     
 70 |     #trained is a boolean value
 71 |     #it indicates whether the current model is valid
 72 |     #in another word,when trained==True, r squared is over 0.7 by default
 73 |     #and the regressand is within two sigma range from the fitted value
 74 |     trained=False
 75 |     
 76 |     #counter counts the days of position holding
 77 |     counter=0
 78 |     
 79 | 
 80 |     for i in range(train_len,len(df)):
 81 |         
 82 |         #when we have uncleared positions
 83 |         if holding!=0:
 84 |             
 85 |             #when counter exceeds holding threshold
 86 |             #we clear our positions and reset all the parameters
 87 |             if counter>holding_threshold:
 88 |                 df.at[i,'signals']=-holding            
 89 |                 holding=0
 90 |                 trained=False
 91 |                 counter=0
 92 |                 
 93 |                 #we use continue to skip this round of iteration
 94 |                 #only if the clearing condition gets triggered
 95 |                 continue
 96 |                 
 97 |             #plz note i make stop loss and stop profit symmetric
 98 |             #thats why we use absolute value of the spread between current price and entry price
 99 |             #usually stop loss and stop profit are asymmetric 
100 |             #as ppl cannot take as much loss as profit
101 |             if np.abs( \
102 |                       df[y].iloc[i]-df[y][df['signals']!=0].iloc[-1] \
103 |                       )>=stop:
104 |                 df.at[i,'signals']=-holding        
105 |                 holding=0
106 |                 trained=False
107 |                 counter=0
108 |             
109 |                 continue
110 |         
111 |             counter+=1
112 |     
113 |         else:
114 |             
115 |             #if we do not have a valid model yet
116 |             #we would keep trying the latest 50 data points
117 |             if not trained:
118 |                 X=sm.add_constant(df[x].iloc[i-train_len:i])
119 |                 Y=df[y].iloc[i-train_len:i]
120 |                 m=sm.OLS(Y,X).fit()
121 |                 
122 |                 #if r squared meets the statistical request
123 |                 #which is 0.7 by default
124 |                 #we can start to build up confidence intervals
125 |                 if m.rsquared>rsquared_threshold:
126 |                     trained=True
127 |                     sigma=np.std(Y-m.predict(X))
128 |                     
129 |                     #plz note that we set the forecast and confidence intervals
130 |                     #for every data point after the current one
131 |                     #this would fill in the blank once our model turns invalid
132 |                     #when we have a new valid model
133 |                     #the new forecast and confidence intervals would cover the former one
134 |                     df.at[i:,'forecast']= \
135 |                     m.predict(sm.add_constant(df[x].iloc[i:]))
136 |                     
137 |                     df.at[i:,'pos2 sigma']= \
138 |                     df['forecast'].iloc[i:]+2*sigma
139 |                     
140 |                     df.at[i:,'neg2 sigma']= \
141 |                     df['forecast'].iloc[i:]-2*sigma
142 |                     
143 |                     df.at[i:,'pos1 sigma']= \
144 |                     df['forecast'].iloc[i:]+sigma
145 |                     
146 |                     df.at[i:,'neg1 sigma']= \
147 |                     df['forecast'].iloc[i:]-sigma
148 |             
149 |             #once we have a valid model
150 |             #we can feel free to generate trading signals
151 |             if trained:
152 |                 if df[y].iloc[i]>df['pos2 sigma'].iloc[i]:
153 |                     df.at[i,'signals']=1
154 |                     holding=1
155 |                     
156 |                     #once the positions are entered
157 |                     #we set confidence intervals back to the fitted value
158 |                     #so we could avoid the confusion in our visualization
159 |                     #for instance. if we dont do that, 
160 |                     #there would be confidence intervals even when the model is broken
161 |                     #we could have been asking why no trade has been executed,
162 |                     #even when actual price falls out of the confidence intervals?
163 |                     df.at[i:,'pos2 sigma']=df['forecast']
164 |                     df.at[i:,'neg2 sigma']=df['forecast']
165 |                     df.at[i:,'pos1 sigma']=df['forecast']
166 |                     df.at[i:,'neg1 sigma']=df['forecast']
167 |                     
168 |                 if df[y].iloc[i]<df['neg2 sigma'].iloc[i]:
169 |                     df.at[i,'signals']=-1
170 |                     holding=-1
171 |                     
172 |                     df.at[i:,'pos2 sigma']=df['forecast']
173 |                     df.at[i:,'neg2 sigma']=df['forecast']
174 |                     df.at[i:,'pos1 sigma']=df['forecast']
175 |                     df.at[i:,'neg1 sigma']=df['forecast']
176 | 
177 |                     
178 |     return df
179 |     
180 | 
181 | 
182 | # In[4]:
183 | 
184 | #this part is to monitor how our portfolio performs over time
185 | #details can be found from heiki ashi
186 | # https://github.com/je-suis-tm/quant-trading/blob/master/Heikin-Ashi%20backtest.py
187 | def portfolio(signals,close_price,capital0=5000):   
188 |     
189 |     positions=capital0//max(signals[close_price])
190 |     portfolio=pd.DataFrame()
191 |     portfolio['close']=signals[close_price]
192 |     portfolio['signals']=signals['signals']
193 |     
194 |     portfolio['holding']=portfolio['signals'].cumsum()* \
195 |     portfolio['close']*positions
196 | 
197 |     portfolio['cash']=capital0-(portfolio['signals']* \
198 |                                 portfolio['close']*positions).cumsum()
199 |    
200 |     portfolio['asset']=portfolio['holding']+portfolio['cash']
201 |     
202 | 
203 |     return portfolio
204 | 
205 | 
206 | # In[5]:
207 | 
208 | #plotting fitted vs actual price with confidence intervals and positions
209 | def plot(signals,close_price):
210 |     
211 |     data=copy.deepcopy(signals[signals['forecast']!=0])
212 |     ax=plt.figure(figsize=(10,5)).add_subplot(111)
213 |     ax.spines['top'].set_visible(False)
214 |     ax.spines['right'].set_visible(False)
215 |     
216 |     data['forecast'].plot(label='Fitted',color='#f4f4f8',alpha=0.7)
217 |     data[close_price].plot(label='Actual',color='#3c2f2f',alpha=0.7)
218 |     
219 |     ax.fill_between(data.index,data['pos1 sigma'], \
220 |                     data['neg1 sigma'],alpha=0.3, \
221 |                     color='#011f4b', label='1 Sigma')
222 |     ax.fill_between(data.index,data['pos2 sigma'], \
223 |                     data['neg2 sigma'],alpha=0.3, \
224 |                     color='#ffc425', label='2 Sigma')
225 |     
226 |     ax.plot(data.loc[data['signals']==1].index, \
227 |             data[close_price][data['signals']==1],marker='^', \
228 |             c='#00b159',linewidth=0,label='LONG',markersize=11, \
229 |             alpha=1)
230 |     ax.plot(data.loc[data['signals']==-1].index, \
231 |             data[close_price][data['signals']==-1],marker='v', \
232 |             c='#ff6f69',linewidth=0,label='SHORT',markersize=11, \
233 |             alpha=1)
234 |     
235 |     plt.title(f'Oil Money Project\n{close_price.upper()} Positions')
236 |     plt.legend(loc='best')
237 |     plt.xlabel('Date')
238 |     plt.ylabel('Price')
239 |     plt.show()
240 | 
241 | 
242 | # In[6]:
243 | 
244 | #plotting portfolio performance over time with positions
245 | def profit(portfolio,close_price):
246 |     
247 |     data=copy.deepcopy(portfolio)
248 |     ax=plt.figure(figsize=(10,5)).add_subplot(111)
249 |     ax.spines['top'].set_visible(False)
250 |     ax.spines['right'].set_visible(False)
251 |     
252 |     data['asset'].plot(label='Total Asset',color='#58668b')
253 |     
254 |     ax.plot(data.loc[data['signals']==1].index, \
255 |             data['asset'][data['signals']==1],marker='^', \
256 |             c='#00b159',linewidth=0,label='LONG',markersize=11, \
257 |             alpha=1)
258 |     ax.plot(data.loc[data['signals']==-1].index, \
259 |             data['asset'][data['signals']==-1],marker='v', \
260 |             c='#ff6f69',linewidth=0,label='SHORT',markersize=11, \
261 |             alpha=1)
262 |     
263 |     plt.title(f'Oil Money Project\n{close_price.upper()} Total Asset')
264 |     plt.legend(loc='best')
265 |     plt.xlabel('Date')
266 |     plt.ylabel('Asset Value')
267 |     plt.show()
268 | 
269 | 
270 | # In[7]:
271 | 
272 | 
273 | def main():
274 |     
275 |     df=pd.read_csv('brent crude nokjpy.csv')
276 |     signals=signal_generation(df,'brent','nok',oil_money)
277 |     p=portfolio(signals,'nok')
278 |     
279 |     #pandas.at[] is the fastest but it doesnt support datetime index
280 |     #so we have to set datetime index after iteration but before visualization
281 |     signals.set_index('date',inplace=True)
282 |     signals.index=pd.to_datetime(signals.index,format='%m/%d/%Y')
283 |     p.set_index(signals.index,inplace=True)
284 |     
285 |     #we only visualize data point from 387 to 600
286 |     #becuz the visualization of 5 years data could be too messy
287 |     plot(signals.iloc[387:600],'nok')
288 |     profit(p.iloc[387:600],'nok')
289 |     
290 | 
291 | if __name__ == '__main__':
292 |     main()
293 | 
294 | 


--------------------------------------------------------------------------------
/Oil Money project/oil production/oil production choropleth.csv:
--------------------------------------------------------------------------------
  1 | Country,Oil Production
  2 | Russia,10551497
  3 | Saudi Arabia,10460710
  4 | United States of America,8875817
  5 | Iraq,4451516
  6 | Iran,3990956
  7 | China,3980650
  8 | Canada,3662694
  9 | United Arab Emirates,3106077
 10 | Kuwait,2923825
 11 | Brazil,2515459
 12 | Venezuela,2276967
 13 | Mexico,2186877
 14 | Nigeria,1999885
 15 | Angola,1769615
 16 | Norway,1647975
 17 | Kazakhstan,1595199
 18 | Qatar,1522902
 19 | Algeria,1348361
 20 | Oman,1006841
 21 | Libya,1003000
 22 | United Kingdom,939760
 23 | Colombia,897784
 24 | Indonesia,833667
 25 | Azerbaijan,833538
 26 | India,734180
 27 | Malaysia,661240
 28 | Ecuador,548421
 29 | Argentina,510560
 30 | Romania,504000
 31 | Egypt,494325
 32 | Vietnam,301850
 33 | Australia,289749
 34 | Thailand,257525
 35 | Sudan,255000
 36 | South Sudan,255000
 37 | Turkmenistan,230779
 38 | Equatorial Guinea,227000
 39 | Gabon,210820
 40 | Denmark,140637
 41 | Chad,110156
 42 | Brunei,109117
 43 | Ghana,100549
 44 | Cameroon,93205
 45 | Pakistan,84746
 46 | Italy,70675
 47 | East Timor,60661
 48 | Trinidad and Tobago,60090
 49 | Bolivia,58077
 50 | Papua New Guinea,56667
 51 | Uzbekistan,52913
 52 | Cuba,50000
 53 | Turkey,49497
 54 | Tunisia,48757
 55 | Germany,46839
 56 | Peru,40266
 57 | New Zealand,35574
 58 | Ukraine,31989
 59 | Ivory Coast,30000
 60 | Syria,30000
 61 | Belarus,25000
 62 | Mongolia,23426
 63 | Albania,22915
 64 | Yemen,22000
 65 | Poland,20104
 66 | Democratic Republic of the Congo,20000
 67 | Philippines,20000
 68 | Republic of Serbia,20000
 69 | Netherlands,18087
 70 | Suriname,17000
 71 | France,16418
 72 | Austria,15161
 73 | Myanmar,15000
 74 | Hungary,13833
 75 | Croatia,13582
 76 | Niger,13000
 77 | Guatemala,8977
 78 | Mauritania,5000
 79 | Chile,4423
 80 | Bangladesh,4189
 81 | Japan,3918
 82 | Greece,3172
 83 | Spain,2667
 84 | Czech Republic,2333
 85 | Belize,2000
 86 | Lithuania,2000
 87 | South Africa,2000
 88 | Bulgaria,1000
 89 | Kyrgyzstan,1000
 90 | Georgia,400
 91 | Israel,390
 92 | Slovakia,200
 93 | Taiwan,196
 94 | Tajikistan,180
 95 | Morocco,160
 96 | Jordan,22
 97 | Slovenia,5
 98 | Afghanistan,0
 99 | Armenia,0
100 | Antarctica,0
101 | French Southern and Antarctic Lands,0
102 | Burundi,0
103 | Belgium,0
104 | Benin,0
105 | Burkina Faso,0
106 | The Bahamas,0
107 | Bosnia and Herzegovina,0
108 | Bermuda,0
109 | Bhutan,0
110 | Botswana,0
111 | Central African Republic,0
112 | Switzerland,0
113 | Republic of the Congo,0
114 | Costa Rica,0
115 | Northern Cyprus,0
116 | Cyprus,0
117 | Djibouti,0
118 | Dominican Republic,0
119 | Eritrea,0
120 | Estonia,0
121 | Ethiopia,0
122 | Finland,0
123 | Fiji,0
124 | Falkland Islands,0
125 | Guinea,0
126 | Gambia,0
127 | Guinea Bissau,0
128 | Greenland,0
129 | French Guiana,0
130 | Guyana,0
131 | Honduras,0
132 | Haiti,0
133 | Ireland,0
134 | Iceland,0
135 | Jamaica,0
136 | Kenya,0
137 | Cambodia,0
138 | South Korea,0
139 | Kosovo,0
140 | Laos,0
141 | Lebanon,0
142 | Liberia,0
143 | Sri Lanka,0
144 | Lesotho,0
145 | Luxembourg,0
146 | Latvia,0
147 | Moldova,0
148 | Madagascar,0
149 | Macedonia,0
150 | Mali,0
151 | Malta,0
152 | Montenegro,0
153 | Mozambique,0
154 | Malawi,0
155 | Namibia,0
156 | New Caledonia,0
157 | Nicaragua,0
158 | Nepal,0
159 | Panama,0
160 | Puerto Rico,0
161 | North Korea,0
162 | Portugal,0
163 | Paraguay,0
164 | Rwanda,0
165 | Western Sahara,0
166 | Senegal,0
167 | Solomon Islands,0
168 | Sierra Leone,0
169 | El Salvador,0
170 | Somaliland,0
171 | Somalia,0
172 | Sweden,0
173 | Swaziland,0
174 | Togo,0
175 | United Republic of Tanzania,0
176 | Uganda,0
177 | Uruguay,0
178 | Vanuatu,0
179 | West Bank,0
180 | Zambia,0
181 | Zimbabwe,0


--------------------------------------------------------------------------------
/Oil Money project/oil production/oil production choropleth.py:
--------------------------------------------------------------------------------
 1 | 
 2 | # coding: utf-8
 3 | 
 4 | # In[1]:
 5 | 
 6 | 
 7 | import folium
 8 | import os
 9 | os.chdir('h:/')
10 | import pandas as pd
11 | 
12 | 
13 | # In[2]:
14 | 
15 | 
16 | #this table comes from wikipedia
17 | # https://en.wikipedia.org/wiki/List_of_countries_by_oil_production
18 | #but i have changed the names of some countries
19 | #try to keep names consistent with geojson file
20 | df=pd.read_csv('oil production choropleth.csv')
21 | 
22 | 
23 | # In[3]:
24 | 
25 | 
26 | df['Oil Production']=df['Oil Production'].apply(lambda x: x/1000)
27 | 
28 | 
29 | # In[4]:
30 | 
31 | 
32 | #location takes two arguments, latitude and longitude
33 | #zoom_start implies zoom level
34 | #1 is world map, 2 is continent map, 3 is region map
35 | #4 is country map, 5 is county map etc
36 | m=folium.Map(location=(30,50), zoom_start=4)
37 | 
38 | #geo_data is a geojson file
39 | #its a file that indicates country shape on a map
40 | #we can download country and even more detailed level from the first link
41 | #the second link converts all the files in first link to geojson
42 | #https://gadm.org/download_country_v3.html
43 | #https://mapshaper.org/
44 | #here i found the map shape from github
45 | #i just cannot find the original link
46 | #so i just upload it to the repo
47 | 
48 | #data is the dataframe
49 | #columns would be the columns we use in that dataframe
50 | #we need one column for the region name and the other one for value
51 | #the region name should be consistent with the region name in geojson
52 | #and key_on denotes the key in geojson for region names
53 | #fill_color is just matplotlib cmap
54 | #fill_opacity,line_opacity are plotting options
55 | #legend_name is just the name of the label in matplotlib
56 | #threshold_scale can only take up to six values in a list
57 | #for simplicity, we can use from branca.utilities import split_six
58 | #to get the quantile data equally divided into six parts
59 | m.choropleth(
60 |  geo_data=(open("worldmapshape.json",encoding = "utf_8_sig").read()),
61 |  name='choropleth',
62 |  data=df,
63 |  columns=['Country', 'Oil Production'],
64 |  key_on='properties.name',
65 |  fill_color='YlOrRd',
66 |  fill_opacity=0.7,
67 |  line_opacity=0.2,
68 |  legend_name='Oil Production Thousand Barrels/Day',
69 |  threshold_scale=[0.0,1.0, 150.0, 800.0, 2000.0, 4000.0]
70 | )
71 | 
72 | #layout control is just a map filter
73 | #we can unselect choropleth any time
74 | folium.LayerControl().add_to(m)
75 | display(m)
76 | 
77 | #in general, folium is a really good wrap up for leaflet.js
78 | #it saves me a lot of time from learning javascript
79 | #it is very straight forward, a very flat learning curve
80 | #there is only one thing i hate about it
81 | #which is the location name is always in local language
82 | #at least google map provides english plus local language
83 | #this is quite annoying, other than that, its pretty cool
84 | 


--------------------------------------------------------------------------------
/Oil Money project/oil production/oil production cost curve.csv:
--------------------------------------------------------------------------------
 1 | Country,Operational cost dollar per barrel,Capital cost dollar per barrel,Total cost dollar per barrel,Reserve k mil barrels,Daily production mil barrels,2015 average price
 2 | United Kingdom,21.8,30.7,52.5,2.5423575,0.963415531,52.4
 3 | Brazil,17.3,31.5,48.8,12.99978129,2.524976918,52.4
 4 | Canada,18.7,22.4,41,171.5123079,4.388135578,52.4
 5 | US,21.5,14.8,36.2,47.987,12.77291501,52.4
 6 | Norway,24,12.1,36.1,8.005041226,1.9400792,52.4
 7 | Angola,18.8,16.6,35.4,9.524,1.795646188,52.4
 8 | Colombia,15.5,19.8,35.3,2.308,1.005574436,52.4
 9 | Nigeria,16.2,15.3,31.6,37.062,2.201408745,52.4
10 | China,15.6,14.3,29.9,25.62646431,4.308835068,52.4
11 | Mexico,18.3,10.7,29.1,7.9765,2.586540847,52.4
12 | Kazakhstan,16.3,11.5,27.8,30,1.694757886,52.4
13 | Libya,16.6,7.2,23.8,48.363,0.436643836,52.4
14 | Venezuela,9.6,13.9,23.5,300.8783,2.630863,52.4
15 | Algeria,13.2,7.2,20.4,12.2,1.557665233,52.4
16 | Russian Federation,8.9,8.4,17.2,102.3752,11.00667742,52.4
17 | Iran,6.9,5.7,12.6,158.4,3.852759885,52.4
18 | United Arab Emirates,6.6,5.7,12.3,97.8,3.897972489,52.4
19 | Iraq,5.6,5.1,10.7,142.503,3.985940667,52.4
20 | Saudi Arabia,4.5,5.4,9.9,266.455,11.99793798,52.4
21 | Kuwait,3.7,4.8,8.5,101.5,3.060775981,52.4
22 | 


--------------------------------------------------------------------------------
/Oil Money project/oil production/oil production cost curve.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | import numpy as np
  8 | import matplotlib.pyplot as plt
  9 | import os
 10 | os.chdir('d:/')
 11 | import pandas as pd
 12 | 
 13 | 
 14 | # In[2]:
 15 | 
 16 | 
 17 | #traditional commodity cost curve
 18 | #input two pandas series, the third one is optional
 19 | def cost_curve(x,y1,y2=None,
 20 |                hline_var=0,hline_color='k',hline_name='',
 21 |                colormap='tab20c',legends=None,notes=None,
 22 |                ylabel='',xlabel='',title='',fig_size=(10,5)):
 23 |     y2 = [] if y2 is None else y2
 24 |     legends = [] if legends is None else legends
 25 |     notes = [] if notes is None else notes
 26 |     
 27 |     ax=plt.figure(figsize=fig_size).add_subplot(111)
 28 |     ax.spines['top'].set_visible(False)
 29 |     ax.spines['right'].set_visible(False)
 30 |     
 31 |     #set bar location on x axis
 32 |     wid=x
 33 |     cumwid=[0]
 34 |     for i in range(1,len(wid)):
 35 |         cumwid.append(wid[i-1]+cumwid[-1])
 36 | 
 37 |     #assign colors to different bars from cmap
 38 |     cmap=plt.cm.get_cmap(colormap)
 39 |     colors=[cmap(i) for i in np.linspace(0,1,len(y1))]
 40 |     colors2=[tuple([i/1.3 for i in j if i!=1]+[1.0]) for j in colors]
 41 |     
 42 |     for i in range(len(y1)):
 43 |         plt.bar(cumwid[i],            
 44 |                 y1[i],width=wid[i],label=legends[i] if len(legends)>0 else '',
 45 |                    color=colors[i],align='edge')
 46 |         
 47 |         if len(y2)>0:
 48 |             plt.bar(cumwid[i],            
 49 |                     y2[i],width=wid[i],
 50 |                    color=colors2[i],bottom=y1[i],align='edge'
 51 |                    )
 52 |     
 53 |     #plot percentile line if needed
 54 |     plt.axhline(y=hline_var, linestyle='--',
 55 |                 c=hline_color,label=hline_name)
 56 | 
 57 |     plt.title(title,pad=20)
 58 |     ax.yaxis.labelpad=10
 59 |     ax.xaxis.labelpad=10
 60 |     plt.ylabel(ylabel)
 61 |     plt.xlabel(xlabel)
 62 |     
 63 |     #slightly expand x axis to look nicer
 64 |     plt.xlim(min(cumwid),max(cumwid)+list(wid)[-1])
 65 |     plt.xticks([min(cumwid),
 66 |                 np.mean([min(cumwid),
 67 |                          max(cumwid)+list(wid)[-1]]),
 68 |                 1.01*max(cumwid)+list(wid)[-1]])
 69 |     
 70 |     #if cost curve breakdown is provided
 71 |     #add legends to the right
 72 |     if len(y2)>0:
 73 |         plt.text(1.1*max(cumwid)+list(wid)[-1],
 74 |              list(y1)[-1]/2,notes[0],
 75 |                  verticalalignment='center', horizontalalignment='center')
 76 |         plt.text(1.1*max(cumwid)+list(wid)[-1],
 77 |              list(y1)[-1]+list(y2)[-1]/2,notes[1],
 78 |                 verticalalignment='center', horizontalalignment='center')
 79 |     
 80 |     #legends of cost curve for different entities is plotted below the chart
 81 |     plt.legend(loc=6,bbox_to_anchor=(0.12, -0.4), ncol=4)    
 82 |     
 83 |     plt.show()
 84 |     
 85 | 
 86 | 
 87 | # In[3]:
 88 | 
 89 | #why there is only 2015 data?
 90 | #well, they said data is the new oil
 91 | #especially true when it comes to oil data
 92 | 
 93 | #i can only find oil production cost data of 2015 from the below address
 94 | # https://www.statista.com/statistics/597669/cost-breakdown-of-producing-one-barrel-of-oil-in-the-worlds-leading-oil-producing-countries/
 95 | #and i have to do some scraping to actually get the data
 96 | #if u dont know scraping, that will be a problem
 97 | #data scientists cant wait for engineers to feed you data
 98 | #maybe its time for you to learn
 99 | # https://github.com/je-suis-tm/web-scraping
100 | 
101 | #also you can use oil breakeven price to replace cost data
102 | #the following two links contain quite a lot of information
103 | #sadly, fiscal breakeven price is widely used by opec countries
104 | #if u have other countries in mind, you will be disappointed
105 | # https://www.cfr.org/report/interactive-oil-exporters-external-breakeven-prices
106 | # http://graphics.wsj.com/oil-barrel-breakdown/
107 | 
108 | #daily production data and proven reserve comes from bp
109 | # https://www.bp.com/en/global/corporate/energy-economics/statistical-review-of-world-energy.html
110 | #production capacity is a typical x axis for cost curve
111 | #you can find them from imf or eia, but again, only for opec countries
112 | #therefore, i used daily production data as alternative
113 | # https://www.imf.org/~/media/Files/Publications/REO/MCD-CCA/2018/May/English/mreo0518-statisticalappendix-elsx.ashx
114 | # https://www.eia.gov/opendata/qb.php?sdid=STEO.COPC_AG.A
115 | 
116 | df=pd.read_csv('global oil cost curve.csv')
117 | 
118 | 
119 | # In[4]:
120 | 
121 | 
122 | cost_curve(df['Daily production mil barrels'],
123 |            df['Operational cost dollar per barrel'],
124 |            df['Capital cost dollar per barrel'],
125 |            legends=df['Country'],
126 |            notes=['Operational Cost','Capital Cost'],
127 |            hline_var=np.percentile(df['Total cost dollar per barrel'],90),
128 |            hline_color='#e08314',
129 |            hline_name='90% Percentile',
130 |            xlabel='Daily Production, Million Barrels',
131 |            ylabel='US Dollar per Barrel',
132 |            title='2015 Global Oil Cost Curve')
133 | 
134 | 


--------------------------------------------------------------------------------
/Oil Money project/preview/cad after.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad after.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad before.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad before.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad crude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad crude.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad currency.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad currency.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad elbow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad elbow.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad groups.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad groups.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad kmeans.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad kmeans.gif


--------------------------------------------------------------------------------
/Oil Money project/preview/cad model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad model.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad silhouette.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad silhouette.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad wcs in aud.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad wcs in aud.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cad wcs in usd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cad wcs in usd.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop after 2017.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop after 2017.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop before 2017.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop before 2017.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop groups.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop groups.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop model.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop model.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop profit distribution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop profit distribution.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop profit heatmap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop profit heatmap.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop trading asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop trading asset.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop trading positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop trading positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs brl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs brl.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs crude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs crude.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs gold.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs gold.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs mxn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs mxn.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs usd vs mxn.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs usd vs mxn.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs usd.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs usd.png


--------------------------------------------------------------------------------
/Oil Money project/preview/cop vs vas.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/cop vs vas.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok EG failed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok EG failed.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok asset value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok asset value.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok brent crude.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok brent crude.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok correlation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok correlation.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok fitted vs actual.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok fitted vs actual.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok model summary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok model summary.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok oil money positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok oil money positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok profit distribution.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok profit distribution.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok profit heatmap.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok profit heatmap.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok trading asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok trading asset.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok trading positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok trading positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok trend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok trend.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok vs brent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok vs brent.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok vs gdp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok vs gdp.png


--------------------------------------------------------------------------------
/Oil Money project/preview/nok vs ir.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/nok vs ir.png


--------------------------------------------------------------------------------
/Oil Money project/preview/oil production bubble map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/oil production bubble map.png


--------------------------------------------------------------------------------
/Oil Money project/preview/oil production choropleth.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/oil production choropleth.PNG


--------------------------------------------------------------------------------
/Oil Money project/preview/oil production cost curve.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/oil production cost curve.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub 2015 positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub 2015 positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub 2016 positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub 2016 positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub 2017 positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub 2017 positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub 2017- trend.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub 2017- trend.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub 2018 positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub 2018 positions.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub model -2016.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub model -2016.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub model 2017-.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub model 2017-.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub ols -2016.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub ols -2016.PNG


--------------------------------------------------------------------------------
/Oil Money project/preview/rub ols 2017-.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub ols 2017-.PNG


--------------------------------------------------------------------------------
/Oil Money project/preview/rub stepwise1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub stepwise1.png


--------------------------------------------------------------------------------
/Oil Money project/preview/rub stepwise2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Oil Money project/preview/rub stepwise2.png


--------------------------------------------------------------------------------
/Ore Money project/README.md:
--------------------------------------------------------------------------------
1 | This is an upcoming project similar to Quant Trading - Oil Money. It is designed to be an upgraded version of oil money trading strategy with introduction of more sophisticated models and alternative datasets. The project will examine the causality between iron ore spot price and forex of iron ore exporting countries. If the project can replicate the success of oil money trading strategy, we shall be able to apply it to the currency of any major natural resource exporter and correlated commodity contracts.
2 | 
3 | The following figure is the global iron ore production bubble map (check <a href=https://github.com/je-suis-tm/quant-trading/blob/master/Ore%20Money%20project/iron%20ore%20production/iron%20ore%20production%20bubble%20map.py>subfolder</a> for how it is visualized, this can also be done in choropleth, check this <a href=https://github.com/je-suis-tm/quant-trading/blob/master/Oil%20Money%20project/oil%20production/oil%20production%20choropleth.py>link</a> for how to create a choropleth). It lists out a few iron ore and currency arbitrage opportunities.
4 | 
5 | ![alt text](https://github.com/je-suis-tm/quant-trading/blob/master/Ore%20Money%20project/preview/iron%20ore%20production%20bubble%20map.png)
6 | 


--------------------------------------------------------------------------------
/Ore Money project/iron ore production/iron ore production bubble map.csv:
--------------------------------------------------------------------------------
 1 | region,iron ore production,latitude,longitude
 2 | Australia,817000,-24.15,133.08
 3 | Brazil,397000,-10.47,-52.55
 4 | China,375000,31.55,108.2
 5 | India,156000,22.37,79.13
 6 | Russia,101000,65.45,97.35
 7 | South Africa,73000,-30.44,25.12
 8 | Ukraine,67000,49.3,32.28
 9 | United States,46000,37.91,-97.02
10 | Canada,46000,58.27,-105.42
11 | Iran,27000,32.44,53.3
12 | Sweden,25000,64.2,18.03
13 | Kazakhstan,21000,47.1,67.3
14 | Mexico,18840,20.2,-100.1
15 | Chile,17109,-33.24,-70.9
16 | Venezuela,16800,7.3,-65.55
17 | Sierra Leone,11895,7.3,-11.17
18 | Malaysia,11588,3.59,101.91
19 | Peru,10126,-11,-75
20 | Turkey,8589,39.57,35.54
21 | Mongolia,6736,46,105
22 | Vietnam,4708,17.05,107.55
23 | Indonesia,4000,-1.09,115.49
24 | Norway,3409,60.55,7.45
25 | Egypt,3320,26.01,30.14
26 | North Korea,3054,39.09,126.3
27 | Algeria,1067,29.42,3.08
28 | Philippines,1057,11.4,124.03
29 | 


--------------------------------------------------------------------------------
/Ore Money project/iron ore production/iron ore production bubble map.py:
--------------------------------------------------------------------------------
 1 | 
 2 | # coding: utf-8
 3 | 
 4 | # In[1]:
 5 | 
 6 | 
 7 | #installing basemap is pretty painful for anaconda
 8 | #try conda install -c conda-forge basemap
 9 | #if there is filenotfounderror
10 | #try conda install -c conda-forge proj4
11 | import pandas as pd
12 | import numpy as np
13 | import matplotlib.pyplot as plt
14 | from mpl_toolkits.basemap import Basemap
15 | import os
16 | os.chdir('d:/')
17 | 
18 | 
19 | # In[2]:
20 | 
21 | 
22 | #need data with value and coordinates to plot
23 | #the iron ore production data of 2015 comes from wikipedia
24 | # https://en.wikipedia.org/wiki/List_of_countries_by_iron_ore_production
25 | #the coordinates of each country's capital come from someone's personal blog
26 | # https://lab.lmnixon.org/4th/worldcapitals.html
27 | #note that the capital doesnt necessarily locate at the centre of a country
28 | #to make the figure look better, i slightly change the coordinates
29 | df=pd.read_csv('iron ore production bubble map.csv')
30 | 
31 | 
32 | # In[3]:
33 | 
34 | 
35 | ax=plt.figure(figsize=(50,20)).add_subplot(111)
36 | 
37 | #draw up a world map
38 | #fill continents, lakes and country boundaries
39 | m = Basemap()
40 | m.drawmapboundary(fill_color='white', linewidth=0)
41 | m.fillcontinents(color='#c0c0c0',alpha=0.5, lake_color='white')
42 | m.drawcountries(linewidth=1,color='#a79c93')
43 | 
44 | size=df['iron ore production']/max(df['iron ore production'])*40000
45 | 
46 | #this is crucial if we use a different map projection
47 | x,y=m(df['longitude'].tolist(),df['latitude'].tolist())
48 | 
49 | m.scatter(x,y, s=size, linewidths=2, edgecolors="#6c6b74",           
50 |           alpha=0.8,c=df['iron ore production'],cmap='autumn_r')
51 | 
52 | for i in range(len(x)):
53 |     plt.text(x[i],y[i],               
54 |              '%s '%(df['region'][i]),              
55 |              horizontalalignment='center', 
56 |             verticalalignment='center', 
57 |              size=25)
58 | 
59 | cb=plt.colorbar(ticks=[10000,800000])
60 | cb.ax.set_yticklabels(cb.ax.get_yticklabels(), fontsize=20)
61 | cb.ax.set_ylabel('Iron Ore Production 1000 Tonnes per Year',fontsize=20,rotation=270)
62 | plt.title('Iron Ore Production by Countries', fontsize=42)
63 | 
64 | plt.show()
65 | 
66 | 


--------------------------------------------------------------------------------
/Ore Money project/preview/iron ore production bubble map.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Ore Money project/preview/iron ore production bubble map.png


--------------------------------------------------------------------------------
/Parabolic SAR backtest.py:
--------------------------------------------------------------------------------
  1 | # coding: utf-8
  2 | 
  3 | # In[1]:
  4 | 
  5 | 
  6 | #parabolic stop and reverse is very useful for trend following
  7 | #sar is an indicator below the price when its an uptrend 
  8 | #and above the price when its a downtrend
  9 | #it is very painful to calculate sar, though
 10 | #and many explanations online including wiki cannot clearly explain the process
 11 | #hence, the good idea would be to read info on wikipedia
 12 | #and download an excel spreadsheet made by joeu2004
 13 | #formulas are always more straight forward than descriptions
 14 | #links are shown below
 15 | # https://en.wikipedia.org/wiki/Parabolic_SAR
 16 | # https://www.box.com/s/gbtrjuoktgyag56j6lv0
 17 | 
 18 | import matplotlib.pyplot as plt
 19 | import numpy as np
 20 | import fix_yahoo_finance as yf
 21 | import pandas as pd
 22 | 
 23 | 
 24 | # In[2]:
 25 | 
 26 | #the calculation of sar
 27 | #as rules are very complicated
 28 | #plz check the links above to understand more about it
 29 | 
 30 | def parabolic_sar(new):
 31 |     
 32 |     #this is common accelerating factors for forex and commodity
 33 |     #for equity, af for each step could be set to 0.01
 34 |     initial_af=0.02
 35 |     step_af=0.02
 36 |     end_af=0.2
 37 |     
 38 |     
 39 |     new['trend']=0
 40 |     new['sar']=0.0
 41 |     new['real sar']=0.0
 42 |     new['ep']=0.0
 43 |     new['af']=0.0
 44 | 
 45 |     #initial values for recursive calculation
 46 |     new['trend'][1]=1 if new['Close'][1]>new['Close'][0] else -1
 47 |     new['sar'][1]=new['High'][0] if new['trend'][1]>0 else new['Low'][0]
 48 |     new.at[1,'real sar']=new['sar'][1]
 49 |     new['ep'][1]=new['High'][1] if new['trend'][1]>0 else new['Low'][1]
 50 |     new['af'][1]=initial_af
 51 | 
 52 |     #calculation
 53 |     for i in range(2,len(new)):
 54 |         
 55 |         temp=new['sar'][i-1]+new['af'][i-1]*(new['ep'][i-1]-new['sar'][i-1])
 56 |         if new['trend'][i-1]<0:
 57 |             new.at[i,'sar']=max(temp,new['High'][i-1],new['High'][i-2])
 58 |             temp=1 if new['sar'][i]<new['High'][i] else new['trend'][i-1]-1
 59 |         else:
 60 |             new.at[i,'sar']=min(temp,new['Low'][i-1],new['Low'][i-2])
 61 |             temp=-1 if new['sar'][i]>new['Low'][i] else new['trend'][i-1]+1
 62 |         new.at[i,'trend']=temp
 63 |     
 64 |         
 65 |         if new['trend'][i]<0:
 66 |             temp=min(new['Low'][i],new['ep'][i-1]) if new['trend'][i]!=-1 else new['Low'][i]
 67 |         else:
 68 |             temp=max(new['High'][i],new['ep'][i-1]) if new['trend'][i]!=1 else new['High'][i]
 69 |         new.at[i,'ep']=temp
 70 |     
 71 |     
 72 |         if np.abs(new['trend'][i])==1:
 73 |             temp=new['ep'][i-1]
 74 |             new.at[i,'af']=initial_af
 75 |         else:
 76 |             temp=new['sar'][i]
 77 |             if new['ep'][i]==new['ep'][i-1]:
 78 |                 new.at[i,'af']=new['af'][i-1]
 79 |             else:
 80 |                 new.at[i,'af']=min(end_af,new['af'][i-1]+step_af)
 81 |         new.at[i,'real sar']=temp
 82 |        
 83 |         
 84 |     return new
 85 | 
 86 | # In[3]:
 87 | 
 88 | #generating signals
 89 | #idea is the same as macd oscillator
 90 | #check the website below to learn more
 91 | # https://github.com/je-suis-tm/quant-trading/blob/master/MACD%20oscillator%20backtest.py
 92 | 
 93 | def signal_generation(df,method):
 94 |     
 95 |         new=method(df)
 96 | 
 97 |         new['positions'],new['signals']=0,0
 98 |         new['positions']=np.where(new['real sar']<new['Close'],1,0)
 99 |         new['signals']=new['positions'].diff()
100 |         
101 |         return new
102 | 
103 |     
104 | 
105 | 
106 | 
107 | # In[4]:
108 | 
109 | #plotting of sar and trading positions
110 | #still similar to macd
111 | 
112 | def plot(new,ticker):
113 |     
114 |     fig=plt.figure()
115 |     ax=fig.add_subplot(111)
116 |     
117 |     new['Close'].plot(lw=3,label='%s'%ticker)
118 |     new['real sar'].plot(linestyle=':',label='Parabolic SAR',color='k')
119 |     ax.plot(new.loc[new['signals']==1].index,new['Close'][new['signals']==1],marker='^',color='g',label='LONG',lw=0,markersize=10)
120 |     ax.plot(new.loc[new['signals']==-1].index,new['Close'][new['signals']==-1],marker='v',color='r',label='SHORT',lw=0,markersize=10)
121 |     
122 |     plt.legend()
123 |     plt.grid(True)
124 |     plt.title('Parabolic SAR')
125 |     plt.ylabel('price')
126 |     plt.show()
127 | 
128 | 
129 | # In[5]:
130 | 
131 | def main():
132 |     
133 |     #download data via fix yahoo finance library
134 |     stdate=('2016-01-01')
135 |     eddate=('2018-01-01')
136 |     ticker=('EA')
137 | 
138 |     #slice is used for plotting
139 |     #a two year dataset with 500 variables would be too much for a figure
140 |     slicer=450
141 | 
142 |     df=yf.download(ticker,start=stdate,end=eddate)
143 |     
144 |     #delete adj close and volume
145 |     #as we dont need them
146 |     del df['Adj Close']
147 |     del df['Volume']
148 | 
149 |     #no need to iterate over timestamp index
150 |     df.reset_index(inplace=True)
151 | 
152 |     new=signal_generation(df,parabolic_sar)
153 | 
154 |     #convert back to time series for plotting
155 |     #so that we get a date x axis
156 |     new.set_index(new['date'],inplace=True)
157 | 
158 |     #shorten our plotting horizon and plot
159 |     new=new[slicer:]
160 |     plot(new,ticker) 
161 | 
162 | #how to calculate stats could be found from my other code called Heikin-Ashi
163 | # https://github.com/je-suis-tm/quant-trading/blob/master/heikin%20ashi%20backtest.py
164 | 
165 | 
166 | # In[6]:
167 | 
168 | if __name__ == '__main__':
169 |     main()
170 | 


--------------------------------------------------------------------------------
/Shooting Star backtest.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | #shooting star is my friend's fav indicator
  8 | #the name is poetic and romantic
  9 | #it is merely a vertical flipped hammer
 10 | #hammer and shooting star could be confusing
 11 | #since both of them can be inverted
 12 | #i memorize them via a simple tune
 13 | #if u see thor (with hammer),price shall soar
 14 | #if u see star (shooting star),price shall fall
 15 | #details of shooting star can be found in investopedia
 16 | # https://www.investopedia.com/terms/s/shootingstar.asp
 17 | import pandas as pd
 18 | import matplotlib.pyplot as plt
 19 | import numpy as np
 20 | import yfinance
 21 | 
 22 | 
 23 | # In[2]:
 24 | 
 25 | 
 26 | #criteria of shooting star
 27 | def shooting_star(data,lower_bound,body_size):
 28 | 
 29 |     df=data.copy()
 30 | 
 31 |     #open>close,red color
 32 |     df['condition1']=np.where(df['Open']>=df['Close'],1,0)
 33 | 
 34 |     #a candle with little or no lower wick
 35 |     df['condition2']=np.where(
 36 |         (df['Close']-df['Low'])<lower_bound*abs(
 37 |             df['Close']-df['Open']),1,0)
 38 | 
 39 |     #a candle with a small lower body
 40 |     df['condition3']=np.where(abs(
 41 |         df['Open']-df['Close'])<abs(
 42 |         np.mean(df['Open']-df['Close']))*body_size,1,0)
 43 | 
 44 |     #a long upper wick that is at least two times the size of the lower body
 45 |     df['condition4']=np.where(
 46 |         (df['High']-df['Open'])>=2*(
 47 |             df['Open']-df['Close']),1,0)
 48 | 
 49 |     #price uptrend
 50 |     df['condition5']=np.where(
 51 |         df['Close']>=df['Close'].shift(1),1,0)
 52 |     df['condition6']=np.where(
 53 |         df['Close'].shift(1)>=df['Close'].shift(2),1,0)
 54 | 
 55 |     #the next candle's high must stay 
 56 |     #below the high of the shooting star 
 57 |     df['condition7']=np.where(
 58 |         df['High'].shift(-1)<=df['High'],1,0)
 59 | 
 60 |     #the next candle's close below 
 61 |     #the close of the shooting star
 62 |     df['condition8']=np.where(
 63 |         df['Close'].shift(-1)<=df['Close'],1,0)
 64 |     
 65 |     return df
 66 | 
 67 | 
 68 | # In[3]:
 69 | 
 70 | 
 71 | #signal generation
 72 | #there are eight criteria according to investopedia
 73 | def signal_generation(df,method,
 74 |                       lower_bound=0.2,body_size=0.5,
 75 |                       stop_threshold=0.05,
 76 |                       holding_period=7):
 77 | 
 78 |     #get shooting star conditions
 79 |     data=method(df,lower_bound,body_size)
 80 | 
 81 |     #shooting star should suffice all conditions
 82 |     #in practise,you may find the definition too rigid
 83 |     #its important to relax a bit on the body size
 84 |     data['signals']=data['condition1']*data[
 85 |         'condition2']*data['condition3']*data[
 86 |         'condition4']*data['condition5']*data[
 87 |         'condition6']*data['condition7']*data[
 88 |         'condition8']
 89 | 
 90 |     #shooting star is a short signal
 91 |     data['signals']=-data['signals']
 92 |     
 93 |     #find exit position
 94 |     idxlist=data[data['signals']==-1].index
 95 |     for ind in idxlist:
 96 | 
 97 |         #entry point
 98 |         entry_pos=data['Close'].loc[ind]
 99 | 
100 |         stop=False
101 |         counter=0
102 |         while not stop:
103 |             ind+=1
104 |             counter+=1
105 | 
106 |             #set stop loss/profit at +-5%
107 |             if abs(data['Close'].loc[
108 |                 ind]/entry_pos-1)>stop_threshold:
109 |                 stop=True
110 |                 data['signals'].loc[ind]=1
111 | 
112 |             #set maximum holding period at 7 workdays
113 |             if counter>=holding_period:
114 |                 stop=True
115 |                 data['signals'].loc[ind]=1
116 | 
117 |     #create positions
118 |     data['positions']=data['signals'].cumsum()
119 |     
120 |     return data
121 | 
122 | 
123 | # In[4]:
124 | 
125 | 
126 | #since matplotlib remove the candlestick
127 | #plus we dont wanna install mpl_finance
128 | #we implement our own version
129 | #simply use fill_between to construct the bar
130 | #use line plot to construct high and low
131 | def candlestick(df,ax=None,highlight=None,titlename='',
132 |                 highcol='High',lowcol='Low',
133 |                 opencol='Open',closecol='Close',xcol='Date',
134 |                 colorup='r',colordown='g',highlightcolor='y',
135 |                 **kwargs):  
136 |     
137 |     #bar width
138 |     #use 0.6 by default
139 |     dif=[(-3+i)/10 for i in range(7)]
140 |     
141 |     if not ax:
142 |         ax=plt.figure(figsize=(10,5)).add_subplot(111)
143 |     
144 |     #construct the bars one by one
145 |     for i in range(len(df)):
146 |         
147 |         #width is 0.6 by default
148 |         #so 7 data points required for each bar
149 |         x=[i+j for j in dif]
150 |         y1=[df[opencol].iloc[i]]*7
151 |         y2=[df[closecol].iloc[i]]*7
152 | 
153 |         barcolor=colorup if y1[0]>y2[0] else colordown
154 |         
155 |         #no high line plot if open/close is high
156 |         if df[highcol].iloc[i]!=max(df[opencol].iloc[i],df[closecol].iloc[i]):
157 |             
158 |             #use generic plot to viz high and low
159 |             #use 1.001 as a scaling factor
160 |             #to prevent high line from crossing into the bar
161 |             plt.plot([i,i],
162 |                      [df[highcol].iloc[i],
163 |                       max(df[opencol].iloc[i],
164 |                           df[closecol].iloc[i])*1.001],c='k',**kwargs)
165 |     
166 |         #same as high
167 |         if df[lowcol].iloc[i]!=min(df[opencol].iloc[i],df[closecol].iloc[i]):             
168 |             
169 |             plt.plot([i,i],
170 |                      [df[lowcol].iloc[i],
171 |                       min(df[opencol].iloc[i],
172 |                           df[closecol].iloc[i])*0.999],c='k',**kwargs)
173 |         
174 |         #treat the bar as fill between
175 |         plt.fill_between(x,y1,y2,
176 |                          edgecolor='k',
177 |                          facecolor=barcolor,**kwargs)
178 |         
179 |         if highlight:
180 |             if df[highlight].iloc[i]==-1:
181 |                 plt.fill_between(x,y1,y2,
182 |                          edgecolor='k',
183 |                          facecolor=highlightcolor,**kwargs)
184 | 
185 |     #only show 5 xticks
186 |     plt.xticks([])
187 |     plt.grid(True)
188 |     plt.title(titlename)
189 | 
190 | 
191 | # In[5]:
192 | 
193 | 
194 | #plotting the backtesting result
195 | def plot(data,name):   
196 |     
197 |     #first plot is candlestick to showcase
198 |     ax1=plt.subplot2grid((250,1),(0,0),
199 |                          rowspan=120,
200 |                          ylabel='Candlestick')
201 |     candlestick(data,ax1,
202 |                 highlight='signals',
203 |                 highlightcolor='#FFFF00')
204 | 
205 |     #the second plot is the actual price 
206 |     #with long/short positions as up/down arrows
207 |     ax2=plt.subplot2grid((250,1),(130,0),
208 |                          rowspan=120,
209 |                          ylabel='£ per share',
210 |                          xlabel='Date')
211 |     ax2.plot(data.index,
212 |              data['Close'],
213 |              label=name)
214 | 
215 |     #long/short positions are attached to 
216 |     #the real close price of the stock
217 |     #set the line width to zero
218 |     #thats why we only observe markers
219 |     ax2.plot(data.loc[data['signals']==-1].index,
220 |              data['Close'].loc[data['signals']==-1],
221 |              marker='v',lw=0,c='r',label='short',
222 |              markersize=10)
223 |     ax2.plot(data.loc[data['signals']==1].index,
224 |              data['Close'].loc[data['signals']==1],
225 |              marker='^',lw=0,c='g',label='long',
226 |              markersize=10)
227 | 
228 |     #only show five tickers
229 |     plt.xticks(range(0,len(data),len(data)//5),
230 |                data['Date'][0::len(data)//5].dt.date)
231 |     
232 |     plt.grid(True)
233 |     plt.legend(loc='lower left')
234 |     plt.tight_layout(pad=0.1)
235 |     plt.show()
236 | 
237 | 
238 | # In[6]:
239 | 
240 | 
241 | def main():
242 |     
243 |     #initializing
244 |     stdate='2000-01-01'
245 |     eddate='2021-11-04'
246 |     name='Vodafone'
247 |     ticker='VOD.L'
248 | 
249 |     df=yfinance.download(ticker,start=stdate,end=eddate)
250 |     df.reset_index(inplace=True)
251 |     df['Date']=pd.to_datetime(df['Date'])
252 | 
253 |     #signal generation
254 |     new=signal_generation(df,shooting_star)
255 | 
256 |     #get subset for better viz to highlight shooting star
257 |     subset=new.loc[5268:5283].copy()
258 |     subset.reset_index(inplace=True,drop=True)
259 | 
260 |     #viz
261 |     plot(subset,name)
262 | 
263 | 
264 | # In[7]:
265 | 
266 | 
267 | if __name__ == '__main__':
268 |     main()
269 | 
270 | 


--------------------------------------------------------------------------------
/Smart Farmers project/check consistency.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | import os
  8 | os.chdir('H:/')
  9 | import pandas as pd
 10 | 
 11 | 
 12 | # In[2]:
 13 | 
 14 | 
 15 | prod=pd.read_csv('Production_Crops_E_All_Data_(Normalized).csv',
 16 |                  encoding='latin-1')
 17 | 
 18 | prix=pd.read_csv('Prices_E_All_Data_(Normalized).csv',
 19 |                  encoding='latin-1')
 20 | 
 21 | land=pd.read_csv('Inputs_LandUse_E_All_Data_(Normalized).csv',
 22 |                  encoding='latin-1')
 23 | 
 24 | 
 25 | # In[3]:
 26 | 
 27 | 
 28 | global beginyear,endyear
 29 | beginyear=2012;
 30 | endyear=2019
 31 | 
 32 | 
 33 | # In[4]:
 34 | 
 35 | 
 36 | mapping=pd.read_csv('mapping.csv')
 37 | 
 38 | 
 39 | # In[5]:
 40 | 
 41 | 
 42 | #select malaysia from 2012-2018
 43 | malay_land=land[land['Year'].isin(range(beginyear,endyear))][land['Area']=='Malaysia'][land['Element'].isin(['Area'])][land['Item'].isin(['Cropland'])]
 44 | 
 45 | malay_prod=prod[prod['Year'].isin(range(beginyear,endyear))][prod['Area']=='Malaysia'][prod['Element'].isin(['Area harvested','Production'])]
 46 | 
 47 | malay_prod=malay_prod.merge(mapping,on=['Item Code', 'Item'],how='left')
 48 | 
 49 | 
 50 | # In[6]:
 51 | 
 52 | 
 53 | #remove redundant cols
 54 | for i in ['Area Code','Element Code','Year Code',
 55 |          'Flag','COMMODITY','Item Code',
 56 |           'subclass code','class code',
 57 |           'DEFINITIONS, COVERAGE, REMARKS',]:
 58 |     del malay_prod[i]
 59 | 
 60 | 
 61 | # In[7]:
 62 | 
 63 | 
 64 | #select crops with available data
 65 | a=set(malay_prod['Item'][malay_prod['Element']=='Area harvested'])
 66 | 
 67 | b=set(malay_prod['Item'][malay_prod['Element']=='Production'])
 68 | 
 69 | target_crops=a.intersection(b)
 70 | 
 71 | 
 72 | # In[8]:
 73 | 
 74 | 
 75 | #exclude land usage<1% without price data
 76 | exclude=['Areca nuts',
 77 |  'Bastfibres, other',
 78 |  'Cashew nuts, with shell',
 79 |  'Cereals, Total',
 80 |  'Chillies and peppers, dry',
 81 |  'Citrus Fruit, Total',
 82 |  'Cloves',
 83 |  'Coarse Grain, Total',
 84 |  'Coffee, green',
 85 |  'Coir',
 86 |  'Fibre Crops Primary',
 87 |  'Fruit Primary',
 88 |  'Fruit, citrus nes',
 89 |  'Fruit, fresh nes',
 90 |  'Fruit, tropical fresh nes',
 91 |  'Groundnuts, with shell',
 92 |  'Manila fibre (abaca)',
 93 |  'Nutmeg, mace and cardamoms',
 94 |  'Oilcrops',
 95 |  'Oilcrops, Cake Equivalent',
 96 |  'Oilcrops, Oil Equivalent',
 97 |  'Roots and Tubers, Total',
 98 |  'Roots and tubers nes',
 99 |  'Soybeans',
100 |  'Spices nes',
101 |  'Tea',
102 |  'Treenuts, Total',
103 |  'Vegetables Primary',
104 |  'Vegetables, fresh nes']
105 | 
106 | 
107 | # In[9]:
108 | 
109 | 
110 | #finalize the target
111 | targets=[i for i in target_crops if i not in exclude]
112 | 
113 | 
114 | # In[10]:
115 | 
116 | 
117 | #cleanse
118 | malay_crops=malay_prod[malay_prod['Item'].isin(targets)]
119 | 
120 | 
121 | # In[11]:
122 | 
123 | 
124 | #subtotal
125 | total=malay_prod[malay_prod['class'].isnull()]
126 | 
127 | 
128 | # In[12]:
129 | 
130 | 
131 | #compare sum of area by crops with sum of area by subclass
132 | sss=total[total['Element']=='Area harvested'].groupby(['Item','Year']).sum()
133 | 
134 | ttt=malay_crops[malay_crops['Element']=='Area harvested'].groupby(['class','Year']).sum()
135 | 
136 | 
137 | # In[13]:
138 | 
139 | 
140 | #compare sum of area by crops
141 | malay_crops[malay_crops['Element']=='Area harvested'].groupby(['Year']).sum()
142 | 
143 | 
144 | # In[14]:
145 | 
146 | 
147 | #with cropland
148 | malay_land
149 | 
150 | 


--------------------------------------------------------------------------------
/Smart Farmers project/cleanse data.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | import os
  8 | os.chdir('H:/')
  9 | import pandas as pd
 10 | import numpy as np
 11 | 
 12 | 
 13 | # ### define functions
 14 | 
 15 | # In[2]:
 16 | 
 17 | 
 18 | #etl
 19 | def prepare(target_land,target_prod,target_prix):
 20 | 
 21 |     #land clean up
 22 |     target_land=target_land[['Year','Value']].copy()
 23 | 
 24 |     #prix clean up
 25 |     target_prix=target_prix[['Item','Year','Value']].copy()
 26 | 
 27 |     #create area and clean up
 28 |     target_area=target_prod[target_prod['Element']=='Area harvested'].copy()
 29 | 
 30 |     target_area=target_area[['Item','Year','Value','type','lifespan']].copy()
 31 | 
 32 |     #production clean up
 33 |     target_prod=target_prod[target_prod['Element']=='Production'].copy()
 34 | 
 35 |     target_prod=target_prod[['Item','Year','Value','class']].copy()
 36 | 
 37 |     #compute yield inverse and clean up
 38 |     target_yield_inverse=target_prod.merge(target_area,on=['Item','Year'],how='left')
 39 | 
 40 |     target_yield_inverse['Value']=np.divide(target_yield_inverse['Value_y'],target_yield_inverse['Value_x'])
 41 | 
 42 |     target_yield_inverse=target_yield_inverse[['Item', 'Year','Value','type', 'lifespan']]
 43 | 
 44 |     #sort by item and year
 45 |     for data in [target_prod,target_area,target_prix,target_yield_inverse]:
 46 |         data.sort_values(['Item','Year'],inplace=True)
 47 | 
 48 |     #concatenate
 49 |     global D
 50 |     D={}
 51 | 
 52 |     for currentyear in range(beginyear,endyear):   
 53 | 
 54 |         temp1=target_prod[target_prod['Year']==currentyear].merge(
 55 |         target_area[target_area['Year']==currentyear],on=['Item', 'Year'],how='outer')
 56 |         temp2=target_prix[target_prix['Year']==currentyear].merge(
 57 |         target_yield_inverse[target_yield_inverse['Year']==currentyear],on=['Item', 'Year'],how='outer')
 58 | 
 59 |         temp1.columns=temp1.columns.str.replace('Value_x','production')
 60 |         temp1.columns=temp1.columns.str.replace('Value_y','area')
 61 |         temp2.columns=temp2.columns.str.replace('Value_x','price')
 62 |         temp2.columns=temp2.columns.str.replace('Value_y','yield_i')
 63 | 
 64 |         data=temp1.merge(temp2,on=['Item', 'Year', 'type', 'lifespan'],how='outer')
 65 | 
 66 |         D[currentyear]=data                
 67 |     
 68 |     #compute eco lifespan for perennials
 69 |     for currentyear in range(beginyear,endyear):
 70 |         
 71 |         eco_lifespan=[default_discount for _ in range(len(D[currentyear]))]
 72 |         indices=D[currentyear]['lifespan'].dropna().index
 73 |         perennial=D[currentyear]['lifespan'].dropna().apply(lambda x:(x*eco_coeff-1)/(x*eco_coeff))
 74 | 
 75 |         for i in indices:
 76 |             eco_lifespan[i]=round(perennial.loc[i],4)
 77 | 
 78 |         D[currentyear]['eco lifespan']=eco_lifespan
 79 | 
 80 |     return D
 81 | 
 82 | 
 83 | # ### execution
 84 | 
 85 | # In[3]:
 86 | 
 87 | 
 88 | global beginyear,endyear
 89 | beginyear=2012
 90 | endyear=2019
 91 | 
 92 | 
 93 | # In[4]:
 94 | 
 95 | 
 96 | global eco_coeff,default_discount
 97 | eco_coeff=0.7
 98 | default_discount=0.8
 99 | 
100 | 
101 | # In[5]:
102 | 
103 | 
104 | prod=pd.read_csv('Production_Crops_E_All_Data_(Normalized).csv',
105 |                  encoding='latin-1')
106 | 
107 | prix=pd.read_csv('Prices_E_All_Data_(Normalized).csv',
108 |                  encoding='latin-1')
109 | 
110 | land=pd.read_csv('Inputs_LandUse_E_All_Data_(Normalized).csv',
111 |                  encoding='latin-1')
112 | 
113 | population=pd.read_csv('Population_E_All_Data_(Normalized).csv',
114 |                       encoding='latin-1')
115 | 
116 | gdp=pd.read_csv('Macro-Statistics_Key_Indicators_E_All_Data_(Normalized).csv',
117 |                       encoding='latin-1')
118 | 
119 | 
120 | # In[6]:
121 | 
122 | 
123 | os.chdir('H:/data')
124 | 
125 | 
126 | # In[7]:
127 | 
128 | 
129 | mapping=pd.read_csv('mapping.csv')
130 | 
131 | 
132 | # In[8]:
133 | 
134 | 
135 | #select malaysia from beginyear-endyear
136 | malay_land=land[land['Year'].isin(range(beginyear,endyear))][land['Area']=='Malaysia'][land['Element'].isin(['Area'])][land['Item'].isin(['Cropland'])]
137 | 
138 | malay_prix=prix[prix['Year'].isin(range(beginyear,endyear))][prix['Area']=='Malaysia'][prix['Element']=='Producer Price (USD/tonne)'][prix['Months']=='Annual value']
139 | 
140 | malay_prod=prod[prod['Year'].isin(range(beginyear,endyear))][prod['Area']=='Malaysia'][prod['Element'].isin(['Area harvested','Production'])]
141 | 
142 | malay_pop=population[population['Element']=='Total Population - Both sexes'][population['Area']=='Malaysia'][population['Year']>=beginyear]
143 | 
144 | malay_gdp=gdp[gdp['Element']=='Value US
#39;][gdp['Item']=='Gross Domestic Product per capita'][gdp['Area']=='Malaysia'][gdp['Year'].isin(range(beginyear,endyear))]
145 | 
146 | 
147 | # In[9]:
148 | 
149 | 
150 | #exclude land usage<1%
151 | exclude=[813,236,809,1717,
152 |          512, 782, 656, 149, 667, 809,
153 |          813, 689, 698, 702, 723, 217,
154 |          226, 236, 242,463, 603, 619,
155 |          1720, 1729, 1731, 1732, 1735,
156 |          1738, 1753, 1804, 1841,1814]
157 | 
158 | 
159 | # In[10]:
160 | 
161 | 
162 | #find items without price
163 | a=set(malay_prod['Item Code'][malay_prod['Element']=='Area harvested'])
164 | 
165 | b=set(malay_prod['Item Code'][malay_prod['Element']=='Production'])
166 | 
167 | c=set(malay_prix['Item Code'])
168 | 
169 | sans_prix=[i for i in a.intersection(b) if i not in c]
170 | 
171 | sans_prix=[i for i in sans_prix if i not in exclude]
172 | 
173 | print(sans_prix)
174 | 
175 | 
176 | # In[11]:
177 | 
178 | 
179 | #use austria oilseeds to replace
180 | oilseeds_prix=prix[prix['Area']=='Austria'][prix['Element']=='Producer Price (USD/tonne)'][prix['Item Code']==339][prix['Year'].isin(range(beginyear,endyear))]
181 | 
182 | 
183 | # In[12]:
184 | 
185 | 
186 | #use avg of maize and rice to replace cereals
187 | cereal_mean=malay_prix[malay_prix['Item'].isin(['Maize','Rice, paddy'])].groupby('Year').mean()['Value'].tolist()
188 | 
189 | cereal_prix=oilseeds_prix.copy()
190 | 
191 | cereal_prix['Value']=cereal_mean
192 | 
193 | cereal_prix['Item']='Cereals (Rice Milled Eqv)'
194 | 
195 | cereal_prix['Item Code']=1817
196 | 
197 | 
198 | # In[13]:
199 | 
200 | 
201 | #add missing
202 | malay_prix=malay_prix.append(oilseeds_prix).append(cereal_prix)
203 | 
204 | malay_prix.reset_index(inplace=True,drop=True)
205 | 
206 | 
207 | # In[14]:
208 | 
209 | 
210 | #concat
211 | malay_prod=malay_prod.merge(mapping,on=['Item Code', 'Item'],how='left')
212 | 
213 | 
214 | # In[15]:
215 | 
216 | 
217 | #find inner join of crops
218 | a=set(malay_prod['Item Code'][malay_prod['Element']=='Area harvested'])
219 | 
220 | b=set(malay_prod['Item Code'][malay_prod['Element']=='Production'])
221 | 
222 | c=set(malay_prix['Item Code'])
223 | 
224 | target_crops=a.intersection(b).intersection(c)
225 | 
226 | targets=[i for i in target_crops if i not in exclude]
227 | 
228 | 
229 | # In[16]:
230 | 
231 | 
232 | #select crops
233 | malay_prod=malay_prod[malay_prod['Item Code'].isin(targets)]
234 | malay_prix=malay_prix[malay_prix['Item Code'].isin(targets)]
235 | 
236 | 
237 | # In[17]:
238 | 
239 | 
240 | #add 2018 land
241 | land_2018=malay_land.iloc[-1:].copy()
242 | land_2018.reset_index(inplace=True,drop=True)
243 | land_2018.at[0,'Year']=2018
244 | land_2018.at[0,'Year Code']=2018
245 | 
246 | malay_land=malay_land.append(land_2018)
247 | 
248 | 
249 | # In[18]:
250 | 
251 | 
252 | #export to csv
253 | malay_land.to_csv('malay_land.csv',index=False)
254 | 
255 | malay_prod.to_csv('malay_prod.csv',index=False)
256 | 
257 | malay_prix.to_csv('malay_prix.csv',index=False)
258 | 
259 | malay_pop.to_csv('malay_pop.csv',index=False)
260 | 
261 | malay_gdp.to_csv('malay_gdp.csv',index=False)
262 | 
263 | 
264 | # In[19]:
265 | 
266 | 
267 | #concat
268 | D=prepare(malay_land,malay_prod,malay_prix)
269 | 
270 | grand=pd.concat([D[i] for i in D])
271 | 
272 | grand.reset_index(inplace=True,drop=True)
273 | 
274 | 
275 | # In[20]:
276 | 
277 | 
278 | #create from synthetic control unit
279 | malay_ginger=[1711.631924118213, 1918.0617150999785, 1926.352662122375, 1904.0526853161766]
280 | 
281 | malay_lettuce=[808.9104313117714, 809.2907573824596, 880.0718990821069, 801.194381901839]
282 | 
283 | malay_maize=[176.02653721679235, 181.61446377135653, 185.21444467527138]
284 | 
285 | malay_orange=[385.20868947337976, 303.6319254759596, 277.51641822220614, 376.92771860265077]
286 | 
287 | malay_sugarcane=[246.78089608441445]
288 | 
289 | malay_tobacco=[4345.956117624693,
290 |  4091.368210599639,
291 |  3638.8980233955585,
292 |  3829.529414768218,
293 |  3758.1754705530975,
294 |  3889.6001454970665]
295 | 
296 | 
297 | # In[21]:
298 | 
299 | 
300 | #fill price
301 | fillnull=dict(zip(grand['Item'][grand['price'].isnull()].unique(),
302 | [malay_tobacco,malay_ginger,malay_lettuce,
303 |  malay_orange,malay_maize,malay_sugarcane,]))
304 | 
305 | for i in fillnull:
306 |     indices=grand[grand['Item']==i][grand['price'].isnull()].index.tolist()
307 |     for j in indices:
308 |         grand.at[j,'price']=fillnull[i][indices.index(j)]
309 | 
310 | 
311 | # In[22]:
312 | 
313 | 
314 | grand.to_csv('grand.csv',index=False)
315 | 
316 | 
317 | # ### synthetic control unit
318 | 
319 | # In[23]:
320 | 
321 | 
322 | # import statsmodels.api as sm
323 | 
324 | # #show missing items
325 | # null=set(grand['Item'][grand['price'].isnull()])
326 | 
327 | # ss=grand[grand['Item'].isin(null)].sort_values(['Item','Year'])
328 | 
329 | # ss
330 | 
331 | # #split here
332 | 
333 | # #create synthetic control unit
334 | # for ii in ss['Item'].unique():
335 |     
336 | #     print(ii)
337 |     
338 | #     #cleanse data
339 | #     temp=prix[prix['Item']==ii][prix['Year'].isin(range(beginyear,endyear))][prix['Element']=='Producer Price (USD/tonne)'][prix['Months']=='Annual value']
340 | #     temp=temp.pivot(index='Year',columns='Area',values='Value')
341 |     
342 | #     #delete null data
343 | #     for i in temp:
344 | #         if temp[i].isnull().any() and i!='Malaysia':
345 | #             del temp[i]
346 |     
347 | #     #train
348 | #     x=temp.loc[temp['Malaysia'].dropna().index]
349 | #     del x['Malaysia']
350 | #     y=temp['Malaysia'].dropna()
351 | #     x.reset_index(inplace=True,drop=True)
352 | #     y.reset_index(inplace=True,drop=True)
353 | 
354 | #     m=sm.OLS(y,x).fit()
355 |     
356 | #     #train result
357 | #     print(m.predict(),y.tolist())
358 |     
359 | #     #test result
360 | #     test=temp[temp['Malaysia'].isnull()]
361 | #     test=test[[i for i in test.columns if i!='Malaysia']]
362 | #     print(m.predict(test).tolist())
363 | 
364 | 


--------------------------------------------------------------------------------
/Smart Farmers project/country selection.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | target_country=['Australia','Spain',
  8 |  'Morocco',
  9 |  'United Kingdom',
 10 |  'Poland',
 11 |  'France',
 12 |  'Mexico',
 13 |  'Bangladesh',
 14 |  'Canada',
 15 |  'Viet Nam',
 16 |  'Thailand',
 17 |  'Guatemala',
 18 |  'Colombia',
 19 |  'Germany',
 20 |  'China',
 21 |  'Brazil',
 22 |  'India',
 23 |  'United States of America',
 24 |  'Malaysia',
 25 |  'Indonesia']
 26 | 
 27 | 
 28 | # In[2]:
 29 | 
 30 | 
 31 | target_year=[2000,
 32 |  2001,
 33 |  2002,
 34 |  2003,
 35 |  2004,
 36 |  2005,
 37 |  2006,
 38 |  2007,
 39 |  2008,
 40 |  2009,
 41 |  2010,
 42 |  2011,
 43 |  2012,
 44 |  2013,
 45 |  2014,
 46 |  2015,
 47 |  2016,
 48 |  2017]
 49 | 
 50 | 
 51 | # In[3]:
 52 | 
 53 | 
 54 | import pandas as pd
 55 | import numpy as np
 56 | import os
 57 | os.chdir('H:/')
 58 | 
 59 | 
 60 | # In[4]:
 61 | 
 62 | 
 63 | prod=pd.read_csv('Production_Crops_E_All_Data_(Normalized).csv',encoding='latin-1')
 64 | 
 65 | 
 66 | # In[5]:
 67 | 
 68 | 
 69 | prix=pd.read_csv('Prices_E_All_Data_(Normalized).csv',encoding='latin-1')
 70 | 
 71 | 
 72 | # In[6]:
 73 | 
 74 | 
 75 | echanger=pd.read_csv('Trade_Crops_Livestock_E_All_Data_(Normalized).csv',encoding='latin-1')
 76 | 
 77 | 
 78 | # In[7]:
 79 | 
 80 | 
 81 | #only compare crops with available data
 82 | target_crops=set(prod['Item']).intersection(set(prix['Item'])).intersection(set(echanger['Item']))
 83 | 
 84 | 
 85 | # # volume
 86 | 
 87 | # In[8]:
 88 | 
 89 | 
 90 | #extract export volume
 91 | trade=echanger[echanger['Element']=='Export Quantity']
 92 | 
 93 | 
 94 | # In[9]:
 95 | 
 96 | 
 97 | #cleanse
 98 | trade=trade[trade['Item'].isin(target_crops)]
 99 | 
100 | trade=trade[trade['Area'].isin(target_country)]
101 | 
102 | trade=trade[trade['Year'].isin(target_year)]
103 | 
104 | trade=trade[['Area','Year','Item','Value']]
105 | 
106 | 
107 | # In[10]:
108 | 
109 | 
110 | #extract production data
111 | prod=prod[prod['Element']=='Production']
112 | 
113 | 
114 | # In[11]:
115 | 
116 | 
117 | #cleanse
118 | prod=prod[prod['Item'].isin(target_crops)]
119 | 
120 | prod=prod[prod['Area'].isin(target_country)]
121 | 
122 | prod=prod[prod['Year'].isin(target_year)]
123 | 
124 | prod=prod[['Area','Year','Item','Value']]
125 | 
126 | 
127 | # In[12]:
128 | 
129 | 
130 | #group by sum
131 | export=trade.groupby(['Area','Year']).sum()
132 | 
133 | 
134 | # In[13]:
135 | 
136 | 
137 | #group by sum
138 | supply=prod.groupby(['Area','Year']).sum()
139 | 
140 | 
141 | # In[14]:
142 | 
143 | 
144 | #create new dataframe to compute export volume percentage
145 | pourcent=pd.DataFrame(index=export.index)
146 | 
147 | 
148 | # In[15]:
149 | 
150 | 
151 | #compute percentage
152 | pourcent['value']=np.divide(export['Value'].tolist(),supply['Value'].tolist())
153 | 
154 | 
155 | # In[16]:
156 | 
157 | 
158 | #clean up index
159 | pourcent.reset_index(inplace=True)
160 | 
161 | 
162 | # In[17]:
163 | 
164 | 
165 | #historical average
166 | mean_pourcent=pourcent[['Area','value']].groupby('Area').mean()
167 | 
168 | 
169 | # In[18]:
170 | 
171 | 
172 | #sort by percentage
173 | mean_pourcent=mean_pourcent.sort_values('value')
174 | 
175 | 
176 | # In[19]:
177 | 
178 | 
179 | #export percentage for a particular year
180 | pourcent[pourcent['Year'].isin([2017])].sort_values('value')
181 | 
182 | 
183 | # # price
184 | 
185 | # In[20]:
186 | 
187 | 
188 | #get usd price
189 | prix=prix[prix['Element']=='Producer Price (USD/tonne)']
190 | 
191 | 
192 | # In[21]:
193 | 
194 | 
195 | #cleanse
196 | prix=prix[prix['Item'].isin(target_crops)]
197 | 
198 | prix=prix[prix['Area'].isin(target_country)]
199 | 
200 | prix=prix[prix['Year'].isin(target_year)]
201 | 
202 | prix=prix[['Area','Year','Item','Value']]
203 | 
204 | 
205 | # In[22]:
206 | 
207 | 
208 | #previously we examine the volume
209 | #now we focus on value
210 | value=prod.merge(prix,on=['Area','Year','Item'],how='inner')
211 | 
212 | 
213 | # In[23]:
214 | 
215 | 
216 | #volume times unit price equals to total value
217 | value['Value']=np.multiply(value['Value_x'],value['Value_y'])
218 | 
219 | 
220 | # In[24]:
221 | 
222 | 
223 | #cleanse
224 | value=value[['Area','Year','Item','Value']]
225 | 
226 | 
227 | # In[25]:
228 | 
229 | 
230 | #get export price
231 | exchange=echanger[echanger['Element']=='Export Value']
232 | 
233 | 
234 | # In[26]:
235 | 
236 | 
237 | #cleanse
238 | exchange=exchange[exchange['Item'].isin(target_crops)]
239 | 
240 | exchange=exchange[exchange['Area'].isin(target_country)]
241 | 
242 | exchange=exchange[exchange['Year'].isin(target_year)]
243 | 
244 | exchange=exchange[['Area','Year','Item','Value']]
245 | 
246 | 
247 | # In[27]:
248 | 
249 | 
250 | #group by sum
251 | temp1=exchange.groupby(['Area','Year']).sum()
252 | 
253 | 
254 | # In[28]:
255 | 
256 | 
257 | #group by sum
258 | temp2=value.groupby(['Area','Year']).sum()
259 | 
260 | 
261 | # In[29]:
262 | 
263 | 
264 | #inner join
265 | temp=temp1.merge(temp2,on=['Area','Year'],how='inner')
266 | 
267 | 
268 | # In[30]:
269 | 
270 | 
271 | #create dataframe to compute export value percentage
272 | percentage=pd.DataFrame(index=temp.index)
273 | 
274 | 
275 | # In[31]:
276 | 
277 | 
278 | #compute export value percentage
279 | percentage['value']=np.divide(temp['Value_x'],temp['Value_y'])
280 | 
281 | 
282 | # In[32]:
283 | 
284 | 
285 | #clean up
286 | percentage.reset_index(inplace=True)
287 | 
288 | 
289 | # In[33]:
290 | 
291 | 
292 | #historical average
293 | mean_percentage=percentage[['Area','value']].groupby('Area').mean()
294 | 
295 | 
296 | # In[34]:
297 | 
298 | 
299 | #sort by percentage
300 | mean_percentage=mean_percentage.sort_values('value')
301 | 
302 | 
303 | # In[35]:
304 | 
305 | 
306 | #export percentage for a particular year
307 | percentage[percentage['Year'].isin([2016])].sort_values('value')
308 | 
309 | 


--------------------------------------------------------------------------------
/Smart Farmers project/data/capita.csv:
--------------------------------------------------------------------------------
 1 | Date,Mid Price
 2 | 12/31/1980,1926.963
 3 | 12/31/1981,1920.127
 4 | 12/31/1982,2006.4821
 5 | 12/31/1983,2189.553
 6 | 12/31/1984,2419.501
 7 | 12/31/1985,2154.458
 8 | 12/31/1986,1864.031
 9 | 12/31/1987,2070.009
10 | 12/31/1988,2213.866
11 | 12/31/1989,2380.512
12 | 12/31/1990,2585.824
13 | 12/31/1991,2885.3059
14 | 12/31/1992,3378.7209
15 | 12/31/1993,3716.9519
16 | 12/31/1994,4027.509
17 | 12/31/1995,4678.0239
18 | 12/31/1996,5175.5562
19 | 12/31/1997,5011.5601
20 | 12/31/1998,3519.7661
21 | 12/31/1999,3762.769
22 | 12/31/2000,4347.73
23 | 12/31/2001,4189.0571
24 | 12/31/2002,4441.8472
25 | 12/31/2003,4740.3159
26 | 12/31/2004,5244.874
27 | 12/31/2005,5678.5972
28 | 12/31/2006,6353.4141
29 | 12/31/2007,7483.4092
30 | 12/31/2008,8769.3945
31 | 12/31/2009,7545.1118
32 | 12/31/2010,9047.2021
33 | 12/31/2011,10398.2363
34 | 12/31/2012,10806.8398
35 | 12/31/2013,10851.6641
36 | 12/31/2014,11165.2588
37 | 12/31/2015,9663.1113
38 | 12/31/2016,9523.2949
39 | 12/31/2017,9960.3232
40 | 12/31/2018,11072.3867
41 | 12/31/2019,11136.8115
42 | 12/31/2020,11484.5049
43 | 12/31/2021,12172.1621
44 | 12/31/2022,12875.417
45 | 12/31/2023,13629.3301
46 | 12/31/2024,14468.3174
47 | 


--------------------------------------------------------------------------------
/Smart Farmers project/data/malay_gdp.csv:
--------------------------------------------------------------------------------
1 | Area Code,Area,Item Code,Item,Element Code,Element,Year Code,Year,Unit,Value,Flag,Note
2 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2012,2012,,10779.504483,Fc,
3 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2013,2013,,10882.259575,Fc,
4 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2014,2014,,11183.869684000001,Fc,
5 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2015,2015,,9808.7171,Fc,
6 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2016,2016,,9659.564758,Fc,
7 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2017,2017,,10085.774790000001,Fc,
8 | 131,Malaysia,22014,Gross Domestic Product per capita,6119,Value US$,2018,2018,,11190.754009,Fc,
9 | 


--------------------------------------------------------------------------------
/Smart Farmers project/data/malay_land.csv:
--------------------------------------------------------------------------------
1 | Area Code,Area,Item Code,Item,Element Code,Element,Year Code,Year,Unit,Value,Flag
2 | 131,Malaysia,6620,Cropland,5110,Area,2012,2012,1000 ha,7544.2,Fm
3 | 131,Malaysia,6620,Cropland,5110,Area,2013,2013,1000 ha,7774.3,Q
4 | 131,Malaysia,6620,Cropland,5110,Area,2014,2014,1000 ha,7804.0,Fm
5 | 131,Malaysia,6620,Cropland,5110,Area,2015,2015,1000 ha,8284.97,Q
6 | 131,Malaysia,6620,Cropland,5110,Area,2016,2016,1000 ha,8305.7,Fm
7 | 131,Malaysia,6620,Cropland,5110,Area,2017,2017,1000 ha,8305.2,Fm
8 | 131,Malaysia,6620,Cropland,5110,Area,2018,2018,1000 ha,8305.2,Fm
9 | 


--------------------------------------------------------------------------------
/Smart Farmers project/data/malay_pop.csv:
--------------------------------------------------------------------------------
 1 | Area Code,Area,Item Code,Item,Element Code,Element,Year Code,Year,Unit,Value,Flag,Note
 2 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2012,2012,1000 persons,29068.189,X,
 3 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2013,2013,1000 persons,29468.923,X,
 4 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2014,2014,1000 persons,29866.603,X,
 5 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2015,2015,1000 persons,30270.962,X,
 6 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2016,2016,1000 persons,30684.654,X,
 7 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2017,2017,1000 persons,31104.646,X,
 8 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2018,2018,1000 persons,31528.033,X,
 9 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2019,2019,1000 persons,31949.777,X,
10 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2020,2020,1000 persons,32365.999,X,
11 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2021,2021,1000 persons,32776.194,X,
12 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2022,2022,1000 persons,33181.072,X,
13 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2023,2023,1000 persons,33579.265,X,
14 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2024,2024,1000 persons,33969.29,X,
15 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2025,2025,1000 persons,34349.936,X,
16 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2026,2026,1000 persons,34720.347,X,
17 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2027,2027,1000 persons,35080.112,X,
18 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2028,2028,1000 persons,35429.087,X,
19 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2029,2029,1000 persons,35767.388,X,
20 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2030,2030,1000 persons,36095.054,X,
21 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2031,2031,1000 persons,36411.996,X,
22 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2032,2032,1000 persons,36717.9,X,
23 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2033,2033,1000 persons,37012.445,X,
24 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2034,2034,1000 persons,37295.251,X,
25 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2035,2035,1000 persons,37566.148,X,
26 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2036,2036,1000 persons,37825.112,X,
27 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2037,2037,1000 persons,38072.527,X,
28 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2038,2038,1000 persons,38309.22,X,
29 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2039,2039,1000 persons,38536.264,X,
30 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2040,2040,1000 persons,38754.574,X,
31 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2041,2041,1000 persons,38964.542,X,
32 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2042,2042,1000 persons,39166.396,X,
33 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2043,2043,1000 persons,39360.773,X,
34 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2044,2044,1000 persons,39548.354,X,
35 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2045,2045,1000 persons,39729.699,X,
36 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2046,2046,1000 persons,39905.062,X,
37 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2047,2047,1000 persons,40074.631,X,
38 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2048,2048,1000 persons,40238.61,X,
39 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2049,2049,1000 persons,40397.168,X,
40 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2050,2050,1000 persons,40550.365,X,
41 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2051,2051,1000 persons,40698.294,X,
42 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2052,2052,1000 persons,40840.8,X,
43 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2053,2053,1000 persons,40977.445,X,
44 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2054,2054,1000 persons,41107.613,X,
45 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2055,2055,1000 persons,41230.759,X,
46 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2056,2056,1000 persons,41346.675,X,
47 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2057,2057,1000 persons,41455.194,X,
48 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2058,2058,1000 persons,41555.888,X,
49 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2059,2059,1000 persons,41648.244,X,
50 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2060,2060,1000 persons,41731.877,X,
51 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2061,2061,1000 persons,41806.614,X,
52 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2062,2062,1000 persons,41872.362,X,
53 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2063,2063,1000 persons,41928.871,X,
54 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2064,2064,1000 persons,41975.9,X,
55 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2065,2065,1000 persons,42013.361,X,
56 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2066,2066,1000 persons,42041.225,X,
57 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2067,2067,1000 persons,42059.669,X,
58 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2068,2068,1000 persons,42068.988,X,
59 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2069,2069,1000 persons,42069.605,X,
60 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2070,2070,1000 persons,42061.944,X,
61 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2071,2071,1000 persons,42046.292,X,
62 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2072,2072,1000 persons,42022.971,X,
63 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2073,2073,1000 persons,41992.517,X,
64 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2074,2074,1000 persons,41955.499,X,
65 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2075,2075,1000 persons,41912.491,X,
66 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2076,2076,1000 persons,41863.892,X,
67 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2077,2077,1000 persons,41810.176,X,
68 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2078,2078,1000 persons,41752.0,X,
69 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2079,2079,1000 persons,41690.087,X,
70 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2080,2080,1000 persons,41625.092,X,
71 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2081,2081,1000 persons,41557.426,X,
72 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2082,2082,1000 persons,41487.446,X,
73 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2083,2083,1000 persons,41415.604,X,
74 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2084,2084,1000 persons,41342.328,X,
75 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2085,2085,1000 persons,41267.997,X,
76 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2086,2086,1000 persons,41192.896,X,
77 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2087,2087,1000 persons,41117.199,X,
78 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2088,2088,1000 persons,41040.987,X,
79 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2089,2089,1000 persons,40964.235,X,
80 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2090,2090,1000 persons,40886.939,X,
81 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2091,2091,1000 persons,40809.104,X,
82 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2092,2092,1000 persons,40730.761,X,
83 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2093,2093,1000 persons,40651.951,X,
84 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2094,2094,1000 persons,40572.639,X,
85 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2095,2095,1000 persons,40492.785,X,
86 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2096,2096,1000 persons,40412.247,X,
87 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2097,2097,1000 persons,40330.825,X,
88 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2098,2098,1000 persons,40248.246,X,
89 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2099,2099,1000 persons,40164.17,X,
90 | 131,Malaysia,3010,Population - Est. & Proj.,511,Total Population - Both sexes,2100,2100,1000 persons,40078.168,X,
91 | 


--------------------------------------------------------------------------------
/Smart Farmers project/estimate demand.py:
--------------------------------------------------------------------------------
  1 | 
  2 | # coding: utf-8
  3 | 
  4 | # In[1]:
  5 | 
  6 | 
  7 | import numpy as np
  8 | import os
  9 | import pandas as pd
 10 | import statsmodels.api as sm
 11 | import matplotlib.pyplot as plt
 12 | import cvxopt
 13 | os.chdir('H:/')
 14 | 
 15 | 
 16 | # ### define functions
 17 | 
 18 | # In[2]:
 19 | 
 20 | 
 21 | #create xy for least square
 22 | def create_xy(target_crop,grande,malay_gdp,malay_pop):
 23 |     
 24 |     y=grande['price'][target_crop]
 25 | 
 26 |     x=pd.concat([malay_gdp['Value'],malay_pop['Value'],
 27 |                        grande['production'][target_crop]],axis=1)
 28 | 
 29 |     x=sm.add_constant(x)
 30 |     
 31 |     #set production negative
 32 |     x[target_crop.lower()]=x[target_crop].apply(lambda x:-1*x)
 33 |     del x[target_crop]
 34 |     
 35 |     return x,y
 36 | 
 37 | 
 38 | # In[3]:
 39 | 
 40 | 
 41 | #linear regression
 42 | def lin_reg(crops,grande,malay_gdp,malay_pop,viz=False):
 43 | 
 44 |     D={}
 45 |     
 46 |     #run regression
 47 |     for target_crop in crops:
 48 |         
 49 |         #create xy
 50 |         x,y=create_xy(target_crop,grande,malay_gdp,malay_pop)
 51 |         
 52 |         m=sm.OLS(y,sm.add_constant(x)).fit()
 53 | 
 54 |         D[target_crop]=(m.rsquared,m.params.tolist())
 55 |         
 56 |         #viz
 57 |         if viz:
 58 |             fig=plt.figure(figsize=(10,5))
 59 |             ax=fig.add_subplot(111)
 60 |             ax.spines['top'].set_visible(False)
 61 |             ax.spines['right'].set_visible(False)
 62 |             plt.ylabel('USD/Tonnes')
 63 |             plt.xlabel('Year')
 64 |             plt.plot(range(beginyear,endyear),m.predict(),label='Est',color='#C8C8A9')
 65 |             plt.plot(range(beginyear,endyear),y,label='Act',color='#EDE574')
 66 |             plt.title(target_crop+' Price')
 67 |             plt.legend()
 68 |             plt.show()
 69 | 
 70 |     return D
 71 | 
 72 | 
 73 | # In[4]:
 74 | 
 75 | 
 76 | def constrained_ols(x,y):
 77 | 
 78 |     linear_coeff=cvxopt.matrix(-1*np.mat(y.tolist())*np.mat(x)).T
 79 |     quadratic_coeff=cvxopt.matrix(np.mat(x).T*np.mat(x))
 80 | 
 81 |     #inequality constraint
 82 |     inequality_coeff=cvxopt.matrix(0.0,(len(x.columns),len(x.columns)))
 83 | 
 84 |     #diagonal matrix
 85 |     #use -1 to achieve larger than
 86 |     inequality_coeff[::len(x.columns)+1]=-1
 87 |     
 88 |     #no constraint for the constant
 89 |     inequality_coeff[0,0]=0
 90 |     inequality_value=cvxopt.matrix([0.0 for _ in range(len(x.columns))])
 91 | 
 92 |     cvxopt.solvers.options['show_progress']=False
 93 | 
 94 |     ans=cvxopt.solvers.qp(P=quadratic_coeff,q=linear_coeff,
 95 |                           G=inequality_coeff,h=inequality_value)['x']
 96 |     
 97 |     return ans
 98 | 
 99 | 
100 | # In[5]:
101 | 
102 | 
103 | #create params by using constrained ols
104 | def get_params(crops,grande,malay_gdp,malay_pop,viz=False):
105 |     
106 |     D={}
107 |     
108 |     for target_crop in crops:
109 | 
110 |         #create xy
111 |         x,y=create_xy(target_crop,grande,malay_gdp,malay_pop)
112 | 
113 |         #constrained ols
114 |         ans=constrained_ols(x,y)
115 | 
116 |         #viz
117 |         if viz:
118 | 
119 |             #get forecast and convert to list
120 |             forecast=np.mat(ans).T*np.mat(x).T
121 |             forecast=forecast.ravel().tolist()[0]
122 |             fig=plt.figure(figsize=(10,5))
123 |             ax=fig.add_subplot(111)
124 |             ax.spines['top'].set_visible(False)
125 |             ax.spines['right'].set_visible(False)
126 |             plt.ylabel('USD/Tonnes')
127 |             plt.xlabel('Year')
128 |             plt.plot(range(beginyear,endyear),forecast,label='Est',color='#C8C8A9',)
129 |             plt.plot(range(beginyear,endyear),y,label='Act',color='#EDE574')
130 |             plt.legend()
131 |             plt.title(target_crop+' Price')
132 |             plt.show()
133 | 
134 |         D[target_crop]=list(ans)
135 |         
136 |     return D
137 | 
138 | 
139 | # ### execution
140 | 
141 | # In[6]:
142 | 
143 | 
144 | global beginyear,endyear
145 | beginyear=2012
146 | endyear=2019
147 | 
148 | 
149 | # In[7]:
150 | 
151 | 
152 | grand=pd.read_csv('grand.csv')
153 | 
154 | malay_pop=pd.read_csv('malay_pop.csv')
155 | 
156 | malay_gdp=pd.read_csv('malay_gdp.csv')
157 | 
158 | 
159 | # In[8]:
160 | 
161 | 
162 | #cleanse
163 | malay_pop=malay_pop[malay_pop['Year'].isin(range(beginyear,endyear))]
164 | malay_pop.reset_index(inplace=True,drop=True)
165 | 
166 | 
167 | # In[9]:
168 | 
169 | 
170 | #pivot
171 | grande=grand.pivot(index='Year',columns='Item',values=['price','production'])
172 | grande.reset_index(inplace=True,drop=True)
173 | 
174 | 
175 | # In[10]:
176 | 
177 | 
178 | #find crops
179 | crops=grande.columns.levels[1]
180 | 
181 | 
182 | # ### linear regression
183 | 
184 | # In[11]:
185 | 
186 | 
187 | D1=lin_reg(crops,grande,malay_gdp,malay_pop,viz=True)
188 | 
189 | 
190 | # In[12]:
191 | 
192 | 
193 | D1
194 | 
195 | 
196 | # ### constrained regression
197 | 
198 | # In[13]:
199 | 
200 | 
201 | D2=get_params(crops,grande,malay_gdp,malay_pop,viz=True)
202 | 
203 | 
204 | # In[14]:
205 | 
206 | 
207 | # #replace unsuccessful result with ols
208 | # for i in D2:
209 | #     if len([j for j in D2[i] if j<0])>0:
210 | #         D2[i]=D1[i][1]        
211 | 
212 | 
213 | # In[15]:
214 | 
215 | 
216 | #create output
217 | output=pd.DataFrame(columns=D2.keys())
218 | 
219 | for i in D2:
220 |     output[i]=D2[i]
221 | 
222 | output=output.T
223 | 
224 | output.columns=['constant','gamma','beta','alpha']
225 | 
226 | output.index.name='Item'
227 | 
228 | output.reset_index(inplace=True)
229 | 
230 | 
231 | # In[16]:
232 | 
233 | 
234 | #merge with grand
235 | tres_grand=grand.merge(output,on='Item',how='left')
236 | 
237 | tres_grand.to_csv('tres_grand.csv',index=False)
238 | 
239 | 
240 | # ### backtest
241 | 
242 | # In[17]:
243 | 
244 | 
245 | forecast=pd.read_csv('forecast.csv')
246 | 
247 | 
248 | # In[18]:
249 | 
250 | 
251 | #extract historical generic 1st
252 | palm=pd.read_csv('palm.csv')
253 | palm.set_index('Date',inplace=True,)
254 | palm.index=pd.to_datetime(palm.index)
255 | palm.columns=['Palm oil','Rubber1','Rubber2']
256 | 
257 | 
258 | # In[19]:
259 | 
260 | 
261 | cme=pd.read_csv('cme.csv')
262 | 
263 | #extract cme palm oil futures 
264 | palm_futures=cme[cme['date']==cme['date'].iloc[-1]][cme['product_id']==2457]
265 | 
266 | palm_futures=palm_futures[['expiration_date','prior_settle']]
267 | palm_futures.columns=['Date','Palm oil']
268 | 
269 | palm_futures.set_index('Date',inplace=True)
270 | palm_futures.index=pd.to_datetime(palm_futures.index)
271 | 
272 | 
273 | # In[20]:
274 | 
275 | 
276 | #concat, currency convert and get annual avg
277 | temp=palm['Palm oil'][str(beginyear):].apply(lambda x:x*0.23).append(palm_futures['Palm oil'][:str(endyear+5)])
278 | 
279 | palmoil=temp.resample('1A').mean()
280 | 
281 | #convert datetime index from year end to beginning
282 | palmoil.index=[pd.to_datetime(str(i)[:5]+'01-01') for i in palmoil.index]
283 | 
284 | 
285 | # In[21]:
286 | 
287 | 
288 | #create oil palm actual and estimated price
289 | oilpalm_act=grand['price'][grand['Item']=='Oil palm fruit']
290 | oilpalm_act.reset_index(inplace=True,drop=True)
291 | 
292 | oilpalm_est=oilpalm_act.iloc[-1:].append(forecast['price'][forecast['Item']=='Oil palm fruit'])
293 | oilpalm_est.reset_index(inplace=True,drop=True)
294 | 
295 | 
296 | # In[22]:
297 | 
298 | 
299 | #dual axis plot
300 | fig=plt.figure(figsize=(10,5))
301 | ax=fig.add_subplot(111)
302 | 
303 | 
304 | ax.set_xlabel('Date')
305 | ax.set_ylabel('Palm Oil Generic 1st',color='#45ADA8')
306 | ax.plot(palmoil.index,palmoil,color='#45ADA8',label='Palm Oil')
307 | ax.tick_params(axis='y',labelcolor='#45ADA8')
308 | ax.yaxis.labelpad=15
309 | 
310 | plt.legend(loc=3)
311 | ax2=ax.twinx()
312 | 
313 | ax2.set_ylabel('Oil Palm Fruit Price',color='#EC2049',rotation=270)
314 | ax2.plot(palmoil.index[:len(oilpalm_act)],oilpalm_act,
315 |          color='#EC2049',label='Oil Palm Act')
316 | ax2.plot(palmoil.index[len(oilpalm_est)-1:],oilpalm_est,
317 |          color='#fea5c4',label='Oil Palm Est',linestyle='-.')
318 | ax2.tick_params(axis='y',labelcolor='#EC2049')
319 | ax2.yaxis.labelpad=15
320 | 
321 | fig.tight_layout()
322 | plt.legend(loc=4)
323 | plt.grid(False)
324 | plt.title('Palm Oil vs Oil Palm')
325 | plt.show()
326 | 
327 | 


--------------------------------------------------------------------------------
/Smart Farmers project/preview/cabbage price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/cabbage price.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/cabbage production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/cabbage production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/cabbage regression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/cabbage regression.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/cocoa price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/cocoa price.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/cocoa production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/cocoa production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/cocoa regression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/cocoa regression.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/coconut price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/coconut price.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/coconut production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/coconut production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/coconut regression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/coconut regression.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/demand model.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/demand model.PNG


--------------------------------------------------------------------------------
/Smart Farmers project/preview/mango price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/mango price.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/mango production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/mango production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/mango regression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/mango regression.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/naive model matrix.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/naive model matrix.PNG


--------------------------------------------------------------------------------
/Smart Farmers project/preview/naive model.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/naive model.PNG


--------------------------------------------------------------------------------
/Smart Farmers project/preview/oil palm price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/oil palm price.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/oil palm production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/oil palm production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/oil palm regression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/oil palm regression.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/oil palm vs palm oil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/oil palm vs palm oil.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/overall production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/overall production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/price change derivation.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/price change derivation.PNG


--------------------------------------------------------------------------------
/Smart Farmers project/preview/pricing mechanism.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/pricing mechanism.PNG


--------------------------------------------------------------------------------
/Smart Farmers project/preview/ricardian model.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/ricardian model.PNG


--------------------------------------------------------------------------------
/Smart Farmers project/preview/rubber price.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/rubber price.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/rubber production.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/rubber production.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/rubber regression.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/rubber regression.png


--------------------------------------------------------------------------------
/Smart Farmers project/preview/workflow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/Smart Farmers project/preview/workflow.png


--------------------------------------------------------------------------------
/data/bitcoin.csv:
--------------------------------------------------------------------------------
  1 | Date,XBTUSD BGN Curncy  (R3),SPX Index  (L2),GOLDLNPM Index  (R2),TLT US Equity  (L1),EEM US Equity  (R1)
  2 | 7/31/2010,0.06,1101.6,1169,100.48,41.4
  3 | 8/31/2010,0.06,1049.33,1246,108.56,40.06
  4 | 9/30/2010,0.06,1141.2,1307,105.51,44.77
  5 | 10/31/2010,0.19,1183.26,1346.75,100.42,46.12
  6 | 11/30/2010,0.21,1180.55,1383.5,98.41,44.78
  7 | 12/31/2010,0.3,1257.64,1405.5,94.12,47.642
  8 | 1/31/2011,0.52,1286.12,1327,91.22,45.81
  9 | 2/28/2011,0.86,1327.22,1411,92.4,45.79
 10 | 3/31/2011,0.78,1325.83,1439,92.13,48.67
 11 | 4/30/2011,2.88,1363.61,1535.5,93.89,50
 12 | 5/31/2011,8.74,1345.2,1536.5,96.69,48.53
 13 | 6/30/2011,16.1,1320.64,1505.5,94.1,47.6
 14 | 7/31/2011,13.5,1292.28,1628.5,97.92,47.11
 15 | 8/31/2011,8.2,1218.89,1813.5,107.03,42.75
 16 | 9/30/2011,5.14,1131.42,1620,120.8,35.095
 17 | 10/31/2011,3.25,1253.3,1722,115.88,40.815
 18 | 11/30/2011,2.97,1246.96,1746,117.88,40.01
 19 | 12/31/2011,4.25,1257.61,1531,121.25,37.94
 20 | 1/31/2012,5.48,1312.41,1744,120.85,42.11
 21 | 2/29/2012,4.86,1365.68,1770,117.43,44.33
 22 | 3/31/2012,4.86,1408.47,1662.5,112.2,42.945
 23 | 4/30/2012,4.95,1397.91,1651.25,117.32,42.215
 24 | 5/31/2012,5.18,1310.33,1558,127.6,37.7
 25 | 6/30/2012,6.65,1362.16,1598.5,125.2,39.135
 26 | 7/31/2012,9.35,1379.32,1622,129.7,39.12
 27 | 8/31/2012,10.16,1406.58,1648.5,127.72,39.28
 28 | 9/30/2012,12.39,1440.67,1776,124.22,41.325
 29 | 10/31/2012,11.2,1412.16,1719,123.36,41.15
 30 | 11/30/2012,12.57,1416.18,1726,124.79,41.785
 31 | 12/31/2012,13.51,1426.19,1657.5,121.18,44.35
 32 | 1/31/2013,20.41,1498.11,1664.75,117.32,44.215
 33 | 2/28/2013,33.38,1514.68,1588.5,118.51,43.205
 34 | 3/31/2013,90.5,1569.19,1598.25,117.76,42.77
 35 | 4/30/2013,133.75,1597.57,1469,123.01,43.29
 36 | 5/31/2013,127.2,1630.74,1394.5,114.45,41.195
 37 | 6/30/2013,96.99,1606.28,1192,110.44,38.5
 38 | 7/31/2013,97.25,1685.73,1314.5,107.7,39.01
 39 | 8/31/2013,126.02,1632.97,1394.75,105.99,38.02
 40 | 9/30/2013,127.09,1681.55,1326.5,106.4,40.755
 41 | 10/31/2013,206.34,1756.54,1324,107.64,42.455
 42 | 11/30/2013,1137,1805.81,1253,104.45,42.35
 43 | 12/31/2013,746.89,1848.36,1204.5,101.86,41.795
 44 | 1/31/2014,806.69,1782.59,1251,108.28,38.19
 45 | 2/28/2014,566.91,1859.45,1326.5,108.57,39.48
 46 | 3/31/2014,465.75,1872.34,1291.75,109.1,41.01
 47 | 4/30/2014,448.23,1883.95,1288.5,111.1,41.33
 48 | 5/31/2014,613.92,1923.57,1250.5,114.1,42.55
 49 | 6/30/2014,646.97,1960.23,1315,113.52,43.23
 50 | 7/31/2014,586.36,1930.67,1285.25,113.98,43.82
 51 | 8/31/2014,479.7,2003.37,1285.75,119.05,45.06
 52 | 9/30/2014,389.68,1972.29,1216.5,116.27,41.56
 53 | 10/31/2014,342.38,2018.05,1164.25,119.25,42.15
 54 | 11/30/2014,375.76,2067.56,1182.75,122.49,41.5
 55 | 12/31/2014,317.36,2058.9,1206,125.92,39.29
 56 | 1/31/2015,229.67,1994.99,1260.25,138.28,39.02
 57 | 2/28/2015,254.87,2104.5,1214,129.53,40.74
 58 | 3/31/2015,244.14,2067.89,1187,130.69,40.13
 59 | 4/30/2015,236.58,2085.51,1180.25,125.95,42.88
 60 | 5/31/2015,231.78,2107.39,1191.4,122.71,41.12
 61 | 6/30/2015,260.97,2063.11,1171,117.46,39.62
 62 | 7/31/2015,284.69,2103.84,1098.4,122.53,37.12
 63 | 8/31/2015,230.41,1972.18,1135,121.42,33.84
 64 | 9/30/2015,236.99,1920.03,1114,123.54,32.78
 65 | 10/31/2015,323.23,2079.36,1142.35,122.78,34.87
 66 | 11/30/2015,376.82,2080.41,1061.9,121.45,33.99
 67 | 12/31/2015,432.12,2043.94,1060,120.58,32.19
 68 | 1/31/2016,377.29,1940.24,1111.8,127.3,30.57
 69 | 2/29/2016,433.91,1932.23,1234.9,130.98,30.32
 70 | 3/31/2016,416.85,2059.74,1237,130.61,34.25
 71 | 4/30/2016,455.15,2065.3,1285.65,129.38,34.39
 72 | 5/31/2016,521.74,2096.96,1212.1,130.16,33.12
 73 | 6/30/2016,665.27,2098.86,1320.75,138.9,34.36
 74 | 7/31/2016,632,2173.6,1342,141.56,36.205
 75 | 8/31/2016,572.41,2170.95,1309.25,139.87,36.53
 76 | 9/30/2016,605.99,2168.27,1322.5,137.51,37.45
 77 | 10/31/2016,702.17,2126.15,1272,131.25,37.14
 78 | 11/30/2016,743.26,2198.81,1178.1,120.24,35.5
 79 | 12/31/2016,952.01,2238.83,1145.9,119.13,35.01
 80 | 1/31/2017,961.69,2278.87,1212.8,120.1,37.34
 81 | 2/28/2017,1192.7,2363.64,1255.6,121.74,37.99
 82 | 3/31/2017,1074.69,2362.72,1244.85,120.71,39.39
 83 | 4/30/2017,1356.03,2384.2,1266.45,122.35,40.06
 84 | 5/31/2017,2286.1,2411.8,1266.2,124.4,41.2
 85 | 6/30/2017,2502.5,2423.41,1242.25,125.12,41.39
 86 | 7/31/2017,2886.71,2470.3,1267.55,124.04,43.8
 87 | 8/31/2017,4741.39,2471.65,1311.75,127.99,44.83
 88 | 9/30/2017,4171.25,2519.36,1283.1,124.76,44.81
 89 | 10/31/2017,6377.45,2575.26,1270.15,124.46,46.28
 90 | 11/30/2017,8134.25,2647.58,1280.2,125.12,46.1
 91 | 12/31/2017,14043.06,2673.61,1291,126.86,47.12
 92 | 1/31/2018,9962.31,2823.81,1345.05,122.73,51.03
 93 | 2/28/2018,10538.02,2713.83,1317.85,118.75,48.02
 94 | 3/31/2018,6852.51,2640.87,1323.85,121.9,48.28
 95 | 4/30/2018,9272.7,2648.05,1313.2,119.1,46.92
 96 | 5/31/2018,7538.28,2705.27,1305.35,121.22,45.69
 97 | 6/30/2018,5899.64,2718.37,1250.45,121.72,43.33
 98 | 7/31/2018,7689.43,2816.29,1220.95,119.7,44.86
 99 | 8/31/2018,7044.6,2901.52,1202.45,120.995,43.17
100 | 9/30/2018,6561.3,2913.98,1187.25,117.27,42.92
101 | 10/31/2018,6302.4,2711.74,1214.95,113.58,39.16
102 | 11/30/2018,3930.38,2760.17,1217.55,115.33,41.08
103 | 12/31/2018,3674.18,2506.85,1279,121.51,39.06
104 | 1/31/2019,3417.04,2704.1,1323.25,121.97,43.1
105 | 2/28/2019,3802.53,2784.49,1319.15,120.02,42.44
106 | 3/31/2019,4092.97,2834.4,1295.4,126.44,42.92
107 | 4/30/2019,5237.87,2945.83,1282.3,123.65,43.93
108 | 5/31/2019,8503.38,2752.06,1295.55,131.83,40.71
109 | 6/30/2019,11392.98,2941.76,1409,132.81,42.91
110 | 7/31/2019,10026.61,2980.38,1427.55,132.89,41.77
111 | 8/31/2019,9626.18,2926.46,1528.4,147.28,40.19
112 | 9/30/2019,8240.9,2976.74,1485.3,143.08,40.87
113 | 10/31/2019,9189.42,3037.56,1510.95,141.24,42.58
114 | 11/30/2019,7710.34,3140.98,1460.15,140.42,42.54
115 | 12/31/2019,7158.27,3230.78,1514.75,135.48,44.87
116 | 1/31/2020,9354.29,3225.52,1584.2,145.9,42.11
117 | 2/29/2020,8633.36,2954.22,1609.85,155.31,40.52
118 | 3/31/2020,6481.37,2584.59,1608.95,164.97,34.13
119 | 4/30/2020,8826.56,2912.43,1702.75,166.74,36.64
120 | 5/31/2020,9505.38,3044.31,1728.7,163.59,37.73
121 | 6/30/2020,9147.09,3100.29,1768.1,163.93,39.99
122 | 7/31/2020,11345.63,3271.12,1964.9,171,43.29
123 | 8/31/2020,11678.6,3500.31,1957.35,162.19,44.54
124 | 9/30/2020,10706.95,3363,1886.9,163.26,44.09
125 | 10/31/2020,13850.1,3269.96,1881.85,157.57,44.71
126 | 11/30/2020,19378.61,3621.63,1762.55,160.02,48.73
127 | 12/31/2020,28996.28,3756.07,1887.6,157.73,51.67
128 | 1/31/2021,32601.26,3714.24,1863.8,152,53.31
129 | 2/28/2021,45248.24,3811.15,1742.85,143.12,53.73
130 | 3/31/2021,58960.2,3972.89,1691.05,135.45,53.34
131 | 4/30/2021,56814.44,4181.17,1767.65,138.64,53.98
132 | 5/31/2021,36690.89,4204.11,1899.95,138.44,54.87
133 | 6/30/2021,34585,4297.5,1763.15,144.35,55.15
134 | 7/31/2021,41540.22,4395.26,1825.75,149.52,51.6
135 | 8/27/2021,47129.53,4470,1786.6,148.45,50.96
136 | 


--------------------------------------------------------------------------------
/data/cme holidays.csv:
--------------------------------------------------------------------------------
 1 | ,DAY,DATE,HOLIDAY
 2 | 0,Wed,2020-01-01,New Year's Day
 3 | 1,Mon,2020-01-20,M L King Day
 4 | 2,Mon,2020-02-17,Presidents' Day
 5 | 3,Fri,2020-04-10,Good Friday
 6 | 4,Mon,2020-05-25,Memorial Day
 7 | 5,Thu,2020-11-26,Thanksgiving Day
 8 | 6,Fri,2020-12-25,Christmas
 9 | 7,Tue,2019-12-31,New Year's Day
10 | 8,Thu,2020-01-02,New Year's Day
11 | 9,Fri,2020-01-17,M L King Day
12 | 10,Tue,2020-01-21,M L King Day
13 | 11,Fri,2020-02-14,Presidents' Day
14 | 12,Tue,2020-02-18,Presidents' Day
15 | 13,Thu,2020-04-09,Good Friday
16 | 14,Mon,2020-04-13,Good Friday
17 | 15,Fri,2020-05-22,Memorial Day
18 | 16,Tue,2020-05-26,Memorial Day
19 | 17,Wed,2020-11-25,Thanksgiving Day
20 | 18,Fri,2020-11-27,Thanksgiving Day
21 | 19,Thu,2020-12-24,Christmas
22 | 20,Mon,2020-12-28,Christmas
23 | 21,Fri,2021-01-01,New Year's Day
24 | 22,Mon,2021-01-18,M L King Day
25 | 23,Mon,2021-02-15,Presidents' Day
26 | 24,Fri,2021-04-02,Good Friday
27 | 25,Mon,2021-05-31,Memorial Day
28 | 26,Thu,2021-11-25,Thanksgiving Day
29 | 27,Sat,2021-12-25,Christmas
30 | 28,Thu,2020-12-31,New Year's Day
31 | 29,Mon,2021-01-04,New Year's Day
32 | 30,Fri,2021-01-15,M L King Day
33 | 31,Tue,2021-01-19,M L King Day
34 | 32,Fri,2021-02-12,Presidents' Day
35 | 33,Tue,2021-02-16,Presidents' Day
36 | 34,Thu,2021-04-01,Good Friday
37 | 35,Mon,2021-04-05,Good Friday
38 | 36,Fri,2021-05-28,Memorial Day
39 | 37,Tue,2021-06-01,Memorial Day
40 | 38,Wed,2021-11-24,Thanksgiving Day
41 | 39,Fri,2021-11-26,Thanksgiving Day
42 | 40,Fri,2021-12-24,Christmas
43 | 41,Mon,2021-12-27,Christmas
44 | 42,Sat,2022-01-01,New Year's Day
45 | 43,Mon,2022-01-17,M L King Day
46 | 44,Mon,2022-02-21,Presidents' Day
47 | 45,Fri,2022-04-15,Good Friday
48 | 46,Mon,2022-05-30,Memorial Day
49 | 47,Thu,2022-11-24,Thanksgiving Day
50 | 48,Sun,2022-12-25,Christmas
51 | 49,Fri,2021-12-31,New Year's Day
52 | 50,Mon,2022-01-03,New Year's Day
53 | 51,Fri,2022-01-14,M L King Day
54 | 52,Tue,2022-01-18,M L King Day
55 | 53,Fri,2022-02-18,Presidents' Day
56 | 54,Tue,2022-02-22,Presidents' Day
57 | 55,Thu,2022-04-14,Good Friday
58 | 56,Mon,2022-04-18,Good Friday
59 | 57,Fri,2022-05-27,Memorial Day
60 | 58,Tue,2022-05-31,Memorial Day
61 | 59,Wed,2022-11-23,Thanksgiving Day
62 | 60,Fri,2022-11-25,Thanksgiving Day
63 | 61,Fri,2022-12-23,Christmas
64 | 62,Mon,2022-12-26,Christmas
65 | 


--------------------------------------------------------------------------------
/data/stoxx50.xlsx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/data/stoxx50.xlsx


--------------------------------------------------------------------------------
/preview/awesome asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/awesome asset.png


--------------------------------------------------------------------------------
/preview/awesome ma.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/awesome ma.png


--------------------------------------------------------------------------------
/preview/awesome oscillator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/awesome oscillator.png


--------------------------------------------------------------------------------
/preview/awesome positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/awesome positions.png


--------------------------------------------------------------------------------
/preview/bollinger bands bottom w pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/bollinger bands bottom w pattern.png


--------------------------------------------------------------------------------
/preview/bollinger bands positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/bollinger bands positions.png


--------------------------------------------------------------------------------
/preview/dual thrust positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/dual thrust positions.png


--------------------------------------------------------------------------------
/preview/heikin-ashi asset value.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/heikin-ashi asset value.png


--------------------------------------------------------------------------------
/preview/heikin-ashi positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/heikin-ashi positions.png


--------------------------------------------------------------------------------
/preview/london breakout positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/london breakout positions.png


--------------------------------------------------------------------------------
/preview/london breakout thresholds.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/london breakout thresholds.png


--------------------------------------------------------------------------------
/preview/macd oscillator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/macd oscillator.png


--------------------------------------------------------------------------------
/preview/macd positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/macd positions.png


--------------------------------------------------------------------------------
/preview/options straddle payoff diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/options straddle payoff diagram.png


--------------------------------------------------------------------------------
/preview/pair trading asset.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/pair trading asset.png


--------------------------------------------------------------------------------
/preview/pair trading eg two step.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/pair trading eg two step.PNG


--------------------------------------------------------------------------------
/preview/pair trading positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/pair trading positions.png


--------------------------------------------------------------------------------
/preview/parabolic sar positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/parabolic sar positions.png


--------------------------------------------------------------------------------
/preview/quantitative-trading-profile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/quantitative-trading-profile.png


--------------------------------------------------------------------------------
/preview/rsi head-shoulder pattern.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/rsi head-shoulder pattern.png


--------------------------------------------------------------------------------
/preview/rsi oscillator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/rsi oscillator.png


--------------------------------------------------------------------------------
/preview/rsi pattern oscillator.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/rsi pattern oscillator.png


--------------------------------------------------------------------------------
/preview/rsi pattern positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/rsi pattern positions.png


--------------------------------------------------------------------------------
/preview/rsi positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/rsi positions.png


--------------------------------------------------------------------------------
/preview/shooting star positions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/shooting star positions.png


--------------------------------------------------------------------------------
/preview/vix calculator.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/je-suis-tm/quant-trading/611b73f2c3f577ac5b28aaa19ac8c43d3236c7a5/preview/vix calculator.PNG


--------------------------------------------------------------------------------