Satu: Permulaan – Trump Melukis Carta K
Ketepatan masa sesuatu berita tidak dapat dipertikaikan. Selepas berita serangan udara AS ke atas Iran tersebar, harga minyak mentah melambung naik secara mendadak dalam masa yang singkat. Dalam proses ini, kenyataan Trump dan Iran serta faktor-faktor lain terus saling mempengaruhi dan menguatkan satu sama lain, mendorong pasaran ke zon harga baharu demi zon harga baharu.
Kita sering bergurau dengan mengatakan "Trump melukis carta K" – sebenarnya, banyak pergerakan harga yang mendadak bukanlah hasil daripada penunjuk teknikal, tetapi disebabkan secara langsung oleh satu tweet, satu ucapan, atau satu kenyataan dasar. Analisis teknikal memberitahu anda "di mana harga sekarang", tetapi berita sering menjadi pemboleh ubah utama untuk "mengapa ia berada di sini dan ke mana ia mungkin pergi seterusnya."
Kepentingan berita tidak boleh dinafikan, tetapi masalahnya juga realistik: manusia tidak boleh memantau pasaran dan berita 24 jam sehari, apatah lagi untuk menangkap berita yang benar-benar akan mencetuskan pergerakan pasaran dalam banjir maklumat. Maka timbullah idea yang mudah – bolehkah kita "melukis" berita terus pada carta K, supaya harga dan berita dipaparkan serentak dalam satu pandangan? Sekurang-kurangnya, selesaikan dahulu masalah "melihat".
Dua: Pemilihan Sumber Berita – MCP bersambung dengan Jin10
Untuk menyambungkan berita, langkah pertama ialah mencari sumber berita yang cukup pantas dan berstruktur agak standard. Kali ini kami memilih data Jin10 dan menyambung melalui MCP (Model Context Protocol), memanggil dua jenis antara muka: list_flash (berita pantas) dan list_news (berita umum).
Saya tidak akan menghabiskan banyak ruang untuk memperkenalkan Jin10 di sini – ia hanyalah pilihan yang kami gunakan buat masa ini. Idea dan sumber berita adalah terpisah; selagi sumber itu boleh memberikan tajuk/kandungan dengan cap masa dan boleh dipanggil menggunakan cara standard seperti MCP, ia boleh digantikan. Tumpuan adalah pada reka bentuk lapisan sambungan ini, bukan pada alat tertentu.
Pengurusan sambungan dan sesi MCP adalah bahagian yang agak "rendahan" tetapi penting dalam sistem ini:
python
def _mcp_post(payload, is_notification=False):
global _mcp_session_id, _mcp_req_id
if not is_notification:
_mcp_req_id += 1
payload["id"] = _mcp_req_id
body = json.dumps(payload, ensure_ascii=False).encode()
req = urllib.request.Request(
JIN10_MCP_URL, data=body, headers=_mcp_headers(), method="POST"
)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
sid = resp.headers.get("Mcp-Session-Id")
if sid:
_mcp_session_id = sid
if resp.status == 202:
return {}
text = resp.read().decode("utf-8", errors="replace")
except urllib.error.HTTPError as e:
raise RuntimeError("HTTP %d: %s" % (e.code, e.read().decode()[:400]))
except urllib.error.URLError as e:
raise RuntimeError("Network: " + str(e))
return _mcp_parse(text)
def mcp_init():
global _mcp_ready
mcp_rpc("initialize", {
"protocolVersion": "2025-11-25",
"capabilities": {},
"clientInfo": {"name": "fuse-fmz", "version": "1.0"},
})
mcp_notify("notifications/initialized")
_mcp_ready = True
Log("MCP ready session_id=" + (_mcp_session_id or "(none)"))
Selepas sesi diwujudkan, menarik berita hanyalah dua panggilan alat, diikuti dengan normalisasi format dan penyahduaan seragam:
python
def refresh_news():
global _cached_news, _last_news_at, _mcp_ready
if not JIN10_MCP_TOKEN:
return
now = int(time.time())
if now - _last_news_at < NEWS_REFRESH_SEC and _cached_news:
return
_last_news_at = now
try:
if not _mcp_ready:
mcp_init()
flash_raw = mcp_call_tool("list_flash")
news_raw = mcp_call_tool("list_news")
combined = (
_normalize(_extract_items(flash_raw), "flash") +
_normalize(_extract_items(news_raw), "news")
)
combined.sort(key=lambda x: x["ts"], reverse=True)
_cached_news = combined[:80]
Log("News updated: %d items" % len(_cached_news))
except Exception as e:
Log("News refresh failed: " + str(e))
_mcp_ready = False
Nama medan yang dikembalikan oleh sumber berita yang berbeza adalah pelbagai (title/content/introduction, time/ts/created_at ...), jadi kami menambah satu lapisan _extract_items + _normalize di tengah untuk menyeragamkan semua format kepada struktur standard {ts, time, title, source, full_text}, supaya logik carta dan penapisan di belakang tidak perlu tahu dari mana data itu datang.
Nota: Anda perlu memohon API MCP untuk menggunakannya.
Tiga: Mekanik Teras – Membuat Berita "Tumbuh" pada Carta K
Seterusnya adalah bahagian yang benar-benar menarik alat ini – meletakkan berita dan carta K dalam rajah yang sama.
Kami menambah siri kedua pada carta, jenis flags, yang dipasang pada siri carta K sebagai "lapisan penanda berita":
python
def init_chart(symbol):
global _chart
_chart = Chart({
"__isStock": True,
"chart": {"style": {"fontFamily": "Microsoft YaHei, SimHei, Arial, sans-serif"}},
"title": {"text": "FUSE " + symbol},
"xAxis": {"type": "datetime"},
"series": [
{
"id": "kline",
"type": "candlestick",
"name": symbol,
"data": [],
},
{
"type": "flags",
"name": "News",
"onSeries": "kline",
"shape": "circlepin",
"color": "#F59E0B",
"fillColor": "#F59E0B",
"width": 16,
"data": [],
},
],
})
_chart.reset()
Setiap kali menyegarkan, mula-mula kemas kini data candlestick secara inkremental, kemudian tapis berita "penting" berdasarkan kata kunci, dan selaraskannya mengikut masa ke bar candlestick yang sepadan:
python
def draw_chart(records):
global _last_bar_time, _last_news_hash, _flagged_news_ts
if not _chart or not records:
return
# Mengesan sama ada berita dikemas kini, jika ya set semula carta dan lukis semula
news_hash = hash(tuple(n.get("ts", 0) for n in _cached_news[:10]))
news_changed = (news_hash != _last_news_hash)
if news_changed:
_chart.reset()
_last_bar_time = 0
_last_news_hash = news_hash
_flagged_news_ts = set()
# series 0: Candlestick, tambah secara inkremental
for r in records:
t = r['Time']
bar = [t, r['Open'], r['High'], r['Low'], r['Close']]
if t > _last_bar_time:
_chart.add(0, bar)
_last_bar_time = t
elif t == _last_bar_time:
_chart.add(0, bar, -1)
# series 1: Bendera berita kata kunci, sepadankan dengan masa bar candlestick
if not _cached_news:
return
kws = [k.strip() for k in NEWS_KEYWORD.split("|") if k.strip()]
kw_news = [n for n in _cached_news if not kws or any(k in n.get("full_text", n["title"]) for k in kws)]
if not kw_news:
return
p_ms = PERIOD_MS.get(KLINE_PERIOD, 60000)
first = records[0]['Time']
last = records[-1]['Time']
by_bar = {}
for n in kw_news:
if not n.get("ts"):
continue
key = (n["ts"] // p_ms) * p_ms
if key not in by_bar:
by_bar[key] = n
for ts, item in sorted(by_bar.items()):
if not (first <= ts <= last):
continue
if ts in _flagged_news_ts:
continue
_chart.add(1, {
"x": ts,
"title": "📰",
"text": item["title"][:100],
})
_flagged_news_ts.add(ts)
Hasilnya: setiap kali tanda 📰 muncul pada carta, apabila tetikus digerakkan ke atasnya, anda boleh melihat tajuk berita yang sepadan, dan lokasinya adalah tepat candlestick yang sepadan dengan masa berita itu berlaku. Titik perubahan harga dan titik masa berita, buat pertama kalinya, dipersembahkan dalam satu pandangan secara intuitif—anda tidak lagi perlu bertukar-tukar antara dua tetingkap untuk melihat "segmen ini disebabkan oleh apa".
NEWS_KEYWORD menyokong penggunaan | untuk memisahkan berbilang kata kunci (contohnya "Iran|Kenaikan Kadar|Nonfarm|Tarif"), sistem akan mengutamakan berita yang sepadan dengan kata kunci untuk ditandakan pada carta, mengelakkan carta dibanjiri dengan berita yang tidak relevan.
Empat, Panel Status: Pasaran, Kedudukan, Berita dalam satu skrin
Selain carta, kami juga membuat satu set jadual status yang dikeluarkan melalui LogStatus, termasuk data pasaran masa nyata, ekuiti dan untung rugi akaun, kedudukan semasa, berita yang sepadan dengan kata kunci, serta berita tergempar terkini:
python
def make_status(symbol, ticker, positions, equity):
# 4. Berita kata kunci (tunjukkan kata kunci yang sepadan, bukan sumber)
kws = [k.strip() for k in NEWS_KEYWORD.split("|") if k.strip()]
kw_rows = []
for item in _cached_news[:40]:
t = item.get("time") or (_D(item["ts"]) if item.get("ts") else "-")
title = item["title"][:90]
text = item.get("full_text", item["title"])
hit_kws = [k for k in kws if k in text]
if hit_kws:
kw_rows.append([t, "/".join(hit_kws), title])
if not kw_rows:
kw_rows = [["-", "-", "Tiada berita berkaitan kata kunci buat masa ini"]]
Ditambah dengan antara muka arahan manual yang mudah—buka beli, buka jual, tutup beli, tutup jual, tutup semua sekali, ubah kuantiti pesanan, semuanya diterima melalui GetCommand():
python
def handle_command(symbol):
global _cur_amount, _last_news_at
cmd = GetCommand()
if not cmd:
return
Log("CMD: " + cmd)
parts = cmd.split(":")
key = parts[0]
val = parts[1] if len(parts) > 1 else ""
if key == "openLong": market_order(symbol, "openLong", _cur_amount)
elif key == "openShort": market_order(symbol, "openShort", _cur_amount)
elif key == "closeLong": market_order(symbol, "closeLong", _cur_amount)
elif key == "closeShort": market_order(symbol, "closeShort", _cur_amount)
elif key == "closeAll": close_all(symbol)
elif key == "amount":
_cur_amount = float(val)
Log("Amount updated: " + str(_cur_amount))
Secara keseluruhan, FUSE pada dasarnya adalah panel pemantauan yang menggabungkan "integrasi maklumat + pelaksanaan manual": ia meletakkan harga, berita, kedudukan, dan status akaun sebanyak mungkin pada skrin yang sama, manakala keputusan masih sepenuhnya di tangan manusia — ia tidak membuat pertimbangan untuk anda, tetapi membantu anda mengelak daripada terlepas pandang sesuatu semasa membuat keputusan.
Bahagian V: Batasan — Manusia Masih Menjadi Pembolehubah Terbesar
Batasan versi ini agak jelas, dan kami tidak cuba mengelaknya.
Pertama, hubungan antara berita dan harga adalah "berbutir kasar" — ia hanya menggantung berita pada palang K-line yang sepadan mengikut cap masa, tanpa mentafsir pada tahap kandungan. Sama ada sesuatu berita itu positif atau negatif, atau sama ada ia akan mencetuskan pergerakan pasaran, terpulang sepenuhnya kepada manusia untuk menilai.
Kedua, penapisan kata kunci itu sendiri adalah pendekatan yang agak mudah. Memenuhi kata kunci tidak bermakna berita itu penting, dan tidak memenuhinya tidak bermakna ia tidak penting. Di sini, kognisi, pengalaman, malah keadaan hari itu individu mungkin memainkan peranan yang besar — berita yang sama, dilihat oleh orang yang berbeza pada carta yang sama, boleh menghasilkan kesimpulan yang sangat berbeza.
Ketiga, keseluruhan proses masih "manusia dalam gelung", dan kelajuan tindak balas terhad oleh kelajuan tindak balas manusia, sedangkan dalam kebanyakan masa, tindak balas pasaran terhadap berita adalah pada tahap minit atau bahkan saat.
Jika anda berminat dengan hala tuju ini, kami akan cuba menghasilkan versi automatik berasaskan model besar pada masa hadapan, membolehkan model membuat tafsiran awal dan penilaian kepentingan berita, sebagai bantuan atau bahkan pengganti kepada keputusan manusia. Jika berminat, sila teruskan mengikuti.
Kod sumber strategi: Sistem Pencetus Berita Masa Nyata
- 1





