Added the ability for the backtester to use unrealised PnL from the Position objects to calculate a tick-by-tick equity curve. Added a performance directory that calculates drawdown statistics. Modified the output.py script to use Seaborn and output the equity curve, returns and drawdown curve.

This commit is contained in:
Michael Halls-Moore
2015-05-15 13:50:34 +01:00
parent a03bc7a1fb
commit 4380200de7
8 changed files with 137 additions and 28 deletions
+58 -13
View File
@@ -7,6 +7,7 @@ import os
import pandas as pd
from qsforex.event.event import OrderEvent
from qsforex.performance.performance import create_drawdowns
from qsforex.portfolio.position import Position
from qsforex.settings import OUTPUT_RESULTS_DIR
@@ -14,7 +15,8 @@ from qsforex.settings import OUTPUT_RESULTS_DIR
class Portfolio(object):
def __init__(
self, ticker, events, home_currency="GBP", leverage=20,
equity=Decimal("100000.00"), risk_per_trade=Decimal("0.02")
equity=Decimal("100000.00"), risk_per_trade=Decimal("0.02"),
backtest=True
):
self.ticker = ticker
self.events = events
@@ -23,9 +25,10 @@ class Portfolio(object):
self.equity = equity
self.balance = deepcopy(self.equity)
self.risk_per_trade = risk_per_trade
self.backtest = backtest
self.trade_units = self.calc_risk_position_size()
self.positions = {}
self.equity = []
self.backtest_file = self.create_equity_file()
def calc_risk_position_size(self):
return self.equity * self.risk_per_trade
@@ -66,16 +69,61 @@ class Portfolio(object):
del[self.positions[currency_pair]]
return True
def append_equity_row(self, time, balance):
d = {"time": time, "balance": balance}
self.equity.append(d)
def create_equity_file(self):
filename = "backtest.csv"
out_file = open(os.path.join(OUTPUT_RESULTS_DIR, filename), "w")
header = "Timestamp,Balance"
for pair in self.ticker.pairs:
header += ",%s" % pair
header += "\n"
out_file.write(header)
if self.backtest:
print(header[:-2])
return out_file
def output_results(self):
filename = "equity.csv"
out_file = os.path.join(OUTPUT_RESULTS_DIR, filename)
df_equity = pd.DataFrame.from_records(self.equity, index='time')
df_equity.to_csv(out_file)
print("Simulation complete and results exported to %s" % filename)
# Closes off the Backtest.csv file so it can be
# read via Pandas without problems
self.backtest_file.close()
in_filename = "backtest.csv"
out_filename = "equity.csv"
in_file = os.path.join(OUTPUT_RESULTS_DIR, in_filename)
out_file = os.path.join(OUTPUT_RESULTS_DIR, out_filename)
# Create equity curve dataframe
df = pd.read_csv(in_file, index_col=0)
df.dropna(inplace=True)
df["Total"] = df.sum(axis=1)
df["Returns"] = df["Total"].pct_change()
df["Equity"] = (1.0+df["Returns"]).cumprod()
# Create drawdown statistics
drawdown, max_dd, dd_duration = create_drawdowns(df["Equity"])
df["Drawdown"] = drawdown
df.to_csv(out_file, index=True)
print("Simulation complete and results exported to %s" % out_filename)
def update_portfolio(self, tick_event):
"""
This updates all positions ensuring an up to date
unrealised profit and loss (PnL).
"""
currency_pair = tick_event.instrument
if currency_pair in self.positions:
ps = self.positions[currency_pair]
ps.update_position_price()
out_line = "%s,%s" % (tick_event.time, self.balance)
for pair in self.ticker.pairs:
if pair in self.positions:
out_line += ",%s" % self.positions[currency_pair].profit_base
else:
out_line += ",0.00"
out_line += "\n"
if self.backtest:
print(out_line[:-2])
self.backtest_file.write(out_line)
def execute_signal(self, signal_event):
side = signal_event.side
@@ -124,7 +172,4 @@ class Portfolio(object):
order = OrderEvent(currency_pair, units, "market", side)
self.events.put(order)
print("Balance: %0.2f" % self.balance)
self.append_equity_row(time, self.balance)
+2
View File
@@ -11,6 +11,7 @@ class TickerMock(object):
"""
def __init__(self):
self.pairs = ["GBPUSD", "EURUSD"]
self.prices = {
"GBPUSD": {"bid": Decimal("1.50328"), "ask": Decimal("1.50349")},
"USDGBP": {"bid": Decimal("0.66521"), "ask": Decimal("0.66512")},
@@ -18,6 +19,7 @@ class TickerMock(object):
}
# =====================================
# GBP Home Currency with GBP/USD traded
# =====================================