Added some more unit tests for Portfolio and Position. Added a README and a requirements file now that qsforex is open-source under an MIT license.

This commit is contained in:
Michael Halls-Moore
2015-03-17 07:48:37 +00:00
parent 6c77cc1deb
commit d9a7444fc2
8 changed files with 189 additions and 39 deletions
+13 -13
View File
@@ -46,7 +46,7 @@ class Portfolio(object):
ps.exposure += exposure
ps.avg_price = new_total_cost/new_total_units
ps.units = new_total_units
ps.update_position_price(remove_price)
ps.update_position_price(remove_price, exposure)
return True
def remove_position_units(
@@ -57,9 +57,9 @@ class Portfolio(object):
else:
ps = self.positions[market]
ps.units -= units
exposure = Decimal(units)
exposure = Decimal(str(units))
ps.exposure -= exposure
ps.update_position_price(remove_price)
ps.update_position_price(remove_price, exposure)
pnl = ps.calculate_pips() * exposure / remove_price
self.balance += pnl.quantize(Decimal("0.01", ROUND_HALF_DOWN))
return True
@@ -71,7 +71,7 @@ class Portfolio(object):
return False
else:
ps = self.positions[market]
ps.update_position_price(remove_price)
ps.update_position_price(remove_price, ps.exposure)
pnl = ps.calculate_pips() * ps.exposure / remove_price
self.balance += pnl.quantize(Decimal("0.01", ROUND_HALF_DOWN))
del[self.positions[market]]
@@ -83,12 +83,12 @@ class Portfolio(object):
units = int(self.trade_units)
# Check side for correct bid/ask prices
#if side == "buy":
add_price = Decimal(str(self.ticker.cur_ask))
remove_price = Decimal(str(self.ticker.cur_bid))
#else:
#add_price = self.ticker.cur_bid
#remove_price = self.ticker.cur_ask
if side == "buy":
add_price = Decimal(str(self.ticker.cur_ask))
remove_price = Decimal(str(self.ticker.cur_bid))
else:
add_price = Decimal(str(self.ticker.cur_bid))
remove_price = Decimal(str(self.ticker.cur_ask))
exposure = Decimal(str(units))
# If there is no position, create one
@@ -97,7 +97,7 @@ class Portfolio(object):
side, market, units, exposure,
add_price, remove_price
)
order = OrderEvent(market, units, "market", "buy")
order = OrderEvent(market, units, "market", side)
self.events.put(order)
# If a position exists add or remove units
else:
@@ -114,7 +114,7 @@ class Portfolio(object):
if units == ps.units:
# Close the position
self.close_position(market, remove_price)
order = OrderEvent(market, units, "market", "sell")
order = OrderEvent(market, units, "market", side)
self.events.put(order)
elif units < ps.units:
# Remove from the position
@@ -130,7 +130,7 @@ class Portfolio(object):
if side == "buy":
new_side = "sell"
else:
new_side = "sell"
new_side = "buy"
new_exposure = Decimal(str(units))
self.add_new_position(
new_side, market, new_units,
+107 -6
View File
@@ -17,7 +17,7 @@ class TestPortfolio(unittest.TestCase):
equity=equity, risk_per_trade=risk_per_trade
)
def test_add_position(self):
def test_add_position_long(self):
side = "LONG"
market = "GBP/USD"
units = 2000
@@ -38,7 +38,28 @@ class TestPortfolio(unittest.TestCase):
self.assertEquals(ps.avg_price, add_price)
self.assertEquals(ps.cur_price, remove_price)
def test_add_position_units(self):
def test_add_position_short(self):
side = "SHORT"
market = "GBP/USD"
units = 2000
exposure = Decimal(str(units))
add_price = Decimal("1.51770")
remove_price = Decimal("1.51819")
self.port.add_new_position(
side, market, units, exposure,
add_price, remove_price
)
ps = self.port.positions[market]
self.assertEquals(ps.side, side)
self.assertEquals(ps.market, market)
self.assertEquals(ps.units, units)
self.assertEquals(ps.exposure, exposure)
self.assertEquals(ps.avg_price, add_price)
self.assertEquals(ps.cur_price, remove_price)
def test_add_position_units_long(self):
side = "LONG"
market = "GBP/USD"
units = 2000
@@ -72,7 +93,41 @@ class TestPortfolio(unittest.TestCase):
self.assertTrue(apu)
self.assertEqual(ps.avg_price, Decimal("1.518735"))
def test_remove_position_units(self):
def test_add_position_units_short(self):
side = "SHORT"
market = "GBP/USD"
units = 2000
exposure = Decimal(str(units))
add_price = Decimal("1.51770")
remove_price = Decimal("1.51819")
# Test for no position
market = "EUR/USD"
apu = self.port.add_position_units(
market, units, exposure,
add_price, remove_price
)
self.assertFalse(apu)
# Add a position and test for real position
market = "GBP/USD"
self.port.add_new_position(
side, market, units, exposure,
add_price, remove_price
)
ps = self.port.positions[market]
# Test for addition of units
add_price = Decimal("1.51878")
remove_price = Decimal("1.51928")
apu = self.port.add_position_units(
market, units, exposure,
add_price, remove_price
)
self.assertTrue(apu)
self.assertEqual(ps.avg_price, Decimal("1.51824"))
def test_remove_position_units_long(self):
side = "LONG"
units = 2000
exposure = Decimal(str(units))
@@ -115,10 +170,56 @@ class TestPortfolio(unittest.TestCase):
self.assertTrue(rpu)
self.assertEqual(ps.units, 7000)
self.assertEqual(ps.exposure, Decimal("7000.00"))
self.assertEqual(ps.profit_base, Decimal("5.11127"))
self.assertEqual(ps.profit_base, Decimal("2.19054"))
self.assertEqual(self.port.balance, Decimal("100002.19"))
def test_close_position(self):
def test_remove_position_units_short(self):
side = "SHORT"
units = 2000
exposure = Decimal(str(units))
add_price = Decimal("1.51770")
remove_price = Decimal("1.51819")
# Test for no position
market = "EUR/USD"
apu = self.port.remove_position_units(
market, units, remove_price
)
self.assertFalse(apu)
# Add a position and then add units to it
market = "GBP/USD"
self.port.add_new_position(
side, market, units, exposure,
add_price, remove_price
)
ps = self.port.positions[market]
add_price = Decimal("1.51878")
remove_price = Decimal("1.51928")
add_units = 8000
add_exposure = Decimal(str(add_units))
apu = self.port.add_position_units(
market, add_units, add_exposure,
add_price, remove_price
)
self.assertEqual(ps.units, 10000)
self.assertEqual(ps.exposure, Decimal("10000.00"))
self.assertEqual(ps.avg_price, Decimal("1.518564"))
# Test removal of (some) of the units
add_price = Decimal("1.52017")
remove_price = Decimal("1.52134")
remove_units = 3000
rpu = self.port.remove_position_units(
market, remove_units, remove_price
)
self.assertTrue(rpu)
self.assertEqual(ps.units, 7000)
self.assertEqual(ps.exposure, Decimal("7000.00"))
self.assertEqual(ps.profit_base, Decimal("-5.48201"))
self.assertEqual(self.port.balance, Decimal("99994.52"))
def test_close_position_long(self):
side = "LONG"
units = 2000
exposure = Decimal(str(units))
@@ -174,7 +275,7 @@ class TestPortfolio(unittest.TestCase):
)
self.assertEqual(ps.units, 7000)
self.assertEqual(ps.exposure, Decimal("7000.00"))
self.assertEqual(ps.profit_base, Decimal("5.11127"))
self.assertEqual(ps.profit_base, Decimal("2.19054"))
self.assertEqual(self.port.balance, Decimal("100001.54"))
cp = self.port.close_position(
market, remove_price
+9 -9
View File
@@ -12,8 +12,8 @@ class Position(object):
self.exposure = Decimal(str(exposure))
self.avg_price = Decimal(str(avg_price))
self.cur_price = Decimal(str(cur_price))
self.profit_base = self.calculate_profit_base()
self.profit_perc = self.calculate_profit_perc()
self.profit_base = self.calculate_profit_base(self.exposure)
self.profit_perc = self.calculate_profit_perc(self.exposure)
def calculate_pips(self):
getcontext.prec = 6
@@ -24,19 +24,19 @@ class Position(object):
Decimal("0.00001"), ROUND_HALF_DOWN
)
def calculate_profit_base(self):
def calculate_profit_base(self, exposure):
pips = self.calculate_pips()
return (pips * self.exposure / self.cur_price).quantize(
return (pips * exposure / self.cur_price).quantize(
Decimal("0.00001"), ROUND_HALF_DOWN
)
def calculate_profit_perc(self):
return (self.profit_base / self.exposure * Decimal("100.00")).quantize(
def calculate_profit_perc(self, exposure):
return (self.profit_base / exposure * Decimal("100.00")).quantize(
Decimal("0.00001"), ROUND_HALF_DOWN
)
def update_position_price(self, cur_price):
def update_position_price(self, cur_price, exposure):
self.cur_price = cur_price
self.profit_base = self.calculate_profit_base()
self.profit_perc = self.calculate_profit_perc()
self.profit_base = self.calculate_profit_base(exposure)
self.profit_perc = self.calculate_profit_perc(exposure)
+5 -7
View File
@@ -9,7 +9,7 @@ class TestLongGBPUSDPosition(unittest.TestCase):
getcontext.prec = 2
side = "LONG"
market = "GBP/USD"
units = 2000
units = Decimal(str(2000))
exposure = Decimal("2000.00")
avg_price = Decimal("1.51819")
cur_price = Decimal("1.51770")
@@ -23,13 +23,11 @@ class TestLongGBPUSDPosition(unittest.TestCase):
self.assertEqual(pos_pips, Decimal("-0.00049"))
def test_calculate_profit_base(self):
profit_base = self.position.calculate_profit_base()
#self.assertEqual(profit_base, Decimal("-0.6457139"))
profit_base = self.position.calculate_profit_base(self.position.exposure)
self.assertEqual(profit_base, Decimal("-0.64571"))
def test_calculate_profit_perc(self):
profit_perc = self.position.calculate_profit_perc()
#self.assertEqual(profit_perc, Decimal("-0.032285695"))
profit_perc = self.position.calculate_profit_perc(self.position.exposure)
self.assertEqual(profit_perc, Decimal("-0.03229"))
@@ -52,11 +50,11 @@ class TestShortGBPUSDPosition(unittest.TestCase):
self.assertEqual(pos_pips, Decimal("0.00049"))
def test_calculate_profit_base(self):
profit_base = self.position.calculate_profit_base()
profit_base = self.position.calculate_profit_base(self.position.exposure)
self.assertEqual(profit_base, Decimal("0.64571"))
def test_calculate_profit_perc(self):
profit_perc = self.position.calculate_profit_perc()
profit_perc = self.position.calculate_profit_perc(self.position.exposure)
self.assertEqual(profit_perc, Decimal("0.03229"))