| Ngày 35 – Python hằng ngày 365 ngày – Tạo và ghi file

Được viết bởi Đặng Trí Thanh vào ngày 02/12/2025 lúc 00:01 | 301 lượt xem

Ngày 35 của chuỗi Python 365 ngày: làm chủ việc tạo, ghi và đọc file trong Python — từ .txt đến cấu hình và log cho bot giao dịch. Bản mở rộng này bao gồm lý thuyết, code mẫu, ứng dụng thực tế và bài tập.

1. Mục tiêu ngày hôm nay

  • Hiểu hàm open() và các chế độ r/w/a/x/r+.
  • Dùng with open(...) để quản lý file an toàn.
  • Làm việc với đường dẫn (os.path / pathlib).
  • Ứng dụng: ghi log giao dịch và đọc cấu hình cho bot.

2. Hàm open() và các chế độ

Mode Ý nghĩa Lỗi thường gặp
“r” Đọc (mặc định) FileNotFoundError nếu chưa có
“w” Ghi mới, xoá cũ Mất dữ liệu cũ nếu ghi nhầm
“a” Ghi tiếp cuối file
“x” Tạo mới (lỗi nếu đã có) FileExistsError
“r+” Đọc + ghi

2.1. Vì sao dùng with open(…)

with tự đóng file khi kết thúc khối lệnh, kể cả khi có ngoại lệ — tránh rò rỉ tài nguyên và giúp code gọn, an toàn.

3. Ghi và đọc file cơ bản

# Ghi
with open("demo.txt", "w", encoding="utf-8") as f:
    f.write("Xin chào Python!
")
    f.write("Ngày 35 - ghi file
")

# Đọc toàn bộ
with open("demo.txt", "r", encoding="utf-8") as f:
    print(f.read())

# Đọc từng dòng
with open("demo.txt", "r", encoding="utf-8") as f:
    for line in f:
        print(line.strip())

4. Quản lý đường dẫn

4.1. Dùng os.path

import os
os.makedirs("logs", exist_ok=True)          # tạo thư mục nếu chưa có
path = os.path.join("logs", "bot.log")      # nối đường dẫn đúng chuẩn
exists = os.path.exists(path)               # kiểm tra tồn tại
print("exists:", exists)

4.2. Dùng pathlib (hiện đại hơn)

from pathlib import Path
p = Path("logs") / "bot.log"
p.parent.mkdir(exist_ok=True)
p.write_text("start
", encoding="utf-8")
print(p.read_text(encoding="utf-8"))

5. Xử lý lỗi khi làm việc với file

try:
    with open("missing.txt", "r", encoding="utf-8") as f:
        print(f.read())
except FileNotFoundError:
    print("File chưa tồn tại - tạo mới")
    with open("missing.txt", "w", encoding="utf-8") as f:
        f.write("created
")
except PermissionError:
    print("Không có quyền ghi file")

6. Ứng dụng thực tế cho bot trading

6.1. Ghi log giao dịch theo từng lệnh

import datetime

def log_trade(symbol, side, price, lot, sl, tp):
    line = f"{datetime.datetime.now()} | {symbol} | {side} | {price} | {lot} | {sl} | {tp}
"
    with open("trades.log", "a", encoding="utf-8") as f:
        f.write(line)

log_trade("BTCUSDT", "BUY", 67000.0, 0.01, 66000.0, 69000.0)

6.2. Đọc cấu hình dạng key=value

def load_config(path="config.txt"):
    cfg = {}
    with open(path, encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if line and not line.startswith("#"):
                k, _, v = line.partition("=")
                cfg[k.strip()] = v.strip()
    return cfg

config = load_config()
print(config.get("lot", "0.01"))

6.3. Lưu checkpoint để bot không “quên”

# ghi trạng thái bot ra file để phục hồi khi restart
with open("state.txt", "w", encoding="utf-8") as f:
    f.write("IN_POSITION|MAGIC=6111|SYMBOL=XAUUSD
")

7. Giới thiệu nhanh CSV (chuẩn bị cho ngày mai)

CSV là định dạng phổ biến lưu dữ liệu giá. Python có module csv:

import csv
with open("prices.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["time", "open", "high", "low", "close"])
    w.writerow(["2026-08-14 10:00", 67000, 67100, 66900, 67050])

7.1. Ghi cấu hình JSON — hiện đại và có cấu trúc

JSON giúp lưu cấu hình bot có cấu trúc, dễ đọc và tương thích với mọi API:

import json

cfg = {
    "symbol": "XAUUSD",
    "lot": 0.01,
    "magic": 6111,
    "max_spread": 0.30,
    "grid_step": 300,
}
with open("config.json", "w", encoding="utf-8") as f:
    json.dump(cfg, f, ensure_ascii=False, indent=2)

with open("config.json", "r", encoding="utf-8") as f:
    loaded = json.load(f)
print(loaded["symbol"], loaded["lot"])

7.2. Ghi log chuẩn bằng module logging

Module logging cho phép ghi theo mức độ (INFO/WARN/ERROR) và xoay file tự động khi đạt kích thước tối đa — rất quan trọng cho bot chạy 24/7:

import logging
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler("bot.log", maxBytes=1_000_000, backupCount=3,
                              encoding="utf-8")
logging.basicConfig(level=logging.INFO,
                    format="%(asctime)s %(levelname)s %(message)s",
                    handlers=[handler])
logging.info("Bot khởi động")
logging.warning("Spread quá rộng: %s", 0.8)
logging.error("Đặt lệnh thất bại")

File xoay vòng: khi bot.log đạt 1MB, tự lưu thành bot.log.1, bot.log.2… — không lo đĩa đầy sau nhiều tháng chạy liên tục.

7.3. Đọc file lớn bằng generator — tiết kiệm RAM

File giá hàng triệu dòng không nên đọc hết một lúc. Dùng generator để xử lý từng dòng:

def read_lines(path):
    with open(path, "r", encoding="utf-8") as f:
        for line in f:
            yield line.strip()

count = 0
for line in read_lines("prices_5y.csv"):
    count += 1
print("Tổng dòng:", count)

7.4. Vì sao nên giữ file mở và dùng buffer

Với bot ghi log liên tục, mở/đóng file mỗi lần ghi rất chậm. Nên giữ file mở trong phiên, gọi flush() định kỳ, hoặc dùng RotatingFileHandler như trên để cân bằng giữa an toàn dữ liệu (không mất log khi crash) và tốc độ ghi.

7.5. Ghi dữ liệu giá vào file theo batch

# giả sử bot nhận tick mỗi giây
ticks = []
def on_tick(price):
    ticks.append(price)
    if len(ticks) >= 60:
        with open("ticks.csv", "a", encoding="utf-8") as f:
            f.write(",".join(map(str, ticks)) + "
")
        ticks.clear()

Ghi theo batch (mỗi 60 tick) giảm số lần mở file — nếu crash chỉ mất vài giây dữ liệu, chấp nhận được.

7.6. File lock — đảm bảo chỉ một bot chạy

Nếu hai tiến trình cùng ghi một file hoặc cùng đặt lệnh sẽ rất nguy hiểm. Dùng file khóa đơn giản:

import os

def acquire_lock(path="bot.lock"):
    if os.path.exists(path):
        return False          # tiến trình khác đang chạy
    open(path, "w").close()
    return True

def release_lock(path="bot.lock"):
    if os.path.exists(path):
        os.remove(path)

Bot gọi acquire_lock() khi khởi động — tránh vô tình chạy hai instance cùng đặt lệnh gấp đôi.

7.7. Nạp lại cấu hình nóng (hot reload)

import json, os, time

def watch_config(path, last_mtime):
    mtime = os.path.getmtime(path)
    if mtime > last_mtime:
        with open(path, encoding="utf-8") as f:
            return json.load(f), mtime
    return None, last_mtime

# trong vòng lặp chính:
cfg = load_config("config.json")
last = os.path.getmtime("config.json")
while running:
    new_cfg, last = watch_config("config.json", last)
    if new_cfg:
        apply_config(new_cfg)   # đổi lot/spread không cần restart
    time.sleep(5)

Kỹ thuật “nạp lại nóng” giúp bạn chỉnh cấu hình bot (lot, magic, spread…) mà không phải khởi động lại — rất tiện khi vận hành thực tế.

7.8. Ghi file theo định dạng CSV chuẩn — nền tảng lưu dữ liệu giá

CSV (Comma-Separated Values) là định dạng phổ biến nhất để lưu dữ liệu giá vì nhẹ, dễ mở bằng Excel và dễ đọc bằng pandas. Khi ghi CSV, luôn dùng newline="" để tránh dòng trống thừa trên Windows:

import csv

with open("ohlc.csv", "w", newline="", encoding="utf-8") as f:
    w = csv.writer(f)
    w.writerow(["time", "open", "high", "low", "close", "volume"])
    w.writerow(["2026-08-14 09:00", 67000, 67120, 66890, 67050, 1234])
    w.writerow(["2026-08-14 09:05", 67050, 67200, 66980, 67110, 987])

Đọc lại bằng pandas rất đơn giản — và đây chính là bước chuẩn bị cho các ngày sau của chuỗi Python 365:

import pandas as pd
df = pd.read_csv("ohlc.csv")
print(df.head())
print("Số dòng:", len(df))

7.9. Đọc file lớn theo chunk — xử lý dữ liệu giá hàng triệu dòng

File OHLC 5 năm của XAUUSD có thể tới vài triệu dòng. Đọc hết vào RAM một lúc sẽ tốn bộ nhớ. Dùng pandas đọc theo chunk hoặc đọc từng dòng bằng generator:

def read_csv_chunks(path, size=100000):
    with open(path, "r", encoding="utf-8") as f:
        header = f.readline()
        rows = []
        for line in f:
            rows.append(line.strip().split(","))
            if len(rows) >= size:
                yield header, rows
                rows = []
        if rows:
            yield header, rows

total = 0
for header, chunk in read_csv_chunks("prices_5y.csv"):
    total += len(chunk)
    print(f"Xử lý chunk {len(chunk)} dòng - tổng {total}")

7.10. Ghi log giao dịch có cấu trúc để phân tích sau này

Thay vì ghi log kiểu text rời rạc, hãy ghi mỗi lệnh một dòng CSV/JSON để cuối tháng bạn thống kê được win rate, tổng pnl, max drawdown:

import json, datetime

def log_order(order):
    rec = {
        "time": datetime.datetime.now().isoformat(),
        "ticket": order["ticket"],
        "symbol": order["symbol"],
        "side": order["side"],
        "lots": order["lots"],
        "open_price": order["open_price"],
        "close_price": order.get("close_price"),
        "pnl": order.get("pnl"),
    }
    with open("orders.jsonl", "a", encoding="utf-8") as f:
        f.write(json.dumps(rec, ensure_ascii=False) + "n")

Định dạng JSONL (mỗi dòng một JSON) rất phù hợp cho log: dễ ghi tiếp, dễ đọc lại, không lo lỗi cú pháp toàn file như mảng JSON thông thường.

7.11. Quản lý nhiều file — cấu trúc thư mục cho dự án bot

Một dự án bot thực tế nên có cấu trúc thư mục rõ ràng để không rối:

my_bot/
├─ config/
│  └─ config.json          # cấu hình bot
├─ data/
│  └─ prices/              # dữ liệu giá tải về
├─ logs/
│  └─ bot.log              # log xoay vòng
├─ state/
│  └─ state.json           # trạng thái để restart
└─ reports/
   └─ monthly.csv          # báo cáo tháng
from pathlib import Path

BASE = Path(__file__).resolve().parent
for d in ["config", "data/prices", "logs", "state", "reports"]:
    (BASE / d).mkdir(parents=True, exist_ok=True)

7.12. Bảo mật file cấu hình — không lộ API key

Cấu hình bot thường chứa API key, mật khẩu. Không nên đưa vào file version control (Git). Dùng biến môi trường hoặc file riêng bị git-ignore:

import os

api_key = os.environ.get("BROKER_API_KEY", "")
if not api_key:
    print("Cảnh báo: thiếu API key, bot sẽ chạy chế độ demo")

Kèm file .gitignore với dòng *.secret.json, .env để tránh vô tình commit lộ mật khẩu lên GitHub — một lỗi rất phổ biến và tốn kém.

7.13. Bài tập nâng cao: xây module lưu trữ dữ liệu cho bot

  1. Viết hàm save_ticks(symbol, ticks) ghi batch 100 tick vào data/prices/<symbol>_ticks.csv.
  2. Viết hàm load_config(path) đọc JSON, tự tạo file mặc định nếu chưa có.
  3. Viết hàm rotate_log(path, max_mb) tự xoay file khi quá kích thước (không dùng thư viện).
  4. Mô phỏng bot ghi 1.000 lệnh vào orders.jsonl rồi đọc lại tính win rate.
  5. Nâng cao: đọc file CSV giá 100.000 dòng, tính trung bình động 20 và in kết quả.

7.14. Đọc và xử lý dữ liệu JSON từ API — lưu vào file

Bot thường lấy dữ liệu từ API (giá, tin tức, lịch kinh tế) dạng JSON. Kỹ năng lưu JSON xuống file để dùng lại là rất cần thiết:

import json, requests

def fetch_and_cache(url, cache_file, ttl=300):
    # kiểm tra cache còn mới không
    if os.path.exists(cache_file):
        age = time.time() - os.path.getmtime(cache_file)
        if age < ttl:
            with open(cache_file, encoding="utf-8") as f:
                return json.load(f)
    # tải mới và lưu
    data = requests.get(url, timeout=10).json()
    with open(cache_file, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False)
    return data

Kỹ thuật cache này giúp bot không gọi API liên tục (tránh bị giới hạn rate limit) và vẫn dùng được dữ liệu khi mất mạng tạm thời.

7.15. So sánh các cách lưu trạng thái bot

Cách Ưu điểm Nhược điểm Dùng khi nào
File text / JSON Đơn giản, dễ đọc Không an toàn ghi đồng thời Bot 1 tiến trình
SQLite An toàn, truy vấn được Hơi nặng nề cho trạng thái đơn giản Cần lịch sử lệnh lâu dài
Global Variable (MT5) Nhẹ, MT5 hỗ trợ sẵn Chỉ số đơn, khó lưu cấu trúc Bot MQL5 / MT5
Redis Nhanh, hỗ trợ multi-proc Cần cài thêm dịch vụ Hệ thống lớn nhiều bot

7.16. Bài tập tổng hợp cuối ngày

Xây một module file_store.py hoàn chỉnh cho bot có các chức năng:

  1. save_state(state: dict) — lưu trạng thái bot ra JSON.
  2. load_state() — đọc lại, trả dict rỗng nếu file chưa có.
  3. append_trade(rec: dict) — ghi 1 lệnh vào orders.jsonl.
  4. read_trades() — đọc toàn bộ lệnh, tính tổng pnl và win rate.
  5. rotate_log(path, max_mb=1) — tự xoay file log.

Gợi ý lời giải bước 4:

def read_trades():
    trades = []
    with open("orders.jsonl", encoding="utf-8") as f:
        for line in f:
            line = line.strip()
            if line:
                trades.append(json.loads(line))
    wins = [t for t in trades if t.get("pnl", 0) > 0]
    total = sum(t.get("pnl", 0) for t in trades)
    win_rate = len(wins) / len(trades) * 100 if trades else 0
    return {"count": len(trades), "total_pnl": round(total, 2), "win_rate": round(win_rate, 1)}

Hoàn thành module này nghĩa là bạn đã nắm trọn kỹ năng file — nền tảng cho toàn bộ các ngày tiếp theo của chuỗi Python 365 về lưu trữ dữ liệu bot.

7.17. Đọc file nhị phân và ghi cấu trúc dữ liệu với struct

Không phải mọi file đều là văn bản. File nhị phân (bin) lưu số liệu gọn hơn, thường dùng trong hệ thống tài chính lưu dữ liệu tick khối lượng lớn. Python có module struct để đóng gói số thành byte:

import struct

# ghi 1 bản ghi: thời gian (float), giá (float), khối lượng (int)
fmt = "d d i"   # double, double, int
rec = struct.pack(fmt, 1700000000.0, 2350.5, 12)
with open("ticks.bin", "ab") as f:
    f.write(rec)

# đọc lại
with open("ticks.bin", "rb") as f:
    data = f.read()
    t, price, vol = struct.unpack(fmt, data[:struct.calcsize(fmt)])
print(t, price, vol)

Lợi ích: file nhị phân nhỏ hơn CSV nhiều lần và đọc nhanh hơn — quan trọng khi lưu hàng triệu tick mỗi ngày cho backtest.

7.18. Dùng pathlib để quản lý đường dẫn gọn gàng

pathlib (Python 3.4+) là cách hiện đại, dễ đọc hơn os.path — đặc biệt khi code chạy được cả Windows lẫn Linux (quan trọng vì bot thường chạy trên VPS Linux):

from pathlib import Path

base = Path(__file__).resolve().parent
data = base / "data" / "prices"
data.mkdir(parents=True, exist_ok=True)

# tạo tên file theo ngày
today = Path("2026-08-14_ohlc.csv")
path = data / today
print("Đường dẫn:", path)
print("Tồn tại?", path.exists())
print("Đuôi:", path.suffix)

# liệt kê file csv trong thư mục
for f in data.glob("*.csv"):
    print(" -", f.name, f.stat().st_size, "bytes")

Path tự xử lý dấu gạch chéo đúng hệ điều hành — code chạy trên máy Windows của bạn và VPS Linux đều như nhau, không lo lỗi đường dẫn.

7.19. Bảo vệ dữ liệu: ghi atomic (không làm hỏng file khi crash)

Khi bot ghi file mà bị crash giữa chừng, file có thể bị hỏng nửa chừng. Kỹ thuật atomic write: ghi vào file tạm rồi đổi tên (rename là thao tác nguyên tử trên hệ điều hành):

import os, tempfile

def atomic_write(path, text):
    d = os.path.dirname(path) or "."
    fd, tmp = tempfile.mkstemp(dir=d, suffix=".tmp")
    try:
        with os.fdopen(fd, "w", encoding="utf-8") as f:
            f.write(text)
        os.replace(tmp, path)   # thay thế nguyên tử
    except Exception:
        os.unlink(tmp)
        raise

atomic_write("state.json", '{"state": "IN_POSITION"}')

Với kỹ thuật này, dù bot crash bất cứ lúc nào, file chính luôn là bản hoàn chỉnh gần nhất — file hỏng chỉ là file tạm bị bỏ đi.

7.20. Ứng dụng tổng hợp: lưu báo cáo giao dịch theo ngày

Kết hợp tất cả kỹ năng, xây module xuất báo cáo giao dịch mỗi ngày:

import csv, json, datetime
from pathlib import Path

def export_daily_report(trades, out_dir="reports"):
    Path(out_dir).mkdir(parents=True, exist_ok=True)
    date = datetime.date.today().isoformat()
    # 1) CSV chi tiết
    csv_path = Path(out_dir) / f"trades_{date}.csv"
    with open(csv_path, "w", newline="", encoding="utf-8") as f:
        w = csv.DictWriter(f, fieldnames=["ticket", "symbol", "side", "lots", "pnl"])
        w.writeheader()
        w.writerows(trades)
    # 2) JSON tóm tắt
    total = sum(t["pnl"] for t in trades)
    wins = sum(1 for t in trades if t["pnl"] > 0)
    summary = {
        "date": date, "count": len(trades),
        "total_pnl": round(total, 2),
        "win_rate": round(wins / len(trades) * 100, 1) if trades else 0,
    }
    atomic_write(Path(out_dir) / f"summary_{date}.json",
                 json.dumps(summary, ensure_ascii=False, indent=2))
    return summary

# ví dụ dữ liệu
trades = [
    {"ticket": 1001, "symbol": "XAUUSD", "side": "BUY", "lots": 0.01, "pnl": 13.5},
    {"ticket": 1002, "symbol": "XAUUSD", "side": "SELL", "lots": 0.01, "pnl": -7.2},
]
print(export_daily_report(trades))

Chạy hàm này cuối mỗi ngày (hoặc tự động trong bot), bạn có ngay bộ báo cáo CSV + JSON để theo dõi hiệu suất mà không cần mở MetaTrader.

7.21. Tổng kết chương — bảng tóm tắt các kỹ năng đã học

Kỹ năng Hàm/Module Ứng dụng cho bot
Mở/đóng file open(), with Đọc ghi cấu hình, log
Đường dẫn os.path / pathlib Quản lý thư mục dự án
JSON json Lưu state, cấu hình, API data
CSV csv, pandas Lưu dữ liệu giá, báo cáo
Log logging + RotatingFileHandler Ghi log bot không lo đầy đĩa
File lớn generator, chunk Xử lý dữ liệu giá triệu dòng
File nhị phân struct Lưu tick gọn, nhanh
Atomic write tempfile + os.replace Không hỏng file khi crash

Nắm vững 8 kỹ năng này, bạn đã đủ nền tảng để xây phần lưu trữ dữ liệu cho bất kỳ bot nào. Ngày mai chúng ta tiếp tục với CSV chuyên sâu và pandas — bước nhảy vào phân tích dữ liệu giá.

7.22. Đọc và phân tích file log với Python

Khi bot chạy lâu ngày, file log có thể lên tới hàng nghìn dòng. Kỹ năng đọc và lọc log giúp bạn tìm lỗi nhanh chóng:

def search_log(path, keyword, last_lines=2000):
    # đọc 2000 dòng cuối, tìm dòng chứa keyword
    with open(path, "r", encoding="utf-8", errors="replace") as f:
        lines = f.readlines()[-last_lines:]
    return [ln.strip() for ln in lines if keyword.lower() in ln.lower()]

# ví dụ: tìm các lần bot lỗi đặt lệnh
errors = search_log("bot.log", "lỗi")
for e in errors[-10:]:
    print(e)

# đếm số lần mỗi loại lỗi
from collections import Counter
counter = Counter()
for ln in open("bot.log", encoding="utf-8", errors="replace"):
    if "ERROR" in ln:
        counter["ERROR"] += 1
    elif "WARN" in ln:
        counter["WARN"] += 1
print(dict(counter))

Ghi log có chuẩn [TRẠNG_THÁI] nội dung giúp bạn lọc theo trạng thái bot, còn ghi mức độ (INFO/WARN/ERROR) giúp đếm nhanh mức độ nghiêm trọng của sự cố.

7.23. Xử lý lỗi encoding khi đọc file tiếng Việt

File ghi trên máy khác (VPS Linux) có thể dùng encoding khác (UTF-8), file cũ có thể là Windows-1252 hoặc cp1258. Luôn chỉ định encoding khi đọc để tránh lỗi ký tự:

def smart_read(path):
    for enc in ("utf-8", "cp1258", "cp1252", "latin-1"):
        try:
            with open(path, "r", encoding=enc) as f:
                return f.read()
        except UnicodeDecodeError:
            continue
    raise ValueError("Không đọc được file")

text = smart_read("baocao.txt")
print(text[:100])

Khi ghi, luôn dùng encoding="utf-8" để file chuẩn, đọc được ở mọi nơi. Khi đọc file từ nguồn không rõ, thử nhiều encoding như hàm trên để không bị lỗi.

7.24. Bài kiểm tra nhanh cuối ngày (tự đánh giá)

Tự trả lời các câu hỏi sau để chắc chắn đã nắm bài:

  1. Khác biệt giữa "w", "a", "x", "r+" là gì? Khi nào dùng "w" an toàn?
  2. Vì sao nên dùng with open(...) thay vì tự đóng file?
  3. Làm sao tạo thư mục con khi ghi file? (gợi ý: mkdir(parents=True, exist_ok=True))
  4. Khi nào dùng JSON thay vì CSV để lưu dữ liệu bot?
  5. Kỹ thuật nào giúp file không bị hỏng khi bot crash giữa chừng? (gợi ý: atomic write)
  6. Tại sao ghi log theo mức độ INFO/WARN/ERROR lại hữu ích?

Nếu trả lời được 5/6 câu, bạn đã hoàn thành tốt ngày hôm nay. Nếu chưa, hãy đọc lại các phần liên quan trước khi sang ngày mai — kiến thức file là nền tảng cho toàn bộ phần lưu trữ dữ liệu của bot.

8. Bài tập thực hành

  1. Tạo file portfolio.txt lưu 5 mã coin theo dõi; đọc và in số dòng.
  2. Viết hàm ghi log có tham số mức độ (INFO/WARN/ERROR).
  3. Mô phỏng bot ghi 100 dòng log, đếm số dòng và in 3 dòng cuối.
  4. Viết file cấu hình cho bot (lot, magic, max_spread) và hàm load_config.
  5. Nâng cao: đọc file CSV giá, tính giá trung bình.

9. Lưu ý quan trọng

  • Luôn chỉ định encoding="utf-8" khi có tiếng Việt.
  • Tạo thư mục trước khi ghi (os.makedirs(..., exist_ok=True)).
  • Bắt lỗi bằng try/except cho các thao tác file nhạy cảm.
  • Khi ghi CSV dùng newline="" để tránh xuống dòng thừa.

10. Kết luận

Ghi/đọc file là kỹ năng nền tảng để bot lưu log, cấu hình và trạng thái. Hoàn thành bài tập để vững tay; ngày mai chúng ta đi sâu CSV — nền tảng lưu dữ liệu giá cho bot.

📌 Theo dõi chuỗi Python 365 ngày tại Hướng Nghiệp Dữ Liệu để xây bot hoàn chỉnh từng bước.

Đặng Trí Thanh

Đặng Trí Thanh

Giám đốc Công nghệ · DNT Digital · Giảng viên HNDL
1.318 Bài viết
15.4k Người theo dõi
120k+ Lượt đọc

Đặng Trí Thanh — Founder & CTO · Hướng Nghiệp Dữ Liệu - DNT Digital. Chuyên đào tạo và triển khai thực chiến Python, MT5 và hệ thống bot auto trading / IB cho học viên và doanh nghiệp.