2022 Stonk thoughts

Contents

2022 Stonk thoughts#

Itafos#

analysis can be performed better here.

Itafos stock valued about slightly above 3 PE. Given general food shortages and high fertilizer prices, would expect the value here to stay, even alternative producers like earth renew are going to need phosphate rock.

With market downturn, its possible this stock will go to 2 or 1 PE, net debt is acceptable.

They expect a slowing in H2 2022 as no one has money anymore.

Decent amount of shareholder equitity

214414000 USD, market cap

jun22_equitity = 214414	* 1000
# market cap in usd
market_cap =363.735 * 1000 * 1000

# ratio is
ratio = jun22_equitity / market_cap
print(ratio)
0.5894786039286843

So if the business is liquidized at the current price, 0.5 is returned to shareholders.

Earning back entire market cap in 2 or 3 years is appealing.

import matplotlib.pyplot as plt
plt.plot([100, 80, 75, 76, 85])
[<matplotlib.lines.Line2D at 0x7f084ff27d90>]
../../_images/ff4c75c7d34b4fbe0eba3a13308f845488cbfe28fdcf9bfbb1a5c51d93c309b2.png

Earning should be sustained for a bit.

Plot of expected earnings over the next few years based on article of expected phosphate rock.

Statistic: Price for phosphate rock from 2015 to 2020 with a forecast for 2021 to 2035 (in U.S. dollars per metric ton) | Statista
Find more statistics at Statista

ZIM#

I believe with the current supply chain issues, the inability to use rivers to ship goods, think container shipping is here to stay, port congestion is unlikely to end anytime soon with all the port strikes. Think zim will be able to maintain revenue for a few years.

Statistic: Age distribution of the world merchant fleet in 2019-2020, by vessel type | Statista
Find more statistics at Statista

Seems like a lot of ships are old, think lifespan time is like 20 years, possible shipping remains high for a bit longer. With the aging fleet, I think https://gcaptain.com/old-is-gold-sky-high-cost-of-ageing-ships-sounds-inflation-sos/ and with supply chain issues with pretty high https://data.worldbank.org/indicator/IS.SHP.GOOD.TU shipping teu, makes sense that zim will be able to maintain high profits. Although the amount of teu went down in 2008, it was still high in recession, the rate may get lower, but I think zim will continue to deliver profits.

import matplotlib.pyplot as plt
# income for each quarter
net_income = [40, 30, 30, 35]
plt.plot(net_income)

profit = [0.5 * income for income in net_income]
plt.plot(profit)

# with current profits I would expect
dividends = 20 + 15 + 15 + 17.5
../../_images/3950c0c0e5390216e276d377abeafeeccdb34f95253080ccfdccf04d340c053a.png

ships last 20 or 25 years

Should get my money back in 2 to 3 years, dont expect shipping profits to collapse, new ships, environmental changes and climate change should keep profits high, and we are shipping more things than ever before.

Generates Record Full Year Net Income of $524 Million, in 2020 before the supply chain issues

import yfinance as yf
import mplfinance as mpf
sp = yf.Ticker("MBCF")
# Consider grabbing for valid date index instead
daily = sp.history(start="2016-01-02")
mpf.plot(daily,type='line')

last_year = sp.history(start="2021-03-14")
mpf.plot(last_year,type='candle',mav=(50, 100),volume=True)
../../_images/f320ff63b44d3ea47b32a413fd382bcac221997caa0a087e1d88df56814ec441.png
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/mplfinance/_arg_validators.py:84: UserWarning: 

 ================================================================= 

   WARNING: YOU ARE PLOTTING SO MUCH DATA THAT IT MAY NOT BE
            POSSIBLE TO SEE DETAILS (Candles, Ohlc-Bars, Etc.)
   For more information see:
   - https://github.com/matplotlib/mplfinance/wiki/Plotting-Too-Much-Data
   
   TO SILENCE THIS WARNING, set `type='line'` in `mpf.plot()`
   OR set kwarg `warn_too_much_data=N` where N is an integer 
   LARGER than the number of data points you want to plot.

 ================================================================ 
  warnings.warn('\n\n ================================================================= '+
../../_images/f92bdc8d31daae684c04cb122f833531b62fd4bd6fc5d233571fcdcea744a960.png

Its a bit above the 50MA, want to wait until its below.

Expect the bullwhip effect to happen a few times.

import numpy as np
def relative_strength(prices, n=14):
    """
    compute the n period relative strength indicator
    http://stockcharts.com/school/doku.php?id=chart_school:glossary_r#relativestrengthindex
    http://www.investopedia.com/terms/r/rsi.asp
    """
    deltas = np.diff(prices)
    seed = deltas[:n + 1]
    up = seed[seed >= 0].sum() / n
    down = -seed[seed < 0].sum() / n
    rs = up / down
    rsi = np.zeros_like(prices)
    rsi[:n] = 100. - 100. / (1. + rs)

    for i in range(n, len(prices)):
        delta = deltas[i - 1]  # cause the diff is 1 shorter

        if delta > 0:
            upval = delta
            downval = 0.
        else:
            upval = 0.
            downval = -delta

        up = (up * (n - 1) + upval) / n
        down = (down * (n - 1) + downval) / n

        rs = up / down
        rsi[i] = 100. - 100. / (1. + rs)

    return rsi

for stock in ["ZIM", "DOLE", "SBSW"]:
    zim = yf.Ticker(stock)
    zim = sp.history(start="2021-03-14")
    zim['rsi'] = relative_strength(zim['Close'],n=7)

    apd = mpf.make_addplot(zim['rsi'],panel=2,color='lime',ylim=(10,90),secondary_y=True)

    mpf.plot(zim,type='candle',volume=True,figscale=1.5,addplot=apd,panel_ratios=(1,0.6), title=f"{stock} - 2021-03-14")
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/mplfinance/_arg_validators.py:84: UserWarning: 

 ================================================================= 

   WARNING: YOU ARE PLOTTING SO MUCH DATA THAT IT MAY NOT BE
            POSSIBLE TO SEE DETAILS (Candles, Ohlc-Bars, Etc.)
   For more information see:
   - https://github.com/matplotlib/mplfinance/wiki/Plotting-Too-Much-Data
   
   TO SILENCE THIS WARNING, set `type='line'` in `mpf.plot()`
   OR set kwarg `warn_too_much_data=N` where N is an integer 
   LARGER than the number of data points you want to plot.

 ================================================================ 
  warnings.warn('\n\n ================================================================= '+
../../_images/a6cf0ebbcf145a6a497ee6924f5b977f435e6ebbbb469f7902b4b7bd5c11039e.png
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/mplfinance/_arg_validators.py:84: UserWarning: 

 ================================================================= 

   WARNING: YOU ARE PLOTTING SO MUCH DATA THAT IT MAY NOT BE
            POSSIBLE TO SEE DETAILS (Candles, Ohlc-Bars, Etc.)
   For more information see:
   - https://github.com/matplotlib/mplfinance/wiki/Plotting-Too-Much-Data
   
   TO SILENCE THIS WARNING, set `type='line'` in `mpf.plot()`
   OR set kwarg `warn_too_much_data=N` where N is an integer 
   LARGER than the number of data points you want to plot.

 ================================================================ 
  warnings.warn('\n\n ================================================================= '+
../../_images/48705cf6d4a9dd691d495c6e284c7189324cef962408de1b9a6204492f2397a2.png
/opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/mplfinance/_arg_validators.py:84: UserWarning: 

 ================================================================= 

   WARNING: YOU ARE PLOTTING SO MUCH DATA THAT IT MAY NOT BE
            POSSIBLE TO SEE DETAILS (Candles, Ohlc-Bars, Etc.)
   For more information see:
   - https://github.com/matplotlib/mplfinance/wiki/Plotting-Too-Much-Data
   
   TO SILENCE THIS WARNING, set `type='line'` in `mpf.plot()`
   OR set kwarg `warn_too_much_data=N` where N is an integer 
   LARGER than the number of data points you want to plot.

 ================================================================ 
  warnings.warn('\n\n ================================================================= '+
../../_images/95bf46af8d19f1b8c7433806a2d301cc77934da9e0d9acede810d7f0cbece3bd.png

Analyze of rollover of XSB, basic facts

2.74 years which is about 2 years and 8.2 months.

Current price 25.72, I am betting once the weak bonds expire, average bond yield at 0.61 %, reinvesting will result in a solid long term yield above the exist values. Assume current yield is rolling from 3 to 4.

TODO make model I can generalize

nav = 3282292855

current_yield = 2.24
# average bond yield for 2.74 years
lower_end = 3.25 
higher_end = 4.5 
avg_yield_for_years = 0.61

# assuming interest rates remain high for about 2.74 years
adjusted_yield_low =  ((lower_end / avg_yield_for_years)-current_yield*0.5) + current_yield * 0.5
adjusted_yield_high = ((higher_end / avg_yield_for_years)-current_yield*0.5) + current_yield * 0.5

# current rates are 
print(adjusted_yield_low)


print(adjusted_yield_high)
# 5.327868852459017
# 7.377049180327869
5.327868852459017
7.377049180327869
../../_images/fed_dotplot.webp

Scenario for 0.75 0.25, 0.25 rate increases.

import pandas as pd
df = pd.read_csv("static_2019_to_now_boc_rates.csv")
last_date_in_csv = "2022-09-20"
first_hike = "2022-10-26"
base_rate = 3.25;
second_hike = "2022-12-07"
last_hike = "2023-01-26"

# list of objects, start, end, rate
future_date = "2023-11-25"
# create loop for this in the future
df['Date'] = pd.to_datetime(df['Date'])
to_first_hike = pd.DataFrame({'Date': pd.date_range(start=last_date_in_csv, end=first_hike,closed='left'), 'V39079': base_rate})

df.append(to_first_hike)
new_df = pd.concat([df, to_first_hike])
base_rate = base_rate + 0.75
to_second_hike = pd.DataFrame({'Date': pd.date_range(start=first_hike, end=second_hike,closed='left'), 'V39079': base_rate})

# have to draw rolling average with plot
new_df = pd.concat([new_df, to_second_hike])
base_rate = base_rate + 0.25
to_third_hike = pd.DataFrame({'Date': pd.date_range(start=second_hike, end=last_hike), 'V39079': base_rate})
new_df = pd.concat([new_df, to_third_hike])

to_future = pd.DataFrame({'Date': pd.date_range(start=last_hike, end=future_date), 'V39079': base_rate})
new_df = pd.concat([new_df, to_future])
to_future = pd.DataFrame({'Date': pd.date_range(start=future_date, end="2024-05-05"), 'V39079': base_rate-0.75})
new_df = pd.concat([new_df, to_future])
new_df = new_df[pd.to_numeric(new_df['V39079'], errors='coerce').notnull()]
# drop duplicates based on date
new_df = new_df.drop_duplicates()
# drop all zero entries

new_df["Rates"] = pd.to_numeric(new_df["V39079"])
new_df= new_df[new_df['Rates'] != 0]
# sort by Date
new_df.sort_values(by='Date', inplace=True)
# plot rolling average
new_df['MA'] = new_df["Rates"].rolling(600, min_periods=1).mean()
new_df.plot("Date", "Rates")
new_df.plot("Date", "MA")
print(new_df)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
Cell In[7], line 13
     11 # create loop for this in the future
     12 df['Date'] = pd.to_datetime(df['Date'])
---> 13 to_first_hike = pd.DataFrame({'Date': pd.date_range(start=last_date_in_csv, end=first_hike,closed='left'), 'V39079': base_rate})
     15 df.append(to_first_hike)
     16 new_df = pd.concat([df, to_first_hike])

File /opt/hostedtoolcache/Python/3.10.15/x64/lib/python3.10/site-packages/pandas/core/indexes/datetimes.py:1008, in date_range(start, end, periods, freq, tz, normalize, name, inclusive, unit, **kwargs)
   1005 if freq is None and com.any_none(periods, start, end):
   1006     freq = "D"
-> 1008 dtarr = DatetimeArray._generate_range(
   1009     start=start,
   1010     end=end,
   1011     periods=periods,
   1012     freq=freq,
   1013     tz=tz,
   1014     normalize=normalize,
   1015     inclusive=inclusive,
   1016     unit=unit,
   1017     **kwargs,
   1018 )
   1019 return DatetimeIndex._simple_new(dtarr, name=name)

TypeError: DatetimeArray._generate_range() got an unexpected keyword argument 'closed'

So this fails to account for proper weighting, accounting for high yield corporate bonds and heavily assumes that a lot of the debt will roll off successfully.

  • Assumes raises in government bonds will transfer over to the private sector (true, but companies may not be able to pay)

  • Assumes assets will roll over (true, but some may not), and greatly increase the yield, at bare minimum over a 2.74 year period would expect the yield to meet at least the benchmark yield, if not slightly higher.

import pandas as pd 

df = pd.read_csv("xsb_holdings.csv")

# find columns with Duration less than 2 years and coupon less than 1.5%
df = df[(df["Duration"] < 2.74)]

print(df)

# total weight

print(df["Weight (%)"].sum())
print(df["Coupon (%)"].mean())
coupon_low = df[(df["Coupon (%)"] < 3)]
print(coupon_low["Coupon (%)"].mean())
print(coupon_low["Weight (%)"].sum())