Update skills: add website-creator, mql-developer, ecommerce-astro

Changes:
- Add FAL_KEY and GEMINI_API_KEY to .env.example
- Update picture-it to use ~/.config/opencode/.env (unified creds)
- Remove shodh-memory skill (no longer used)
- Remove alphaear-* skills (deprecated)
- Remove thai-frontend-dev skill (replaced by website-creator)
- Remove theme-factory skill
- Add mql-developer skill (MQL5 trading)
- Add ecommerce-astro skill (Astro e-commerce)
- Add website-creator skill (Next.js + Payload CMS)
- Update install script for new skills
This commit is contained in:
2026-04-16 17:40:27 +07:00
parent 5053ccdba2
commit b26c8199a5
562 changed files with 59030 additions and 37600 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,237 @@
# Backtesting & Optimization
## Table of Contents
- [Strategy Tester Modes](#strategy-tester-modes)
- [Minimum Requirements for Valid Backtest](#minimum-requirements-for-valid-backtest)
- [OnTester() - Custom Optimization Criterion](#ontester---custom-optimization-criterion-mql5)
- [TesterStatistics() Constants](#testerstatistics-constants)
- [Key Performance Metrics](#key-performance-metrics)
- [Walk-Forward Analysis](#walk-forward-analysis)
- [Avoiding Overfitting](#avoiding-overfitting)
- [Monte Carlo Simulation](#monte-carlo-simulation)
- [Multi-Currency Testing (MQL5)](#multi-currency-testing-mql5)
- [Optimization Modes (MT5)](#optimization-modes-mt5)
- [Frame Functions (Inter-Pass Communication)](#frame-functions-inter-pass-communication)
- [Practical Backtesting Workflow](#practical-backtesting-workflow)
---
## Strategy Tester Modes
| Mode | Speed | Accuracy | When to Use |
|------|-------|----------|-------------|
| Every tick based on real ticks | Slowest | Most realistic | Final validation with broker-specific ticks |
| Every tick | Slow | High | Final validation, scalpers, intra-bar logic |
| 1-minute OHLC | Medium | Good | Most strategies (4 ticks per M1 bar) |
| Open prices only | Fast | Low | Bar-open strategies, rapid optimization |
| Math calculations | Instant | N/A | Pure computation without ticks |
Best practice: Optimize with Open Prices first, validate winners with Every Tick.
## Minimum Requirements for Valid Backtest
- 1,000+ trades minimum (ideally 2,000+)
- 2+ full market cycles (bull + bear + range)
- Multiple years of data
- Consistent spread settings (or variable spread)
## OnTester() - Custom Optimization Criterion (MQL5)
```mql5
double OnTester() {
double profit = TesterStatistics(STAT_PROFIT);
double profitFactor = TesterStatistics(STAT_PROFIT_FACTOR);
double sharpeRatio = TesterStatistics(STAT_SHARPE_RATIO);
double recoveryFactor = TesterStatistics(STAT_RECOVERY_FACTOR);
double maxDrawdown = TesterStatistics(STAT_EQUITY_DD_RELATIVE);
double totalTrades = TesterStatistics(STAT_TRADES);
if(totalTrades < 100) return 0; // Reject small samples
if(maxDrawdown > 25) return 0; // Reject excessive DD
if(profitFactor < 1.3) return 0; // Reject low PF
return sharpeRatio * recoveryFactor * MathSqrt(totalTrades);
}
```
## TesterStatistics() Constants
Complete table:
- STAT_PROFIT - Net profit
- STAT_GROSS_PROFIT - Gross profit
- STAT_GROSS_LOSS - Gross loss
- STAT_TRADES - Total trades
- STAT_PROFIT_TRADES - Winning trades
- STAT_LOSS_TRADES - Losing trades
- STAT_PROFIT_FACTOR - Gross profit / gross loss
- STAT_EXPECTED_PAYOFF - Expected payoff per trade
- STAT_SHARPE_RATIO - Sharpe ratio
- STAT_RECOVERY_FACTOR - Net profit / max drawdown
- STAT_EQUITY_DD - Max equity drawdown in money
- STAT_EQUITY_DD_PERCENT - Max equity drawdown %
- STAT_EQUITY_DD_RELATIVE - Relative equity drawdown %
- STAT_BALANCE_DD - Max balance drawdown
- STAT_BALANCE_DD_PERCENT - Max balance drawdown %
- STAT_MAX_PROFITTRADE - Largest profitable trade
- STAT_MAX_LOSSTRADE - Largest losing trade
- STAT_CONPROFITMAX - Max consecutive profit
- STAT_CONLOSSMAX - Max consecutive loss
- STAT_SHORT_TRADES - Short trades
- STAT_LONG_TRADES - Long trades
- STAT_WIN_SHORT_TRADES - Winning short trades
- STAT_WIN_LONG_TRADES - Winning long trades
## Key Performance Metrics
| Metric | Formula | Good Value | Excellent |
|--------|---------|------------|-----------|
| Sharpe Ratio | (Mean Return - Rf) / StdDev | > 1.0 | > 2.0 |
| Profit Factor | Gross Profit / Gross Loss | > 1.5 | > 2.0 |
| Recovery Factor | Net Profit / Max DD | > 3.0 | > 5.0 |
| Max Drawdown | Peak-to-trough % | < 20% | < 10% |
| Expected Payoff | Net Profit / Total Trades | > 0 | Context |
| Win Rate | Winning / Total | Context | Context |
## Walk-Forward Analysis
### Methodology
```
Window 1: [===== IN-SAMPLE =====][= OOS =]
Window 2: [===== IN-SAMPLE =====][= OOS =]
Window 3: [===== IN-SAMPLE =====][= OOS =]
Window 4: [===== IN-SAMPLE =====][= OOS =]
```
### Steps
1. Split data into overlapping windows (e.g., 12-month IS, 3-month OOS)
2. Optimize on in-sample period
3. Test optimized params on out-of-sample period
4. Slide window forward, repeat
5. Accept if OOS performance >= 50-80% of IS
6. MT5 supports natively: Strategy Tester > Settings > Forward period
### Walk-Forward Efficiency
WFE = (OOS annualized return) / (IS annualized return)
- WFE > 0.5 = acceptable
- WFE > 0.7 = good
- WFE < 0.3 = likely overfitted
## Avoiding Overfitting
### Principles
1. **Parameter stability**: Good params have good neighbors. If X=14 works but X=13 and X=15 don't, it's curve-fitted
2. **Fewer parameters**: Each extra parameter increases overfitting risk exponentially
3. **Out-of-sample testing**: Reserve 25-30% of data untouched
4. **Cross-market validation**: Test EURUSD strategy on GBPUSD
5. **Regime awareness**: Test across trending AND ranging markets
### Signs of Overfitting
- Sharp performance drop in forward test
- Many parameters (>5 optimized)
- Strategy only works on specific date range
- Unrealistically high backtest metrics
- Parameter sensitivity (small changes cause large performance swings)
## Monte Carlo Simulation
### Purpose
Test if results are due to skill or luck by randomizing trade sequence.
### Implementation
```mql5
double OnTester() {
// Collect all trade P&Ls
double trades[];
// ... populate from history ...
int simulations = 1000;
double worstDD95;
for(int sim = 0; sim < simulations; sim++) {
// Fisher-Yates shuffle
// Calculate max drawdown for shuffled sequence
}
// Sort DDs, find 95th percentile
// Reject if 95th percentile DD > 30%
return profit / (1 + worstDD95);
}
```
### What Monte Carlo Tells You
- Expected range of drawdowns (not just the one historical path)
- Probability of ruin at different risk levels
- Confidence interval for returns
- If strategy is fragile (high variance across simulations)
## Multi-Currency Testing (MQL5)
MT5 Strategy Tester supports multi-symbol natively:
```mql5
int OnInit() {
// Reference other symbols to include them in test
int handle_eur = iMA("EURUSD", PERIOD_H1, 14, 0, MODE_SMA, PRICE_CLOSE);
int handle_gbp = iMA("GBPUSD", PERIOD_H1, 14, 0, MODE_SMA, PRICE_CLOSE);
// Tester auto-synchronizes all referenced symbols
return INIT_SUCCEEDED;
}
```
## Optimization Modes (MT5)
| Mode | Description |
|------|-------------|
| Slow (Complete) | Tests every combination (exhaustive) |
| Fast (Genetic) | Genetic algorithm, finds near-optimal efficiently |
| Custom max/min | Optimizes by OnTester() return value |
### Cloud Computing
MQL5 Cloud Network distributes optimization across thousands of agents worldwide.
## Frame Functions (Inter-Pass Communication)
```mql5
// In EA (agent): send data at end of each pass
double OnTester() {
uchar data[];
// serialize results into data
FrameAdd("Results", 1, profit, data);
return profit;
}
// In terminal: receive during optimization
void OnTesterPass() {
ulong pass; string name; long id; double value; uchar data[];
while(FrameNext(pass, name, id, value, data)) {
PrintFormat("Pass #%d: profit=%.2f", pass, value);
}
}
// Control functions
void OnTesterInit() { /* before optimization starts */ }
void OnTesterDeinit() { /* after optimization finishes, aggregate results */ }
```
## Practical Backtesting Workflow
1. **Quick scan**: Open Prices Only, wide parameter ranges, genetic optimization
2. **Narrow down**: Reduce ranges around promising areas, complete optimization
3. **Validate**: Every Tick mode with best parameters
4. **Walk-forward**: Set forward period, verify OOS performance
5. **Monte Carlo**: Randomize trade sequence, check robustness
6. **Multi-market**: Test on correlated instruments
7. **Demo forward test**: Run on demo account for 1-3 months minimum

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,259 @@
# Security & Licensing
## Table of Contents
- [Account-Based Licensing](#account-based-licensing)
- [Server-Side License Validation](#server-side-license-validation)
- [Expiration Date Checks](#expiration-date-checks)
- [Anti-Decompilation Techniques](#anti-decompilation-techniques)
- [Distribution Best Practices](#distribution-best-practices)
- [Node.js License Server Example](#nodejs-license-server-example)
- [MQL4 Specific Considerations](#mql4-specific-considerations)
- [Complete License System Architecture](#complete-license-system-architecture)
---
## Account-Based Licensing
### Simple Multi-Account Check
```mql5
bool CheckAccountLicense() {
long currentAccount = AccountInfoInteger(ACCOUNT_LOGIN);
long authorizedAccounts[] = {12345678, 87654321, 11223344};
for(int i = 0; i < ArraySize(authorizedAccounts); i++) {
if(currentAccount == authorizedAccounts[i]) return true;
}
Alert("Account ", currentAccount, " is not authorized.");
return false;
}
```
### Account + Broker Hash Check (More Secure)
Don't store plain account numbers in code. Hash account + broker + salt:
```mql5
ulong HashString(string s) {
ulong hash = 5381;
for(int i = 0; i < StringLen(s); i++)
hash = ((hash << 5) + hash) + StringGetCharacter(s, i);
return hash;
}
bool ValidateLicense() {
long account = AccountInfoInteger(ACCOUNT_LOGIN);
string server = AccountInfoString(ACCOUNT_SERVER);
string raw = IntegerToString(account) + "|" + server + "|SECRET_SALT";
ulong hash = HashString(raw);
ulong validHashes[] = {0xA1B2C3D4E5F6, 0x1234567890AB};
for(int i = 0; i < ArraySize(validHashes); i++)
if(hash == validHashes[i]) return true;
return false;
}
```
---
## Server-Side License Validation
### Implementation Pattern
```mql5
input string InpLicenseKey = "";
bool ValidateLicenseOnServer() {
long account = AccountInfoInteger(ACCOUNT_LOGIN);
string broker = AccountInfoString(ACCOUNT_SERVER);
string eaName = MQLInfoString(MQL_PROGRAM_NAME);
string json = "{";
json += "\"license_key\":\"" + InpLicenseKey + "\",";
json += "\"account\":" + IntegerToString(account) + ",";
json += "\"broker\":\"" + broker + "\",";
json += "\"ea\":\"" + eaName + "\"";
json += "}";
// Send to server, check response
// Handle network failures (fail-open vs fail-closed decision)
}
```
### Fail-Open vs Fail-Closed
- **Fail-open:** Allow trading if server unreachable (better UX, weaker security)
- **Fail-closed:** Block trading if server unreachable (stronger security, risk of false blocks)
- **Recommended:** Fail-open with cached expiry and offline grace period
### Caching License Locally
Use `GlobalVariableSet` to cache server expiry:
```mql5
GlobalVariableSet("EA_LICENSE_EXPIRY", (double)StringToTime(expiry));
```
On next check, verify cached expiry first, re-validate with server periodically.
---
## Expiration Date Checks
### Hardcoded Expiration
```mql5
datetime expiryDate = D'2025.12.31 23:59:59';
if(TimeCurrent() > expiryDate) {
Alert("EA has expired. Please renew.");
return INIT_FAILED;
}
```
### Server-Cached Expiration
Check `GlobalVariable` cache first, re-validate on timer.
### Demo Mode with Limited Features
After trial period, restrict to demo accounts or limited lot sizes.
### Integration in OnInit
```mql5
int OnInit() {
if(!CheckAccountLicense()) return INIT_FAILED;
if(!CheckExpiration()) return INIT_FAILED;
EventSetTimer(3600); // Re-check every hour
return INIT_SUCCEEDED;
}
void OnTimer() {
if(!CheckExpiration()) ExpertRemove();
}
```
---
## Anti-Decompilation Techniques
### Protection Layers Table
| Layer | Technique | Protection Level | Notes |
|-------|-----------|-----------------|-------|
| 1 | Compile to .ex4/.ex5 | Basic | Strips variable/function names |
| 2 | Code obfuscation | Low | Complex expressions, dummy code |
| 3 | String encryption | Medium | Hide URLs, keys, constants |
| 4 | MQL5 Cloud Protector | High | Asymmetric encryption, native code |
| 5 | DLL offloading | High | Core logic in C++ DLL |
| 6 | Server-side logic | Highest | Core signals never leave server |
### String Obfuscation Example
```mql5
string DecryptString(int key) {
uchar encrypted[] = {104,116,116,112,115,58,47,47};
string result = "";
for(int i = 0; i < ArraySize(encrypted); i++)
result += CharToString((uchar)(encrypted[i] ^ (key % 256)));
return result;
}
```
### MQL5 Cloud Protector
- Available in MetaEditor: Tools > MQL5 Cloud Protector
- Sends compiled .ex5 to MetaQuotes cloud
- Applies asymmetric encryption + unique key signing
- Source code never leaves your machine
- Same protection as MQL5 Market store
- Files NOT bound to specific computer (unlike Market)
- Free to use
---
## Distribution Best Practices
### Do's
- Distribute only .ex4/.ex5 compiled files
- Use MQL5 Market for built-in DRM (hardware + account binding)
- Combine account binding + server validation + Cloud Protector
- Include version checking in licensing server to force updates
- Use unique magic numbers or comments to identify your EA
### Don'ts
- Never distribute .mq4/.mq5 source files
- Never hardcode API keys or server passwords in source
- Don't rely on a single protection layer
- Don't use simple string comparisons for license keys
---
## Node.js License Server Example
### Endpoint: POST /api/validate
```javascript
app.post('/api/validate', (req, res) => {
const { license_key, account, broker, ea, version } = req.body;
// Look up license in database
const license = db.findLicense(license_key);
if(!license) return res.json({ valid: false, message: "Invalid key" });
if(license.expired) return res.json({ valid: false, message: "License expired" });
if(license.maxAccounts && license.accounts.length >= license.maxAccounts) {
if(!license.accounts.includes(account))
return res.json({ valid: false, message: "Max accounts reached" });
}
// Register account if new
if(!license.accounts.includes(account)) {
license.accounts.push(account);
db.updateLicense(license);
}
res.json({
valid: true,
expiry: license.expiryDate,
features: license.features
});
});
```
---
## MQL4 Specific Considerations
### Account Number Check (MQL4)
```mql4
bool CheckLicense() {
int account = AccountNumber(); // MQL4 function
// ... same logic but with int instead of long
}
```
### Hardware ID (MQL4 via DLL)
Can use Windows API via DLL to get hardware identifiers for machine-specific licensing.
---
## Complete License System Architecture
```
EA (MQL5) License Server (Node.js)
| |
|-- POST /validate ----------->|
| {key, account, broker} |-- Check DB
| |-- Validate
|<-- {valid, expiry, features}-|
| |
|-- Cache expiry locally |
|-- Re-check every hour |
| |
|-- POST /heartbeat ---------->| (optional)
| {account, balance, status} |-- Track usage
```

File diff suppressed because it is too large Load Diff