Skip to content

just messing around - #2

Open
karmataklu-bot wants to merge 174 commits into
SamirWagle:mainfrom
karmataklu-bot:main
Open

just messing around#2
karmataklu-bot wants to merge 174 commits into
SamirWagle:mainfrom
karmataklu-bot:main

Conversation

@karmataklu-bot

Copy link
Copy Markdown

I made a brave extension that calcualtes CAR from your data.

If you have time check it out!

Karma Y and others added 7 commits May 4, 2026 10:28
Updated README to reflect new project structure and features.
- Chrome extension (popup, background, analyse UI) for NEPSE CAGR calculation
- Native messaging host (nepse_host.py + wrapper shell script) bridges extension to local Python engine
- nepse_cagr_server.py runs local HTTP server for CAGR calculations
- com.nepse.cagr.json manifest registers native host with Chrome
- CLAUDE.md and SETUP.md document project and setup steps
Updated README to clarify functionality and usage details.
Added a section for the Browser Extension, detailing its architecture, files, and setup instructions for macOS.
@SamirWagle

Copy link
Copy Markdown
Owner

Loved your work. Actually there is automation implemented in this CI/CD Pipeline. We can archive your branch in community-chrome-extension branch. Please create P.R in that branch

Karma Y added 14 commits May 7, 2026 08:36
- index_history.py: scrapes all 18 NEPSE indices from ShareSansar
  - GET request with numeric index_id (not POST with text name)
  - 30-day chunks (server silently returns 0 rows for >= 31 days)
  - JSON response parsing (not HTML table)
  - Incremental mode: resumes from latest CSV date
  - Query mode: --query --index "X" --date YYYY-MM-DD
- run_index_history.py: CLI entry point mirroring run_daily.py pattern
- data/index/: initial historical data for all 18 sub-indices
…to fix MV3 CSP

Chrome MV3 blocks inline <script> blocks and CDN scripts.
- Extract all JS from analyse.html into analyse.js (external file)
- Download chart.umd.js locally (was blocked from cdnjs CDN)
- Light mode theme toggle now works
- Add topbar-right container to group theme and back buttons
- Implement switchPage function for multi-page navigation
- Back button shows on non-analyse pages, hidden on main page
- Back button navigates back to analyse (main) page
- Add corresponding CSS styling for back-btn
@SamirWagle
SamirWagle self-requested a review May 7, 2026 12:08
Karma Y and others added 7 commits May 7, 2026 18:02
…nflict

- Add asterisk note clarifying NEPSE data based on closing prices
- Remove malformed JavaScript code from HTML merge conflict
- Clean up CAGR page section
Remove height:0 from Estimated (1994-2000) legend item. Height override
prevented dashed border from rendering. Now displays properly as dashed line.
- Add company name/ticker search input field for future filtering capability
- Add "My Predictions — Next Cycle Top" section with 4 key indicators:
  * Buffett Indicator (market cap = GDP) reaching 100%
  * Daily trading volume approaching 35–40 Arab
  * Index crossing 5,000
  * 4-year cycle milestone from Sep 2022 bottom (~Sep 2026)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
…ack nav

- Add page-buffett: full blog-style article from PDF (all 5 pages)
- Include Global & Regional Buffett Indicator comparison table (11 markets)
- Wire #buffett-link click → switchPage('buffett') via JS (CSP-safe)
- Fix back button to return to previous page (bullbear → buffett → back = bullbear)
- Add buffett-indicator.pdf to extension and web_accessible_resources in manifest
- Article width set to 749px, minimal dark aesthetic
Karma Y added 30 commits July 27, 2026 10:41
Technical swing/position signal for NEPSE over a 2-week to 3-month horizon,
plus a Telegram alarm for stop, target, and time-stop events.

scripts/karma_signal.py
- Seven hard gates: liquidity, market regime, MA stack, proximity to the
  250-day high, extension, ADX, volume expansion. Score ranks survivors only.
- Two modes: swing (10-30d, +18%/-8%) and position (20-63d, +40%/-12%).
  Stops floor at 1.5x/2.5x ATR so they are never tighter than daily noise.
- Prices are backward-adjusted for bonus shares and rights. Raw NEPSE series
  gap 20-50% on ex-dates, which an unadjusted backtest reads as crashes.
- Backtest, target/stop grid, and walk-forward split, all net of NEPSE
  commission, SEBON fee, DP charge, and 7.5% short-term CGT.

scripts/karma_alerts.py
- Tracks open positions in data/karma_positions.json, checks the latest bar
  against stored levels, and pushes Telegram alerts. Credentials come from the
  environment only.
- Stale data raises a warning rather than an all-clear, and a bar breaching
  both stop and target reports the stop.

Measured, not claimed. The requested 70% hit rate on a +40% target does not
appear anywhere in a 30-config grid; the best is 49.1% at +15%/-15%. Walk-forward
at 2023-01-01 shows material regime dependence: position mode nets +7.03% per
trade in sample versus +1.02% out of sample, and swing nets +0.19% out of
sample, which is noise. Survivorship bias and slippage remain unmodeled.
`size` converts risk-per-trade into position size via the stop width, and
prints the losing-streak drawdown at that risk level.

Risk per trade and position size are different numbers: a 12% stop turns 1%
risk into an 8.3% position. Defaults to 1% risk, 6% total heat, which caps
concurrent positions at 6 — the binding constraint on NEPSE, where positions
are highly correlated and six names behave as one bet in a drawdown.

At the measured 39.6% out-of-sample win rate, 5% risk per trade produces a 40%
drawdown on a 10-trade losing streak, needing +67% to recover.
`tiers` slices out-of-sample outcomes by signal score, to check the assumption
that better-looking setups win more often. Backtest trades now carry their
score and indicator readings so outcomes can be conditioned on them.

Result is not a clean edge. Position mode's top score bucket nets +3.03% per
trade versus +1.02% overall, but the relationship is non-monotonic: the middle
bucket (85-94) nets +0.05%, below the bottom bucket (70-84) at +2.55%. A
genuinely predictive score would rank monotonically. Swing mode shows no
relationship at all.

Recording the tool and the caveat rather than the apparent edge.
Exit change
-----------
The default exit is now an uncapped trailing stop (2xATR behind the highest
close), not a fixed profit target.

This strategy has a set-mining payoff shape: out-of-sample the median trade
loses and the mean trade wins, so the large winners are the entire edge. A
fixed target truncates exactly those outcomes. Measured out-of-sample, per
trade net of costs:

  position  +40% target -> +1.02%   |  trail 2xATR -> +2.04%
  swing     +18% target -> +0.19%   |  trail 2xATR -> +1.18%

Both modes roughly double, medians shrink, holds shorten. 2xATR won
independently in each mode, which is corroboration rather than a fit, but it
was still chosen from three candidates on out-of-sample data — treat the exact
multiple as provisional and the direction as the finding. `--exit target`
restores the old behaviour for comparison.

karma_alerts now ratchets each position's stop on every `watch` run and never
loosens it. Adds `tail` (capped vs trailing) and `tiers` (does selectivity
pay — it does not, the score is non-monotonic) as evidence commands.

Index scrape path
-----------------
`_auto_detect_data_dir()` walked upward for any folder containing `data`. When
`scraper/data/` appeared on 2026-05-04 the walk matched it first, so every
index scrape since wrote to `scraper/data/index/` while the app read
`data/index/`. Every scrape succeeded; the data was simply invisible, and the
market_regime gate spent three months reading a frozen index. Now anchored to
the repo root with no search.

`run_daily.py` also never called the index scrape at all — now wired in after
the price scrape, along with the position-alarm check. Both are
failure-isolated and skippable via --skip-index / --skip-alerts.
Every gate in this signal is a long-only trend filter and NEPSE rose across
most of the sample, so a positive average trade proves nothing on its own.
This adds the test that can actually kill the strategy.

Each trade is now paired against the NEPSE return over its exact entry/exit
window, which isolates selection and timing from market drift. Buy-and-hold
is charged its real costs too (one round trip, 5% long-term CGT) rather than
being compared gross against a net strategy.

Two findings worth recording:

Capacity is not a constraint. 13.6 (swing) and 29.4 (position) positions are
open on an average day, so a 6-slot heat cap always fills and the annualized
figure does not need an idle-cash haircut.

Alpha is not significant. The per-trade t-stat of 2.09 was an artifact of
treating dozens of simultaneously-open, market-correlated trades as
independent draws. Collapsing to one observation per month drops it to 0.72,
and per quarter to 0.32; swing goes negative quarterly. Only 41% of months
show positive alpha. The verdict line now refuses to print "beats" below a
t-stat of 2.
The benchmark showed higher returns than buy-and-hold with no significance,
which leaves the strategy unexplained rather than disproven. This tests one
mechanism, with the hypothesis written into the docstring before the first
run: a ratcheting stop should earn its keep when the market falls and cost
something when it rises.

With ~13 independent quarters, a hypothesis invented after seeing the split
would be indistinguishable from curve-fitting, so it is committed up front.

Confirmed in both modes. Alpha is +2.11% (swing) and +2.59% (position) in
windows where NEPSE fell, and -2.16% / -1.50% where it rose; swing's
bull-market underperformance is significantly negative at t=-3.16. The two
effects roughly cancel, which explains why total alpha tested insignificant.

This is a drawdown reducer, not a return generator. Judge it on the equity
curve, not the return.

Also audited survivorship rather than continuing to assert it: 228 of 261
delisted names already carry full price history, and delisted names are 1%
of post-2023 trades. Caveat removed from the docs as overstated.
The spec was two Telegram events — stop loss, and profit target. Replacing
the fixed target with a trailing stop removed the second one entirely:
`_check` was documented "No profit target" and could only fire on a stop or
a time stop. Positions now carry a target again and `watch` alerts on it.

The trailing stop still runs, so a position can exit on the trail before
reaching the target. That is deliberate — capping winners roughly halved net
return — so the target message reports the level was reached, shows where the
trail sits, and leaves the sell decision to the trader.

A bar touching both stop and target reports the stop. `target` is read with
.get() so a position written by an earlier build cannot crash the watcher
that guards every other position.

Scan now defaults to 10 candidates per mode and prints the measured odds:

  position  41.9% win, frequency odds 0.72:1 AGAINST, payoff 2.04:1,
            money odds 1.47:1 for, EV +2.04%/trade
  swing     40.8% win, frequency odds 0.69:1 AGAINST, payoff 1.90:1,
            money odds 1.31:1 for, EV +1.19%/trade

Both ratios are printed because quoting either alone misleads: this loses
more often than it wins and profits on win size. Nothing here is 4:1.

The odds are identical across recommended stocks by design — `tiers` showed
score has no monotonic link to outcome, so a per-ticker probability would be
fabricated. Only `rewd:risk` varies, being the name's own ATR stop width.
Three months of index scrapes landed in scraper/data/index/ instead of
data/index/ because _auto_detect_data_dir walked upward and matched
scraper/data/ (created by company_id_mapping.json on 2026-05-04) before the
repo root. Every scrape succeeded, so nothing errored — the app simply kept
reading files frozen at 2026-05-06.

954 rows added, 0 removed: purely additive, no existing history rewritten.
All 17 live sub-indices now run through 2026-07-27. The `insurance` folder
stays at 2018-07-16, which is correct — NEPSE retired that sub-index when it
split into life and non-life.

The path fix is in 1393003; this is the data it should have been writing.
Recording a trade is not consent to be messaged about it. The workflow is
scan, decide, record the fill and target, and only then arm the alarm — so
positions now start with alerts_enabled false and are armed by hand:

    karma_alerts.py open NABIL --entry 560 --target-price 700
    karma_alerts.py alerts on NABIL

An unarmed position is still tracked: levels are checked, the stop still
ratchets, and a breach still updates status and prints. It simply sends
nothing. Tracking and notification are separate concerns, and conflating them
trains you to ignore the stream that carries the one alert that mattered.

Arming warns when Telegram is unconfigured, and says explicitly that the
shell it was armed in is not the environment cron runs `watch` in. A silent
credential gap is the failure this project has already been bitten by twice.

Also adds --target-price for an absolute level, which is what you actually
decide at the desk rather than a percentage off a default.
An armed position with no credentials in the environment is the worst state
this tool can occupy: it looks guarded and is not. Previously nothing was said
until a level actually tripped, and by then the message had nowhere to go —
so a quiet run read as "all clear" when nothing could have been delivered.

`watch` now checks for credentials before checking any level and warns on
stderr naming the armed tickers. This matters most under cron, which does not
read an interactive shell's exports: a token set in a terminal makes
test-telegram succeed there and changes nothing about the 17:30 run.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants