Files

300 lines
18 KiB
PHP

<?php
/**
* Forex Trading Journal Database Layer (SQLite)
* High-performance, robust SQLite storage for Forex accounts, trades, and analytics.
*/
class Database {
private static ?PDO $pdo = null;
private static string $dbFile = __DIR__ . '/forex_journal.db';
public static function getConnection(): PDO {
if (self::$pdo === null) {
$isNew = !file_exists(self::$dbFile);
self::$pdo = new PDO('sqlite:' . self::$dbFile);
self::$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
self::$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
// Enable WAL mode for high concurrency and performance
self::$pdo->exec('PRAGMA journal_mode = WAL;');
self::$pdo->exec('PRAGMA foreign_keys = ON;');
self::initializeSchema();
if ($isNew || self::isAccountTableEmpty()) {
self::seedInitialData();
}
}
return self::$pdo;
}
private static function initializeSchema(): void {
$db = self::$pdo;
// 1. Trading Accounts Table
$db->exec("
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
broker TEXT DEFAULT 'IC Markets',
account_number TEXT DEFAULT '',
currency TEXT DEFAULT 'USD',
initial_balance REAL NOT NULL DEFAULT 10000.0,
leverage REAL DEFAULT 100.0,
account_type TEXT DEFAULT 'Real', -- Real, Demo, PropFirm
is_default INTEGER DEFAULT 0,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
");
// 2. Trades Table
$db->exec("
CREATE TABLE IF NOT EXISTS trades (
id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER NOT NULL,
ticket TEXT DEFAULT '',
symbol TEXT NOT NULL, -- EURUSD, XAUUSD, GBPUSD, etc.
trade_type TEXT NOT NULL, -- buy, sell
lot_size REAL NOT NULL DEFAULT 0.1,
open_price REAL NOT NULL,
close_price REAL DEFAULT NULL,
stop_loss REAL DEFAULT NULL,
take_profit REAL DEFAULT NULL,
open_time DATETIME NOT NULL,
close_time DATETIME DEFAULT NULL,
pips REAL DEFAULT 0.0,
profit REAL DEFAULT 0.0, -- Net P&L in currency ($)
commission REAL DEFAULT 0.0,
swap REAL DEFAULT 0.0,
status TEXT DEFAULT 'closed', -- open, closed
strategy TEXT DEFAULT 'Price Action',
timeframe TEXT DEFAULT 'M15',
session TEXT DEFAULT 'London', -- London, New York, Asian, Overlap
risk_reward REAL DEFAULT NULL,
risk_percent REAL DEFAULT 1.0,
emotion TEXT DEFAULT 'Disciplined', -- Disciplined, Greedy, Fearful, Revenge, Confident, Impatient, Neutral
entry_notes TEXT DEFAULT '',
exit_notes TEXT DEFAULT '',
lessons TEXT DEFAULT '',
screenshot_entry TEXT DEFAULT '',
screenshot_exit TEXT DEFAULT '',
tags TEXT DEFAULT '',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE
);
");
// 3. Daily Notes / Journal Mindset Log
$db->exec("
CREATE TABLE IF NOT EXISTS daily_logs (
id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER NOT NULL,
log_date DATE NOT NULL,
notes TEXT DEFAULT '',
mindset_score INTEGER DEFAULT 5, -- 1 to 5
market_condition TEXT DEFAULT 'Normal',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (account_id) REFERENCES accounts(id) ON DELETE CASCADE,
UNIQUE(account_id, log_date)
);
");
// 4. Strategies / Setups Table
$db->exec("
CREATE TABLE IF NOT EXISTS strategies (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL UNIQUE,
description TEXT DEFAULT '',
color TEXT DEFAULT '#3b82f6',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
");
// Create indexes for fast analytical aggregations
$db->exec("CREATE INDEX IF NOT EXISTS idx_trades_account ON trades(account_id);");
$db->exec("CREATE INDEX IF NOT EXISTS idx_trades_open_time ON trades(open_time);");
$db->exec("CREATE INDEX IF NOT EXISTS idx_trades_close_time ON trades(close_time);");
$db->exec("CREATE INDEX IF NOT EXISTS idx_trades_symbol ON trades(symbol);");
$db->exec("CREATE INDEX IF NOT EXISTS idx_trades_status ON trades(status);");
}
private static function isAccountTableEmpty(): bool {
$stmt = self::$pdo->query("SELECT COUNT(*) FROM accounts");
return ((int)$stmt->fetchColumn()) === 0;
}
public static function seedInitialData(): void {
$db = self::$pdo;
// Default Strategies
$strategies = [
['Price Action', 'Break of structure, support & resistance', '#3b82f6'],
['ICT / SMC', 'Order blocks, Fair Value Gaps (FVG) and liquidity sweeps', '#8b5cf6'],
['Trend Following', 'Moving average pullbacks and trend continuation', '#10b981'],
['Scalping', 'Fast momentum moves on M1/M5 charts', '#f59e0b'],
['Reversal / Divergence', 'RSI/MACD divergence at key supply/demand levels', '#ec4899'],
['News Trading', 'High-impact economic news releases', '#ef4444'],
];
$stmtStrat = $db->prepare("INSERT OR IGNORE INTO strategies (name, description, color) VALUES (?, ?, ?)");
foreach ($strategies as $strat) {
$stmtStrat->execute($strat);
}
// Default Accounts
$stmtAcc = $db->prepare("
INSERT INTO accounts (name, broker, account_number, currency, initial_balance, leverage, account_type, is_default)
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
");
$stmtAcc->execute(['حساب معاملاتی اصلی (Main Live)', 'IC Markets', '8841920', 'USD', 10000.0, 100.0, 'Real', 1]);
$mainAccountId = (int)$db->lastInsertId();
$stmtAcc->execute(['چالش پراپ فرم ۱۰۰ هزار دلاری (FTMO Prop)', 'FTMO', '2948173', 'USD', 100000.0, 100.0, 'PropFirm', 0]);
// Seed rich realistic trading history for main account
self::generateSampleTrades($mainAccountId, 10000.0);
}
public static function generateSampleTrades(int $accountId, float $startBalance = 10000.0): void {
$db = self::$pdo;
// Trade definition array:
// [0:symbol, 1:type, 2:lot, 3:openPrice, 4:closePrice, 5:sl, 6:tp, 7:pips, 8:profit, 9:comm, 10:swap, 11:strat, 12:tf, 13:session, 14:rr, 15:emotion, 16:entryNotes, 17:exitNotes, 18:lessons]
$tradeDataList = [
['EURUSD', 'buy', 1.0, 1.08200, 1.08650, 1.07950, 1.08700, 45.0, 450.0, -7.0, 0.0, 'ICT / SMC', 'H1', 'London', 1.8, 'Disciplined', 'جاروب نقدینگی لندن و واکنش به FVG', 'رسیدن به هدف نقدینگی سقف روز قبل', 'پایبندی به استراتژی'],
['XAUUSD', 'buy', 0.5, 2350.20, 2368.50, 2342.00, 2370.00, 183.0, 915.0, -5.0, -2.1, 'Price Action', 'M15', 'New York', 2.2, 'Confident', 'تست مجدد سطح حمایتی ۲۳۵۰ با کندل تاییدیه پین بار', 'خروج در نزدیکی تارگت با سود عالی', 'صبر برای پولبک کلید موفقیت بود'],
['GBPUSD', 'sell', 1.2, 1.27600, 1.27150, 1.27850, 1.27000, 45.0, 540.0, -8.4, 1.5, 'Trend Following', 'M15', 'London', 1.8, 'Disciplined', 'شکست خط روند نزولی و پولبک به EMA50', 'ریسک فری شد و در تارگت ۲ بسته شد', 'مدیریت ترید عالی'],
['USDJPY', 'buy', 1.0, 154.800, 154.450, 154.400, 155.600, -35.0, -225.0, -7.0, 0.0, 'Scalping', 'M5', 'Asian', 0.0, 'Fearful', 'ورود با مومنتوم صعودی ضعیف در نشست آسیا', 'اصابت به استاپ لاس به دلیل اسپرد بالا', 'در سشن آسیا نباید روی جفت‌های کم‌نوسان بدون کاتالیزور معامله کرد'],
['EURUSD', 'sell', 1.5, 1.08900, 1.08350, 1.09150, 1.08200, 55.0, 825.0, -10.5, 0.0, 'ICT / SMC', 'M15', 'New York', 2.2, 'Disciplined', 'شکار نقدینگی سشن نیویورک و تایید با اردر بلاک خرسی', 'کست کامل سود در کف سشن لندن', 'ورود دقیق روی اوردربلاک'],
['XAUUSD', 'sell', 0.8, 2385.00, 2392.50, 2392.00, 2370.00, -75.0, -600.0, -8.0, 0.0, 'Reversal / Divergence', 'M15', 'New York', 0.0, 'Greedy', 'ورود برخلاف روند قدرتمند بدون واگرایی قطعی', 'استاپ خورد، طلا شتاب شدید صعودی داشت', 'هرگز نباید جلوی قطار سریع‌السیر ایستاد!'],
['BTCUSD', 'buy', 0.2, 63200.0, 65100.0, 62400.0, 65500.0, 1900.0, 380.0, -3.0, -0.5, 'Trend Following', 'H4', 'London', 2.4, 'Confident', 'حفظ حمایت رند ۶۳۰۰۰ و تشکیل الگوی دابل باتم', 'خروج پله‌ای در ۶۵۱۰۰', 'معامله سوینگی تمیز'],
['US30', 'buy', 0.5, 39200.0, 39650.0, 39000.0, 39700.0, 450.0, 225.0, -5.0, 0.0, 'Scalping', 'M5', 'New York', 2.2, 'Disciplined', 'اوپن سشن نیویورک و جهش شاخص داوجونز', 'خروج سریع با تارگت تعیین شده', 'مدیریت ریسک عالی'],
['GBPUSD', 'buy', 1.0, 1.26800, 1.27400, 1.26500, 1.27500, 60.0, 600.0, -7.0, 0.0, 'ICT / SMC', 'M15', 'London', 2.0, 'Disciplined', 'برگشت قیمت از ناحیه تقاضا و جاروب کف آسیا', 'تارگت لمس شد', 'برنامه معاملاتی مو به مو اجرا شد'],
['EURUSD', 'sell', 1.0, 1.08500, 1.08800, 1.08800, 1.08000, -30.0, -300.0, -7.0, 0.0, 'Price Action', 'M15', 'London', 0.0, 'Impatient', 'ورود عجولانه قبل از بسته شدن کندل ۱۵ دقیقه', 'برگشت قیمت به بالا و استاپ', 'صبر برای کلوز کندل ضروری است'],
['XAUUSD', 'buy', 0.7, 2360.00, 2378.00, 2353.00, 2380.00, 180.0, 1260.0, -7.0, -1.8, 'ICT / SMC', 'H1', 'Overlap', 2.5, 'Disciplined', 'تلاقی FVG با لول ۵۰٪ فیبوناچی در اورلپ لندن/نیویورک', 'تارگت کامل محقق شد', 'یکی از بهترین معاملات ماه'],
['AUDUSD', 'sell', 1.0, 0.66800, 0.66350, 0.67050, 0.66200, 45.0, 450.0, -7.0, 1.2, 'Trend Following', 'H1', 'Asian', 1.8, 'Neutral', 'شکست رنج قیمتی و تاییدیه با داده‌های اشتغال استرالیا', 'خروج با سود خوب', 'انطباق خوب با تحلیل فاندامنتال'],
['USDJPY', 'sell', 1.0, 156.200, 155.300, 156.600, 155.000, 90.0, 580.0, -7.0, 0.0, 'Price Action', 'H1', 'New York', 2.2, 'Disciplined', 'سقف دو قلو در تایم فریم یک ساعته با کندل اینگالف نزولی', 'خروج در حمایت کلیدی ۱۵۵.۳۰', 'معامله بسیار روان و بدون استرس'],
['EURUSD', 'buy', 1.2, 1.08300, 1.08100, 1.08050, 1.08900, -20.0, -240.0, -8.4, 0.0, 'Scalping', 'M5', 'London', 0.0, 'Neutral', 'تلاش برای گرفتن بریک اوت سریع که فیک اوت شد', 'حد ضرر نجات داد', 'ضرر کوچک و کنترل‌شده'],
['XAUUSD', 'buy', 0.6, 2375.00, 2394.00, 2368.00, 2395.00, 190.0, 1140.0, -6.0, 0.0, 'ICT / SMC', 'M15', 'New York', 2.7, 'Confident', 'سوییپ نقدینگی کف روزانه در اخبار CPI و واکنش انفجاری', 'خروج در تارگت ۲۳۹۴', 'مدیریت پوزیشن عالی حین خبر'],
['GBPUSD', 'sell', 1.0, 1.27500, 1.27100, 1.27800, 1.27000, 40.0, 400.0, -7.0, 0.0, 'Price Action', 'M15', 'London', 1.3, 'Disciplined', 'برخورد به سقف کانال نزولی', 'برداشت سود مطمئن', 'معامله استاندارد طبق پلن'],
['BTCUSD', 'sell', 0.25, 66500.0, 64200.0, 67200.0, 64000.0, 2300.0, 575.0, -3.5, 0.0, 'Reversal / Divergence', 'H4', 'New York', 3.2, 'Disciplined', 'واگرایی منفی مشهود در RSI تایم ۴ ساعته', 'ریزش عالی بیت‌کوین و سیو سود', 'نسبت ریسک به ریوارد بالای ۳'],
['US30', 'sell', 0.4, 39800.0, 39950.0, 39950.0, 39400.0, -150.0, -60.0, -4.0, 0.0, 'Scalping', 'M5', 'New York', 0.0, 'Neutral', 'ترید خلاف ترند در اوپن بازار', 'استاپ کوچک خورد', 'مدیریت ریسک عالی'],
['EURUSD', 'buy', 1.0, 1.08400, 1.08920, 1.08150, 1.09000, 52.0, 520.0, -7.0, 0.0, 'ICT / SMC', 'H1', 'London', 2.1, 'Disciplined', 'پر شدن FVG سشن لندن و چرخش روند', 'تارگت لمس شد', 'آرامش و صبر کامل'],
['XAUUSD', 'buy', 0.5, 2390.00, 2415.00, 2382.00, 2420.00, 250.0, 1250.0, -5.0, -2.5, 'Trend Following', 'H4', 'New York', 3.1, 'Confident', 'شکست سقف تاریخی قبلی و پولبک استاندارد', 'سود فوق‌العاده با تارگت بالای ۲۴۱۵', 'رعایت تمام اصول استراتژی'],
['USDJPY', 'buy', 1.0, 155.100, 155.650, 154.750, 155.800, 55.0, 355.0, -7.0, 0.0, 'Price Action', 'M15', 'Asian', 1.5, 'Disciplined', 'حمایت معتبر در سشن توکیو', 'خروج با سود خوب در شروع سشن لندن', 'ترید تمیز'],
['GBPUSD', 'buy', 1.0, 1.26900, 1.26600, 1.26600, 1.27600, -30.0, -300.0, -7.0, 0.0, 'Price Action', 'M15', 'London', 0.0, 'Fearful', 'انتشار داده‌های ضعیف بریتانیا و استاپ خوردن', 'استاپ رعایت شد', 'پایبندی به استاپ لاس نجات‌بخش بود'],
['EURUSD', 'sell', 1.0, 1.08800, 1.08300, 1.09050, 1.08200, 50.0, 500.0, -7.0, 0.0, 'ICT / SMC', 'M15', 'New York', 2.0, 'Disciplined', 'شکار نقدینگی خریداران بالای ۱.۰۸۸۰ و ریزش سریع', 'خروج در کف روز', 'ستاپ کاملاً مکانیکی و تست شده']
];
$stmtTrade = $db->prepare("
INSERT INTO trades (
account_id, ticket, symbol, trade_type, lot_size,
open_price, close_price, stop_loss, take_profit,
open_time, close_time, pips, profit, commission, swap,
status, strategy, timeframe, session, risk_reward, emotion,
entry_notes, exit_notes, lessons, tags
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
");
$baseTime = time() - (count($tradeDataList) * 86400 * 0.9);
$ticketBase = 7418290;
foreach ($tradeDataList as $i => $t) {
$ticket = (string)($ticketBase + $i * 17);
$openTs = $baseTime + ($i * 75000) + rand(1000, 15000);
$durationSec = rand(1800, 28800);
$closeTs = $openTs + $durationSec;
$openTimeStr = date('Y-m-d H:i:s', $openTs);
$closeTimeStr = date('Y-m-d H:i:s', $closeTs);
$stmtTrade->execute([
$accountId,
$ticket,
$t[0],
$t[1],
$t[2],
$t[3],
$t[4],
$t[5],
$t[6],
$openTimeStr,
$closeTimeStr,
$t[7],
$t[8],
$t[9],
$t[10],
'closed',
$t[11],
$t[12],
$t[13],
$t[14],
$t[15],
$t[16],
$t[17],
$t[18],
'Live,DayTrade,Forex'
]);
}
// Add 2 open trades
$stmtTrade->execute([
$accountId,
(string)($ticketBase + 991),
'XAUUSD',
'buy',
0.5,
2405.50,
null,
2396.00,
2425.00,
date('Y-m-d H:i:s', time() - 3600),
null,
45.0,
225.0,
-5.0,
0.0,
'open',
'ICT / SMC',
'H1',
'London',
2.0,
'Disciplined',
'ورود پس از شکست مقاومت ۲۴۰۰ و تثبیت بالای آن',
'',
'',
'OpenTrade,Swing'
]);
$stmtTrade->execute([
$accountId,
(string)($ticketBase + 992),
'EURUSD',
'sell',
1.0,
1.08620,
null,
1.08850,
1.08100,
date('Y-m-d H:i:s', time() - 7200),
null,
12.0,
120.0,
-7.0,
0.0,
'open',
'Price Action',
'M15',
'London',
2.2,
'Confident',
'تشکیل الگوی پوشای نزولی در لول مقاومت روزانه',
'',
'',
'OpenTrade,Intraday'
]);
}
}