Browse Source

Broker: initial implementation

master
Denis Tereshkin 8 years ago
parent
commit
d19aed2a5a
  1. 25
      src/naiback/broker/broker.py
  2. 25
      src/naiback/broker/position.py
  3. 43
      tests/test_broker.py
  4. 12
      tests/test_position.py

25
src/naiback/broker/broker.py

@ -1,4 +1,6 @@ @@ -1,4 +1,6 @@
from naiback.broker.position import Position
class Broker:
"""
Broker has several responsibilities (so called SRP, or several responsibilities principle):
@ -9,6 +11,27 @@ class Broker: @@ -9,6 +11,27 @@ class Broker:
"""
def __init__(self, initial_cash=100000.):
self.cash = initial_cash
self.cash_ = initial_cash
self.positions = []
self.commission_percentage = 0
def cash(self):
return self.cash_
def add_position(self, ticker, price, amount):
volume = abs(price * amount)
if amount > 0:
if volume * (1 + self.commission_percentage) > self.cash_:
return None
pos = Position(ticker)
pos.enter(price, amount)
self.cash_ -= (volume + volume * self.commission_percentage)
self.positions.append(pos)
return pos
def set_commission(self, percentage):
self.commission_percentage = percentage
def all_positions(self):
return self.positions

25
src/naiback/broker/position.py

@ -3,13 +3,22 @@ class Position: @@ -3,13 +3,22 @@ class Position:
def __init__(self, ticker):
self.ticker = ticker
self.entry_price = None
self.entry_price_ = None
self.entry_metadata = {}
self.exit_price = None
self.exit_price_ = None
self.exit_metadata = {}
self.size = None
self.size_ = None
self.total_pnl = 0
def entry_price(self):
return self.entry_price_
def exit_price(self):
return self.exit_price_
def size(self):
return self.size_
def entry_commission(self):
return self.entry_metadata['commission']
@ -20,15 +29,15 @@ class Position: @@ -20,15 +29,15 @@ class Position:
return self.total_pnl
def enter(self, price, amount, **kwargs):
self.entry_price = price
self.size = amount
self.entry_price_ = price
self.size_ = amount
for k, v in kwargs.items():
self.entry_metadata[k] = v
def exit(self, price):
self.exit_price = price
self.total_pnl += (self.exit_price - self.entry_price) * self.size
self.size = 0
self.exit_price_ = price
self.total_pnl += (self.exit_price() - self.entry_price()) * self.size()
self.size_ = 0

43
tests/test_broker.py

@ -0,0 +1,43 @@ @@ -0,0 +1,43 @@
import pytest
import datetime
from naiback.broker.broker import Broker
def test_broker_cash():
broker = Broker(initial_cash=100000.)
assert broker.cash() == 100000.
def test_broker_add_position_enough_cash():
broker = Broker(initial_cash=100)
pos = broker.add_position('FOO', price=10, amount=1)
assert pos.entry_price() == 10
assert pos.size() == 1
def test_broker_add_position_not_enough_cash():
broker = Broker(initial_cash=100)
pos = broker.add_position('FOO', price=1000, amount=1)
assert pos is None
def test_broker_percentage_commissions():
broker = Broker(initial_cash=100)
broker.set_commission(percentage=0.05) # 0.05%
pos = broker.add_position('FOO', price=10, amount=1)
should_be_cash = 100 - 10 - 10 * 0.01 * 0.05
assert (broker.cash() - should_be_cash) < 0.00001
def test_broker_all_position():
broker = Broker(initial_cash=100)
pos = broker.add_position('FOO', price=10, amount=1)
assert pos in broker.all_positions()

12
tests/test_position.py

@ -11,8 +11,8 @@ def position(): @@ -11,8 +11,8 @@ def position():
def test_position_enter(position):
position.enter(3.50, 10)
assert position.entry_price == 3.50
assert position.size == 10
assert position.entry_price() == 3.50
assert position.size() == 10
def test_position_enter_metadata(position):
position.enter(3.50, 10, commission=0.1, bar=42)
@ -30,14 +30,14 @@ def test_position_exit(position): @@ -30,14 +30,14 @@ def test_position_exit(position):
position.enter(3.50, 10)
position.exit(4.50)
assert position.exit_price == 4.50
assert position.size == 0
assert position.exit_price() == 4.50
assert position.size() == 0
def test_position_enter_short(position):
position.enter(3.50, -10)
assert position.entry_price == 3.50
assert position.size == -10
assert position.entry_price() == 3.50
assert position.size() == -10
def test_position_exit_pnl(position):
position.enter(3.50, 10)

Loading…
Cancel
Save