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:
2426
skills/mql-developer/references/architecture-patterns.md
Normal file
2426
skills/mql-developer/references/architecture-patterns.md
Normal file
File diff suppressed because it is too large
Load Diff
237
skills/mql-developer/references/backtesting.md
Normal file
237
skills/mql-developer/references/backtesting.md
Normal 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
|
||||
1057
skills/mql-developer/references/external-communication.md
Normal file
1057
skills/mql-developer/references/external-communication.md
Normal file
File diff suppressed because it is too large
Load Diff
1062
skills/mql-developer/references/indicators-and-ui.md
Normal file
1062
skills/mql-developer/references/indicators-and-ui.md
Normal file
File diff suppressed because it is too large
Load Diff
2051
skills/mql-developer/references/mql4-reference.md
Normal file
2051
skills/mql-developer/references/mql4-reference.md
Normal file
File diff suppressed because it is too large
Load Diff
1255
skills/mql-developer/references/mql5-reference.md
Normal file
1255
skills/mql-developer/references/mql5-reference.md
Normal file
File diff suppressed because it is too large
Load Diff
259
skills/mql-developer/references/security-licensing.md
Normal file
259
skills/mql-developer/references/security-licensing.md
Normal 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
|
||||
```
|
||||
1142
skills/mql-developer/references/trading-operations.md
Normal file
1142
skills/mql-developer/references/trading-operations.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user