Exemplo n.º 1
0
 def test_sellOption(self) -> None:
     symbol = "MTCH  190215P00045000"
     ts = self.tradesBySymbol[symbol]
     self.assertEqual(len(ts), 1)
     self.assertEqual(ts[0].date.date(), date(2019, 2, 4))
     self.assertEqual(
         ts[0].instrument,
         Option(
             underlying="MTCH",
             currency=Currency.USD,
             optionType=OptionType.PUT,
             expiration=date(2019, 2, 15),
             strike=Decimal("45"),
             exchange="CBOE2",
         ),
     )
     self.assertEqual(ts[0].quantity, Decimal("-1"))
     self.assertEqual(ts[0].amount,
                      Cash(currency=Currency.USD, quantity=Decimal("55")))
     self.assertEqual(
         ts[0].fees,
         Cash(currency=Currency.USD, quantity=Decimal("1.320915")))
     self.assertEqual(ts[0].price,
                      Cash(currency=Currency.USD, quantity=Decimal("0.55")))
     self.assertEqual(ts[0].flags, TradeFlags.CLOSE)
Exemplo n.º 2
0
    def test_subtractCash(self, cur: Currency, a: Decimal, b: Decimal) -> None:
        cashA = Cash(currency=cur, quantity=a)
        cashB = Cash(currency=cur, quantity=b)

        cashC = cashA - cashB
        self.assertEqual(cashC.currency, cur)
        self.assertEqual(cashC.quantity, Cash.quantize(a - b))
Exemplo n.º 3
0
    def test_cashInequality(self, cur: Currency, a: Decimal, b: Decimal) -> None:
        cashA = Cash(currency=cur, quantity=a)
        cashB = Cash(currency=cur, quantity=a + b)
        self.assertNotEqual(cashA, cashB)

        cashB = Cash(currency=cur, quantity=a - b)
        self.assertNotEqual(cashA, cashB)
Exemplo n.º 4
0
    def test_currencyInterest(self) -> None:
        # IBKR interest accruals don't have dates associated with them, so
        # they'll be tagged with the last day in the period being looked at.
        ts = self.activityByDate[date(2019, 3, 1)]

        # BASE_SUMMARY should be excluded
        self.assertEqual(len(ts), 2)

        self.assertEqual(
            ts[0],
            CashPayment(
                date=ts[0].date,
                instrument=None,
                proceeds=Cash(currency=Currency.AUD,
                              quantity=Decimal("-4.29")),
            ),
        )

        self.assertEqual(
            ts[1],
            CashPayment(
                date=ts[1].date,
                instrument=None,
                proceeds=Cash(currency=Currency.USD, quantity=Decimal("2.26")),
            ),
        )
Exemplo n.º 5
0
 def test_buyForex(self) -> None:
     symbol = "GBPUSD"
     ts = self.tradesBySymbol[symbol]
     self.assertEqual(len(ts), 2)
     self.assertEqual(ts[0].date.date(), date(2019, 2, 12))
     self.assertEqual(
         ts[0].instrument,
         Forex(
             baseCurrency=Currency.GBP,
             quoteCurrency=Currency.USD,
             exchange="IDEALFX",
         ),
     )
     self.assertEqual(ts[0].quantity, Decimal("3060"))
     self.assertEqual(
         ts[0].amount,
         Cash(currency=Currency.USD, quantity=Decimal("-3936.231")))
     self.assertEqual(ts[0].fees,
                      Cash(currency=Currency.USD, quantity=Decimal("2")))
     self.assertEqual(ts[0].flags, TradeFlags.OPEN)
     self.assertEqual(
         ts[1].instrument,
         Forex(
             baseCurrency=Currency.GBP,
             quoteCurrency=Currency.USD,
             exchange="IDEALFX",
         ),
     )
     self.assertEqual(ts[1].quantity, Decimal("50"))
     self.assertEqual(
         ts[1].amount,
         Cash(currency=Currency.USD, quantity=Decimal("-64.36")))
     self.assertEqual(ts[1].fees,
                      Cash(currency=Currency.USD, quantity=Decimal("2")))
     self.assertEqual(ts[1].flags, TradeFlags.OPEN)
Exemplo n.º 6
0
def convertCashToCurrency(quoteCurrency: Currency, cash: Sequence[Cash],
                          dataProvider: MarketDataProvider) -> Cash:
    currencyRates = dict(
        currencyConversionRates(
            quoteCurrency=quoteCurrency,
            otherCurrencies=(c.currency for c in cash
                             if c.currency != quoteCurrency),
            dataProvider=dataProvider,
        ))
    currencyRates[quoteCurrency] = Cash(currency=quoteCurrency,
                                        quantity=Decimal(1))

    for c in cash:
        if not c.currency in currencyRates:
            raise RuntimeError(
                f"Unable to fetch currency rate for {c.currency} to convert {c}"
            )

    return reduce(
        operator.add,
        (Cash(
            currency=quoteCurrency,
            quantity=c.quantity * currencyRates[c.currency].quantity,
        ) for c in cash),
        Cash(currency=quoteCurrency, quantity=Decimal(0)),
    )
Exemplo n.º 7
0
    def test_shortSaleAndCover(self) -> None:
        ts = self.activityByDate[date(2018, 1, 2)]
        self.assertEqual(len(ts), 2)

        self.assertEqual(
            ts[0],
            Trade(
                date=ts[0].date,
                instrument=Stock("HD", Currency.USD),
                quantity=Decimal("-6"),
                amount=Cash(currency=Currency.USD, quantity=Decimal("1017.3")),
                fees=Cash(currency=Currency.USD, quantity=Decimal("4.96")),
                flags=TradeFlags.OPEN,
            ),
        )

        self.assertEqual(
            ts[1],
            Trade(
                date=ts[1].date,
                instrument=Stock("HD", Currency.USD),
                quantity=Decimal("6"),
                amount=Cash(currency=Currency.USD,
                            quantity=Decimal("-1033.12")),
                fees=Cash(currency=Currency.USD, quantity=Decimal("4.95")),
                flags=TradeFlags.CLOSE,
            ),
        )
Exemplo n.º 8
0
 def test_buyOption(self) -> None:
     symbol = "HYG   191115P00087000"
     ts = self.tradesBySymbol[symbol]
     self.assertEqual(len(ts), 1)
     self.assertEqual(ts[0].date.date(), date(2019, 2, 12))
     self.assertEqual(
         ts[0].instrument,
         Option(
             underlying="HYG",
             currency=Currency.USD,
             optionType=OptionType.PUT,
             expiration=date(2019, 11, 15),
             strike=Decimal("87"),
             exchange="PSE",
         ),
     )
     self.assertEqual(ts[0].quantity, Decimal("1"))
     self.assertEqual(ts[0].amount,
                      Cash(currency=Currency.USD, quantity=Decimal("-565")))
     self.assertEqual(
         ts[0].fees, Cash(currency=Currency.USD,
                          quantity=Decimal("0.7182")))
     self.assertEqual(ts[0].price,
                      Cash(currency=Currency.USD, quantity=Decimal("5.65")))
     self.assertEqual(ts[0].flags, TradeFlags.OPEN)
Exemplo n.º 9
0
def _parseFidelityTransaction(t: _FidelityTransaction) -> Optional[Activity]:
    if t.action == "DIVIDEND RECEIVED":
        return CashPayment(
            date=_parseFidelityTransactionDate(t.date),
            instrument=Stock(t.symbol, currency=Currency[t.currency]),
            proceeds=Cash(currency=Currency[t.currency],
                          quantity=Decimal(t.amount)),
        )
    elif t.action == "INTEREST EARNED":
        return CashPayment(
            date=_parseFidelityTransactionDate(t.date),
            instrument=None,
            proceeds=Cash(currency=Currency[t.currency],
                          quantity=Decimal(t.amount)),
        )

    flags = None
    # TODO: Handle 'OPENING TRANSACTION' and 'CLOSING TRANSACTION' text for options transactions
    if t.action.startswith("YOU BOUGHT"):
        flags = TradeFlags.OPEN
    elif t.action.startswith("YOU SOLD"):
        flags = TradeFlags.CLOSE
    elif t.action.startswith("REINVESTMENT"):
        flags = TradeFlags.OPEN | TradeFlags.DRIP

    if not flags:
        return None

    return _forceParseFidelityTransaction(t, flags=flags)
Exemplo n.º 10
0
    def test_subtractIncompatibleCash(
        self, curs: List[Currency], a: Decimal, b: Decimal
    ) -> None:
        cashA = Cash(currency=curs[0], quantity=a)
        cashB = Cash(currency=curs[1], quantity=b)

        with self.assertRaises(ValueError):
            cashA - cashB
 def test_reinvestShares(self) -> None:
     ts = self.activityByDate[date(2017, 11, 9)]
     self.assertEqual(
         ts[3],
         Trade(
             date=ts[3].date,
             instrument=Stock("ROBO", Currency.USD),
             quantity=Decimal("0.234"),
             amount=Cash(currency=Currency.USD, quantity=Decimal("-6.78")),
             fees=Cash(currency=Currency.USD, quantity=Decimal("0.00")),
             flags=TradeFlags.OPEN | TradeFlags.DRIP,
         ),
     )
Exemplo n.º 12
0
 def test_securityTransferSale(self) -> None:
     ts = self.activityByDate[date(2018, 1, 4)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Stock("MSFT", Currency.USD),
             quantity=Decimal("-10"),
             amount=Cash(currency=Currency.USD, quantity=Decimal("920.78")),
             fees=Cash(currency=Currency.USD, quantity=Decimal("13.65")),
             flags=TradeFlags.CLOSE,
         ),
     )
Exemplo n.º 13
0
 def test_reinvestShares(self) -> None:
     ts = self.activityByDate[date(2017, 3, 29)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Stock("VOO", Currency.USD),
             quantity=Decimal("0.1062"),
             amount=Cash(currency=Currency.USD, quantity=Decimal("-22.95")),
             fees=Cash(currency=Currency.USD, quantity=Decimal(0)),
             flags=TradeFlags.OPEN | TradeFlags.DRIP,
         ),
     )
Exemplo n.º 14
0
 def test_buyGBPStock(self) -> None:
     symbol = "GAW"
     ts = self.tradesBySymbol[symbol]
     self.assertEqual(len(ts), 1)
     self.assertEqual(ts[0].date.date(), date(2019, 2, 12))
     self.assertEqual(ts[0].instrument,
                      Stock(symbol, Currency.GBP, exchange="LSE"))
     self.assertEqual(ts[0].quantity, Decimal("100"))
     self.assertEqual(
         ts[0].amount, Cash(currency=Currency.GBP,
                            quantity=Decimal("-3050")))
     self.assertEqual(
         ts[0].fees, Cash(currency=Currency.GBP, quantity=Decimal("21.25")))
     self.assertEqual(ts[0].flags, TradeFlags.OPEN)
Exemplo n.º 15
0
 def test_redeemBond(self) -> None:
     ts = self.activityByDate[date(2018, 6, 2)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Bond(symbol="912586AC5", currency=Currency.USD),
             quantity=Decimal("-10000"),
             amount=Cash(currency=Currency.USD, quantity=Decimal("10000")),
             fees=Cash(currency=Currency.USD, quantity=Decimal(0)),
             flags=TradeFlags.CLOSE | TradeFlags.EXPIRED,
         ),
     )
Exemplo n.º 16
0
 def test_buyUSDStock(self) -> None:
     symbol = "AAPL"
     ts = self.tradesBySymbol[symbol]
     self.assertEqual(len(ts), 1)
     self.assertEqual(ts[0].date.date(), date(2019, 2, 12))
     self.assertEqual(ts[0].instrument,
                      Stock(symbol, Currency.USD, exchange="ISLAND"))
     self.assertEqual(ts[0].quantity, Decimal("17"))
     self.assertEqual(
         ts[0].amount, Cash(currency=Currency.USD,
                            quantity=Decimal("-2890")))
     self.assertEqual(ts[0].fees,
                      Cash(currency=Currency.USD, quantity=Decimal("1")))
     self.assertEqual(ts[0].flags, TradeFlags.OPEN)
Exemplo n.º 17
0
 def test_buyStock(self) -> None:
     ts = self.activityByDate[date(2017, 2, 22)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Stock("VOO", Currency.USD),
             quantity=Decimal("23"),
             amount=Cash(currency=Currency.USD,
                         quantity=Decimal("-4981.11")),
             fees=Cash(currency=Currency.USD, quantity=Decimal("6.95")),
             flags=TradeFlags.OPEN,
         ),
     )
Exemplo n.º 18
0
 def test_buyBond(self) -> None:
     ts = self.activityByDate[date(2018, 3, 25)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Bond(symbol="912586AC5", currency=Currency.USD),
             quantity=Decimal("10000"),
             amount=Cash(currency=Currency.USD,
                         quantity=Decimal("-9956.80")),
             fees=Cash(currency=Currency.USD, quantity=Decimal(0)),
             flags=TradeFlags.OPEN,
         ),
     )
Exemplo n.º 19
0
 def test_buySecurity(self) -> None:
     ts = self.activityByDate[date(2016, 4, 20)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Stock("VTI", Currency.USD),
             quantity=Decimal("12"),
             amount=Cash(currency=Currency.USD,
                         quantity=Decimal("-3456.78")),
             fees=Cash(currency=Currency.USD, quantity=Decimal("0.00")),
             flags=TradeFlags.OPEN,
         ),
     )
 def test_buySecurity(self) -> None:
     ts = self.activityByDate[date(2017, 9, 23)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Stock("USFD", Currency.USD),
             quantity=Decimal("178"),
             amount=Cash(currency=Currency.USD,
                         quantity=Decimal("-5427.15")),
             fees=Cash(currency=Currency.USD, quantity=Decimal("4.95")),
             flags=TradeFlags.OPEN,
         ),
     )
Exemplo n.º 21
0
 def test_sellSecurity(self) -> None:
     ts = self.activityByDate[date(2016, 10, 13)]
     self.assertEqual(len(ts), 1)
     self.assertEqual(
         ts[0],
         Trade(
             date=ts[0].date,
             instrument=Stock("VWO", Currency.USD),
             quantity=Decimal("-4"),
             amount=Cash(currency=Currency.USD,
                         quantity=Decimal("1234.56")),
             fees=Cash(currency=Currency.USD, quantity=Decimal("0.00")),
             flags=TradeFlags.CLOSE,
         ),
     )
Exemplo n.º 22
0
def currencyConversionRates(
    quoteCurrency: Currency,
    otherCurrencies: Iterable[Currency],
    dataProvider: MarketDataProvider,
) -> Iterable[Tuple[Currency, Cash]]:
    instruments = (Forex(
        baseCurrency=min(currency, quoteCurrency),
        quoteCurrency=max(currency, quoteCurrency),
    ) for currency in otherCurrencies)

    return (
        (instrument.baseCurrency,
         quote.market) if instrument.quoteCurrency == quoteCurrency else
        (
            instrument.quoteCurrency,
            Cash(
                currency=instrument.baseCurrency,
                # FIXME: This unfortunately does not retain much precision when
                # dividing by JPY in particular (where the integral portion can
                # be quite large).
                # See https://github.com/bankroll-py/bankroll/issues/37.
                quantity=Decimal(1) / quote.market.quantity,
            ),
        ) for instrument, quote in dataProvider.fetchQuotes(instruments)
        if quote.market and isinstance(instrument, Forex))
Exemplo n.º 23
0
def _parseVanguardTransaction(t: _VanguardTransaction) -> Optional[Activity]:
    if t.transactionType == "Dividend":
        return CashPayment(
            date=_parseVanguardTransactionDate(t.tradeDate),
            instrument=Stock(t.symbol if t.symbol else t.investmentName,
                             currency=Currency.USD),
            proceeds=Cash(currency=Currency.USD,
                          quantity=Decimal(t.netAmount)),
        )

    validTransactionTypes = set([
        "Buy",
        "Sell",
        "Reinvestment",
        "Corp Action (Redemption)",
        "Transfer (outgoing)",
    ])

    if t.transactionType not in validTransactionTypes:
        return None

    flagsByTransactionType = {
        "Buy": TradeFlags.OPEN,
        "Sell": TradeFlags.CLOSE,
        "Reinvestment": TradeFlags.OPEN | TradeFlags.DRIP,
        "Corp Action (Redemption)": TradeFlags.CLOSE,
        "Transfer (outgoing)": TradeFlags.CLOSE,
    }

    return _forceParseVanguardTransaction(
        t, flags=flagsByTransactionType[t.transactionType])
Exemplo n.º 24
0
def uniformCurrencyQuotes(
    currency: SearchStrategy[Currency] = from_type(Currency),
    bid: SearchStrategy[Optional[Decimal]] = optionals(cashAmounts()),
    ask: SearchStrategy[Optional[Decimal]] = optionals(cashAmounts()),
    last: SearchStrategy[Optional[Decimal]] = optionals(cashAmounts()),
    close: SearchStrategy[Optional[Decimal]] = optionals(cashAmounts()),
    grow_ask: bool = True,
) -> SearchStrategy[Quote]:
    return currency.flatmap(lambda cur: quotes(
        bid=bid.map(lambda x: Cash(currency=cur, quantity=x) if x else None),
        ask=ask.map(lambda x: Cash(currency=cur, quantity=x) if x else None),
        last=last.map(lambda x: Cash(currency=cur, quantity=x) if x else None),
        close=close.map(lambda x: Cash(currency=cur, quantity=x)
                        if x else None),
        grow_ask=grow_ask,
    ))
Exemplo n.º 25
0
 def test_buyFutureOption(self) -> None:
     symbol = "GBUJ9 C1335"
     ts = self.tradesBySymbol[symbol]
     self.assertEqual(len(ts), 1)
     self.assertEqual(ts[0].date.date(), date(2019, 3, 4))
     self.assertEqual(
         ts[0].instrument,
         FutureOption(
             symbol=symbol,
             currency=Currency.USD,
             underlying="BPM9",
             optionType=OptionType.CALL,
             expiration=date(2019, 4, 5),
             strike=Decimal("1.335"),
             multiplier=Decimal(62500),
             exchange="GLOBEX",
         ),
     )
     self.assertEqual(ts[0].quantity, Decimal("1"))
     self.assertEqual(ts[0].amount, helpers.cashUSD(Decimal("-918.75")))
     self.assertEqual(ts[0].fees, helpers.cashUSD(Decimal("2.47")))
     self.assertEqual(
         ts[0].price, Cash(currency=Currency.USD,
                           quantity=Decimal("0.0147")))
     self.assertEqual(ts[0].flags, TradeFlags.OPEN)
Exemplo n.º 26
0
    def fetchQuotes(
        self,
        instruments: Iterable[Instrument],
        dataType: _MarketDataType = _MarketDataType.DELAYED_FROZEN,
    ) -> Iterable[Tuple[Instrument, Quote]]:
        self._client.reqMarketDataType(dataType.value)

        contractsByInstrument = self.qualifyContracts(instruments)

        # Note: this blocks until all tickers come back. When we want this to be async, we'll need to use reqMktData().
        # See https://github.com/jspahrsummers/bankroll/issues/13.
        tickers = self._client.reqTickers(*contractsByInstrument.values())

        for ticker in tickers:
            instrument = next((i for (i, c) in contractsByInstrument.items()
                               if c == ticker.contract))

            bid: Optional[Cash] = None
            ask: Optional[Cash] = None
            last: Optional[Cash] = None
            close: Optional[Cash] = None

            factor = 1

            # Tickers are quoted in GBX despite all the other data being in GBP.
            if instrument.currency == Currency.GBP:
                factor = 100

            if (ticker.bid
                    and math.isfinite(ticker.bid)) and not ticker.bidSize == 0:
                bid = Cash(currency=instrument.currency,
                           quantity=Decimal(ticker.bid) / factor)
            if (ticker.ask
                    and math.isfinite(ticker.ask)) and not ticker.askSize == 0:
                ask = Cash(currency=instrument.currency,
                           quantity=Decimal(ticker.ask) / factor)
            if (ticker.last and math.isfinite(
                    ticker.last)) and not ticker.lastSize == 0:
                last = Cash(currency=instrument.currency,
                            quantity=Decimal(ticker.last) / factor)
            if ticker.close and math.isfinite(ticker.close):
                close = Cash(
                    currency=instrument.currency,
                    quantity=Decimal(ticker.close) / factor,
                )

            yield (instrument, Quote(bid=bid, ask=ask, last=last, close=close))
 def test_v(self) -> None:
     self.assertEqual(self.positions[5].instrument,
                      Stock("V", Currency.USD))
     self.assertEqual(self.positions[5].quantity, Decimal("20"))
     self.assertEqual(
         self.positions[5].costBasis,
         Cash(currency=Currency.USD, quantity=Decimal("2600")),
     )
 def test_aapl(self) -> None:
     self.assertEqual(self.positions[1].instrument,
                      Stock("AAPL", Currency.USD))
     self.assertEqual(self.positions[1].quantity, Decimal("100"))
     self.assertEqual(
         self.positions[1].costBasis,
         Cash(currency=Currency.USD, quantity=Decimal("14000")),
     )
 def test_robo(self) -> None:
     self.assertEqual(self.positions[2].instrument,
                      Stock("ROBO", Currency.USD))
     self.assertEqual(self.positions[2].quantity, Decimal("10"))
     self.assertEqual(
         self.positions[2].costBasis,
         Cash(currency=Currency.USD, quantity=Decimal("300")),
     )
 def test_tBill(self) -> None:
     self.assertEqual(self.positions[0].instrument,
                      Bond("942792RU5", Currency.USD))
     self.assertEqual(self.positions[0].quantity, 10000)
     self.assertEqual(
         self.positions[0].costBasis,
         Cash(currency=Currency.USD, quantity=Decimal("9800")),
     )