├── charts ├── pnl.png └── heatmap.png ├── fx_draw ├── eur_chf_up_down.png ├── eur_chf_weekly.png ├── eur_chf_volatility.png └── fx_viz.py ├── price.py ├── heatmap.gnuplot ├── strategy.py ├── pnl.gnuplot ├── README.md ├── order.py ├── market.py ├── account.py ├── test.py └── histories └── EURUSD_day.csv /charts/pnl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/efx-backtest/master/charts/pnl.png -------------------------------------------------------------------------------- /charts/heatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/efx-backtest/master/charts/heatmap.png -------------------------------------------------------------------------------- /fx_draw/eur_chf_up_down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/efx-backtest/master/fx_draw/eur_chf_up_down.png -------------------------------------------------------------------------------- /fx_draw/eur_chf_weekly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/efx-backtest/master/fx_draw/eur_chf_weekly.png -------------------------------------------------------------------------------- /fx_draw/eur_chf_volatility.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nst/efx-backtest/master/fx_draw/eur_chf_volatility.png -------------------------------------------------------------------------------- /price.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | class Price: 4 | 5 | def __init__(self, timestamp, open, low, high, close): 6 | self.timestamp = timestamp; 7 | self.open = open 8 | self.low = low 9 | self.high = high 10 | self.close = close 11 | 12 | def __str__(self): 13 | return " %s - %s %s %s" % (self.timestamp.strftime("%Y-%m-%d"), self.open, self.low, self.high, self.close) 14 | 15 | def contains_price(self, value): 16 | return value <= self.high and value >= self.high 17 | -------------------------------------------------------------------------------- /heatmap.gnuplot: -------------------------------------------------------------------------------- 1 | set terminal png 2 | set output "heatmap.png" 3 | 4 | set title "EUR/USD: PnL since 2001 following daily trend, SL/TP at X+200" 5 | 6 | unset key 7 | set tic scale 0 8 | 9 | #set palette rgbformula -7,2,-7 10 | #set palette color 11 | set palette negative 12 | set palette defined 13 | 14 | #set ticslevel 0 15 | #set cbtics scale 1000 16 | 17 | #show palette gradient 18 | 19 | set cbrange [-6000:6000] 20 | #set cblabel "PnL" 21 | #unset cbtics 22 | 23 | set xrange [0:99.5] 24 | set yrange [0:1118] 25 | 26 | set view map 27 | 28 | splot 'map.txt' matrix with image 29 | -------------------------------------------------------------------------------- /strategy.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | from order import * 4 | from account import * 5 | 6 | class Strategy: 7 | 8 | def __init__(self, sl_tp_delta=200): 9 | self.account = Account() 10 | self.prices = [] 11 | self.sl_tp_delta = sl_tp_delta 12 | 13 | def order_for_price(self, p): 14 | o = None 15 | 16 | if p == None: 17 | return None 18 | 19 | #sl_tp_delta = 200 # pips 20 | sl_tp = self.sl_tp_delta 21 | 22 | previous_price = self.prices[-1] if len(self.prices) > 0 else None 23 | if previous_price == None: 24 | o = Order("BUY", 10000, p.close, sl_tp, sl_tp) # first trade 25 | 26 | self.prices.append(p) 27 | 28 | if o: 29 | o.open(p.timestamp) # market order.. 30 | return o 31 | 32 | if self.account.exposure() >= 10000: 33 | return None 34 | 35 | if p.close >= previous_price.close: 36 | o = Order("SELL", 10000, p.close, sl_tp, sl_tp) 37 | elif p.close <= previous_price.close: 38 | o = Order("BUY", 10000, p.close, sl_tp, sl_tp) 39 | else: 40 | raise Exception 41 | 42 | if o: 43 | o.open(p.timestamp) # market order.. 44 | return o 45 | 46 | return None 47 | -------------------------------------------------------------------------------- /pnl.gnuplot: -------------------------------------------------------------------------------- 1 | set terminal png 2 | 3 | set title "EUR_USD and PnL, spread 1.8 pips" 4 | 5 | set format x "%Y%m%d_%H%M%S" 6 | 7 | set xdata time 8 | set timefmt "%Y%m%d_%H%M%S" 9 | 10 | set grid 11 | 12 | set output "pnl.png" 13 | 14 | set format x "" 15 | 16 | set lmargin 9 17 | set rmargin 2 18 | 19 | set multiplot 20 | set size 1.0, 0.7 21 | set origin 0, 0.3 22 | set bmargin 0 23 | 24 | plot "pnl.txt" using 1:2 notitle with lines 25 | 26 | #set xlabel "Columbia infAP" 27 | #set ylabel "IICT infAP" 28 | #set nokey 29 | #set xtics 2 30 | #set title "Semi-log scaling" 31 | #set yrange [75:105] 32 | #set ytics (105, 100, 95, 90, 85, 80) 33 | #set xrange [50:253] 34 | #set lmargin 9 35 | #set rmargin 2 36 | #set grid 37 | #set logscale y 38 | #set title "Mean of 5 most similar transactions (percent)" 39 | #plot "plot.txt" with lines 40 | #set output "fx.png" 41 | #set title "Mean of 5 most similar transactions (percent)" 42 | #plot "fx_found.txt" using 1:2 with lines 43 | #plot "fx_found.txt" with lines 44 | 45 | 46 | unset label 1 47 | unset label 2 48 | unset title 49 | set bmargin 50 | set format x 51 | set size 1.0, 0.3 52 | set origin 0.0, 0.0 53 | set tmargin 0 54 | 55 | #set xtics ("6/03" 66, "7/03" 87, "8/03" 109, "9/03" 130, "10/03" 151, "11/03" 174, "12/03" 193, "1/04" 215, "2/04" 235) 56 | 57 | #set autoscale y 58 | 59 | plot 'pnl.txt' using 1:3 notitle with impulses lt 3 60 | unset multiplot 61 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Backtest your eForex trading strategies 2 | 3 | #### Goal 4 | 5 | Ever wondered what would have happened if you had always followed the trend? or if you had played against the market? or if you had set a take-profit of twice as much as the stop-loss? 6 | 7 | `efx-backtest` is a minimalist eForex backtest engine. It lets you implement an automatic trading strategy in a few lines of Python and provides you with a profit-and-loss chart as well as simple metrics. 8 | 9 | #### Usage 10 | 11 | Implement the class Strategy. The simulation engine (`market.py`) will call `order_for_price(p)` with each price in the histoy file. For each price, the Strategy instance is expected to return a BUY or SELL order (instance of Order) or `None` if nothing is to be done. See `Strategy.py` for an implementation example. 12 | 13 | The cumulated PnL is written on stdout and can be ploted by gnuplot. Metrics are written on stderr. 14 | 15 | $ python market.py > pnl.txt && gnuplot pnl.gnuplot && open pnl.png 16 | 17 | number of prices : 2949 18 | number of trades : 256 (145 win, 110 lose) 19 | max_exposure : 10000.000000 20 | realized pnl : 9271.000000 21 | unrealized pnl : 205.000000 22 | 23 | ![eForex PnL](charts/pnl.png "eforex pnl chart") 24 | 25 | #### Heatmap 26 | 27 | In `market.py`, `HEATMAP = True` instanciates several strategies with a parameter. It runs the whole history with each strategy instance. It then outputs heatmap data, usable by `heatmap.gnuplot`. 28 | 29 | $ python market.py > heatmap.txt && gnuplot heatmap.gnuplot && open heatmap.png 30 | 31 | In the following heatmap, we can see that following the trend of the previous day on EUR/USD since 2001 and a SL/TP at ± 278 pips would have resulted in a 6000 pips profit after 1000 days (with a max. exposure of 10k USD). 32 | 33 | ![eForex Heatmap](charts/heatmap.png "eforex pnl heatmap") 34 | -------------------------------------------------------------------------------- /order.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import datetime 4 | 5 | DEBUG = False 6 | 7 | SPREAD = 1.8 8 | 9 | class Order: 10 | 11 | def __init__(self, BUY_OR_SELL, amount, price, stop_loss_pips, take_profit_pips): 12 | if BUY_OR_SELL not in ("BUY", "SELL"): 13 | raise AttributeError 14 | 15 | if not amount > 0: 16 | raise AttributeError 17 | 18 | if not price > 0: 19 | raise AttributeError 20 | 21 | sl_tp_modifier = 1 if BUY_OR_SELL == "BUY" else -1 22 | 23 | self.buy_or_sell = BUY_OR_SELL 24 | self.amount = amount 25 | self.price = price 26 | self.stop_loss = price - (stop_loss_pips / 10000.0) * sl_tp_modifier 27 | self.take_profit = price + (take_profit_pips / 10000.0) * sl_tp_modifier 28 | self.status = None # None, "opened", "closed" 29 | self.pips = 0.0 30 | self.timestamp_open = None 31 | self.timestamp_close = None 32 | 33 | def __str__(self): 34 | s = " [%s - %s] (%s) %s %s %s (SL:%s TP:%s)" % (self.timestamp_open, self.timestamp_close, self.status, self.buy_or_sell, self.amount, self.price, self.stop_loss, self.take_profit) 35 | if self.status == "closed": 36 | s += "\t %s" % self.pnl() 37 | return s 38 | 39 | def open(self, timestamp): 40 | self.status = "opened" 41 | self.pips = SPREAD * -1 42 | self.timestamp_open = timestamp + datetime.timedelta(microseconds=1) 43 | 44 | if DEBUG: 45 | print "\t\t\t OPEN", self 46 | 47 | def close(self, timestamp, close_price_value): 48 | self.status = "closed" 49 | 50 | self.timestamp_close = timestamp # in fact it will be shorter be we miss st / tp prices 51 | 52 | if self.buy_or_sell == "BUY": 53 | if close_price_value >= self.take_profit: 54 | self.pips += (self.take_profit - self.price) * 10000 55 | elif close_price_value <= self.stop_loss: 56 | self.pips -= (self.price - self.stop_loss) * 10000 57 | else: 58 | raise Exception 59 | 60 | elif self.buy_or_sell == "SELL": 61 | if close_price_value <= self.take_profit: 62 | self.pips += (self.price - self.take_profit) * 10000 63 | elif close_price_value >= self.stop_loss: 64 | self.pips -= (self.stop_loss - self.price) * 10000 65 | else: 66 | raise Exception 67 | 68 | else: 69 | raise Exception 70 | 71 | if DEBUG: 72 | print "\t\t\t CLOSE", self 73 | 74 | def pnl(self): 75 | return self.pips * (self.amount / 10000) 76 | -------------------------------------------------------------------------------- /market.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # input: price list 4 | # action: send prices to strategies, gets orders 5 | # output: chartable data and stats 6 | 7 | from strategy import * 8 | from price import * 9 | import datetime 10 | import sys 11 | 12 | DEBUG = False 13 | HEATMAP = False 14 | 15 | # http://www.fxhistoricaldata.com/ 16 | def price_from_line(line): 17 | if line.startswith('<'): 18 | return None 19 | 20 | (currency, date_string, time_string, open_string, low_string, high_string, close_string) = line.split(',') 21 | 22 | dt = "%s %s" % (date_string, time_string) 23 | 24 | timestamp = datetime.datetime.strptime(dt, "%Y%m%d %H:%M:%S") 25 | 26 | open = float(open_string) 27 | low = float(low_string) 28 | high = float(high_string) 29 | close = float(close_string) 30 | 31 | return Price(timestamp, open, low, high, close) 32 | 33 | def pretty_timestamp(ts): 34 | return ts.strftime("%Y%m%d_%H%M%S") 35 | 36 | def run_simulation(): 37 | path = "EURUSD_day.csv" 38 | 39 | pnl = 0.0 40 | equity = 0.0 41 | prices_count = 0 42 | max_exposure = 0 43 | current_price = None 44 | 45 | f = open('histories/' + path) 46 | 47 | strategies = [] 48 | 49 | a = None 50 | 51 | for sl_tp_delta in range(200, 300): 52 | s = Strategy(sl_tp_delta) 53 | strategies.append(s) 54 | 55 | if HEATMAP: 56 | pass 57 | else: 58 | sltp = 78 59 | strategies = strategies[sltp:sltp+1] 60 | 61 | for line in f.xreadlines(): 62 | p = price_from_line(line) 63 | if not p: 64 | continue 65 | prices_count += 1 66 | 67 | if DEBUG: 68 | print p 69 | 70 | for s in strategies: 71 | 72 | a = s.account 73 | 74 | a.execute_orders_with_price_change(p) 75 | 76 | o = s.order_for_price(p) 77 | if o: 78 | a.add_order(o) 79 | #print o 80 | 81 | max_exposure = max(a.exposure(), max_exposure) 82 | 83 | if HEATMAP: 84 | print a.pnl_realized() + a.pnl_unrealized_with_price(p), # heatmap 85 | else: 86 | print "%s\t%s\t%s" % (pretty_timestamp(p.timestamp), p.close, a.pnl_realized() + a.pnl_unrealized_with_price(p)) 87 | 88 | if HEATMAP: 89 | print "" 90 | 91 | if not HEATMAP: 92 | a = strategies[0].account 93 | 94 | sys.stderr.write("number of prices : %d\n" % prices_count) 95 | sys.stderr.write("number of trades : %s (%d win, %d lose)\n" % (a.trades_count(), a.win_count, a.lose_count)) 96 | sys.stderr.write("max_exposure : %f\n" % max_exposure) 97 | sys.stderr.write("realized pnl : %f\n" % a.pnl_realized()) 98 | sys.stderr.write("unrealized pnl : %f\n" % a.pnl_unrealized_with_price(p)) 99 | 100 | if __name__=='__main__': 101 | run_simulation() 102 | -------------------------------------------------------------------------------- /account.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | class Account: 4 | 5 | def __init__(self): 6 | self.orders = [] 7 | self.win_count = 0 8 | self.lose_count = 0 9 | 10 | def add_order(self, o): 11 | self.orders.append(o) 12 | 13 | def execute_orders_with_price_change(self, p): 14 | 15 | for o in self.orders: 16 | 17 | if o.status == "closed": 18 | continue 19 | 20 | elif o.status is None: 21 | if o.price >= p.low and o.price <= p.high: 22 | o.open(p.timestamp) 23 | 24 | elif o.status == "opened": 25 | 26 | if p.contains_price(o.stop_loss): 27 | o.close(p.timestamp, o.stop_loss) 28 | self.lose_count += 1 29 | elif p.contains_price(o.take_profit): 30 | o.close(p.timestamp, o.take_profit) 31 | self.win_count += 1 32 | 33 | elif o.buy_or_sell == "BUY": 34 | if p.low <= o.stop_loss: 35 | o.close(p.timestamp, o.stop_loss) 36 | self.lose_count += 1 37 | elif p.high >= o.take_profit: 38 | o.close(p.timestamp, o.take_profit) 39 | self.win_count += 1 40 | 41 | elif o.buy_or_sell == "SELL": 42 | if p.high >= o.stop_loss: 43 | o.close(p.timestamp, o.stop_loss) 44 | self.lose_count += 1 45 | elif p.low <= o.take_profit: 46 | o.close(p.timestamp, o.take_profit) 47 | self.win_count += 1 48 | 49 | def exposure(self): 50 | opened_orders = filter(lambda o:o.status == "opened", self.orders) 51 | return sum(map(lambda o:o.amount, opened_orders)) 52 | 53 | def pnl_realized(self): 54 | n = 0.0 55 | for o in filter(lambda o:o.status == "closed", self.orders): 56 | n += o.pnl() 57 | return n 58 | 59 | def pnl_unrealized_with_price(self, price): 60 | n = 0.0 61 | 62 | for o in self.opened_orders(): 63 | if o.buy_or_sell == "BUY": 64 | n += o.price - price.close 65 | elif o.buy_or_sell == "SELL": 66 | n += price.close - o.price 67 | else: 68 | print "--", o.buy_or_sell 69 | raise Exception 70 | 71 | return n * 10000 72 | 73 | def trades_count(self): 74 | return len(filter(lambda o:o.status != None, self.orders)) 75 | 76 | def opened_orders(self): 77 | return filter(lambda o:o.status == "opened", self.orders) 78 | 79 | def closed_orders(self): 80 | return filter(lambda o:o.status == "closed", self.orders) 81 | 82 | def opened_or_closed_orders(self): 83 | return filter(lambda o:o.status == "opened" or o.status == "closed", self.orders) 84 | 85 | def latest_closed_order(self): 86 | l = self.closed_orders() 87 | return l[-1] if len(l) > 0 else None 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | 3 | from strategy import * 4 | from price import * 5 | from order import * 6 | from account import * 7 | 8 | import datetime 9 | 10 | t = datetime.datetime.now() 11 | 12 | def to_date(str): 13 | return datetime.datetime.strptime(str, "%Y-%m-%d") 14 | 15 | def pips_to_pnl(pips, amount, pips_precision=4): 16 | return (1.0 / pow(10, pips_precision)) * pips * amount 17 | 18 | class Testpnl(unittest.TestCase): 19 | 20 | amount = 10000 21 | 22 | def setUp(self): 23 | pass 24 | 25 | def test_pnl(self): 26 | o = Order("BUY", self.amount, 1.2, 10, 10) 27 | self.assertTrue(o.status == None) 28 | 29 | o.open(t) 30 | self.assertEqual(o.status, 'opened') 31 | 32 | o.close(t, 1.2010) # 10 pips 33 | self.assertEqual(o.status, 'closed') 34 | 35 | actual = o.pnl() 36 | self.assertAlmostEqual(10 - SPREAD, actual) 37 | 38 | raw_pnl = pips_to_pnl(10, self.amount) 39 | spread_amount = pips_to_pnl(SPREAD, self.amount) 40 | net_pnl = raw_pnl - spread_amount 41 | self.assertAlmostEqual(actual, net_pnl) 42 | 43 | def test_buy_sl(self): 44 | o = Order("BUY", self.amount, 1.2, 15, 20) 45 | o.open(t) 46 | o.close(t, 1.0) # -15 pips 47 | actual = o.pnl() 48 | self.assertAlmostEqual(-15 - SPREAD, actual) 49 | 50 | def test_buy_tp(self): 51 | o = Order("BUY", self.amount, 1.2, 15, 20) 52 | o.open(t) 53 | o.close(t, 2.0) # +20 pips 54 | actual = o.pnl() 55 | self.assertAlmostEqual(+20 - SPREAD, actual) 56 | 57 | def test_sell_sl(self): 58 | o = Order("SELL", self.amount, 1.2, 15, 20) 59 | o.open(t) 60 | o.close(t, 2.0) # -15 pips 61 | actual = o.pnl() 62 | self.assertAlmostEqual(-15 - SPREAD, actual) 63 | 64 | def test_sell_tp(self): 65 | o = Order("SELL", self.amount, 1.2, 15, 20) 66 | o.open(t) 67 | o.close(t, 1.0) # +20 pips 68 | actual = o.pnl() 69 | self.assertAlmostEqual(20 - SPREAD, actual) 70 | 71 | def test_open_order_no_change(self): 72 | a = Account() 73 | 74 | ts = datetime.date(2011, 1, 29) 75 | p = Price(ts, 10, 8, 13, 10) 76 | 77 | o = Order("BUY", 10000, 7.5, 0.1, 0.1) 78 | a.add_order(o) 79 | 80 | a.execute_orders_with_price_change(p) 81 | 82 | self.assertEqual(o.status, None) 83 | 84 | def test_order_opening(self): 85 | a = Account() 86 | 87 | ts = datetime.date(2011, 1, 29) 88 | p = Price(ts, 10, 8, 13, 10) 89 | 90 | o = Order("BUY", 10000, 9, 0.1, 0.1) 91 | a.add_order(o) 92 | 93 | a.execute_orders_with_price_change(p) 94 | 95 | self.assertEqual(o.status, "opened") 96 | 97 | def test_order_close_loss(self): 98 | a = Account() 99 | 100 | ts = datetime.date(2011, 1, 29) 101 | p = Price(ts, 10, 8, 13, 10) 102 | 103 | o = Order("BUY", 10000, 9, 1, 1) 104 | o.open(t) 105 | a.add_order(o) 106 | 107 | a.execute_orders_with_price_change(p) 108 | 109 | self.assertEqual(o.status, "closed") 110 | 111 | self.assertTrue(o.pnl() < 0) 112 | 113 | def test_order_close_win(self): 114 | a = Account() 115 | 116 | ts = datetime.date(2011, 1, 29) 117 | p = Price(ts, 10, 8, 15, 10) 118 | 119 | o = Order("BUY", 10000, 9, 100000, 5000) 120 | o.open(t) 121 | a.add_order(o) 122 | 123 | a.execute_orders_with_price_change(p) 124 | 125 | self.assertEqual(o.status, "closed") 126 | 127 | self.assertTrue(o.pnl() > 0) 128 | 129 | if __name__ == '__main__': 130 | unittest.main() 131 | -------------------------------------------------------------------------------- /fx_draw/fx_viz.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | import datetime 4 | import sys 5 | from datetime import date 6 | import Image,ImageDraw 7 | import math 8 | 9 | class Price: 10 | 11 | def __init__(self, timestamp, open, low, high, close): 12 | self.timestamp = timestamp; 13 | self.open = open 14 | self.low = low 15 | self.high = high 16 | self.close = close 17 | 18 | def __str__(self): 19 | return " %s - %f %f %f %f" % (self.timestamp.strftime("%Y-%m-%d"), self.open, self.low, self.high, self.close) 20 | 21 | def var_delta(self, p0): 22 | 23 | if not p0: 24 | return 0.0 25 | 26 | return 1.0 - self.open / p0.open 27 | 28 | def var_volatility(self, p0): 29 | if not p0: 30 | return 0.0 31 | 32 | return self.high - self.low 33 | 34 | def var_min_pips(self, p0, pips): 35 | if not p0: 36 | return 0.0 37 | 38 | var = self.open - p0.open 39 | if abs(var) > (pips / 1000.0): 40 | return var 41 | else: 42 | return 0.0 43 | 44 | def color_since(self, p0): 45 | var = self.var_delta(p0) * 2 46 | #var = self.var_volatility(p0) 47 | #var = self.var_min_pips(p0, 4) 48 | 49 | var_norm = abs(var) 50 | 51 | intensity = int(var_norm * 50000) 52 | 53 | if intensity > 255: 54 | intensity = 255 55 | 56 | color = ("white") 57 | 58 | if var > 0.0: 59 | color = (0,intensity,0) 60 | else: 61 | color = (intensity,0,0) 62 | 63 | return color 64 | 65 | # http://www.fxhistoricaldata.com/ 66 | def price_from_line(line): 67 | if line.startswith('<'): 68 | return None 69 | 70 | (currency, date_string, time_string, open_string, low_string, high_string, close_string) = line.split(',') 71 | 72 | dt = "%s %s" % (date_string, time_string) 73 | 74 | timestamp = datetime.datetime.strptime(dt, "%Y%m%d %H:%M:%S") 75 | 76 | open = float(open_string) 77 | low = float(low_string) 78 | high = float(high_string) 79 | close = float(close_string) 80 | 81 | return Price(timestamp, open, low, high, close) 82 | 83 | def pretty_timestamp(ts): 84 | return ts.strftime("%Y%m%d_%H%M%S") 85 | 86 | path = "EURCHF_hour.csv" 87 | prices = [] 88 | 89 | f = open(path) 90 | 91 | for line in f.xreadlines(): 92 | p = price_from_line(line) 93 | if p: 94 | prices.append(p) 95 | 96 | #print len(prices) 97 | 98 | #datetime.date(2010, 6, 16) 99 | 100 | current_week_number = None 101 | 102 | weeks = [] 103 | 104 | for p in prices: 105 | week_number = p.timestamp.isocalendar()[1] 106 | 107 | if len(weeks) == 0 or week_number != current_week_number: 108 | weeks.append([]) 109 | current_week_number = week_number 110 | 111 | weeks[-1].append(p) 112 | 113 | nb_weeks = len(weeks) 114 | 115 | print "-- nb_weeks:", len(weeks) 116 | 117 | # draw 118 | 119 | img = Image.new("RGB", (24 * 5 - 1, nb_weeks), "black") 120 | draw = ImageDraw.Draw(img) 121 | 122 | p0 = None 123 | 124 | #var_min = 100 125 | #var_max = -100 126 | 127 | #vars_per_weekdayhour = [] 128 | #for i in range(24 * 5 - 1): 129 | # vars_per_weekdayhour.append([]) 130 | 131 | for i in range(len(weeks)): 132 | prices = weeks[i] 133 | 134 | for p in prices: 135 | color = p.color_since(p0) 136 | 137 | #var = p.var_volatility(p0) 138 | 139 | p0 = p 140 | 141 | day_index = p.timestamp.weekday() 142 | week_number = p.timestamp.isocalendar()[1] 143 | year = p.timestamp.year 144 | # print year, week_number, day_index 145 | 146 | #print vars_per_weekday[day_index] 147 | 148 | x = day_index * 24 + p.timestamp.hour 149 | 150 | #vars_per_weekdayhour[x].append(var) 151 | 152 | draw.point((x,i), fill=color) 153 | 154 | img.save("eur_chf_weekly.png", "PNG") 155 | 156 | #for i in range(len(vars_per_weekdayhour)): 157 | # vars = vars_per_weekdayhour[i] 158 | # 159 | # if len(vars) == 0: 160 | # print "-- error, len(vars) == 0" 161 | # continue 162 | # print "%d \t %d \t %.5f" % (i / 24, i % 24, 1000 * sum(vars) / len(vars)) 163 | 164 | 165 | #print var_min * 10000 166 | #print var_max * 10000 167 | -------------------------------------------------------------------------------- /histories/EURUSD_day.csv: -------------------------------------------------------------------------------- 1 | ,,