def test_all_cases_of_set_broker_commission(): """ Tests that _set_broker_commission correctly sets the appropriate broker commission model depending upon user choice. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() # Broker commission is None sb1 = SimulatedBroker(start_dt, exchange, data_handler) assert sb1.fee_model.__class__.__name__ == "ZeroFeeModel" # Broker commission is specified as a subclass # of FeeModel abstract base class bc2 = ZeroFeeModel() sb2 = SimulatedBroker( start_dt, exchange, data_handler, fee_model=bc2 ) assert sb2.fee_model.__class__.__name__ == "ZeroFeeModel" # FeeModel is mis-specified and thus # raises a TypeError with pytest.raises(TypeError): SimulatedBroker( start_dt, exchange, data_handler, fee_model="bad_fee_model" )
def test_initial_settings_for_default_simulated_broker(): """ Tests that the SimulatedBroker settings are set correctly for default settings. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() # Test a default SimulatedBroker sb1 = SimulatedBroker(start_dt, exchange, data_handler) assert sb1.start_dt == start_dt assert sb1.current_dt == start_dt assert sb1.exchange == exchange assert sb1.account_id is None assert sb1.base_currency == "USD" assert sb1.initial_funds == 0.0 assert type(sb1.fee_model) == ZeroFeeModel tcb1 = dict( zip( settings.SUPPORTED['CURRENCIES'], [0.0] * len(settings.SUPPORTED['CURRENCIES']) ) ) assert sb1.cash_balances == tcb1 assert sb1.portfolios == {} assert sb1.open_orders == {} # Test a SimulatedBroker with some parameters set sb2 = SimulatedBroker( start_dt, exchange, data_handler, account_id="ACCT1234", base_currency="GBP", initial_funds=1e6, fee_model=ZeroFeeModel() ) assert sb2.start_dt == start_dt assert sb2.current_dt == start_dt assert sb2.exchange == exchange assert sb2.account_id == "ACCT1234" assert sb2.base_currency == "GBP" assert sb2.initial_funds == 1e6 assert type(sb2.fee_model) == ZeroFeeModel tcb2 = dict( zip( settings.SUPPORTED['CURRENCIES'], [0.0] * len(settings.SUPPORTED['CURRENCIES']) ) ) tcb2["GBP"] = 1e6 assert sb2.cash_balances == tcb2 assert sb2.portfolios == {} assert sb2.open_orders == {}
def test_set_initial_open_orders(): """ Check _set_initial_open_orders method for return of an empty dictionary. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) assert sb._set_initial_open_orders() == {}
def test_good_set_initial_funds(): """ Checks _set_initial_funds sets the initial funds correctly if it is a positive floating point value. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler, initial_funds=1e4) assert sb._set_initial_funds(1e4) == 1e4
def test_update_sets_correct_time(): """ Tests that the update method sets the current time correctly. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) new_dt = pd.Timestamp('2017-10-07 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) sb.update(new_dt) assert sb.current_dt == new_dt
def test_list_all_portfolio(): """ Tests list_all_portfolios method for: * If empty portfolio dictionary, return empty list * If non-empty, return sorted list via the portfolio IDs """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) # If empty portfolio dictionary, return empty list assert sb.list_all_portfolios() == [] # If non-empty, return sorted list via the portfolio IDs sb.create_portfolio(portfolio_id=1234, name="My Portfolio #1") sb.create_portfolio(portfolio_id="z154", name="My Portfolio #2") sb.create_portfolio(portfolio_id="abcd", name="My Portfolio #3") res_ports = sorted([ p.portfolio_id for p in sb.list_all_portfolios() ]) test_ports = ["1234", "abcd", "z154"] assert res_ports == test_ports
def test_bad_set_initial_funds(): """ Checks _set_initial_funds raises ValueError if initial funds amount is negative. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() with pytest.raises(ValueError): SimulatedBroker(start_dt, exchange, data_handler, initial_funds=-56.34)
def test_good_set_base_currency(): """ Checks _set_base_currency sets the currency correctly if it is supported by QSTrader. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler, base_currency="EUR") assert sb.base_currency == "EUR"
def _create_broker(self): broker = SimulatedBroker(self.start_day, self.data_handler, keep_order_time=self.keep_order_time, deal_volume_time=self.deal_volume_time, initial_cash=self.initial_cash, initial_position=self.initial_position, cac_stat=self.cac_stat) return broker
def test_bad_set_base_currency(): """ Checks _set_base_currency raises ValueError if a non-supported currency is attempted to be set as the base currency. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() with pytest.raises(ValueError): SimulatedBroker(start_dt, exchange, data_handler, base_currency="XYZ")
def test_set_cash_balances(): """ Checks _set_cash_balances for zero and non-zero initial_funds. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() # Zero initial funds sb1 = SimulatedBroker( start_dt, exchange, data_handler, initial_funds=0.0 ) tcb1 = dict( zip( settings.SUPPORTED['CURRENCIES'], [0.0] * len(settings.SUPPORTED['CURRENCIES']) ) ) assert sb1._set_cash_balances() == tcb1 # Non-zero initial funds sb2 = SimulatedBroker( start_dt, exchange, data_handler, initial_funds=12345.0 ) tcb2 = dict( zip( settings.SUPPORTED['CURRENCIES'], [0.0] * len(settings.SUPPORTED['CURRENCIES']) ) ) tcb2["USD"] = 12345.0 assert sb2._set_cash_balances() == tcb2
def test_pcm_fixed_weight_optimiser_fixed_alpha_weights_call_end_to_end( helpers): """ Tests the full portfolio base class logic for carrying out rebalancing. TODO: DataHandler is mocked. A non-disk based data source should be utilised instead. """ first_dt = pd.Timestamp('2019-01-01 15:00:00', tz=pytz.utc) asset_list = ['EQ:SPY', 'EQ:AGG', 'EQ:TLT', 'EQ:GLD'] initial_funds = 1e6 account_id = '1234' port_id = '1234' cash_buffer_perc = 0.05 exchange = SimulatedExchange(first_dt) universe = StaticUniverse(asset_list) mock_asset_prices_first = { 'EQ:SPY': 56.87, 'EQ:AGG': 219.45, 'EQ:TLT': 178.33, 'EQ:GLD': 534.21 } data_handler = Mock() data_handler.get_asset_latest_ask_price.side_effect = \ lambda self, x: mock_asset_prices_first[x] broker = SimulatedBroker(first_dt, exchange, data_handler, account_id, initial_funds=initial_funds) broker.create_portfolio(port_id, 'Portfolio') broker.subscribe_funds_to_portfolio(port_id, initial_funds) order_sizer = DollarWeightedCashBufferedOrderSizeGeneration( broker, port_id, data_handler, cash_buffer_perc) optimiser = FixedWeightPortfolioOptimiser(data_handler) alpha_weights = { 'EQ:SPY': 0.345, 'EQ:AGG': 0.611, 'EQ:TLT': 0.870, 'EQ:GLD': 0.0765 } alpha_model = FixedSignalsAlphaModel(alpha_weights) pcm = PortfolioConstructionModel(broker, port_id, universe, order_sizer, optimiser, alpha_model) result_first = pcm(first_dt) expected_first = [ Order(first_dt, 'EQ:AGG', 1390), Order(first_dt, 'EQ:GLD', 71), Order(first_dt, 'EQ:SPY', 3029), Order(first_dt, 'EQ:TLT', 2436) ] helpers.assert_order_lists_equal(result_first, expected_first)
def test_create_portfolio(): """ Tests create_portfolio method for: * If portfolio_id already in the dictionary keys, raise ValueError * If it isn't, check that they portfolio and open orders dictionary was created correctly. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) # If portfolio_id isn't in the dictionary, then check it # was created correctly, along with the orders dictionary sb.create_portfolio(portfolio_id=1234, name="My Portfolio") assert "1234" in sb.portfolios assert isinstance(sb.portfolios["1234"], Portfolio) assert "1234" in sb.open_orders assert isinstance(sb.open_orders["1234"], queue.Queue) # If portfolio is already in the dictionary # then raise ValueError with pytest.raises(ValueError): sb.create_portfolio( portfolio_id=1234, name="My Portfolio" )
def test_get_account_cash_balance(): """ Tests get_account_cash_balance method for: * If currency is None, return the cash_balances * If the currency code isn't in the cash_balances dictionary, then raise ValueError * Otherwise, return the appropriate cash balance """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker( start_dt, exchange, data_handler, initial_funds=1000.0 ) # If currency is None, return the cash balances sbcb1 = sb.get_account_cash_balance() tcb1 = dict( zip( settings.SUPPORTED['CURRENCIES'], [0.0] * len(settings.SUPPORTED['CURRENCIES']) ) ) tcb1["USD"] = 1000.0 assert sbcb1 == tcb1 # If the currency code isn't in the cash_balances # dictionary, then raise ValueError with pytest.raises(ValueError): sb.get_account_cash_balance(currency="XYZ") # Otherwise, return appropriate cash balance assert sb.get_account_cash_balance(currency="USD") == 1000.0 assert sb.get_account_cash_balance(currency="EUR") == 0.0
def test_withdraw_funds_from_account(): """ Tests withdraw_funds_from_account method for: * Raising ValueError with negative amount * Raising ValueError for lack of cash * Correctly setting cash_balances for positive amount """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler, initial_funds=1e6) # Raising ValueError with negative amount with pytest.raises(ValueError): sb.withdraw_funds_from_account(-4306.23) # Raising ValueError for lack of cash with pytest.raises(ValueError): sb.withdraw_funds_from_account(2e6) # Correctly setting cash_balances for a positive amount sb.withdraw_funds_from_account(3e5) assert sb.cash_balances[sb.base_currency] == 7e5
def test_subscribe_funds_to_account(): """ Tests subscribe_funds_to_account method for: * Raising ValueError with negative amount * Correctly setting cash_balances for a positive amount """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) # Raising ValueError with negative amount with pytest.raises(ValueError): sb.subscribe_funds_to_account(-4306.23) # Correctly setting cash_balances for a positive amount sb.subscribe_funds_to_account(165303.23) assert sb.cash_balances[sb.base_currency] == 165303.23
def _create_broker(self): """ Create the SimulatedBroker with an appropriate default portfolio identifiers. Returns ------- `SimulatedBroker` The simulated broker instance. """ broker = SimulatedBroker(self.start_dt, self.exchange, self.data_handler, account_id=self.account_name, initial_funds=self.initial_cash, fee_model=self.fee_model) broker.create_portfolio(self.portfolio_id, self.portfolio_name) broker.subscribe_funds_to_portfolio(self.portfolio_id, self.initial_cash) return broker
def test_get_account_total_market_value(): """ Tests get_account_total_market_value method for: * The correct market values after cash is subscribed. """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) # Subscribe all necessary funds and create portfolios sb.subscribe_funds_to_account(300000.0) sb.create_portfolio(portfolio_id="1", name="My Portfolio #1") sb.create_portfolio(portfolio_id="2", name="My Portfolio #1") sb.create_portfolio(portfolio_id="3", name="My Portfolio #1") sb.subscribe_funds_to_portfolio("1", 100000.0) sb.subscribe_funds_to_portfolio("2", 100000.0) sb.subscribe_funds_to_portfolio("3", 100000.0) # Check that the market value is correct res_equity = sb.get_account_total_equity() test_equity = { "1": 100000.0, "2": 100000.0, "3": 100000.0, "master": 300000.0 } assert res_equity == test_equity
def test_submit_order(): """ Tests the execute_order method for: * Raises ValueError if no portfolio_id * Raises ValueError if bid/ask is (np.NaN, np.NaN) * Checks that bid/ask are correctly set dependent upon order direction * Checks that portfolio values are correct after carrying out a transaction """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) # Raising KeyError if portfolio_id not in keys exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) asset = AssetMock("Royal Dutch Shell Class B", "EQ:RDSB") quantity = 100 order = OrderMock(asset.symbol, quantity) with pytest.raises(KeyError): sb.submit_order("1234", order) # Raises ValueError if bid/ask is (np.NaN, np.NaN) exchange_exception = ExchangeMockException() sbnp = SimulatedBroker(start_dt, exchange_exception, data_handler) sbnp.create_portfolio(portfolio_id=1234, name="My Portfolio #1") quantity = 100 order = OrderMock(asset.symbol, quantity) with pytest.raises(ValueError): sbnp._execute_order(start_dt, "1234", order) # Checks that bid/ask are correctly set dependent on # order direction # Positive direction exchange_price = ExchangeMockPrice() data_handler_price = DataHandlerMockPrice() sbwp = SimulatedBroker(start_dt, exchange_price, data_handler_price) sbwp.create_portfolio(portfolio_id=1234, name="My Portfolio #1") sbwp.subscribe_funds_to_account(175000.0) sbwp.subscribe_funds_to_portfolio("1234", 100000.00) quantity = 1000 order = OrderMock(asset.symbol, quantity) sbwp.submit_order("1234", order) sbwp.update(start_dt) port = sbwp.portfolios["1234"] assert port.total_cash == 46530.0 assert port.total_non_cash_equity == 53470.0 assert port.total_equity == 100000.0 assert port.pos_handler.positions[asset.symbol].book_cost == 53470.0 assert port.pos_handler.positions[asset.symbol].unrealised_gain == 0.0 assert port.pos_handler.positions[asset.symbol].market_value == 53470.0 assert port.pos_handler.positions[asset.symbol].unrealised_percentage_gain == 0.0 assert port.pos_handler.positions[asset.symbol].quantity == 1000 # Negative direction exchange_price = ExchangeMockPrice() sbwp = SimulatedBroker(start_dt, exchange_price, data_handler_price) sbwp.create_portfolio(portfolio_id=1234, name="My Portfolio #1") sbwp.subscribe_funds_to_account(175000.0) sbwp.subscribe_funds_to_portfolio("1234", 100000.00) quantity = -1000 order = OrderMock(asset.symbol, quantity) sbwp.submit_order("1234", order) sbwp.update(start_dt) port = sbwp.portfolios["1234"] assert port.total_cash == 153450.0 assert port.total_non_cash_equity == -53450.0 assert port.total_equity == 100000.0 assert port.pos_handler.positions[asset.symbol].book_cost == -53450.0 assert port.pos_handler.positions[asset.symbol].unrealised_gain == 0.0 assert port.pos_handler.positions[asset.symbol].market_value == -53450.0 assert port.pos_handler.positions[asset.symbol].unrealised_percentage_gain == 0.0 assert port.pos_handler.positions[asset.symbol].quantity == -1000
def test_get_portfolio_non_cash_equity(): """ Tests get_portfolio_non_cash_equity method for: * Raising ValueError if portfolio_id not in keys * Correctly obtaining the market value after cash transfers """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) # Raising KeyError if portfolio_id not in keys with pytest.raises(KeyError): sb.get_portfolio_non_cash_equity("5678") # Create fund transfers and portfolio sb.create_portfolio(portfolio_id=1234, name="My Portfolio #1") sb.subscribe_funds_to_account(175000.0) sb.subscribe_funds_to_portfolio("1234", 100000.00) # Check correct values obtained after cash transfers assert sb.get_portfolio_total_equity("1234") == 100000.0
def test_withdraw_funds_from_portfolio(): """ Tests withdraw_funds_from_portfolio method for: * Raising ValueError with negative amount * Raising ValueError if portfolio does not exist * Raising ValueError for a lack of cash * Correctly setting cash_balances for a positive amount """ start_dt = pd.Timestamp('2017-10-05 08:00:00', tz=pytz.UTC) exchange = ExchangeMock() data_handler = DataHandlerMock() sb = SimulatedBroker(start_dt, exchange, data_handler) # Raising ValueError with negative amount with pytest.raises(ValueError): sb.withdraw_funds_from_portfolio("1234", -4306.23) # Raising KeyError if portfolio doesn't exist with pytest.raises(KeyError): sb.withdraw_funds_from_portfolio("1234", 5432.12) # Add in cash balance to the account sb.create_portfolio(portfolio_id=1234, name="My Portfolio #1") sb.subscribe_funds_to_account(165303.23) sb.subscribe_funds_to_portfolio("1234", 100000.00) # Raising ValueError if not enough cash with pytest.raises(ValueError): sb.withdraw_funds_from_portfolio("1234", 200000.00) # If everything else worked, check balances are correct sb.withdraw_funds_from_portfolio("1234", 50000.00) assert sb.cash_balances[sb.base_currency] == 115303.23000000001 assert sb.portfolios["1234"].total_cash == 50000.00