First commit
This commit is contained in:
+28
@@ -0,0 +1,28 @@
|
||||
*~
|
||||
*.py[co]
|
||||
|
||||
# Packages
|
||||
*.egg
|
||||
*.egg-info
|
||||
dist
|
||||
build
|
||||
eggs
|
||||
parts
|
||||
bin
|
||||
var
|
||||
sdist
|
||||
develop-eggs
|
||||
.installed.cfg
|
||||
|
||||
# Installer logs
|
||||
pip-log.txt
|
||||
|
||||
# Unit test / coverage reports
|
||||
.coverage
|
||||
.tox
|
||||
|
||||
#Translations
|
||||
*.mo
|
||||
|
||||
#Mr Developer
|
||||
.mr.developer.cfg
|
||||
@@ -0,0 +1,129 @@
|
||||
from copy import deepcopy
|
||||
|
||||
from position import Position
|
||||
|
||||
|
||||
class Portfolio(object):
|
||||
def __init__(
|
||||
self, ticker, base="GBP", leverage=20,
|
||||
equity=100000.0, risk_per_trade=0.02
|
||||
):
|
||||
self.ticker = ticker
|
||||
self.base = base
|
||||
self.leverage = leverage
|
||||
self.equity = equity
|
||||
self.balance = deepcopy(self.equity)
|
||||
self.risk_per_trade = risk_per_trade
|
||||
self.trade_units = self.calc_risk_position_size()
|
||||
self.positions = {}
|
||||
|
||||
def calc_risk_position_size(self):
|
||||
return self.equity * self.risk_per_trade
|
||||
|
||||
def add_new_position(
|
||||
self, side, market, units, exposure,
|
||||
add_price, remove_price
|
||||
):
|
||||
ps = Position(
|
||||
side, market, units, exposure,
|
||||
add_price, remove_price
|
||||
)
|
||||
self.positions[market] = ps
|
||||
|
||||
def add_position_units(
|
||||
self, market, units, exposure,
|
||||
add_price, remove_price
|
||||
):
|
||||
if market not in self.positions:
|
||||
return False
|
||||
else:
|
||||
ps = self.positions[market]
|
||||
new_total_units = ps.units + units
|
||||
new_total_cost = ps.avg_price*ps.units + add_price*units
|
||||
ps.exposure += exposure
|
||||
ps.avg_price = new_total_cost/new_total_units
|
||||
ps.units = new_total_units
|
||||
ps.update_position_price(remove_price)
|
||||
return True
|
||||
|
||||
def remove_position_units(
|
||||
self, market, units, remove_price
|
||||
):
|
||||
if market not in self.positions:
|
||||
return False
|
||||
else:
|
||||
ps = self.positions[market]
|
||||
ps.units -= units
|
||||
ps.exposure -= float(units)
|
||||
ps.update_position_price(self, remove_price)
|
||||
pnl = ps.profit_base
|
||||
self.balance += pnl
|
||||
return True
|
||||
|
||||
def close_position(
|
||||
self, market, remove_price
|
||||
):
|
||||
if market not in self.positions:
|
||||
return False
|
||||
else:
|
||||
ps = self.positions[market]
|
||||
ps.update_position_price(self, remove_price)
|
||||
pnl = ps.profit_base
|
||||
self.balance += pnl
|
||||
del[self.positions[market]]
|
||||
return True
|
||||
|
||||
def execute_signal(self, signal_event):
|
||||
side = signal_event.side
|
||||
market = signal_event.market
|
||||
units = self.risk_per_trade
|
||||
|
||||
# Check side for correct bid/ask prices
|
||||
if side == "LONG":
|
||||
add_price = self.ticker.cur_ask
|
||||
remove_price = self.ticker.cur_bid
|
||||
else:
|
||||
add_price = self.ticker.cur_bid
|
||||
remove_price = self.ticker.cur_ask
|
||||
exposure = float(units)
|
||||
|
||||
# If there is no position, create one
|
||||
if market not in self.positions:
|
||||
self.add_new_position(
|
||||
side, market, units, exposure,
|
||||
add_price, remove_price
|
||||
)
|
||||
# If a position exists add or remove units
|
||||
else:
|
||||
# Check if the sides equal
|
||||
if side == ps[market].side:
|
||||
# Add to the position
|
||||
add_position_units(
|
||||
market, units, exposure,
|
||||
add_price, remove_price
|
||||
)
|
||||
else:
|
||||
# Check if the units close out the position
|
||||
if units == ps.units:
|
||||
# Close the position
|
||||
self.close_position(market, remove_price)
|
||||
elif units < ps.units:
|
||||
# Remove from the position
|
||||
self.remove_position_units(
|
||||
market, units, remove_price
|
||||
)
|
||||
else: # units > ps.units
|
||||
# Close the position and add a new one with
|
||||
# additional units of opposite side
|
||||
new_units = units - ps.units
|
||||
self.close_position(market, remove_price)
|
||||
|
||||
if side == "LONG":
|
||||
new_side = "SHORT"
|
||||
else:
|
||||
new_side = "LONG"
|
||||
new_exposure = float(units)
|
||||
self.add_new_position(
|
||||
new_side, market, new_units,
|
||||
new_exposure, add_price, remove_price
|
||||
)
|
||||
@@ -0,0 +1,68 @@
|
||||
import unittest
|
||||
|
||||
#from position import Position
|
||||
from portfolio import Portfolio
|
||||
|
||||
|
||||
class TestPortfolio(unittest.TestCase):
|
||||
def setUp(self):
|
||||
base = "GBP"
|
||||
leverage = 20
|
||||
equity = 100000.0
|
||||
risk_per_trade = 0.02
|
||||
ticker = {}
|
||||
self.port = Portfolio(
|
||||
ticker, base=base, leverage=leverage,
|
||||
equity=equity, risk_per_trade=risk_per_trade
|
||||
)
|
||||
|
||||
def test_add_position(self):
|
||||
side = "LONG"
|
||||
market = "GBP/USD"
|
||||
units = 2000
|
||||
exposure = float(units)
|
||||
add_price = 1.51819
|
||||
remove_price = 1.51770
|
||||
|
||||
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(self):
|
||||
side = "LONG"
|
||||
units = 2000
|
||||
exposure = float(units)
|
||||
add_price = 1.51928
|
||||
remove_price = 1.51878
|
||||
|
||||
# Test for no position
|
||||
market = "EUR/USD"
|
||||
apu = self.port.add_position_units(
|
||||
market, units, exposure,
|
||||
add_price, remove_price
|
||||
)
|
||||
self.assertFalse(apu)
|
||||
|
||||
# Test for real position
|
||||
market = "GBP/USD"
|
||||
apu = self.port.add_position_units(
|
||||
market, units, exposure,
|
||||
add_price, remove_price
|
||||
)
|
||||
self.assertTrue(apu)
|
||||
ps = self.port.positions[market]
|
||||
|
||||
self.assertAlmostEqual(ps.avg_price, 1.518735)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,32 @@
|
||||
class Position(object):
|
||||
def __init__(
|
||||
self, side, market, units,
|
||||
exposure, avg_price, cur_price
|
||||
):
|
||||
self.side = side
|
||||
self.market = market
|
||||
self.units = units
|
||||
self.exposure = exposure
|
||||
self.avg_price = avg_price
|
||||
self.cur_price = cur_price
|
||||
self.profit_base = self.calculate_profit_base()
|
||||
self.profit_perc = self.calculate_profit_perc()
|
||||
|
||||
def calculate_pips(self):
|
||||
mult = 1.0
|
||||
if self.side == "SHORT":
|
||||
mult = -1.0
|
||||
return mult * (self.cur_price - self.avg_price)
|
||||
|
||||
def calculate_profit_base(self):
|
||||
pips = self.calculate_pips()
|
||||
return pips * self.exposure / self.cur_price
|
||||
|
||||
def calculate_profit_perc(self):
|
||||
return self.profit_base / self.exposure * 100.0
|
||||
|
||||
def update_position_price(self, cur_price):
|
||||
self.cur_price = cur_price
|
||||
self.profit_base = self.calculate_profit_base()
|
||||
self.profit_perc = self.calculate_profit_perc()
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import unittest
|
||||
|
||||
from position import Position
|
||||
|
||||
|
||||
class TestLongGBPUSDPosition(unittest.TestCase):
|
||||
def setUp(self):
|
||||
side = "LONG"
|
||||
market = "GBP/USD"
|
||||
units = 2000
|
||||
exposure = 2000.0
|
||||
avg_price = 1.51819
|
||||
cur_price = 1.51770
|
||||
self.position = Position(
|
||||
side, market, units, exposure,
|
||||
avg_price, cur_price
|
||||
)
|
||||
|
||||
def test_calculate_pips(self):
|
||||
pos_pips = self.position.calculate_pips()
|
||||
self.assertAlmostEqual(pos_pips, -0.00049)
|
||||
|
||||
def test_calculate_profit_base(self):
|
||||
profit_base = self.position.calculate_profit_base()
|
||||
self.assertAlmostEqual(profit_base, -0.6457139)
|
||||
|
||||
def test_calculate_profit_perc(self):
|
||||
profit_perc = self.position.calculate_profit_perc()
|
||||
self.assertAlmostEqual(profit_perc, -0.032285695)
|
||||
|
||||
|
||||
class TestShortGBPUSDPosition(unittest.TestCase):
|
||||
def setUp(self):
|
||||
side = "SHORT"
|
||||
market = "GBP/USD"
|
||||
units = 2000
|
||||
exposure = 2000.0
|
||||
avg_price = 1.51819
|
||||
cur_price = 1.51770
|
||||
self.position = Position(
|
||||
side, market, units, exposure,
|
||||
avg_price, cur_price
|
||||
)
|
||||
|
||||
def test_calculate_pips(self):
|
||||
pos_pips = self.position.calculate_pips()
|
||||
self.assertAlmostEqual(pos_pips, 0.00049)
|
||||
|
||||
def test_calculate_profit_base(self):
|
||||
profit_base = self.position.calculate_profit_base()
|
||||
self.assertAlmostEqual(profit_base, 0.6457139)
|
||||
|
||||
def test_calculate_profit_perc(self):
|
||||
profit_perc = self.position.calculate_profit_perc()
|
||||
self.assertAlmostEqual(profit_perc, 0.032285695)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user