Files
lm_code/交易/bitmart-优化v2.py
Your Name b5af5b07f3 哈哈
2026-02-15 02:16:45 +08:00

188 lines
7.7 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
EMA趋势策略 - 精简参数优化 v2
已知EMA(8/21/120) ATR>0.03% SL=0.4% MaxH=1800 → 方向PnL +327, 净亏 -101
优化目标:找到净盈利 > 0 的参数组合
策略核心减少交易次数更长EMA/更高ATR门槛提高每笔质量
"""
import sys, time, datetime, sqlite3
from pathlib import Path
class EMA:
__slots__ = ('k', 'v')
def __init__(self, p):
self.k = 2.0 / (p + 1)
self.v = None
def update(self, price):
if self.v is None:
self.v = price
else:
self.v = price * self.k + self.v * (1 - self.k)
return self.v
def load():
db = Path(__file__).parent.parent / 'models' / 'database.db'
s = int(datetime.datetime(2025,1,1).timestamp()) * 1000
e = int(datetime.datetime(2026,1,1).timestamp()) * 1000
conn = sqlite3.connect(str(db))
rows = conn.cursor().execute(
"SELECT id,open,high,low,close FROM bitmart_eth_1m WHERE id>=? AND id<? ORDER BY id", (s,e)
).fetchall()
conn.close()
# pre-convert to list of tuples for speed
out = []
for r in rows:
out.append((datetime.datetime.fromtimestamp(r[0]/1000.0), r[1], r[2], r[3], r[4]))
return out
def bt(data, fp, sp, bp, atr_min, sl_pct, mh):
bal = 1000.0
pos = 0; op = 0.0; ot = None; ps = 0.0; pend = None
ef = EMA(fp); es = EMA(sp); eb = EMA(bp)
hs_buf = []; ls_buf = []; cs_buf = []
pf_ = None; ps_ = None
tc=0; wc=0; dpnl=0.0; tfee=0.0; treb=0.0
hsl = sl_pct * 1.5
LEV=50; RP=0.005; TF=0.0006; RR=0.90; MH=200; AP=14
for dt, o_, h_, l_, c_ in data:
p = c_
hs_buf.append(h_); ls_buf.append(l_); cs_buf.append(p)
fast = ef.update(p); slow = es.update(p); big = eb.update(p)
atr_pct = 0.0
if len(hs_buf) > AP + 1:
s = 0.0
for i in range(-AP, 0):
tr = hs_buf[i] - ls_buf[i]
d1 = abs(hs_buf[i] - cs_buf[i-1])
d2 = abs(ls_buf[i] - cs_buf[i-1])
if d1 > tr: tr = d1
if d2 > tr: tr = d2
s += tr
atr_pct = s / (AP * p) if p > 0 else 0
cu = pf_ is not None and pf_ <= ps_ and fast > slow
cd = pf_ is not None and pf_ >= ps_ and fast < slow
pf_ = fast; ps_ = slow
if pos != 0 and ot is not None:
pp = (p - op) / op if pos == 1 else (op - p) / op
hsec = (dt - ot).total_seconds()
if -pp >= hsl:
pnl_ = ps * pp; cv = ps*(1+pp); cf = cv*TF; of_=ps*TF; tt=of_+cf; rb=tt*RR
bal += pnl_ - cf + rb; dpnl += pnl_; tfee += tt; treb += rb
tc += 1; wc += (1 if pnl_ > 0 else 0)
pos=0; op=0; ot=None; ps=0; pend=None
continue
if hsec >= MH:
do_c = False
if -pp >= sl_pct: do_c = True
elif hsec >= mh: do_c = True
elif pos == 1 and cd: do_c = True
elif pos == -1 and cu: do_c = True
elif pend == 'cl' and pos == 1: do_c = True
elif pend == 'cs' and pos == -1: do_c = True
if do_c:
pnl_ = ps * pp; cv = ps*(1+pp); cf = cv*TF; of_=ps*TF; tt=of_+cf; rb=tt*RR
bal += pnl_ - cf + rb; dpnl += pnl_; tfee += tt; treb += rb
tc += 1; wc += (1 if pnl_ > 0 else 0)
pos=0; op=0; ot=None; ps=0; pend=None
if atr_pct >= atr_min:
if (cd or fast < slow) and p < big:
ns = bal * RP * LEV
if ns >= 1: bal -= ns*TF; pos=-1; op=p; ot=dt; ps=ns
elif (cu or fast > slow) and p > big:
ns = bal * RP * LEV
if ns >= 1: bal -= ns*TF; pos=1; op=p; ot=dt; ps=ns
continue
else:
if pos == 1 and cd: pend = 'cl'
elif pos == -1 and cu: pend = 'cs'
if pos == 0 and atr_pct >= atr_min:
if cu and p > big:
ns = bal * RP * LEV
if ns >= 1: bal -= ns*TF; pos=1; op=p; ot=dt; ps=ns
elif cd and p < big:
ns = bal * RP * LEV
if ns >= 1: bal -= ns*TF; pos=-1; op=p; ot=dt; ps=ns
if pos != 0:
p = data[-1][4]
pp = (p - op) / op if pos == 1 else (op - p) / op
pnl_ = ps * pp; cv = ps*(1+pp); cf = cv*TF; of_=ps*TF; tt=of_+cf; rb=tt*RR
bal += pnl_ - cf + rb; dpnl += pnl_; tfee += tt; treb += rb
tc += 1; wc += (1 if pnl_ > 0 else 0)
net = bal - 1000.0
wr = wc/tc*100 if tc > 0 else 0
return net, tc, wr, dpnl, treb, tfee - treb
def main():
print("Loading...", flush=True)
data = load()
print(f"{len(data)} bars loaded\n", flush=True)
# 精简参数网格 - 只测最有潜力的范围
combos = []
for fp in [8, 13, 20, 30]:
for sp in [21, 34, 55, 80]:
if fp >= sp: continue
for bp in [120, 200]:
for am in [0.0003, 0.0006, 0.001, 0.0015, 0.002]:
for sl in [0.004, 0.006, 0.008, 0.01]:
for mh in [1200, 1800, 3600]:
combos.append((fp, sp, bp, am, sl, mh))
print(f"Combos: {len(combos)}", flush=True)
results = []
t0 = time.time()
for idx, (fp, sp, bp, am, sl, mh) in enumerate(combos):
net, tc, wr, dp, reb, nf = bt(data, fp, sp, bp, am, sl, mh)
results.append((net, tc, wr, dp, reb, nf, fp, sp, bp, am, sl, mh))
if (idx+1) % 30 == 0:
el = time.time() - t0
eta = el/(idx+1)*(len(combos)-idx-1)
print(f" [{idx+1}/{len(combos)}] {el:.0f}s done, ~{eta:.0f}s left", flush=True)
tt = time.time() - t0
print(f"\nAll done! {len(results)} combos in {tt:.1f}s\n", flush=True)
results.sort(key=lambda x: x[0], reverse=True)
profitable = [r for r in results if r[0] > 0]
print(f"Profitable: {len(profitable)} / {len(results)} ({len(profitable)/len(results)*100:.1f}%)\n", flush=True)
print(f"{'='*130}", flush=True)
print(f" TOP 30", flush=True)
print(f"{'='*130}", flush=True)
print(f" {'#':>3} {'F':>3} {'S':>3} {'B':>4} {'ATR':>6} {'SL':>5} {'MH':>5} | {'Net%':>7} {'Net$':>9} {'#Trd':>6} {'WR':>6} {'DirPnL':>9} {'Rebate':>9} {'NetFee':>8}", flush=True)
print(f" {'-'*120}", flush=True)
for i, (net, tc, wr, dp, reb, nf, fp, sp, bp, am, sl, mh) in enumerate(results[:30]):
mk = " <-- $$" if net > 0 else ""
print(f" {i+1:>3} {fp:>3} {sp:>3} {bp:>4} {am*100:>5.2f}% {sl*100:>4.1f}% {mh:>5} | {net/10:>+6.2f}% {net:>+8.2f} {tc:>6} {wr:>5.1f}% {dp:>+8.2f} {reb:>8.2f} {nf:>8.2f}{mk}", flush=True)
if profitable:
print(f"\n{'='*130}", flush=True)
print(f" ALL PROFITABLE COMBOS", flush=True)
print(f"{'='*130}", flush=True)
for i, (net, tc, wr, dp, reb, nf, fp, sp, bp, am, sl, mh) in enumerate(profitable):
print(f" {i+1:>3} EMA({fp}/{sp}/{bp}) ATR>{am*100:.2f}% SL={sl*100:.1f}% MH={mh}s | net={net:+.2f} ({net/10:+.2f}%) trades={tc} WR={wr:.1f}% dirPnL={dp:+.2f} rebate={reb:.2f} netFee={nf:.2f}", flush=True)
# Save
csv = Path(__file__).parent.parent / 'param_results.csv'
with open(csv, 'w', encoding='utf-8-sig') as f:
f.write("fast,slow,big,atr_min,stop_loss,max_hold,net_pct,net_usd,trades,win_rate,dir_pnl,rebate,net_fee\n")
for net, tc, wr, dp, reb, nf, fp, sp, bp, am, sl, mh in results:
f.write(f"{fp},{sp},{bp},{am},{sl},{mh},{net/10:.4f},{net:.4f},{tc},{wr:.2f},{dp:.4f},{reb:.4f},{nf:.4f}\n")
print(f"\nSaved: {csv}", flush=True)
if __name__ == '__main__':
main()