Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

pep8 police :) #30

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions backtest/backtest.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@


class Backtest(object):

"""
Enscapsulates the settings and components for carrying out
an event-driven backtest on the foreign exchange markets.
"""

def __init__(
self, pairs, data_handler, strategy,
strategy_params, portfolio, execution,
equity=100000.0, heartbeat=0.0,
self, pairs, data_handler, strategy,
strategy_params, portfolio, execution,
equity=100000.0, heartbeat=0.0,
max_iters=10000000000
):
"""
Expand Down
5 changes: 3 additions & 2 deletions backtest/output.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import os, os.path
import os
import os.path

import pandas as pd
import matplotlib
Expand Down Expand Up @@ -31,7 +32,7 @@
# Plot three charts: Equity curve, period returns, drawdowns
fig = plt.figure()
fig.patch.set_facecolor('white') # Set the outer colour to white

# Plot the equity curve
ax1 = fig.add_subplot(311, ylabel='Portfolio value')
equity["Equity"].plot(ax=ax1, color=sns.color_palette()[0])
Expand Down
22 changes: 12 additions & 10 deletions data/price.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@


class PriceHandler(object):

"""
PriceHandler is an abstract base class providing an interface for
all subsequent (inherited) data handlers (both live and historic).
Expand Down Expand Up @@ -42,14 +43,14 @@ def _set_up_prices_dict(self):
be more robust and straightforward to follow.
"""
prices_dict = dict(
(k, v) for k,v in [
(k, v) for k, v in [
(p, {"bid": None, "ask": None, "time": None}) for p in self.pairs
]
)
inv_prices_dict = dict(
(k, v) for k,v in [
(k, v) for k, v in [
(
"%s%s" % (p[3:], p[:3]),
"%s%s" % (p[3:], p[:3]),
{"bid": None, "ask": None, "time": None}
) for p in self.pairs
]
Expand All @@ -65,16 +66,17 @@ def invert_prices(self, pair, bid, ask):
"""
getcontext().rounding = ROUND_HALF_DOWN
inv_pair = "%s%s" % (pair[3:], pair[:3])
inv_bid = (Decimal("1.0")/bid).quantize(
inv_bid = (Decimal("1.0") / bid).quantize(
Decimal("0.00001")
)
inv_ask = (Decimal("1.0")/ask).quantize(
inv_ask = (Decimal("1.0") / ask).quantize(
Decimal("0.00001")
)
return inv_pair, inv_bid, inv_ask


class HistoricCSVPriceHandler(PriceHandler):

"""
HistoricCSVPriceHandler is designed to read CSV files of
tick data for each requested currency pair and stream those
Expand Down Expand Up @@ -129,7 +131,7 @@ def _open_convert_csv_files_for_day(self, date_str):
"""
Opens the CSV files from the data directory, converting
them into pandas DataFrames within a pairs dictionary.

The function then concatenates all of the separate pairs
for a single day into a single data frame that is time
ordered, allowing tick data events to be added to the queue
Expand All @@ -138,7 +140,7 @@ def _open_convert_csv_files_for_day(self, date_str):
for p in self.pairs:
pair_path = os.path.join(self.csv_dir, '%s_%s.csv' % (p, date_str))
self.pair_frames[p] = pd.io.parsers.read_csv(
pair_path, header=True, index_col=0,
pair_path, header=True, index_col=0,
parse_dates=True, dayfirst=True,
names=("Time", "Ask", "Bid", "AskVolume", "BidVolume")
)
Expand All @@ -147,7 +149,7 @@ def _open_convert_csv_files_for_day(self, date_str):

def _update_csv_for_day(self):
try:
dt = self.file_dates[self.cur_date_idx+1]
dt = self.file_dates[self.cur_date_idx + 1]
except IndexError: # End of file dates
return False
else:
Expand All @@ -173,10 +175,10 @@ def stream_next_tick(self):
# End of the current days data
if self._update_csv_for_day():
index, row = next(self.cur_date_pairs)
else: # End of the data
else: # End of the data
self.continue_backtest = False
return

getcontext().rounding = ROUND_HALF_DOWN
pair = row["Pair"]
bid = Decimal(str(row["Bid"])).quantize(
Expand Down
19 changes: 11 additions & 8 deletions data/streaming.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@


class StreamingForexPrices(PriceHandler):

def __init__(
self, domain, access_token,
self, domain, access_token,
account_id, pairs, events_queue
):
self.domain = domain
Expand All @@ -31,10 +32,10 @@ def invert_prices(self, pair, bid, ask):
"""
getcontext().rounding = ROUND_HALF_DOWN
inv_pair = "%s%s" % (pair[3:], pair[:3])
inv_bid = (Decimal("1.0")/bid).quantize(
inv_bid = (Decimal("1.0") / bid).quantize(
Decimal("0.00001")
)
inv_ask = (Decimal("1.0")/ask).quantize(
inv_ask = (Decimal("1.0") / ask).quantize(
Decimal("0.00001")
)
return inv_pair, inv_bid, inv_ask
Expand All @@ -46,8 +47,8 @@ def connect_to_stream(self):
requests.packages.urllib3.disable_warnings()
s = requests.Session()
url = "https://" + self.domain + "/v1/prices"
headers = {'Authorization' : 'Bearer ' + self.access_token}
params = {'instruments' : pair_list, 'accountId' : self.account_id}
headers = {'Authorization': 'Bearer ' + self.access_token}
params = {'instruments': pair_list, 'accountId': self.account_id}
req = requests.Request('GET', url, headers=headers, params=params)
pre = req.prepare()
resp = s.send(pre, stream=True, verify=False)
Expand All @@ -67,12 +68,13 @@ def stream_to_queue(self):
msg = json.loads(dline)
except Exception as e:
self.logger.error(
"Caught exception when converting message into json: %s" % str(e)
"Caught exception when converting message into json: %s" % str(
e)
)
return
if "instrument" in msg or "tick" in msg:
self.logger.debug(msg)
getcontext().rounding = ROUND_HALF_DOWN
getcontext().rounding = ROUND_HALF_DOWN
instrument = msg["tick"]["instrument"].replace("_", "")
time = msg["tick"]["time"]
bid = Decimal(str(msg["tick"]["bid"])).quantize(
Expand All @@ -84,7 +86,8 @@ def stream_to_queue(self):
self.prices[instrument]["bid"] = bid
self.prices[instrument]["ask"] = ask
# Invert the prices (GBP_USD -> USD_GBP)
inv_pair, inv_bid, inv_ask = self.invert_prices(instrument, bid, ask)
inv_pair, inv_bid, inv_ask = self.invert_prices(
instrument, bid, ask)
self.prices[inv_pair]["bid"] = inv_bid
self.prices[inv_pair]["ask"] = inv_ask
self.prices[inv_pair]["time"] = time
Expand Down
9 changes: 6 additions & 3 deletions event/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ class Event(object):


class TickEvent(Event):

def __init__(self, instrument, time, bid, ask):
self.type = 'TICK'
self.instrument = instrument
Expand All @@ -12,7 +13,7 @@ def __init__(self, instrument, time, bid, ask):

def __str__(self):
return "Type: %s, Instrument: %s, Time: %s, Bid: %s, Ask: %s" % (
str(self.type), str(self.instrument),
str(self.type), str(self.instrument),
str(self.time), str(self.bid), str(self.ask)
)

Expand All @@ -21,6 +22,7 @@ def __repr__(self):


class SignalEvent(Event):

def __init__(self, instrument, order_type, side, time):
self.type = 'SIGNAL'
self.instrument = instrument
Expand All @@ -30,7 +32,7 @@ def __init__(self, instrument, order_type, side, time):

def __str__(self):
return "Type: %s, Instrument: %s, Order Type: %s, Side: %s" % (
str(self.type), str(self.instrument),
str(self.type), str(self.instrument),
str(self.order_type), str(self.side)
)

Expand All @@ -39,6 +41,7 @@ def __repr__(self):


class OrderEvent(Event):

def __init__(self, instrument, units, order_type, side):
self.type = 'ORDER'
self.instrument = instrument
Expand All @@ -53,4 +56,4 @@ def __str__(self):
)

def __repr__(self):
return str(self)
return str(self)
14 changes: 7 additions & 7 deletions examples/mac.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@
if __name__ == "__main__":
# Trade on GBP/USD and EUR/USD
pairs = ["GBPUSD", "EURUSD"]

# Create the strategy parameters for the
# MovingAverageCrossStrategy
strategy_params = {
"short_window": 500,
"short_window": 500,
"long_window": 2000
}

# Create and execute the backtest
backtest = Backtest(
pairs, HistoricCSVPriceHandler,
MovingAverageCrossStrategy, strategy_params,
Portfolio, SimulatedExecution,
pairs, HistoricCSVPriceHandler,
MovingAverageCrossStrategy, strategy_params,
Portfolio, SimulatedExecution,
equity=settings.EQUITY
)
backtest.simulate_trading()
backtest.simulate_trading()
20 changes: 12 additions & 8 deletions execution/execution.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@


class ExecutionHandler(object):

"""
Provides an abstract base class to handle all execution in the
backtesting and live trading system.
Expand All @@ -31,18 +32,21 @@ def execute_order(self):


class SimulatedExecution(object):

"""
Provides a simulated execution handling environment. This class
actually does nothing - it simply receives an order to execute.

Instead, the Portfolio object actually provides fill handling.
This will be modified in later versions.
"""

def execute_order(self, event):
pass


class OANDAExecutionHandler(ExecutionHandler):

def __init__(self, domain, access_token, account_id):
self.domain = domain
self.access_token = access_token
Expand All @@ -60,16 +64,16 @@ def execute_order(self, event):
"Authorization": "Bearer " + self.access_token
}
params = urlencode({
"instrument" : instrument,
"units" : event.units,
"type" : event.order_type,
"side" : event.side
"instrument": instrument,
"units": event.units,
"type": event.order_type,
"side": event.side
})
self.conn.request(
"POST",
"/v1/accounts/%s/orders" % str(self.account_id),
"POST",
"/v1/accounts/%s/orders" % str(self.account_id),
params, headers
)
response = self.conn.getresponse().read().decode("utf-8").replace("\n","").replace("\t","")
response = self.conn.getresponse().read().decode(
"utf-8").replace("\n", "").replace("\t", "")
self.logger.debug(response)

12 changes: 6 additions & 6 deletions performance/performance.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,18 @@ def create_drawdowns(pnl):
drawdown, duration - Highest peak-to-trough drawdown and duration.
"""

# Calculate the cumulative returns curve
# Calculate the cumulative returns curve
# and set up the High Water Mark
hwm = [0]

# Create the drawdown and duration series
idx = pnl.index
drawdown = pd.Series(index = idx)
duration = pd.Series(index = idx)
drawdown = pd.Series(index=idx)
duration = pd.Series(index=idx)

# Loop over the index range
for t in range(1, len(idx)):
hwm.append(max(hwm[t-1], pnl.ix[t]))
drawdown.ix[t]= (hwm[t]-pnl.ix[t])
duration.ix[t]= (0 if drawdown.ix[t] == 0 else duration.ix[t-1]+1)
hwm.append(max(hwm[t - 1], pnl.ix[t]))
drawdown.ix[t] = (hwm[t] - pnl.ix[t])
duration.ix[t] = (0 if drawdown.ix[t] == 0 else duration.ix[t - 1] + 1)
return drawdown, drawdown.max(), duration.max()
Loading