• Wednesday

    • Schwab update: got another daily bump of interest (problem), called and they said it was expected. Because the trade took 2 days to settle (still insane), the sameday withdrawal put me on margin (for 2 days). It shouldn’t increase tomorrow, when I’ll check again. They’re putting a credit/waiver in process for the 2 days of interest.
      • For reference, in case this slips while I’m in Europe, and the margin/interest does a sneak accumulation in the background over a long period of time: the first person I talked to was a woman (didn’t catch name) at 877-870-7317 x70180, and the second person I talked to was Dan (no extension, was the next operator).
    • Absolutely loving the chess cheating scandal between Magnus/Niemann over the past 2 weeks.
      • From the Sinquefield withdrawal after Magnus’ first classical loss with white in >50 games to the JB generation resignation (where magnus went 10-4-1 including the intentional loss).
      • Really comes down to whether you believe Magnus as a person or not. The absence of public evidence is not an argument. There are many reasons (legal, mole, others) why remaining silent is the best option. I originally thought the most likely explanation was: The initial withdrawal+tweet (accusation) was an emotional overreaction, and then the world blew it up into a bigger deal that became increasingly embarrassing, and Magnus was soberly cornered and couldn’t really walk it down. But then he doubled down with the JB resignation, stating firmly that he believes his position strongly. So…should we? Compare the two characters. Magnus: world champion for a decade, known sportsmanship, never had a moral mishap like this before. Hans: known cheater, banned from online tournament sites, multiple times, has lied about extent of cheating before.
      • Today he gave his first interview after the round robin finished. He said he’d state more after the tournament.
    • Updated vscode, a bunch of drivers, and a bunch of windows patches.
      • Audio stopped working. Uninstalled the realtek driver, restarting the computer, fixed (without reinstalling any driver…?).
    • Edited some of the merge request settings in gitlab (require approval, tests must pass, etc).
      • Gitlab has a rebase button from the frontend, which can handle the conflict-less case – pretty great.
    • Apple wallet.
      • Tried to add my business credit card to apple wallet – the issuer (boa) doesn’t support it for this card yet.
      • Tried to add driver’s license too, since I updated to ios 16 – it’s not yet supported for NY, only for AZ and MD.
    • The user@host at the end of the public key stored on the server is effectively a comment. Anyone with the private key can ssh into that server. They don’t have to be the same user@host.
    • Supercontest.
      • CICD. Finished https://gitlab.com/bmahlstedt/supercontest/-/issues/161.
        • Installed tox in wsl.
        • Note that test-requirements is mixed with regular requirements for the final compilation. At installation, we only install the desired (by using -r with reqs and -c to constrain to compiled-reqs).
        • .gitlab-ci.yml -> makefile -> tox -> pylint/bandit/pytest
        • And remember “make test-js” exists and runs eslint and jasmine. Don’t think I ever got this passing though. Continuing to leave it off for now.
        • There was a conflict in flask-graphql and graphene on versions of graphql_core.
        • req-compile (Spencer’s tool) wasn’t able to resolve this (and looked unmaintained at this point), so I switched the resolver back over to the industry standard pip-tools.
        • Updated the reqs: https://gitlab.com/bmahlstedt/supercontest/-/commit/021c39e14444be5e50f34dbd7a61ff3dcaef5e96?view=parallel
          • Removed importlib-metadata and typed-ast and zipp.
          • Upgraded setuptools 50.3 -> 65.3 (undid later)
        • Delinted with ~moderate changes. No necessary changes to bandit or pytest, passed.
        • Removed the make requirement of test on update-reqs. You can pip-compile without testing anything. You can run tests without updating reqs. I want update to be manual, on my schedule.
        • The gitlab ci runners with metadata issue on python installation of my compiled reqs into the image. My tests and deploys work locally.
          • Image python37.
          • –use-deprecated=backtrack-on-build-failures and legacy-resolver did not work (in custom pip install command in tox).
          • Neither did apt install build-essential g++ python-dev, etc.
          • Nor upgrading pip and setuptools.
          • Nothing worked, so I switched from the python:37 image (debian) to the ubuntu:18.04 image and manually installed py37. That didn’t work either.
          • Aha! The issue is in jsmin itself, which runs use_2to3 in its setup, which was deprecated by setuptools in version 58! https://github.com/tikitu/jsmin/issues/33
          • Added that constraint to an input requirement, works now.
          • Well, explicitly added it to RIGHT BEFORE the env provisioning, which tox handles. If you pass it as a member of the compiled reqs, it tries to install your pinned setuptools version (alongside all other reqs) USING the new/bad version of setuptools, which still fails.
          • Sidenote – python images are debian (unless directly specifying slim or alpine).
        • After merge to master, massaged the deploy stage a bit as well.
          • Ansible doesn’t have a ubuntu/python image newer than 16/3. Reverted the 18/3 change. It’s just running ansible (no app components).
          • Didn’t finish. Unreachable by ssh, don’t want to autonomously manage the fragile key management and known hosts and ssh agents of client vs server and everything along with it. I have way more important things to work on. Will deploy manually for the time being.
        • Came back to this a bit later. Updated the privkey var in gitlab to the same ssh key I use in wsl, which I know is authorized. Also ran an ssh-agent and ssh-add in the docker build session, even though I’m passing the keyfile directly to the ansible call.
          • Full instructions on https://docs.gitlab.com/ee/ci/ssh_keys/
      • Updated league banner, committed league lines, submitted my picks.
      • Added a brand new admin view to change the status of a game.
        • https://gitlab.com/bmahlstedt/supercontest/-/issues/162
        • Super helpful when the live scoresheets are behind, which is often on sunday. I can lock a game with a view clicks from the internet anywhere, rather than sshing into the production server (often railed on mem/cpu) and doing it manually in psql.
      • Docker build efficiency.
        • https://gitlab.com/bmahlstedt/supercontest/-/issues/148
        • The prod dockerfile is already split into two stages. The first builds wheels for all deps from the list of compiled requirements. The second installs them and starts the app/db.
        • The build/install steps of the deps is what takes a couple minutes. This is not an inefficiency in docker steps, I need a proper python pkg cache. Won’t-do for now.
  • Tuesday

    • Continuation of Schwab being dumb yesterday:
      • My withdrawal triggered a margin loan because the cash from tsla sales hadn’t cleared yet.
      • Called and asked why the settlement wasn’t restricted to their back office, yielding the usual instantaneous result for the client. They said it was strange, and it isn’t scheduled to clear until tomorrow
      • This results in 2 days of margin interest (on >10k). She said to call back tomorrow after it clears and she’ll waive/credit it.
    • Always remember: Taxes are the biggest expense in almost every individual’s life. More than mortgage/rent, more than bills, more than medicine, more than food. You are paying your government for their services more than you’re paying your doctor for your health, more than you’re paying your mechanic for your car, more than your grocer for your nutrition, more than the home you live in, more than your phone, more than anything else.
    • Mahlstedt LLC tax notes.
      • Entity Classification Election (ECE). Have to file within 75 days, otherwise LLCs default to sole proprietorship. Can save up to 15.3% for various taxes including FICA if you elect differently.
      • First year deductions for business filing:
        • 5k in startup costs.
        • 5k in organizational expenses.
      • First year deductions for personal filing:
        • Business costs, passthrough to personal. If the costs of the startup exceed the revenue, you can pass the losses through to your personal filing.
      • Recurring deductions:
        • Write loan agreement from personal to business. Then you can deduct business expenses from personal. No max.
        • Healthcare. If you offer through business, you can deduct.
        • IRS code section 199A. Deduct 20% of business profits on personal returns.
          • https://www.irs.gov/newsroom/tax-cuts-and-jobs-act-provision-11011-section-199a-qualified-business-income-deduction-faqs
        • Home office deduction (from personal). Regular method (% of everything, but beware recapture tax) vs simplified option ($5/sqft*300sqft=$1500 deduction).
  • Monday

    • Podcast is a portmanteau of iPod and broadcast! iPod’s name was originally given because it was the smaller version of the mac computer, like a pod of a mothership.
    • Porsche going public, ~75B.
    • Apple One is Apple TV, iCloud+, Apple Music, Apple Fitness+, Apple News+, and Apple Arcade.
    • Apple Card is 3% back at Apple. 2% on Apple Pay (basically everything else).
    • Registered the Carta account to sign LP docs. Basic info, wire info, w-9, accredited investor / qualified purchaser / qualified client. And then, specific fund info: LPA (~50pg) and subscription agreement.
    • Schwab’s webapp is garbage. Failed 2FA, failed withdrawals, failed trades. Tried multiple times today to liquidate a few positions, all failed. Ended up doing it from the mobile app.
    • BoA’s “autopay” feature for business credit cards (and customer support) is also garbage. 2 hours on the phone in total.
      • There should be a single link on all main pages; this is one of the first primary things an account owner sets up.
      • Can’t go through mobile app, links you to full site.
      • Full site has nothing on the payments menus. You have to go through the transfers menu (it’s not autopay, it’s a recurring transfer……….). Once you do, there’s no option to just do something like “pay full statement balance on the first of every month, from my business checking account to my business credit account” – crazy.
      • Called – had to wait for robomenus hearing “set up autopay” as “order checks” as well as encouraging the download of the mobile app for better support (devops SWE inside me screaming).
      • The first human I talked to only knew the Personal side. They transferred me to the Business specialists, repeating the whole process above.
      • Then they said that my business account was not categorized in “Bill Pay” mode, it was “Payments and Invoicing” mode – a setting that occurs when the account is setup. So they had to transfer me to the P&I team to help with autopay.
      • That hold was over an hour 🙂
      • Then they said that they were the Fraud department, not the Payments and Invoicing department. Transferred again.
      • This one then picked up and said they were the Small Businesses department (lol, jesus christ) and transferred me over to Payments and Invoicing (888-287-4637).
      • End of the road, last woman knew! It’s not an “enable online banking” or “enable eBill” issue. Older accounts are Bill Pay; newer accounts (like mine) are Payments and Invoicing. However the new system does not use the old BillPay infrastructure, it uses the Transfer tooling to handle autopay. And the transfer tooling is only capability of sending a Fixed amount at a frequency, not the dynamic “full balance” or “statement balance” etc. So if you want to pay your exact bill amount, you cannot do it automatically – you must do it manually. Fucking insane. Breaks my software heart, could roll this feature out in less than a day. They said that they hope that feature is released soon and I’ll get an email notification when it’s out.
    • Remember, inherently; if you buy a bunch of a stock, it will go up, and then you hold a lot of that higher-price stock. But then the same thing happens when you try to sell it off; price will go back down as you liquidate. So, if you can find opportunities to buy normally (affecting the price) and sell abnormally (without affecting the price), you have a profit window.
      • An example: Buy a lot in the morning, when liquidity is lower. Prices will increase. Sell it in the afternoon, when liquidity is higher. Prices will still decrease, but not as much.
      • Another: lock in a price ahead of time with the seller (futures contract, whatever): then do the normal buy, and the fixed sell.
  • Friday

    • Adobe bought Figma for 20B. Current revenue ~400M/yr.
    • Coinbase (15%) is #2 in liquid staking after lido (28%).
    • pg_netstat, network data for postgres: https://github.com/supabase/pg_netstat
    • Tried to add a few more event types to calendly, but you need their paid tier to have more than 1. So I’m just going to calibrate the 1 as needed.
      • Better, generic link now: https://calendly.com/bmahlstedt/meeting
    • Master-feeder fund structure, how to handle back office.
      • https://www.investopedia.com/terms/m/master-feeder-fund.asp
      • Investors put their specific capital in feeder funds. It’s a legally-separate entity from the master. You pay management/performance fees here. You pay taxes here. An investor can feed into multiple master funds, if desired.
      • The asset manager (at the top) pools all feeder funds into a master fund and executes all trade activity out of that.
      • Logistically: the feeder funds basically buy shares of the master fund, receiving interest/dividends/gains/etc similar to how they would with standard equity. You may also receive tax benefits from the partnership with the master.
    • Discussed standards for rate aggregation, cost of diff capital sources, how and why to negotiate, what optimizes for who.
    • Call vs deploy timelines, interest IRR. Most lenders <10d (for decent size) and shortterm LOC to cover less than that.
    • Don’t need a full vehicle to pledge collateral. Just need a lock. Can be a legal entity like an LLC, can be just a new account, can be a simple bool column in a database that tracks an asset row.
    • Compared some fund admin.
    • Ten Percent Shareholder – someone who owns more than 10% of the total voting power. Obviously.
    • SAR = stock appreciation right = just like a stock award but you get it in cash and never own the equity. Over a predetermined period, you receive the appreciation in USD as if you have held and then sold that asset at the end.
    • Looked more at securitize.
    • Researched LPAs, LPACs.
  • Thursday

    • Watched Lex Fridman. Demis Hassabis. AI. DeepMind. AlphaZero. Really like this guy. https://www.youtube.com/watch?v=Gfr50f6ZBvo. Potentially – humans are a construction of the universe in order for it to deploy tools to understand itself.
    • https://www.wsj.com/articles/elon-musk-fortune-fight-jared-birchall-igor-kurganov-11657308426. Jared Birchall, Elon’s wealth manager. Igor Kurganov, friend of Elon’s and poker player, brainstormed about how to use the wealth altruistically. Elon almost made Igor wealth manager. Jared fought it (and ultimately retained the position).
    • Watched Lex Fridman. Nick Lane. Life. https://www.youtube.com/watch?v=tOtdJcco3YM.
    • Tons of work work.
  • Wednesday

    If you’re calling a route, url_defaults ONLY runs if args are missing (and does so first). These values assert the final URL. url_route_preprocessor always runs. It does not affect the actual path (just information handling serverside).

    If you’re calling url_for() to build links, only url_defaults runs; url_route_preprocessor functions do not.

    Since sbsc’s resolvers on season/week/etc should ALWAYS run, they should go in defaults as well as processors. Both paths need to clean the user input. And then only the preprocessors have the expander functions, attributing additional info to g.

    Hoisting highlight to the top
    • Remember in general: path args are used to define which resources you’re fetching on that view. Query params are used to filter/sort/etc those resources.
    • Upgraded to ios 16, can edit/unsend messages now.
    • Starbucks Odyssey program, earn NFTs (“Journey Stamps”) and rewards on Polygon. Blockchain loyalty program.
    • US News annual college rankings out. Overall: Princeton -> MIT -> Stanford/Harvard/Yale. Berkeley 20 overall (#1 public). Engineering: MIT -> Stanford -> Berkeley. CS: Carnegie Mellon and MIT tied for 1st, Berkeley 3rd.
    • Supercontest.
      • Pointed the prod app back at the espn scorestrip (last thurs), which is still fetchable now that they’ve updated.
      • Successfully ran the checkout flow through stripe (last sat). Still works.
        • Remember stripe takes 2.9% + 30c per transaction. So for sbsc, where a transaction is $50, our effective rate is 3.5%.
        • It automatically pays out to my bank account monthly, I believe.
        • Was succeeding, but still leaving an unhandled exception in the request. Triggering sentry email and bad logs. “int” is not iterable – this is because I was doing Response(200) but it needs the resp first and status second (https://flask.palletsprojects.com/en/2.2.x/api/#flask.Response) so simple change of Response(status=200).
      • Added Harner to the admin list (last sun).
      • System updates.
        • dpkg was reporting inconsistent state for python3-apt, dpkg removed and apt reinstalled.
        • Google chrome was reporting an expired signature in the apt sources list, commented out.
        • Apt update and upgrade (>300 pkgs).
        • Autoremove and autoclean.
        • Installed the new digital ocean droplet agent so could access browser console again.
        • Upgraded the droplet from ubuntu 18.04.03 to 18.04.6.
      • Analytics.
        • Compared Google Analytics to FlaskMonitoringDashboard.
        • About 70 users last week, avg engagement time 2min.
        • Most users in LA, but spread over chicago, NY, parkersburg, san jose, atlanta, all over.
        • ~75% mobile.
        • Overall, both are incredible tools. FMD comes with more configured out of the box, so you get rich statistic by route/user/etc with very small integration. Google requires more config to get the same (obviously, you’re going through tag instead of direct flask).
        • FlaskDebugToolbar should be thrown in the conversation as well – maybe my favorite of the 3. FDT does full profiling though, db calls, everything. Actual instrumentation.
      • Finished https://gitlab.com/bmahlstedt/supercontest/-/issues/151.
        • Rewrote all the blueprints/routes/args.
        • Moved the statistics page over to the contest views, since you have dropdown options.
        • Added the dropdown options: season, user, league.
        • Remember:
          • url_defaults is to set values that aren’t already defined in the values dict. It’s exactly as named. If you’re calling a route, the preprocessor runs BEFORE defaults. However, url_defaults runs in all url_for() calls, whereass the preprocessor does not. So for populating all the links around the site, retaining the already-specified-args (season, league, etc), you must use defaults.
            • Sidenote – that’s why you see url_defaults calls all over the logs, about 10x the logs for preprocessor runs. The frontend is building the menu links.
          • url_value_preprocessor is to perform logic using the values dict. The most common case is extracting the information for use elsewhere (usually to the g object, so all routes using that preprocessor can have access to it). This runs before the route/view runs, so you can remove the args from that function if you’ve attributed them to g instead (you still need to have them in the endpoint spec of the route definition tho, of course). If you extract something from the values dict in url_value_preprocessor, just do values.setdefault(key, g.value) to reuse the g value since you already handled its logic.
        • Meh, overall I hate this structure. Annoying to do on a static site. Will do it properly once a ts/react SPA. Astronomically easier to manage selections and state.
        • Added a TON of cool stats views. Updating the stats logic heavily. Some interesting conclusions (all data since 2018):
          • Best coverage overall: Bills (58.6%). Worst: Jets (33.9%).
          • Best overperformance (points beyond line cover): Bills (3.2). Worst: Jets (-4.1).
          • Largest avg line: Chiefs (-5.9). Worst: Jets (6.7).
          • Our league picks chiefs the most, then rams, then pats.
          • Underdog (51.5%) covers more than favorite (46.1%).
          • No obvious correlation between line other variables (eg better coverage for favored team as line increases).
          • The favorite covers more on thurs than sun or mon.
          • Best coverage when favorite: Dolphins (61.1%). Worst: Giants (30%).
          • Best coverage when underdog: Ravens (85.7%). Worst: Jets (33.3%).
          • Visiting team (51.2%) covers more than home team (46.4%).
          • The home team covers more on sun than thurs or mon.
          • Best coverage when home team: Dolphins (67.7%). Worst: Falcons (28.6%).
          • Best coverage when visitor: Bengals (67.9%). Worst: Jets (29.6%).
        • Changed plotly.js to yref: paper so I didn’t have to hardcode the axis limits (mins and maxes) for the vertical line.
        • found a typo (“BANGLES” lol) from westgate in 2020. run this in prod to fix.
          • update lines set home_team=’BENGALS’ where id=835
        • Updated banner/lines for week 2.
          • Westgate/superbook did not update the redirect to week 2, AND the format of week 2’s table was different. Terrible.
        • Made the full change from redskins to commanders: https://gitlab.com/bmahlstedt/supercontest/-/issues/160
        • Closed the milestone: https://gitlab.com/bmahlstedt/supercontest/-/milestones/6
  • Sunday

    • Wrote a linter, explored python’s AST.
    • https://sadh.life/post/ast/
    • The builtin module “ast” is pretty good, exposing much of the tree. ast.dump(ast.parse(code), indent=2)
    • 4 categories of nodes: Literals, Variables, Statements, Expressions.
    • Each is really just a module, which is a file, which is a list of statements.
    • AST handles the chaining of operators (like 1 < 2 < 3 < 4 < 5 < 6 < x < 10 < 11).
    • Statements can be assignments, imports, assertions.
    • Expressions evaluate to a value. 2 == 3 is an expression. Comparators, function calls…
    • Python of course lets you run any expression as a statement, just discarding the return value.
    • ctx=Load vs ctx=Store. This is the context; for variable assignment, it will be store. For most others (where you’re loading info from storage), it will be Load. There’s also Del.
    • So the AST just arranges all these statements into a tree.
    • You can specifically traverse and inspect the tree with ast.NodeVisitor.
    • This is how you implement a linter. Traverse the entire AST of a module, search for what you want (eg, cyclomatic complexity by checking nested if statements, or total args in a function, etc), and enforce some conditions on it.
    • You may compile() and exec() a tree, effectively running the python from its AST rather than its source code.
    • If you have access to the source code, you may obviously modify it. If you have access to the AST, you may obviously modify that as well. This is common with NodeTransformer. Change numbers, modify statements, whatever; then just compile and exec your modified tree to get a program that behaves how you’d like it to.
    • An AST doesn’t store whitespace or other style-related information. That’s stored in a CST, concrete syntax tree. That’s the space that formatters operate in (while linters can stay in the abstract tree).