108 lines
4.2 KiB
Python
108 lines
4.2 KiB
Python
# clean_history_csv.py
|
|
#
|
|
# Deskripsi:
|
|
# Skrip ini membaca file 'history.csv' yang diekspor dari MT5,
|
|
# membersihkan nama kolom yang tidak standar, memperbaiki format angka,
|
|
# dan menyimpannya sebagai file baru yang rapi bernama 'history_cleaned.csv'.
|
|
|
|
import pandas as pd
|
|
import logging
|
|
import re
|
|
|
|
# --- Konfigurasi ---
|
|
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
|
|
|
|
INPUT_FILE = 'history.csv'
|
|
OUTPUT_FILE = 'history_cleaned.csv'
|
|
|
|
def clean_numeric_value(value):
|
|
"""Membersihkan string angka dari berbagai format secara robust."""
|
|
if pd.isna(value):
|
|
return None
|
|
if isinstance(value, (int, float)):
|
|
return value
|
|
if isinstance(value, str):
|
|
try:
|
|
# Hapus semua spasi
|
|
s = str(value).strip().replace(' ', '')
|
|
# Hitung jumlah titik
|
|
dots = s.count('.')
|
|
# Jika ada lebih dari satu titik, anggap sebagai pemisah ribuan
|
|
# dan hapus semua kecuali yang terakhir.
|
|
if dots > 1:
|
|
s = s.replace('.', '', dots - 1)
|
|
return float(s)
|
|
except (ValueError, TypeError):
|
|
return None # Kembalikan None jika konversi gagal
|
|
return None
|
|
|
|
def main():
|
|
"""Fungsi utama untuk membersihkan file CSV."""
|
|
try:
|
|
df = pd.read_csv(INPUT_FILE, on_bad_lines='skip')
|
|
logging.info(f"Berhasil membaca {len(df)} baris dari '{INPUT_FILE}'.")
|
|
except FileNotFoundError:
|
|
logging.error(f"File '{INPUT_FILE}' tidak ditemukan. Pastikan file ada di direktori yang sama.")
|
|
return
|
|
except Exception as e:
|
|
logging.error(f"Gagal membaca file CSV: {e}")
|
|
return
|
|
|
|
# Menghapus kolom terakhir jika kosong (sering terjadi saat ekspor dari MT5)
|
|
if df.columns[-1].startswith('Unnamed'):
|
|
df = df.iloc[:, :-1]
|
|
|
|
original_columns = df.columns.tolist()
|
|
|
|
# Membersihkan nama kolom: huruf kecil, ganti spasi dengan _, hapus karakter aneh
|
|
df.columns = [re.sub(r'\.\d+$', '', col).strip().lower().replace(' ', '_').replace('/', '') for col in original_columns]
|
|
|
|
# Menangani kolom duplikat ('Time', 'Price') dari ekspor MT5
|
|
cols = df.columns.tolist()
|
|
time_indices = [i for i, name in enumerate(cols) if name == 'time']
|
|
price_indices = [i for i, name in enumerate(cols) if name == 'price']
|
|
|
|
if len(time_indices) > 1:
|
|
cols[time_indices[0]] = 'open_time'
|
|
cols[time_indices[1]] = 'close_time'
|
|
if len(price_indices) > 1:
|
|
cols[price_indices[0]] = 'open_price'
|
|
cols[price_indices[1]] = 'close_price'
|
|
df.columns = cols
|
|
|
|
logging.info(f"Nama kolom asli: {original_columns}")
|
|
logging.info(f"Nama kolom distandarkan: {df.columns.tolist()}")
|
|
|
|
# Mengganti nama 'position' menjadi 'ticket' untuk konsistensi
|
|
if 'position' in df.columns:
|
|
df.rename(columns={'position': 'ticket'}, inplace=True)
|
|
logging.info("Mengganti nama kolom 'position' menjadi 'ticket'.")
|
|
|
|
# Membersihkan kolom-kolom numerik
|
|
numeric_cols = ['open_price', 'close_price', 'profit', 'volume', 'commission', 'swap', 's_l', 't_p']
|
|
for col in numeric_cols:
|
|
if col in df.columns:
|
|
df[col] = df[col].apply(clean_numeric_value)
|
|
|
|
# Menghapus baris di mana data penting gagal dibersihkan
|
|
df.dropna(subset=['open_price', 'profit', 'ticket'], inplace=True)
|
|
df['ticket'] = df['ticket'].astype(int)
|
|
logging.info(f"Jumlah baris setelah pembersihan data numerik: {len(df)}.")
|
|
|
|
# Memilih dan menyusun ulang kolom-kolom penting
|
|
final_cols_order = [
|
|
'ticket', 'open_time', 'symbol', 'type', 'volume', 'open_price', 's_l', 't_p',
|
|
'close_time', 'close_price', 'commission', 'swap', 'profit'
|
|
]
|
|
# Filter hanya kolom yang ada di dataframe
|
|
existing_cols = [col for col in final_cols_order if col in df.columns]
|
|
cleaned_df = df[existing_cols]
|
|
|
|
try:
|
|
cleaned_df.to_csv(OUTPUT_FILE, index=False)
|
|
logging.info(f"Berhasil! {len(cleaned_df)} baris data yang bersih telah disimpan ke '{OUTPUT_FILE}'.")
|
|
except Exception as e:
|
|
logging.error(f"Gagal menyimpan file yang sudah dibersihkan: {e}")
|
|
|
|
if __name__ == '__main__':
|
|
main()
|