DEV Community

Alexander M
Alexander M

Posted on

Flip Check: the 70% rule, plus the flags a hard-money lender actually uses

Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle Submission 💸

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

Flip Check is a small open-source tool for people who wholesale houses or buy them to fix and flip. The input is the text they already have in a message: asking price, rehab, after-repair value, rent, and the hard-money terms. The output is a verdict, go, conditional, or no-go, and the figures behind it.

The figures are the 70% rule and the maximum allowable offer, the cushion between that offer and the price, flip profit after closing costs, selling costs, points, and interest carry, cash-on-cash on the cash actually left in the deal, DSCR when there is rent and a loan payment, and a list of flags a hard-money lender tends to catch. Those flags include a loan over 70% of ARV, loan-to-cost over 90%, a rehab bigger than the purchase, a term shorter than the hold, and profit under $15,000 or under 10% of ARV.

The arithmetic is ordinary Python. An open-weight model writes a two-paragraph memo from those figures. It is told not to redo the math. If you do not set an API key, an offline writer produces the memo from the same figures, and the verdict does not change.

Who would pay: a wholesaler screening a lot of deals in a week, or a newer flipper who has had a lender kick a file back for something the 70% rule does not show. I would charge for a check that runs before they call the seller back. I would not charge for a platform. I have not charged anyone, and I have not put this in front of a user.

This is the sample deal in the repo:

Asking 165k, needs about 48k rehab, ARV 310k, rents for $1,950. Hard money at 11.5% interest-only, 2 points, 12-month term, 85% LTC. Taxes $210/mo, insurance $140/mo. Plan to hold 5 months.

The 70% rule MAO is $169,000. The price is $4,000 under that, so a napkin check says yes. Profit after the default costs (3% buyer closing, 8% selling costs, two points, and five months of interest, tax, and insurance) is $53,204. Cash in is $50,946, so cash-on-cash on the project is 104.4%, about 250.6% if you annualize a five-month hold. DSCR on the bridge payment is 0.78, which means the rent does not carry the hard-money loan. The verdict is conditional, because a $4,000 cushion is under the $5,000 floor the tool uses. One missed repair item and the deal fails the same rule it just passed. The weak bridge DSCR is reported as a note. A hard-money lender is not running a 1.25 DSCR test on a 12-month interest-only note, and treating it as a no-go would cry wolf on almost every flip.

That split is the product. The rule of thumb and the lender's file are not the same test. The 70% rule already hides a rough allowance for costs and profit. Flip Check still subtracts the line items, and it shows which defaults it used, because those are different questions.

Demo

There is no hosted demo. I did not deploy it.

python3 -m pip install -e ".[dev]"
python3 -m uvicorn app.main:app --host 127.0.0.1 --port 8741
Enter fullscreen mode Exit fullscreen mode

Open http://127.0.0.1:8741 and use Load example, or visit /?example=1. The same check from the CLI:

python3 -m app.cli --example
Enter fullscreen mode Exit fullscreen mode
Verdict                CONDITIONAL
70% rule MAO           $169,000
Price                  $165,000
Cushion                $4,000
Profit                 $53,204
Cash in                $50,946
Cash-on-cash           104.4%
Annualized             250.6%
DSCR on the bridge     0.78
Loan                   $181,050
ARV LTV                58.4%
Loan-to-cost           85.0%
Enter fullscreen mode Exit fullscreen mode

The screenshot below is the local page on that example.

Flip Check showing a conditional verdict on the sample deal

Code

Flip Check

MIT license.

How I Built It

The decision path is split on purpose.

app/analyze.py computes every figure the verdict uses. The thresholds are constants at the top of that file. tests/test_analyze.py locks the sample deal, including the interest-only carry, and it locks a standard $100,000, 6%, 30-year payment at $599.55. The model never sees a deal until those numbers exist.

app/parse.py reads a paste. It accepts JSON, key: value lines, and a looser message like the sample (165k, $1,950, 11.5%, 2 points, 85% LTC, hold 5 months). Parsing is deterministic and tested with no network call.

app/explain.py sends the finished analysis to an OpenAI-compatible chat completions endpoint. Three environment variables configure it:

  • FLIPCHECK_BASE_URL, default http://127.0.0.1:11434/v1, which is Ollama's local OpenAI-compatible server
  • FLIPCHECK_MODEL, default qwen2.5:7b, the Ollama tag for Qwen2.5 7B Instruct, an open-weight model
  • FLIPCHECK_API_KEY, no default. Empty means do not call the network

llama.cpp's server, vLLM, or a hosted endpoint for the same class of model work by changing those three. The prompt says the verdict is already decided, to use only the numbers in the JSON, and to write two short paragraphs with no slogans. If the key is missing, the request fails, or the reply is empty, the offline writer fills the same two paragraphs from the same JSON. The page labels which one you got. Tests use a fake client to check that the base URL, the key, and the model are the ones from the environment, and that a failed call falls back without putting the key in the warning.

The web UI is one HTML page served by FastAPI. The CLI is python3 -m app.cli. Both call app/service.py. pytest covers the math, the parser, the API, and the CLI. Nothing in the test run calls a live model.

Why Does Open Innovation Matter?

A wholesaler's deal tape is the business. Price, rehab, rent, and the lender's terms describe an off-market lead. Pointing Flip Check at a local open-weight model keeps that text on the machine. A closed API would make the privacy of the lead depend on a vendor's retention policy.

The job the model does is small. It writes a memo from numbers the code already produced, which is why a 7B open-weight model is enough and why the verdict does not depend on it. Someone screening deals may run this dozens of times a week. Local inference is electricity. A closed meter charges for the paragraph, which is the cheap part, and still does not make the arithmetic more trustworthy. If a better small model comes out, you change FLIPCHECK_MODEL. The analyzer stays.

I did not fine-tune anything. The open piece is the right to run the explaining model yourself and to replace it. The part that has to be right every time is not a model at all.

My Agent Session

I do not have a DevRelay session for this build. Nothing to embed. The commits are the record of how it went together.

Top comments (0)