Merge pull request #12 from mindesbunister/copilot/add-fartcoin-perp-market

Add FARTCOIN-PERP market with percentage-based position sizing
This commit is contained in:
mindesbunister
2025-12-06 19:07:58 +01:00
committed by GitHub
4 changed files with 274 additions and 0 deletions

View File

@@ -85,6 +85,9 @@ export async function GET() {
ETHEREUM_POSITION_SIZE: parseFloat(env.ETHEREUM_POSITION_SIZE || '4'), ETHEREUM_POSITION_SIZE: parseFloat(env.ETHEREUM_POSITION_SIZE || '4'),
ETHEREUM_LEVERAGE: parseFloat(env.ETHEREUM_LEVERAGE || '1'), ETHEREUM_LEVERAGE: parseFloat(env.ETHEREUM_LEVERAGE || '1'),
ETHEREUM_USE_PERCENTAGE_SIZE: env.ETHEREUM_USE_PERCENTAGE_SIZE === 'true', ETHEREUM_USE_PERCENTAGE_SIZE: env.ETHEREUM_USE_PERCENTAGE_SIZE === 'true',
FARTCOIN_ENABLED: env.FARTCOIN_ENABLED === 'true',
FARTCOIN_POSITION_SIZE: parseFloat(env.FARTCOIN_POSITION_SIZE || '20'),
FARTCOIN_LEVERAGE: parseFloat(env.FARTCOIN_LEVERAGE || '10'),
// Risk management // Risk management
STOP_LOSS_PERCENT: parseFloat(env.STOP_LOSS_PERCENT || '-1.5'), STOP_LOSS_PERCENT: parseFloat(env.STOP_LOSS_PERCENT || '-1.5'),
@@ -161,6 +164,9 @@ export async function POST(request: NextRequest) {
ETHEREUM_ENABLED: settings.ETHEREUM_ENABLED.toString(), ETHEREUM_ENABLED: settings.ETHEREUM_ENABLED.toString(),
ETHEREUM_POSITION_SIZE: settings.ETHEREUM_POSITION_SIZE.toString(), ETHEREUM_POSITION_SIZE: settings.ETHEREUM_POSITION_SIZE.toString(),
ETHEREUM_LEVERAGE: settings.ETHEREUM_LEVERAGE.toString(), ETHEREUM_LEVERAGE: settings.ETHEREUM_LEVERAGE.toString(),
FARTCOIN_ENABLED: settings.FARTCOIN_ENABLED.toString(),
FARTCOIN_POSITION_SIZE: settings.FARTCOIN_POSITION_SIZE.toString(),
FARTCOIN_LEVERAGE: settings.FARTCOIN_LEVERAGE.toString(),
// Risk management // Risk management
STOP_LOSS_PERCENT: settings.STOP_LOSS_PERCENT.toString(), STOP_LOSS_PERCENT: settings.STOP_LOSS_PERCENT.toString(),

View File

@@ -23,6 +23,9 @@ interface TradingSettings {
ETHEREUM_POSITION_SIZE: number ETHEREUM_POSITION_SIZE: number
ETHEREUM_LEVERAGE: number ETHEREUM_LEVERAGE: number
ETHEREUM_USE_PERCENTAGE_SIZE: boolean ETHEREUM_USE_PERCENTAGE_SIZE: boolean
FARTCOIN_ENABLED: boolean
FARTCOIN_POSITION_SIZE: number
FARTCOIN_LEVERAGE: number
// Risk management // Risk management
STOP_LOSS_PERCENT: number STOP_LOSS_PERCENT: number
@@ -467,6 +470,81 @@ export default function SettingsPage() {
})()} })()}
</Section> </Section>
{/* FARTCOIN Section */}
<Section title="🎈 Fartcoin (FARTCOIN-PERP)" description="Individual settings for Fartcoin perpetual trading (PROFIT MODE)">
<div className="mb-4 p-3 bg-pink-500/10 border border-pink-500/30 rounded-lg">
<p className="text-sm text-pink-400">
Enable/disable FARTCOIN trading with percentage-based position sizing for profit generation. Uses % of portfolio instead of fixed USD amounts.
</p>
<div className="mt-2 inline-flex items-center gap-2 px-3 py-1 bg-purple-500/20 rounded-full">
<span className="text-xs font-bold text-purple-300">PERCENTAGE-BASED</span>
</div>
</div>
<div className="flex items-center justify-between p-4 bg-slate-700/30 rounded-lg mb-4">
<div className="flex-1">
<div className="text-white font-medium mb-1">🟢 Enable Fartcoin Trading</div>
<div className="text-slate-400 text-sm">
Accept FARTCOIN-PERP trade signals from TradingView
</div>
</div>
<button
onClick={() => updateSetting('FARTCOIN_ENABLED', !settings.FARTCOIN_ENABLED)}
className={`relative inline-flex h-8 w-14 items-center rounded-full transition-colors ${
settings.FARTCOIN_ENABLED ? 'bg-green-500' : 'bg-slate-600'
}`}
>
<span
className={`inline-block h-6 w-6 transform rounded-full bg-white transition-transform ${
settings.FARTCOIN_ENABLED ? 'translate-x-7' : 'translate-x-1'
}`}
/>
</button>
</div>
<Setting
label="FARTCOIN Position Size (% of Portfolio)"
value={settings.FARTCOIN_POSITION_SIZE}
onChange={(v) => updateSetting('FARTCOIN_POSITION_SIZE', v)}
min={1}
max={100}
step={1}
description={`Percentage of your portfolio to allocate per FARTCOIN trade. With ${settings.FARTCOIN_LEVERAGE}x leverage = ${(settings.FARTCOIN_POSITION_SIZE * settings.FARTCOIN_LEVERAGE).toFixed(0)}% notional exposure. Example: 20% × $${collateral.toFixed(0)} = $${(settings.FARTCOIN_POSITION_SIZE / 100 * collateral).toFixed(2)} base.`}
/>
<Setting
label="FARTCOIN Leverage"
value={settings.FARTCOIN_LEVERAGE}
onChange={(v) => updateSetting('FARTCOIN_LEVERAGE', v)}
min={1}
max={10}
step={1}
description="Leverage multiplier for Fartcoin positions (max 10x based on Drift margin requirements)."
/>
{(() => {
const fartcoinRisk = {
maxLoss: (settings.FARTCOIN_POSITION_SIZE / 100 * collateral * settings.FARTCOIN_LEVERAGE) * 0.015,
fullWin: (settings.FARTCOIN_POSITION_SIZE / 100 * collateral * settings.FARTCOIN_LEVERAGE) * 0.018,
}
return (
<div className="p-4 bg-slate-700/50 rounded-lg">
<div className="text-sm text-slate-300 mb-2">FARTCOIN Risk/Reward (% of Portfolio)</div>
<div className="flex gap-4 text-xs">
<div>
<span className="text-red-400">Max Loss: </span>
<span className="text-white font-bold">${fartcoinRisk.maxLoss.toFixed(2)}</span>
</div>
<div>
<span className="text-green-400">Full Win: </span>
<span className="text-white font-bold">${fartcoinRisk.fullWin.toFixed(2)}</span>
</div>
<div>
<span className="text-purple-400">R:R </span>
<span className="text-white font-bold">1:{(fartcoinRisk.fullWin / fartcoinRisk.maxLoss).toFixed(2)}</span>
</div>
</div>
</div>
)
})()}
</Section>
{/* Global Position Sizing (Fallback) */} {/* Global Position Sizing (Fallback) */}
<Section title="💰 Global Position Sizing (Fallback)" description="Default settings for symbols without specific config (e.g., BTC)"> <Section title="💰 Global Position Sizing (Fallback)" description="Default settings for symbols without specific config (e.g., BTC)">
<div className="mb-4 p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg"> <div className="mb-4 p-3 bg-yellow-500/10 border border-yellow-500/30 rounded-lg">

View File

@@ -28,6 +28,7 @@ export interface TradingConfig {
// Per-symbol settings // Per-symbol settings
solana?: SymbolSettings solana?: SymbolSettings
ethereum?: SymbolSettings ethereum?: SymbolSettings
fartcoin?: SymbolSettings
// Risk management (as percentages of entry price - LEGACY, used as fallback) // Risk management (as percentages of entry price - LEGACY, used as fallback)
stopLossPercent: number // Negative number (e.g., -1.5) stopLossPercent: number // Negative number (e.g., -1.5)
@@ -135,6 +136,12 @@ export const DEFAULT_TRADING_CONFIG: TradingConfig = {
leverage: 1, // 1x leverage = $4 notional leverage: 1, // 1x leverage = $4 notional
usePercentageSize: false, usePercentageSize: false,
}, },
fartcoin: {
enabled: false, // DISABLED BY DEFAULT
positionSize: 20, // 20% of portfolio (for profit generation)
leverage: 10, // 10x leverage
usePercentageSize: true, // PERCENTAGE-BASED (not fixed USD)
},
// Risk parameters (LEGACY FALLBACK - used when ATR unavailable) // Risk parameters (LEGACY FALLBACK - used when ATR unavailable)
stopLossPercent: -1.5, // Fallback: -1.5% if no ATR stopLossPercent: -1.5, // Fallback: -1.5% if no ATR
@@ -234,6 +241,14 @@ export const SUPPORTED_MARKETS: Record<string, MarketConfig> = {
positionSize: 40, // $40 base capital positionSize: 40, // $40 base capital
leverage: 1, // 1x leverage = $40 total exposure leverage: 1, // 1x leverage = $40 total exposure
}, },
'FARTCOIN-PERP': {
symbol: 'FARTCOIN-PERP',
driftMarketIndex: 22,
pythPriceFeedId: '2sZomfWMDuQLcFak3nuharXorHrZ3hK8iaML6ZGSHtso',
minOrderSize: 1, // 1 FARTCOIN minimum
tickSize: 0.0001,
// Use per-symbol config below
},
} }
// Map TradingView symbols to Drift markets // Map TradingView symbols to Drift markets
@@ -243,6 +258,7 @@ export function normalizeTradingViewSymbol(tvSymbol: string): string {
if (upper.includes('SOL')) return 'SOL-PERP' if (upper.includes('SOL')) return 'SOL-PERP'
if (upper.includes('BTC')) return 'BTC-PERP' if (upper.includes('BTC')) return 'BTC-PERP'
if (upper.includes('ETH')) return 'ETH-PERP' if (upper.includes('ETH')) return 'ETH-PERP'
if (upper.includes('FARTCOIN')) return 'FARTCOIN-PERP'
// Default to SOL if unknown // Default to SOL if unknown
console.warn(`Unknown symbol ${tvSymbol}, defaulting to SOL-PERP`) console.warn(`Unknown symbol ${tvSymbol}, defaulting to SOL-PERP`)
@@ -277,6 +293,14 @@ export function getPositionSizeForSymbol(symbol: string, baseConfig: TradingConf
} }
} }
if (symbol === 'FARTCOIN-PERP' && baseConfig.fartcoin) {
return {
size: baseConfig.fartcoin.positionSize,
leverage: baseConfig.fartcoin.leverage,
enabled: baseConfig.fartcoin.enabled,
}
}
// Fallback to market-specific config, then global config // Fallback to market-specific config, then global config
const marketConfig = getMarketConfig(symbol) const marketConfig = getMarketConfig(symbol)
return { return {
@@ -352,6 +376,13 @@ export async function getActualPositionSizeForSymbol(
enabled: baseConfig.ethereum.enabled, enabled: baseConfig.ethereum.enabled,
} }
usePercentage = baseConfig.ethereum.usePercentageSize ?? false usePercentage = baseConfig.ethereum.usePercentageSize ?? false
} else if (symbol === 'FARTCOIN-PERP' && baseConfig.fartcoin) {
symbolSettings = {
size: baseConfig.fartcoin.positionSize,
leverage: baseConfig.fartcoin.leverage,
enabled: baseConfig.fartcoin.enabled,
}
usePercentage = baseConfig.fartcoin.usePercentageSize ?? false
} else { } else {
// Fallback to market-specific or global config // Fallback to market-specific or global config
const marketConfig = getMarketConfig(symbol) const marketConfig = getMarketConfig(symbol)
@@ -502,6 +533,16 @@ export function getConfigFromEnv(): Partial<TradingConfig> {
? process.env.ETHEREUM_USE_PERCENTAGE_SIZE === 'true' ? process.env.ETHEREUM_USE_PERCENTAGE_SIZE === 'true'
: false, : false,
}, },
fartcoin: {
enabled: process.env.FARTCOIN_ENABLED === 'true',
positionSize: process.env.FARTCOIN_POSITION_SIZE
? parseFloat(process.env.FARTCOIN_POSITION_SIZE)
: 20,
leverage: process.env.FARTCOIN_LEVERAGE
? parseInt(process.env.FARTCOIN_LEVERAGE)
: 10,
usePercentageSize: process.env.FARTCOIN_USE_PERCENTAGE_SIZE !== 'false', // Default true
},
leverage: process.env.LEVERAGE leverage: process.env.LEVERAGE
? parseInt(process.env.LEVERAGE) ? parseInt(process.env.LEVERAGE)
: undefined, : undefined,

View File

@@ -0,0 +1,149 @@
# FARTCOIN-PERP Market Configuration
## Market Details
- **Market Index**: 22
- **Symbol**: FARTCOIN-PERP
- **Oracle**: `2sZomfWMDuQLcFak3nuharXorHrZ3hK8iaML6ZGSHtso`
- **Min Order Size**: 1 FARTCOIN
- **Tick Size**: $0.0001 (price precision)
- **Initial Margin Ratio**: 10% (max 10x leverage)
- **Maintenance Margin Ratio**: 5%
## Configuration Mode
- **Mode**: PROFIT GENERATION 💰
- **Position Sizing**: Percentage-based (20% of portfolio default)
- **Default Leverage**: 10x
- **Status**: Disabled by default
## Environment Variables
```bash
FARTCOIN_ENABLED=false # Enable/disable FARTCOIN trading
FARTCOIN_POSITION_SIZE=20 # 20% of portfolio (not fixed USD)
FARTCOIN_LEVERAGE=10 # Max 10x leverage
FARTCOIN_USE_PERCENTAGE_SIZE=true # Always true for FARTCOIN
```
## Enabling FARTCOIN Trading
### Via Settings UI (Recommended)
1. Navigate to Settings page: `http://localhost:3001/settings`
2. Scroll to "🎈 Fartcoin (FARTCOIN-PERP)" section
3. Toggle "Enable Fartcoin Trading" to ON
4. Adjust position size percentage (recommended: 10-20%)
5. Set leverage (recommended: 10x for max profit potential)
6. Click "Save Settings"
### Via Environment Variables
1. Edit `.env` file
2. Set `FARTCOIN_ENABLED=true`
3. Adjust `FARTCOIN_POSITION_SIZE` (1-100%)
4. Adjust `FARTCOIN_LEVERAGE` (1-10x)
5. Restart container: `docker restart trading-bot-v4`
### Via TradingView Alert
Ensure your TradingView alert includes FARTCOIN symbol:
```json
{
"symbol": "FARTCOINUSDT", // or "FARTCOIN"
"direction": "long",
// ... other fields
}
```
The bot will automatically normalize `FARTCOINUSDT` to `FARTCOIN-PERP`.
## Position Size Calculation
**Percentage-based sizing formula:**
```
Base Capital = Portfolio × Position Size %
Notional Position = Base Capital × Leverage
Example with $1000 portfolio:
- Position Size: 20% → Base = $200
- Leverage: 10x → Notional = $2000
- Risk Exposure: $200 (max loss is your base capital)
```
**Risk/Reward Example:**
- Portfolio: $1000
- Position: 20% ($200) × 10x = $2000 notional
- Max Loss (1.5% SL): $30 (1.5% of $2000)
- Target Win (1.8% TP): $36 (1.8% of $2000)
- R:R Ratio: 1:1.2
## Key Differences from SOL/ETH
| Feature | FARTCOIN | SOL | ETH |
|---------|----------|-----|-----|
| **Position Sizing** | Percentage-based | Fixed USD | Fixed USD |
| **Default Enabled** | ❌ Disabled | ✅ Enabled | ✅ Enabled |
| **Purpose** | Profit generation | Profit generation | Data collection |
| **Default Size** | 20% portfolio | $210 fixed | $4 fixed |
| **Default Leverage** | 10x | 10x | 1x |
| **Max Leverage** | 10x | 20x | 20x |
## Risk Management
**Important Notes:**
1. **Disabled by default** - Safe to deploy, enable when ready
2. **Max 10x leverage** - Based on Drift Protocol margin requirements
3. **Percentage sizing** - Automatically scales with portfolio size
4. **Quality thresholds** - Uses same signal quality filters as SOL/ETH
5. **Adaptive leverage** - Quality score determines actual leverage used
## TradingView Setup
Your indicator should include FARTCOIN detection logic. The bot will:
1. Receive webhook with `"symbol": "FARTCOINUSDT"`
2. Normalize to `FARTCOIN-PERP` via `normalizeTradingViewSymbol()`
3. Check if `FARTCOIN_ENABLED=true`
4. Calculate position size as percentage of free collateral
5. Apply max 10x leverage
6. Execute trade on Drift Protocol market index 22
## Testing Checklist
Before enabling FARTCOIN trading in production:
- [ ] Settings UI shows FARTCOIN section correctly
- [ ] Toggle switch works (enabled/disabled state)
- [ ] Position size slider adjusts (1-100%)
- [ ] Leverage slider adjusts (1-10x)
- [ ] Risk calculator shows correct values
- [ ] Percentage calculation: 20% of $1000 = $200 base
- [ ] Notional calculation: $200 × 10x = $2000
- [ ] Settings save successfully to .env
- [ ] Container restart applies new settings
- [ ] Symbol detection works: "FARTCOINUSDT" → "FARTCOIN-PERP"
- [ ] Test trade executes with correct position size
## Monitoring
Watch for these log messages when FARTCOIN trading is active:
```
📊 Percentage sizing: 20% of $1000.00 = $200.00
📊 Adaptive leverage: Quality 92 → 10x leverage
🎯 Opening position: FARTCOIN-PERP LONG $2000.00 (10x leverage)
✅ Position opened successfully
```
## Troubleshooting
**FARTCOIN trades not executing:**
1. Check `FARTCOIN_ENABLED=true` in settings
2. Verify symbol normalization in logs
3. Check quality score meets threshold (90+ for LONG, 95+ for SHORT)
4. Ensure TradingView alert includes correct symbol
5. Verify free collateral > required amount
**Position size incorrect:**
1. Check `FARTCOIN_USE_PERCENTAGE_SIZE=true` (should always be true)
2. Verify percentage calculation in logs
3. Check free collateral value
4. Ensure leverage is between 1-10x
**Settings not saving:**
1. Check .env file permissions (should be writable)
2. Verify container has access to .env file
3. Check logs for "Settings updated" message
4. Restart container after manual .env changes