Upload folder using huggingface_hub
Browse files- README.md +13 -14
- __pycache__/app.cpython-312.pyc +0 -0
- __pycache__/rebalance_engine.cpython-312.pyc +0 -0
- app.py +53 -37
- rebalance_engine.py +22 -8
README.md
CHANGED
|
@@ -7,23 +7,22 @@ sdk: gradio
|
|
| 7 |
sdk_version: 5.50.0
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
-
short_description:
|
| 11 |
---
|
| 12 |
|
| 13 |
# DeepQuant
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
|
| 19 |
-
**
|
| 20 |
-
-
|
| 21 |
-
|
| 22 |
-
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
- **MarketGuruLLM** (fine-tuned Qwen2.5-14B) narrates every rebalance and decision in plain English.
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
|
|
|
| 7 |
sdk_version: 5.50.0
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
+
short_description: The AI-managed fund - researched, run and governed by AI
|
| 11 |
---
|
| 12 |
|
| 13 |
# DeepQuant
|
| 14 |
|
| 15 |
+
**The AI-managed fund** β its strategy was *discovered by AI research agents* through 150+ walk-forward-validated
|
| 16 |
+
strategy configurations over 21 years of survivorship-free data, and it is *run and governed by AI* end-to-end on a
|
| 17 |
+
live Alpaca **paper** account with no human in the loop.
|
| 18 |
|
| 19 |
+
- **The secret recipe:** ~60 U.S. large caps selected monthly by a proprietary AI-researched quality signal, plus a
|
| 20 |
+
crisis-only defense (40% into bonds + gold, only in confirmed severe bears). The exact signal is undisclosed;
|
| 21 |
+
every trade and result is published live.
|
| 22 |
+
- **AI governance:** a weekly AI investment committee (Analyst β adversarial Critic β PM on Claude Opus 5) reviews
|
| 23 |
+
the fund against its own 20-year envelope; changes require walk-forward proof via a code validator. Failure β HOLD.
|
| 24 |
+
- **Deterministic where it must be:** execution, risk rails (long-only, 10% cap, no leverage), reconciliation.
|
| 25 |
+
- **MarketGuruLLM** narrates every move in plain English; daily AI self-diagnosis is published openly.
|
|
|
|
| 26 |
|
| 27 |
+
Strategy v2, live since September 2026 (v1's full record is archived). Simulated research results, paper trading β
|
| 28 |
+
not investment advice.
|
__pycache__/app.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/app.cpython-312.pyc and b/__pycache__/app.cpython-312.pyc differ
|
|
|
__pycache__/rebalance_engine.cpython-312.pyc
CHANGED
|
Binary files a/__pycache__/rebalance_engine.cpython-312.pyc and b/__pycache__/rebalance_engine.cpython-312.pyc differ
|
|
|
app.py
CHANGED
|
@@ -137,6 +137,12 @@ def refresh():
|
|
| 137 |
"Weight": f"{mv / eq * 100:.1f}%" if eq else "β",
|
| 138 |
"Unrealized P&L": f'${float(p["unrealized_pl"]):,.2f} ({float(p["unrealized_plpc"]) * 100:+.1f}%)',
|
| 139 |
})
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
df = pd.DataFrame(rows) if rows else pd.DataFrame([{"Symbol": "β no open positions β"}])
|
| 141 |
curve, cnote = build_curve()
|
| 142 |
stamp = f"_Last updated {now} Β· auto-refreshes every 30 min_"
|
|
@@ -205,14 +211,16 @@ def load_governance():
|
|
| 205 |
.replace("impl_alert", "implementation-drift alert").replace("SPY/MTUM", "the S&P 500 and the momentum factor"))
|
| 206 |
if len(why) > 420:
|
| 207 |
why = why[:420].rsplit(" ", 1)[0] + "β¦"
|
| 208 |
-
CONMAP = {"
|
| 209 |
-
"
|
| 210 |
-
|
|
|
|
|
|
|
| 211 |
return (f"### π€ Strategy governance β the crew's latest decision\n"
|
| 212 |
f"**{act.strip()}** β {why}\n"
|
| 213 |
f"- **Tracking:** 8-week gap vs S&P {tr.get('gap_8w_pp', 'β')}pp β the {tr.get('pctile_8w', 'β')}th percentile "
|
| 214 |
f"of the strategy's 20-year history Β· implementation drift {tr.get('impl_drift_pp', 'β')}pp vs its own simulation\n"
|
| 215 |
-
f"- **Live strategy (v{inc.get('version', '?')}):** {sig}
|
| 216 |
f"defense = {DEFMAP.get(inc.get('defense'), inc.get('defense'))} Β· {CONMAP.get(inc.get('construction'), '10% cap Β· β€5/sector')} Β· rebalanced every {inc.get('cadence_weeks', 1)} week(s)\n"
|
| 217 |
f"- **Change policy:** decided weekly by an agentic crew β Analyst β adversarial Critic β PM (Claude Opus 5) β "
|
| 218 |
f"gated by a code validator. A variant is adopted only if it beats the incumbent on the full 20-year "
|
|
@@ -263,7 +271,7 @@ def rebalance_preview():
|
|
| 263 |
reg = tgt["regime"]
|
| 264 |
mode = "π’ OFFENSE" if reg["offense"] else "π‘οΈ DEFENSE"
|
| 265 |
sma = (f"SPY 10-wk {reg['sma10']:,.0f} vs 40-wk {reg['sma40']:,.0f}" if reg.get("sma10") else reg.get("warn", ""))
|
| 266 |
-
mix = "
|
| 267 |
nb = sum(1 for o in orders if o["side"] == "BUY")
|
| 268 |
ns = sum(1 for o in orders if o["side"] == "SELL")
|
| 269 |
traded = sum(abs(o["delta"]) for o in orders)
|
|
@@ -273,43 +281,50 @@ def rebalance_preview():
|
|
| 273 |
banner = (f"## {mode}\n**Regime:** {sma} Β· equity **${eq:,.0f}** Β· target: {mix}\n\n{summ}\n\n"
|
| 274 |
f"<sub>Preview only β **no orders are placed**. This is exactly what the agent would submit at the next "
|
| 275 |
f"Monday-after-open rebalance (notional market orders, sells before buys).</sub>")
|
| 276 |
-
|
| 277 |
-
|
|
|
|
|
|
|
|
|
|
| 278 |
if orders:
|
| 279 |
-
|
| 280 |
-
|
| 281 |
-
|
|
|
|
| 282 |
else:
|
| 283 |
-
odf = pd.DataFrame([{"
|
| 284 |
return banner, tdf, odf
|
| 285 |
|
| 286 |
|
| 287 |
with gr.Blocks(title="DeepQuant β live paper monitor", theme=gr.themes.Soft()) as demo:
|
| 288 |
gr.Markdown(
|
| 289 |
"# π€ DeepQuant\n"
|
| 290 |
-
"###
|
| 291 |
-
"DeepQuant
|
| 292 |
-
"
|
| 293 |
-
"
|
| 294 |
-
"
|
| 295 |
-
"**The
|
| 296 |
-
"-
|
| 297 |
-
"
|
| 298 |
-
"
|
| 299 |
-
"
|
| 300 |
-
"(
|
| 301 |
-
"-
|
| 302 |
-
"
|
| 303 |
-
"-
|
| 304 |
-
"
|
| 305 |
-
"
|
| 306 |
-
"-
|
| 307 |
-
"
|
| 308 |
-
"
|
| 309 |
-
"
|
| 310 |
-
"
|
| 311 |
-
"
|
| 312 |
-
"
|
|
|
|
|
|
|
|
|
|
| 313 |
with gr.Tabs():
|
| 314 |
with gr.Tab("π Live account"):
|
| 315 |
stats = gr.Markdown()
|
|
@@ -319,7 +334,8 @@ with gr.Blocks(title="DeepQuant β live paper monitor", theme=gr.themes.Soft())
|
|
| 319 |
plot = gr.LinePlot(x="date", y="value", color="series", height=320,
|
| 320 |
y_title="Account value ($)", x_title="")
|
| 321 |
note = gr.Markdown(visible=False)
|
| 322 |
-
gr.Markdown("### π
|
|
|
|
| 323 |
tbl = gr.Dataframe(wrap=True, interactive=False)
|
| 324 |
gov = gr.Markdown()
|
| 325 |
with gr.Row():
|
|
@@ -333,10 +349,10 @@ with gr.Blocks(title="DeepQuant β live paper monitor", theme=gr.themes.Soft())
|
|
| 333 |
pbtn = gr.Button("β³ Recompute preview", scale=0)
|
| 334 |
with gr.Row():
|
| 335 |
with gr.Column():
|
| 336 |
-
gr.Markdown("**Target
|
| 337 |
tgt_tbl = gr.Dataframe(interactive=False, wrap=True)
|
| 338 |
with gr.Column():
|
| 339 |
-
gr.Markdown("**Proposed orders** β sells
|
| 340 |
ord_tbl = gr.Dataframe(interactive=False, wrap=True)
|
| 341 |
with gr.Accordion("𧬠Why hybrid? Who decides what β and why", open=False):
|
| 342 |
gr.Markdown(
|
|
|
|
| 137 |
"Weight": f"{mv / eq * 100:.1f}%" if eq else "β",
|
| 138 |
"Unrealized P&L": f'${float(p["unrealized_pl"]):,.2f} ({float(p["unrealized_plpc"]) * 100:+.1f}%)',
|
| 139 |
})
|
| 140 |
+
rows.sort(key=lambda r: -float(str(r["Mkt value"]).replace("$", "").replace(",", "")))
|
| 141 |
+
hidden = max(0, len(rows) - 10)
|
| 142 |
+
rows = rows[:10]
|
| 143 |
+
if hidden:
|
| 144 |
+
rows.append({"Symbol": f"β¦ +{hidden} more", "Qty": "", "Avg entry": "", "Last": "",
|
| 145 |
+
"Mkt value": "undisclosed", "Weight": "", "Unrealized P&L": ""})
|
| 146 |
df = pd.DataFrame(rows) if rows else pd.DataFrame([{"Symbol": "β no open positions β"}])
|
| 147 |
curve, cnote = build_curve()
|
| 148 |
stamp = f"_Last updated {now} Β· auto-refreshes every 30 min_"
|
|
|
|
| 211 |
.replace("impl_alert", "implementation-drift alert").replace("SPY/MTUM", "the S&P 500 and the momentum factor"))
|
| 212 |
if len(why) > 420:
|
| 213 |
why = why[:420].rsplit(" ", 1)[0] + "β¦"
|
| 214 |
+
CONMAP = {"n60_nocap": "~60 AI-selected large caps Β· 10% position cap", "n50_nocap": "~50 AI-selected large caps Β· 10% cap",
|
| 215 |
+
"n75_nocap": "~75 AI-selected large caps Β· 10% cap", "abs10_sec5": "10% cap Β· β€5/sector"}
|
| 216 |
+
REGMAP = {"gate20": "crisis-only severity gate (trend break + deep-drawdown confirmation)",
|
| 217 |
+
"gate15": "severity gate (15%)", "cross10/40": "10/40-week trend crossover"}
|
| 218 |
+
sig = "proprietary AI-researched quality signal (undisclosed)"
|
| 219 |
return (f"### π€ Strategy governance β the crew's latest decision\n"
|
| 220 |
f"**{act.strip()}** β {why}\n"
|
| 221 |
f"- **Tracking:** 8-week gap vs S&P {tr.get('gap_8w_pp', 'β')}pp β the {tr.get('pctile_8w', 'β')}th percentile "
|
| 222 |
f"of the strategy's 20-year history Β· implementation drift {tr.get('impl_drift_pp', 'β')}pp vs its own simulation\n"
|
| 223 |
+
f"- **Live strategy (v{inc.get('version', '?')}):** {sig} Β· {REGMAP.get(inc.get('regime'), inc.get('regime'))} Β· "
|
| 224 |
f"defense = {DEFMAP.get(inc.get('defense'), inc.get('defense'))} Β· {CONMAP.get(inc.get('construction'), '10% cap Β· β€5/sector')} Β· rebalanced every {inc.get('cadence_weeks', 1)} week(s)\n"
|
| 225 |
f"- **Change policy:** decided weekly by an agentic crew β Analyst β adversarial Critic β PM (Claude Opus 5) β "
|
| 226 |
f"gated by a code validator. A variant is adopted only if it beats the incumbent on the full 20-year "
|
|
|
|
| 271 |
reg = tgt["regime"]
|
| 272 |
mode = "π’ OFFENSE" if reg["offense"] else "π‘οΈ DEFENSE"
|
| 273 |
sma = (f"SPY 10-wk {reg['sma10']:,.0f} vs 40-wk {reg['sma40']:,.0f}" if reg.get("sma10") else reg.get("warn", ""))
|
| 274 |
+
mix = "~60 AI-selected stocks Β· 100% equity" if reg["offense"] else "60% stocks + 20% AGG + 20% GLD (crisis defense)"
|
| 275 |
nb = sum(1 for o in orders if o["side"] == "BUY")
|
| 276 |
ns = sum(1 for o in orders if o["side"] == "SELL")
|
| 277 |
traded = sum(abs(o["delta"]) for o in orders)
|
|
|
|
| 281 |
banner = (f"## {mode}\n**Regime:** {sma} Β· equity **${eq:,.0f}** Β· target: {mix}\n\n{summ}\n\n"
|
| 282 |
f"<sub>Preview only β **no orders are placed**. This is exactly what the agent would submit at the next "
|
| 283 |
f"Monday-after-open rebalance (notional market orders, sells before buys).</sub>")
|
| 284 |
+
import collections as _c
|
| 285 |
+
secagg = _c.defaultdict(float)
|
| 286 |
+
for r in tgt["rows"]: secagg[r["sector"]] += r["weight"]
|
| 287 |
+
tdf = pd.DataFrame([{"Sector": k, "Target %": f"{v*100:.1f}%", "Names": sum(1 for r in tgt["rows"] if r["sector"] == k)}
|
| 288 |
+
for k, v in sorted(secagg.items(), key=lambda x: -x[1])])
|
| 289 |
if orders:
|
| 290 |
+
buys = sum(o["delta"] for o in orders if o["side"] == "BUY")
|
| 291 |
+
sells = sum(-o["delta"] for o in orders if o["side"] == "SELL")
|
| 292 |
+
odf = pd.DataFrame([{"Side": "π’ BUY", "Orders": sum(1 for o in orders if o["side"] == "BUY"), "Total": f"${buys:,.0f}"},
|
| 293 |
+
{"Side": "π» SELL", "Orders": sum(1 for o in orders if o["side"] == "SELL"), "Total": f"${sells:,.0f}"}])
|
| 294 |
else:
|
| 295 |
+
odf = pd.DataFrame([{"Side": "β already at target β", "Orders": 0, "Total": "$0"}])
|
| 296 |
return banner, tdf, odf
|
| 297 |
|
| 298 |
|
| 299 |
with gr.Blocks(title="DeepQuant β live paper monitor", theme=gr.themes.Soft()) as demo:
|
| 300 |
gr.Markdown(
|
| 301 |
"# π€ DeepQuant\n"
|
| 302 |
+
"### The AI-managed fund β researched by AI, run by AI, governed by AI\n"
|
| 303 |
+
"DeepQuant is managed end-to-end by artificial intelligence β **no human in the loop**. Its strategy was not "
|
| 304 |
+
"designed by a portfolio manager: it was **discovered by AI research agents** that stress-tested more than **150 "
|
| 305 |
+
"strategy configurations across 21 years of survivorship-free market data** β every candidate walk-forward "
|
| 306 |
+
"validated, net of costs, across five market eras β before converging on the recipe running today.\n\n"
|
| 307 |
+
"**The secret recipe:** the exact selection signal is proprietary. What we disclose: the fund holds **~60 U.S. "
|
| 308 |
+
"large caps chosen by an AI-researched quality signal**, refreshed monthly β and a **crisis-only defense**: in "
|
| 309 |
+
"confirmed severe bears (and only then) it shifts 40% into bonds and gold. No reaction to noise, corrections, "
|
| 310 |
+
"or headlines. Every trade and every result is published here, live.\n\n"
|
| 311 |
+
"**The AI stack:**\n"
|
| 312 |
+
"- π§ **[MarketFM](https://huggingface.co/spaces/s-ttp/marketfm-ranker)** β the AI research platform: ~11,800 US "
|
| 313 |
+
"stocks, 4.1M stock-weeks, 21 point-in-time factors β the laboratory where the recipe was found and is re-verified\n"
|
| 314 |
+
"- π€ **An AI investment committee** (Claude Opus 5) β every week an **Analyst** reviews the fund against its own "
|
| 315 |
+
"20-year statistical envelope, an adversarial **Critic** attacks, and a **PM** rules: hold, adopt a better-proven "
|
| 316 |
+
"variant, or pause β every action gated by a **code validator** (walk-forward proof required, rate-limited)\n"
|
| 317 |
+
"- βοΈ **Deterministic execution** β orders, risk rails (long-only, 10% cap, no leverage), market-hours gates, "
|
| 318 |
+
"self-reconciliation: the parts where correctness must be provable are code, not judgment\n"
|
| 319 |
+
"- π©Ί **AI self-diagnosis** β daily health checks published openly; deviation from the strategy's own history is "
|
| 320 |
+
"flagged before anyone asks\n"
|
| 321 |
+
"- π¬ **[MarketGuruLLM](https://huggingface.co/s-ttp/qwen25-14b-fin-lora)** β a fine-tuned analyst model explains "
|
| 322 |
+
"every move in plain English\n\n"
|
| 323 |
+
"**Simulated record of the recipe (2005β2026, walk-forward, net of costs):** ~14%/yr vs the S&P 500's ~11% Β· "
|
| 324 |
+
"beat the index in **14 of 22 years** and **~69% of all rolling 3-year windows** Β· 2008 cushioned to β29% Β· "
|
| 325 |
+
"verified across 4 of 5 market eras.\n"
|
| 326 |
+
"<sub>Fully autonomous, paper trading, read-only page. Strategy v2, live since September 2026; the v1 record is "
|
| 327 |
+
"archived and available. Simulated results β past performance β future. Not investment advice.</sub>")
|
| 328 |
with gr.Tabs():
|
| 329 |
with gr.Tab("π Live account"):
|
| 330 |
stats = gr.Markdown()
|
|
|
|
| 334 |
plot = gr.LinePlot(x="date", y="value", color="series", height=320,
|
| 335 |
y_title="Account value ($)", x_title="")
|
| 336 |
note = gr.Markdown(visible=False)
|
| 337 |
+
gr.Markdown("### π Top 10 holdings\n<sub>Top 10 by value. The full ~60-name book is not disclosed β "
|
| 338 |
+
"the selection signal is the fund's proprietary, AI-researched recipe.</sub>")
|
| 339 |
tbl = gr.Dataframe(wrap=True, interactive=False)
|
| 340 |
gov = gr.Markdown()
|
| 341 |
with gr.Row():
|
|
|
|
| 349 |
pbtn = gr.Button("β³ Recompute preview", scale=0)
|
| 350 |
with gr.Row():
|
| 351 |
with gr.Column():
|
| 352 |
+
gr.Markdown("**Target allocation by sector** <sub>(name-level book undisclosed)</sub>")
|
| 353 |
tgt_tbl = gr.Dataframe(interactive=False, wrap=True)
|
| 354 |
with gr.Column():
|
| 355 |
+
gr.Markdown("**Proposed orders** β aggregate (sells execute first)")
|
| 356 |
ord_tbl = gr.Dataframe(interactive=False, wrap=True)
|
| 357 |
with gr.Accordion("𧬠Why hybrid? Who decides what β and why", open=False):
|
| 358 |
gr.Markdown(
|
rebalance_engine.py
CHANGED
|
@@ -42,14 +42,15 @@ def cap_weights(w, mw=MAX_W):
|
|
| 42 |
return w
|
| 43 |
|
| 44 |
|
| 45 |
-
DEFAULT_CFG = {"signal": ["
|
| 46 |
-
"defense": "mom60_agg20_gld20", "cadence_weeks":
|
| 47 |
|
| 48 |
|
| 49 |
CONSTRUCTIONS = {
|
| 50 |
-
"
|
| 51 |
-
"
|
| 52 |
-
"
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
|
|
@@ -83,12 +84,25 @@ def _spy_series():
|
|
| 83 |
return pd.Series({k: float(v["5. adjusted close"]) for k, v in ts.items()}).sort_index()
|
| 84 |
|
| 85 |
|
| 86 |
-
def
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 87 |
try:
|
| 88 |
s = _spy_series()
|
| 89 |
p = float(s.iloc[-1]); s10 = float(s.rolling(10).mean().iloc[-1]); s20 = float(s.rolling(20).mean().iloc[-1])
|
| 90 |
s40 = float(s.rolling(40).mean().iloc[-1]); p_prev = float(s.iloc[-2]); s40_prev = float(s.rolling(40).mean().iloc[-2])
|
| 91 |
-
if rule
|
|
|
|
|
|
|
|
|
|
| 92 |
off = p >= s40
|
| 93 |
elif rule == "dual20/40":
|
| 94 |
off = True if (p >= s20 and p >= s40) else (False if (p < s20 and p < s40) else (s10 >= s40))
|
|
@@ -118,7 +132,7 @@ def _rank_sleeve(df, cols, sign=1, con=None):
|
|
| 118 |
continue
|
| 119 |
sec[r["sector"]] = sec.get(r["sector"], 0) + 1
|
| 120 |
picked.append(r)
|
| 121 |
-
if len(picked) >= SIZE:
|
| 122 |
break
|
| 123 |
mc = np.array([r["marketcap"] for r in picked], float)
|
| 124 |
if con["rule"] == "rel":
|
|
|
|
| 42 |
return w
|
| 43 |
|
| 44 |
|
| 45 |
+
DEFAULT_CFG = {"signal": ["n_roa"], "regime": "gate20",
|
| 46 |
+
"defense": "mom60_agg20_gld20", "cadence_weeks": 4, "construction": "n60_nocap", "version": 2}
|
| 47 |
|
| 48 |
|
| 49 |
CONSTRUCTIONS = {
|
| 50 |
+
"n60_nocap": {"rule": "abs", "hard": 0.10, "buffer": 0.0, "sector_cap": 999, "size": 60},
|
| 51 |
+
"n50_nocap": {"rule": "abs", "hard": 0.10, "buffer": 0.0, "sector_cap": 999, "size": 50},
|
| 52 |
+
"n75_nocap": {"rule": "abs", "hard": 0.10, "buffer": 0.0, "sector_cap": 999, "size": 75},
|
| 53 |
+
"abs10_sec5": {"rule": "abs", "hard": 0.10, "buffer": 0.0, "sector_cap": 5, "size": 50},
|
| 54 |
}
|
| 55 |
|
| 56 |
|
|
|
|
| 84 |
return pd.Series({k: float(v["5. adjusted close"]) for k, v in ts.items()}).sort_index()
|
| 85 |
|
| 86 |
|
| 87 |
+
def _trail_dd(closes, n=52):
|
| 88 |
+
win = list(closes[-n:])
|
| 89 |
+
if len(win) < 26:
|
| 90 |
+
return 0.0
|
| 91 |
+
pk, m = win[0], 0.0
|
| 92 |
+
for p in win:
|
| 93 |
+
pk = max(pk, p); m = max(m, 1 - p/pk)
|
| 94 |
+
return m
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def detect_regime(rule="gate20"):
|
| 98 |
try:
|
| 99 |
s = _spy_series()
|
| 100 |
p = float(s.iloc[-1]); s10 = float(s.rolling(10).mean().iloc[-1]); s20 = float(s.rolling(20).mean().iloc[-1])
|
| 101 |
s40 = float(s.rolling(40).mean().iloc[-1]); p_prev = float(s.iloc[-2]); s40_prev = float(s.rolling(40).mean().iloc[-2])
|
| 102 |
+
if rule in ("gate20", "gate15"):
|
| 103 |
+
thr = 0.20 if rule == "gate20" else 0.15
|
| 104 |
+
off = not ((s10 < s40) and (_trail_dd(list(s.values)) >= thr))
|
| 105 |
+
elif rule == "single40":
|
| 106 |
off = p >= s40
|
| 107 |
elif rule == "dual20/40":
|
| 108 |
off = True if (p >= s20 and p >= s40) else (False if (p < s20 and p < s40) else (s10 >= s40))
|
|
|
|
| 132 |
continue
|
| 133 |
sec[r["sector"]] = sec.get(r["sector"], 0) + 1
|
| 134 |
picked.append(r)
|
| 135 |
+
if len(picked) >= con.get("size", SIZE):
|
| 136 |
break
|
| 137 |
mc = np.array([r["marketcap"] for r in picked], float)
|
| 138 |
if con["rule"] == "rel":
|