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

185 lines
7.7 KiB
Python
Raw 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趋势策略 - 精准优化(~50组参数约2分钟
已知基线EMA(8/21/120) ATR>0.03% SL=0.4% MH=1800s
→ 14066 trades, dirPnL +327, netFee 428, net -101
核心优化思路:
1. 提高 ATR 门槛 → 减少交易次数,只在波动大时交易
2. 加长 EMA 周期 → 减少交叉频率
3. 放宽止损 → 让趋势有更多空间发展
4. 延长最大持仓 → 捕获更大行情
"""
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, x):
self.v = x if self.v is None else x * 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()
return [(datetime.datetime.fromtimestamp(r[0]/1000.0), r[1], r[2], r[3], r[4]) for r in rows]
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)
H=[]; L=[]; C=[]
pf_=None; ps_=None
tc=0; wc=0; dpnl=0.0; tfee=0.0; treb=0.0
hsl=sl_pct*1.5; AP=14
def _close(price, dt_):
nonlocal bal,pos,op,ot,ps,pend,tc,wc,dpnl,tfee,treb
pp = (price-op)/op if pos==1 else (op-price)/op
pnl_=ps*pp; cv=ps*(1+pp); cf=cv*0.0006; of_=ps*0.0006; tt=of_+cf; rb=tt*0.9
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
def _open(d, price, dt_):
nonlocal bal,pos,op,ot,ps
ns=bal*0.005*50
if ns<1: return
bal-=ns*0.0006
pos=1 if d=='L' else -1; op=price; ot=dt_; ps=ns
for dt,o_,h_,l_,c_ in data:
p=c_; H.append(h_); L.append(l_); C.append(p)
fast=ef.update(p); slow=es.update(p); big=eb.update(p)
atr_pct=0.0
if len(H)>AP+1:
s=0.0
for i in range(-AP,0):
tr=H[i]-L[i]; d1=abs(H[i]-C[i-1]); d2=abs(L[i]-C[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:
pp=(p-op)/op if pos==1 else (op-p)/op
hsec=(dt-ot).total_seconds()
if -pp>=hsl: _close(p,dt); continue
if hsec>=200:
dc=False
if -pp>=sl_pct: dc=True
elif hsec>=mh: dc=True
elif pos==1 and cd: dc=True
elif pos==-1 and cu: dc=True
elif pend=='cl' and pos==1: dc=True
elif pend=='cs' and pos==-1: dc=True
if dc:
_close(p,dt)
if atr_pct>=atr_min:
if (cd or fast<slow) and p<big: _open('S',p,dt)
elif (cu or fast>slow) and p>big: _open('L',p,dt)
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: _open('L',p,dt)
elif cd and p<big: _open('S',p,dt)
if pos!=0: _close(data[-1][4], data[-1][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\n", flush=True)
# ~50 targeted combos
combos = []
# Group 1: baseline variations (tweak one param at a time)
base = (8, 21, 120, 0.0003, 0.004, 1800)
# Vary ATR threshold (most impactful for reducing trades)
for am in [0.0003, 0.0005, 0.0008, 0.001, 0.0013, 0.0015, 0.002, 0.003]:
combos.append((8, 21, 120, am, 0.004, 1800))
combos.append((8, 21, 120, am, 0.006, 1800))
combos.append((8, 21, 120, am, 0.008, 1800))
# Vary EMA periods
for fp, sp in [(13, 34), (13, 55), (20, 55), (20, 80), (30, 80)]:
for am in [0.0005, 0.001, 0.0015, 0.002]:
combos.append((fp, sp, 120, am, 0.005, 1800))
combos.append((fp, sp, 200, am, 0.005, 1800))
combos.append((fp, sp, 120, am, 0.008, 3600))
combos.append((fp, sp, 200, am, 0.008, 3600))
# Vary max hold
for mh in [900, 1800, 3600, 5400, 7200]:
combos.append((8, 21, 120, 0.001, 0.005, mh))
combos.append((13, 34, 120, 0.001, 0.005, mh))
combos.append((13, 34, 200, 0.001, 0.008, mh))
# Remove duplicates
combos = list(set(combos))
combos.sort()
print(f"Combos: {len(combos)}\n", 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) % 10 == 0:
el=time.time()-t0; eta=el/(idx+1)*(len(combos)-idx-1)
print(f" [{idx+1}/{len(combos)}] {el:.0f}s / ~{eta:.0f}s left", flush=True)
tt = time.time() - t0
results.sort(key=lambda x: x[0], reverse=True)
profitable = [r for r in results if r[0] > 0]
print(f"\nDone! {tt:.1f}s | Profitable: {len(profitable)}/{len(results)}\n", flush=True)
print("="*125, flush=True)
print(" TOP 30 RESULTS (sorted by Net P&L)", flush=True)
print("="*125, 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" {'-'*119}", 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{'='*125}", flush=True)
print(f" ALL {len(profitable)} PROFITABLE COMBOS", flush=True)
print(f"{'='*125}", 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}% dir={dp:+.2f} reb={reb:.2f} fee={nf:.2f}", flush=True)
else:
print("\n No profitable combos found. The EMA trend strategy may need a fundamentally different approach.", flush=True)
# Show the closest to profitable
print(f"\n Closest to breakeven:", flush=True)
for i,(net,tc,wr,dp,reb,nf,fp,sp,bp,am,sl,mh) in enumerate(results[:5]):
gap = -net
print(f" EMA({fp}/{sp}/{bp}) ATR>{am*100:.2f}% SL={sl*100:.1f}% MH={mh}s | net={net:+.2f}$ gap_to_profit={gap:.2f}$ trades={tc} dir={dp:+.2f}", flush=True)
print("="*125, flush=True)
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()