Lots of changes. Modified the Position object to handle more of the actual position calculations instead of the Portfolio. Added more unit tests for both Position and Portfolio. Allowed Positions to trade in currencies other than GBPUSD and in base/quotes which aren't the home currency. Modified the backtester to be single-threaded and added a basic Moving Average Crossover strategy. Also added a basic equity curve output script.

This commit is contained in:
Michael Halls-Moore
2015-04-21 13:01:20 +01:00
parent e74777802b
commit e84512e1e7
11 changed files with 700 additions and 376 deletions
+162 -28
View File
@@ -4,18 +4,39 @@ import unittest
from position import Position
class TickerMock(object):
"""
A mock object that allows a representation of the
ticker/pricing handler.
"""
def __init__(self):
self.prices = {
"GBPUSD": {"bid": Decimal("1.50328"), "ask": Decimal("1.50349")},
"USDGBP": {"bid": Decimal("0.66521"), "ask": Decimal("0.66512")},
"EURUSD": {"bid": Decimal("1.07832"), "ask": Decimal("1.07847")}
}
# =====================================
# GBP Home Currency with GBP/USD traded
# =====================================
class TestLongGBPUSDPosition(unittest.TestCase):
"""
Unit tests that cover going long GBP/USD with an account
denominated currency of GBP, using 2,000 units of GBP/USD.
"""
def setUp(self):
getcontext.prec = 2
home_currency = "GBP"
position_type = "long"
market = "GBP/USD"
currency_pair = "GBPUSD"
units = Decimal("2000")
exposure = Decimal("2000.00")
bid = Decimal("1.50328")
ask = Decimal("1.50349")
ticker = TickerMock()
self.position = Position(
position_type, market,
units, exposure, bid, ask
home_currency, position_type,
currency_pair, units, ticker
)
def test_calculate_init_pips(self):
@@ -23,11 +44,11 @@ class TestLongGBPUSDPosition(unittest.TestCase):
self.assertEqual(pos_pips, Decimal("-0.00021"))
def test_calculate_init_profit_base(self):
profit_base = self.position.calculate_profit_base(self.position.exposure)
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("-0.27939"))
def test_calculate_init_profit_perc(self):
profit_perc = self.position.calculate_profit_perc(self.position.exposure)
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("-0.01397"))
def test_calculate_updated_values(self):
@@ -35,32 +56,37 @@ class TestLongGBPUSDPosition(unittest.TestCase):
Check that after the bid/ask prices move, that the updated
pips, profit and percentage profit calculations are correct.
"""
bid = Decimal("1.50486")
ask = Decimal("1.50586")
self.position.update_position_price(bid, ask, self.position.exposure)
prices = self.position.ticker.prices
prices["GBPUSD"] = {"bid": Decimal("1.50486"), "ask": Decimal("1.50586")}
prices["USDGBP"] = {"bid": Decimal("0.66451"), "ask": Decimal("0.66407")}
self.position.update_position_price()
# Check pips
pos_pips = self.position.calculate_pips()
self.assertEqual(pos_pips, Decimal("0.00137"))
# Check profit base
profit_base = self.position.calculate_profit_base(self.position.exposure)
self.assertEqual(profit_base, Decimal("1.82077"))
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("1.82076"))
# Check profit percentage
profit_perc = self.position.calculate_profit_perc(self.position.exposure)
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("0.09104"))
class TestShortGBPUSDPosition(unittest.TestCase):
"""
Unit tests that cover going short GBP/USD with an account
denominated currency of GBP, using 2,000 units of GBP/USD.
"""
def setUp(self):
getcontext.prec = 2
home_currency = "GBP"
position_type = "short"
market = "GBP/USD"
currency_pair = "GBPUSD"
units = Decimal("2000")
exposure = Decimal("2000.00")
bid = Decimal("1.50328")
ask = Decimal("1.50349")
ticker = TickerMock()
self.position = Position(
position_type, market,
units, exposure, bid, ask
home_currency, position_type,
currency_pair, units, ticker
)
def test_calculate_init_pips(self):
@@ -68,11 +94,11 @@ class TestShortGBPUSDPosition(unittest.TestCase):
self.assertEqual(pos_pips, Decimal("-0.00021"))
def test_calculate_init_profit_base(self):
profit_base = self.position.calculate_profit_base(self.position.exposure)
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("-0.27935"))
def test_calculate_init_profit_perc(self):
profit_perc = self.position.calculate_profit_perc(self.position.exposure)
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("-0.01397"))
def test_calculate_updated_values(self):
@@ -80,19 +106,127 @@ class TestShortGBPUSDPosition(unittest.TestCase):
Check that after the bid/ask prices move, that the updated
pips, profit and percentage profit calculations are correct.
"""
bid = Decimal("1.50486")
ask = Decimal("1.50586")
self.position.update_position_price(bid, ask, self.position.exposure)
prices = self.position.ticker.prices
prices["GBPUSD"] = {"bid": Decimal("1.50486"), "ask": Decimal("1.50586")}
prices["USDGBP"] = {"bid": Decimal("0.66451"), "ask": Decimal("0.66407")}
self.position.update_position_price()
# Check pips
pos_pips = self.position.calculate_pips()
self.assertEqual(pos_pips, Decimal("-0.00258"))
# Check profit base
profit_base = self.position.calculate_profit_base(self.position.exposure)
self.assertEqual(profit_base, Decimal("-3.42661"))
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("-3.42660"))
# Check profit percentage
profit_perc = self.position.calculate_profit_perc(self.position.exposure)
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("-0.17133"))
# =====================================
# GBP Home Currency with EUR/USD traded
# =====================================
class TestLongEURUSDPosition(unittest.TestCase):
"""
Unit tests that cover going long EUR/USD with an account
denominated currency of GBP, using 2,000 units of EUR/USD.
"""
def setUp(self):
getcontext.prec = 2
home_currency = "GBP"
position_type = "long"
currency_pair = "EURUSD"
units = Decimal("2000")
ticker = TickerMock()
self.position = Position(
home_currency, position_type,
currency_pair, units, ticker
)
def test_calculate_init_pips(self):
pos_pips = self.position.calculate_pips()
self.assertEqual(pos_pips, Decimal("-0.00015"))
def test_calculate_init_profit_base(self):
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("-0.19956"))
def test_calculate_init_profit_perc(self):
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("-0.00998"))
def test_calculate_updated_values(self):
"""
Check that after the bid/ask prices move, that the updated
pips, profit and percentage profit calculations are correct.
"""
prices = self.position.ticker.prices
prices["GBPUSD"] = {"bid": Decimal("1.50486"), "ask": Decimal("1.50586")}
prices["USDGBP"] = {"bid": Decimal("0.66451"), "ask": Decimal("0.66407")}
prices["EURUSD"] = {"bid": Decimal("1.07811"), "ask": Decimal("1.07827")}
self.position.update_position_price()
# Check pips
pos_pips = self.position.calculate_pips()
self.assertEqual(pos_pips, Decimal("-0.00036"))
# Check profit base
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("-0.47845"))
# Check profit percentage
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("-0.02392"))
class TestLongEURUSDPosition(unittest.TestCase):
"""
Unit tests that cover going short EUR/USD with an account
denominated currency of GBP, using 2,000 units of EUR/USD.
"""
def setUp(self):
getcontext.prec = 2
home_currency = "GBP"
position_type = "short"
currency_pair = "EURUSD"
units = Decimal("2000")
ticker = TickerMock()
self.position = Position(
home_currency, position_type,
currency_pair, units, ticker
)
def test_calculate_init_pips(self):
pos_pips = self.position.calculate_pips()
self.assertEqual(pos_pips, Decimal("-0.00015"))
def test_calculate_init_profit_base(self):
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("-0.19954"))
def test_calculate_init_profit_perc(self):
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("-0.00998"))
def test_calculate_updated_values(self):
"""
Check that after the bid/ask prices move, that the updated
pips, profit and percentage profit calculations are correct.
"""
prices = self.position.ticker.prices
prices["GBPUSD"] = {"bid": Decimal("1.50486"), "ask": Decimal("1.50586")}
prices["USDGBP"] = {"bid": Decimal("0.66451"), "ask": Decimal("0.66407")}
prices["EURUSD"] = {"bid": Decimal("1.07811"), "ask": Decimal("1.07827")}
self.position.update_position_price()
# Check pips
pos_pips = self.position.calculate_pips()
self.assertEqual(pos_pips, Decimal("0.00005"))
# Check profit base
profit_base = self.position.calculate_profit_base()
self.assertEqual(profit_base, Decimal("0.06641"))
# Check profit percentage
profit_perc = self.position.calculate_profit_perc()
self.assertEqual(profit_perc, Decimal("0.00332"))
if __name__ == "__main__":
unittest.main()