← Back

How to Trade with Magellan

A practical handbook for getting the most out of Magellan's grid trading strategy on SOL/USDC.

This guide assumes you understand Solana, token swaps, and basic DeFi concepts. It focuses on how to think about the parameters and how to adapt to market conditions — not on installation or configuration syntax.


Table of Contents

  1. How Magellan Makes Money
  2. Understanding the Grid
  3. The Cost of Trading
  4. Tuning for Profit: The Core Parameters
  5. Risk Parameters: Protecting Your Capital
  6. Capital Sizing and Unit Allocation
  7. Reading the Market: When to Use Which Settings
  8. Worked Examples
  9. The Paper-to-Live Pipeline
  10. Common Mistakes
  11. Advanced Tactics
  12. Quick Reference Cheat Sheet

Appendices


1. How Magellan Makes Money

Magellan is a scalping grid trader. It profits from the natural oscillation of SOL's price — the constant small ups and downs that happen even in a flat market.

The core cycle:

Price drops → Bot buys SOL cheap
Price recovers → Bot sells SOL at a small profit
Repeat

Each unit operates independently: buy low, sell high, recycle. A single trade might only make 0.2–0.5% profit, but across 10 units running 24/7, these small gains compound.

A day in the life of Unit 3 (with $50 per unit, TAKE_PROFIT=0.7, SOL at ~$150):

09:14 UTC — SOL drops to $148.50 → Unit 3's buy target hit → buys 0.3367 SOL for $50
09:14 UTC — Unit 3 is now HOLDING, watching for $149.54 (+0.7% above $148.50)
11:42 UTC — SOL recovers to $149.54 → sell triggers → sells 0.3367 SOL for $50.35
11:42 UTC — Unit 3 earns $0.35 gross, ~$0.15 in fees → $0.20 net profit
11:42 UTC — Unit 3 returns to IDLE → immediately gets a new buy target below market
           → Ready to do it all over again

One unit, one cycle, $0.20 profit. Multiply by 9 active units, several cycles per day, 365 days a year.

Key insight: Magellan doesn't need SOL to go up. It needs SOL to move. A sideways market with frequent 0.5–2% oscillations is ideal. A straight line (up or down) is the enemy.

Why grid trading works on SOL/USDC

SOL/USDC is one of the most liquid pairs on Solana with tight spreads on Jupiter. SOL typically oscillates 1–5% intraday even on "quiet" days. That's exactly the range Magellan exploits:


2. Understanding the Grid

The grid is the backbone of the strategy. Think of it as a ladder of buy orders placed below the current price.

How it works

With SOL at $150 and these settings:

SPLIT_NUMBER=10
PRINCIPAL_LEFTOVER=10
GRID_SPREAD=0.5

The bot creates 9 active units (10 total minus 1 buffer) with staggered buy targets:

UnitBuy TargetDistance Below Market
1$149.25-0.5%
2$148.50-1.0%
3$147.75-1.5%
4$147.00-2.0%
5$146.25-2.5%
6$145.50-3.0%
7$144.75-3.5%
8$144.00-4.0%
9$143.25-4.5%

When price drops to a unit's target, that unit buys. When price recovers by TAKE_PROFIT% above its entry, it sells.

The grid center

The grid center is the reference price from which all buy targets are calculated. It updates when:

Units already holding SOL are never affected by grid resets — only unfilled WAITING_TO_BUY orders get recalculated.

Example: grid center is $150, GRID_RESET_THRESHOLD=2. SOL rises to $154 (+2.7% drift):

Before reset:                          After reset:
Grid center: $150                      Grid center: $154
Unit 1: WAITING at $149.25 (stale)  →  Unit 1: WAITING at $153.23
Unit 2: WAITING at $148.50 (stale)  →  Unit 2: WAITING at $152.46
Unit 3: HOLDING at $147.75 entry    →  Unit 3: HOLDING at $147.75 (unchanged!)
Unit 4: WAITING at $147.00 (stale)  →  Unit 4: WAITING at $151.69

Unit 3 was already holding SOL — it keeps its position and its original take-profit target. Only the unfilled WAITING units get fresh targets near the new price.

Grid spread: wide vs. narrow

The deepest unit sits at active_units × GRID_SPREAD below the current price (since the first unit starts at 1 × GRID_SPREAD below market):

GRID_SPREADMax Depth (9 units)Behavior
0.1%0.9% below marketDense — many units trigger on small dips
0.3%2.7% below marketBalanced — good for typical SOL volatility
0.5%4.5% below marketWide — catches deeper dips, fewer triggers
1.0%9.0% below marketVery wide — only triggers on significant drops

Narrow grids (0.1–0.2%) fire more often but risk having many units buy at nearly the same price. If the price keeps dropping, they all take losses together.

Wide grids (0.5–1.0%) catch different price levels, giving you better average entries during a dip. But in a tight oscillation range, only the top 1–2 units ever trigger.


3. The Cost of Trading

Every trade has a cost. If your take-profit doesn't cover these costs, you lose money on winning trades.

Live mode costs

In live mode, round-trip costs come from:

Cost ComponentTypical RangeNotes
DEX pool fees0.05–0.15% per sideCharged by underlying AMMs (Raydium, Orca); Jupiter routes through them
Slippage0.01–0.10% per sideDepends on trade size and pool depth
Solana tx fee~0.000005 SOLNegligible (~$0.001)
Priority fees0–0.0001 SOLOnly during congestion; set PRIORITY_FEE=Y and PRIORITY_FEE_LAMPORTS in config.env, or leave PRIORITY_FEE=N for Jupiter auto-estimation

Typical round-trip cost in live mode: 0.1–0.5%

This means:

Paper mode costs

Paper mode simulates costs with SLIPPAGE_BPS and PAPER_DEX_FEE:

Round-trip cost = (SLIPPAGE_BPS / 2 / 10000 + PAPER_DEX_FEE) × 2

With the code defaults (SLIPPAGE_BPS=50, PAPER_DEX_FEE=0.0025): ~1.0% round-trip — deliberately pessimistic so paper profits don't give false confidence.

For faster paper testing with more trade activity, lower these values (e.g., SLIPPAGE_BPS=10, PAPER_DEX_FEE=0.0005 → ~0.2% round-trip). When transitioning to live, keep your TAKE_PROFIT well above real costs.

The profitability formula

Net profit per trade = TAKE_PROFIT% - round-trip cost%

Example with $50 unit size in live mode:

Same setup with TAKE_PROFIT=0.3%:

Rule of thumb for live trading: set TAKE_PROFIT to at least 2× your expected round-trip cost. If you estimate ~0.3% round-trip costs, use TAKE_PROFIT=0.7 (2.3× costs). This gives you a safety margin for slippage spikes.


4. Tuning for Profit: The Core Parameters

These four parameters determine how and when the bot trades.

TAKE_PROFIT — how much you make per trade

This is the percentage gain above entry price that triggers a sell.

TAKE_PROFIT=0.7   # Sell when +0.7% above buy price

Higher take-profit (0.7–1.0%):

Lower take-profit (0.3–0.5%):

Example: SOL at $150 with TAKE_PROFIT=0.7

Example: Same setup with TAKE_PROFIT=0.3

STOP_LOSS — how much you're willing to lose per trade

This is the percentage drop below entry price that triggers a forced sell.

STOP_LOSS=2   # Cut losses at -2% below buy price

Stop-loss exists to free up capital. Without it, a unit that bought during a downturn sits idle holding a losing position forever, unable to participate in new trades.

Tight stop-loss (1–1.5%):

Wide stop-loss (2–3%):

Example — tight vs. wide stop-loss in the same scenario:
Unit buys at $150. SOL drops to $147 (-2%), then recovers to $152 (+1.3%):

SettingWhat happensResult
STOP_LOSS=1.5Stop-loss triggers at $147.75 → forced sell → unit misses the recovery-$0.75 loss (1.5% of $50 unit)
STOP_LOSS=2Stop-loss triggers at $147.00 → forced sell → unit misses the recovery-$1.00 loss (2% of $50 unit)
STOP_LOSS=3Price never hits $145.50 → unit holds through the dip → sells at $151.05 (+0.7%)+$0.35 profit

The wide stop-loss won here because the dip reversed. But if SOL had kept falling to $140, the wide stop-loss would have lost $1.50 (3%) instead of $0.75 (1.5%).

The stop-loss / take-profit ratio matters — and costs make it worse. Trading costs (DEX fees + slippage) reduce your net profit on wins but increase your actual loss on stop-outs (you pay fees on the losing sell too). In live mode, with ~0.3% round-trip costs:

TAKE_PROFITSTOP_LOSSNet WinActual LossWins to RecoverMin Win Rate
0.7%2%~0.4%~2.3%~6 wins>85%
0.7%3%~0.4%~3.3%~8 wins>89%
0.5%2%~0.2%~2.3%~12 wins>92%
0.5%1.5%~0.2%~1.8%~9 wins>90%

These numbers look demanding, but grid trading in oscillating markets naturally produces very high win rates (often 90–95%) because most buy-the-dip entries recover. The key is ensuring the rare stop-loss events don't wipe out accumulated gains.

The practical takeaway: wider TAKE_PROFIT (0.7%+) and moderate STOP_LOSS (2%) give you the best cushion. Avoid thin take-profits with wide stop-losses — the math is brutal.

GRID_SPREAD — how far apart your buy orders are

GRID_SPREAD=0.3   # 0.3% between each unit's buy target

This controls how deep your grid extends below the current price. The deepest unit sits at GRID_SPREAD × active_units below market (since the first unit starts at 1 × GRID_SPREAD).

With 9 active units and GRID_SPREAD=0.3, your deepest unit is 2.7% below the current price.

Key trade-off: wider spread = better diversified entries but fewer triggers.

A good starting rule: set GRID_SPREAD roughly equal to TAKE_PROFIT / 2. This way, if price drops enough to trigger 2-3 units, it only needs to recover by TAKE_PROFIT% for all of them to profit.

Example: TAKE_PROFIT=0.7, GRID_SPREAD=0.35, SOL at $150:

If GRID_SPREAD were too wide (e.g., 1%), Unit 2 would be at $148.50 and Unit 3 at $147 — most units would never trigger in a small dip, leaving them idle.

GRID_RESET_THRESHOLD — when to rebuild the grid

GRID_RESET_THRESHOLD=2   # Reset grid if price drifts >2% from center

When the market moves significantly in one direction, your grid of unfilled buy orders becomes stale — they're all too far from the current price to ever trigger. The grid reset cancels them and rebuilds around the new price.

Low threshold (1–1.5%): resets frequently, keeps grid close to market. Risk: grid "chases" the price during trends, buying at each new level during a sustained drop.

High threshold (2–3%): only resets on significant moves. More stable, but if price slowly drifts away, the grid sits idle for longer.

Example — low vs. high threshold during a $150 → $155 trend:

ThresholdResets atWhat happens
1.5%$152.25 (+1.5%)Grid rebuilds at $152 — then again at $154.28 — constant chasing. If it's a downtrend disguised as noise, units keep buying higher and higher
3%$154.50 (+3%)Grid stays anchored at $150 until +3% drift. Fewer resets, but unfilled buy orders may sit stale for hours until the reset finally fires

In a genuine uptrend, low threshold = more opportunities. In a fakeout, low threshold = more exposure. There's no perfect answer — match it to your risk tolerance.

Rule of thumb: set GRID_RESET_THRESHOLD to approximately your grid's max depth. If your deepest unit sits at ~2.7% below market (9 units × 0.3% spread), a reset threshold of 2–3% ensures the grid rebuilds before it becomes completely out of range.


5. Risk Parameters: Protecting Your Capital

MAX_DAILY_LOSS — the circuit breaker

MAX_DAILY_LOSS=5   # Halt if daily losses exceed 5% of PRINCIPAL

With PRINCIPAL=500, this means trading halts if you lose $25 in a single day.

When triggered, the bot enters sell-only mode: it still executes stop-loss sells on holding positions (to limit further damage) but doesn't open any new buys. Resets at midnight UTC.

LevelMAX_DAILY_LOSSWith $500 principalWith $5,000 principal
Conservative3%Halts after -$15Halts after -$150
Moderate5%Halts after -$25Halts after -$250
Aggressive10%Halts after -$50Halts after -$500

Example: you're running with PRINCIPAL=500, SPLIT_NUMBER=10, STOP_LOSS=3, MAX_DAILY_LOSS=3, and SOL flash-crashes -8%. Three units get stopped out at -3% each, losing $1.50 × 3 = $4.50. Two more units stop out: total = $7.50. By the time a sixth unit stops out, daily loss reaches $9.00. Three more stop-losses and you hit -$13.50 (all 9 active units). The grid resets at the new lower price — the next stop-loss pushes you past -$15 → bot halts, preventing further damage while the crash plays out. Without the limit, the grid would keep resetting and buying into the continuing crash, snowballing losses far beyond $13.50.

PRINCIPAL_LEFTOVER — the safety buffer

PRINCIPAL_LEFTOVER=10   # Keep 10% of principal in reserve

This reserves units as an idle buffer. With 10 units and 10% leftover, 9 units trade and 1 stays idle.

Why keep a buffer?

  1. Slippage absorption — if a buy costs slightly more than the unit size due to slippage, the extra USDC comes from the unallocated buffer
  2. Downside cushion — if all active units get stopped out simultaneously, the buffer USDC remains in the wallet, preventing total capital deployment at unfavorable prices
  3. Recovery capacity — after a drawdown, buffer units can be deployed into the new (lower) grid with fresh buy targets

Example: PRINCIPAL=500, SPLIT_NUMBER=10, PRINCIPAL_LEFTOVER=10:

For live trading, 10% is a solid default. Drop to 5% once you're comfortable with the bot's behavior. Only set 0% if you fully trust the parameters and understand you're maximizing exposure.

HAPPY_GAIN — taking profits off the table

HAPPY_GAIN=3   # Transfer profits when cumulative gains reach 3 USDC

When cumulative profit reaches this threshold, the bot transfers exactly HAPPY_GAIN USDC to your BENEFICIARY_WALLET. Any excess carries over (e.g., if cumulative is $3.40 and HAPPY_GAIN is $3, it sends $3.00 and keeps $0.40 as the new cumulative balance). This is your profit extraction mechanism — it moves gains to a separate wallet so they're no longer at risk.

Frequent transfers (low HAPPY_GAIN, e.g. 2–3 USDC): safer, you realize gains often, but more transfer transactions.

Infrequent transfers (high HAPPY_GAIN, e.g. 10–20 USDC): fewer transactions, but more unrealized profit sitting in the operator wallet at risk.

Tip: set HAPPY_GAIN relative to your daily expected profit. If you expect ~$5/day, HAPPY_GAIN=3 transfers roughly twice daily.


6. Capital Sizing and Unit Allocation

How much capital?

Magellan works at any scale, but there's a practical minimum:

PRINCIPALSPLIT_NUMBERUnit SizeViability
$505$10Minimum viable — small trades, limited grid depth
$10010$10Good starting point for paper testing
$50010$50Solid for live trading — meaningful per-trade profit
$1,00015~$67Good density — 15-level grid catches more oscillations
$5,00020$250Deep grid with significant per-trade returns

How many units?

More units = denser grid = more buy levels. But there are diminishing returns:

The MIN_TRADE_USDC guard

MIN_TRADE_USDC=1   # Skip trades below 1 USDC

Jupiter has a minimum swap of ~$0.15, but you should set this higher. Tiny trades generate more fees relative to their size. With a unit size of $50, set MIN_TRADE_USDC=1 (or even $5). The goal is to avoid dust-sized trades that waste gas.


7. Reading the Market: When to Use Which Settings

Choppy / sideways market (best case)

SOL bouncing between $148–$152 with no clear trend.

TAKE_PROFIT=0.5
STOP_LOSS=2
GRID_SPREAD=0.2
GRID_RESET_THRESHOLD=2

Why: tight take-profit catches frequent oscillations. Grid spread is narrow because price isn't moving far. Stop-loss is wide enough to avoid false triggers.

Expected behavior: many units cycle through buy→sell within hours. High trade volume, consistent small profits.

Play-by-play ($500 principal, 9 active units, $50/unit):

08:00  SOL $150.00 — Grid placed. Unit 1 target: $149.70, Unit 2: $149.40...
08:45  SOL $149.60 — Units 1-2 buy
09:20  SOL $150.10 — Units 1-2 sell (+0.5% each) → +$0.50 profit
10:30  SOL $149.40 — Units 1-3 buy (grid re-placed after recycle)
11:15  SOL $150.20 — All 3 sell → +$0.75 profit
14:00  SOL $148.80 — Units 1-5 buy (deeper dip)
15:45  SOL $149.55 — Units 1-5 sell → +$1.25 profit
         Daily total: 10 round-trips, ~$2.50 profit, zero stop-losses

Volatile market (moderate swings)

SOL swinging 3–8% intraday.

TAKE_PROFIT=0.7
STOP_LOSS=3
GRID_SPREAD=0.5
GRID_RESET_THRESHOLD=3

Why: wider take-profit and grid spread match the larger swings. Higher stop-loss gives positions room to survive temporary dips before recovering.

Expected behavior: fewer but larger profits. Some units may hold for hours waiting for the recovery swing. Occasional stop-losses during sharp drops, but winning trades more than compensate.

Trending up

SOL climbing steadily from $140 to $160 over a few days.

TAKE_PROFIT=0.7
STOP_LOSS=2
GRID_SPREAD=0.3
GRID_RESET_THRESHOLD=1.5

Why: lower grid reset threshold keeps the grid chasing the price upward. Units buy on pullbacks and sell on the next leg up. Works well as long as the trend has regular pullbacks.

Expected behavior: grid resets frequently as price climbs. Each reset anchors new buy targets just below the new price. Units cycle on pullback oscillations within the trend.

Warning: a clean uptrend with no pullbacks means no buy triggers. The bot sits idle waiting for a dip. That's fine — it means you're not buying into an overextended move.

Trending down

SOL falling steadily from $160 to $140.

This is the hardest market for grid trading. Units buy on what looks like a dip, but the price keeps falling, triggering stop-losses.

TAKE_PROFIT=0.7
STOP_LOSS=1.5
GRID_SPREAD=0.5
GRID_RESET_THRESHOLD=3
MAX_DAILY_LOSS=3

Why: tighter stop-loss limits damage per position. Wider grid spread means units are spread across a broader range (not all buying near the top of the drop). Low daily loss cap halts trading early. High reset threshold prevents the grid from chasing the price down.

Expected behavior: some stop-losses triggered. Daily loss limit may halt trading — and that's a good thing. The bot protects capital and waits for conditions to improve.

Play-by-play ($500 principal, 9 active units, $50/unit):

08:00  SOL $160 — Grid placed. Units 1-9 waiting at $159.20 to $156.00
09:30  SOL $158 — Units 1-4 buy (targets hit)
10:15  SOL $156 — Units 5-7 buy, Units 1-4 still holding (waiting for recovery)
11:00  SOL $155 — Units 1-4 hit stop-loss at -1.5% → sell → -$3.00 total loss
11:00  SOL $155 — Units 5-7 now holding, waiting for +0.7% recovery
12:30  SOL $153 — Units 5-7 hit stop-loss → sell → -$2.25 total loss
12:30  Daily P&L: -$5.25 → still under $15 limit (3% of $500)
14:00  SOL $151 — Grid resets. New units buy at $150.24 to $147.50
15:00  SOL $149 — New units hit stop-loss → -$3.38 loss
15:00  Daily P&L: -$8.63 → still under $15 limit
16:00  SOL $147 — More stop-losses → daily loss reaches -$15 → BOT HALTS
         Bot saved you from the continued slide to $140 by end of day

Low volatility / dead market

SOL stuck at $150.00 ± $0.20 for days.

Grid trading doesn't work here. There's not enough movement to trigger buys, or if units buy, the price doesn't move enough to hit take-profit.

Best approach: either leave the bot running with Level 1 (Conservative) settings and accept low activity, or pause it and wait for volatility to return. Don't lower TAKE_PROFIT below your cost threshold just to get trades — you'll lose on every cycle.


8. Worked Examples

Example A: Conservative live setup ($500)

You want to trade real capital with minimal risk.

MODE=live
PRINCIPAL=500
SPLIT_NUMBER=10
PRINCIPAL_LEFTOVER=10
TAKE_PROFIT=0.7
STOP_LOSS=3
GRID_SPREAD=0.5
GRID_RESET_THRESHOLD=3
MAX_DAILY_LOSS=3
HAPPY_GAIN=5
SLIPPAGE_BPS=50

Breakdown:

With SOL oscillating ~2% daily, you might see 5–15 completed trades per day, mostly winners. A realistic daily return: $1–3 (0.2–0.6% of principal).

Example B: Active paper testing ($100)

You want to see lots of trades and understand the bot's behavior.

MODE=paper
PRINCIPAL=100
SPLIT_NUMBER=10
PRINCIPAL_LEFTOVER=10
TAKE_PROFIT=0.3
STOP_LOSS=1.5
GRID_SPREAD=0.2
GRID_RESET_THRESHOLD=1.5
MAX_DAILY_LOSS=7
SLIPPAGE_BPS=10
PAPER_DEX_FEE=0.0005

Breakdown:

Use the dashboard's History tab to study patterns: when do stop-losses cluster? What time of day generates the most wins? How often does the grid reset?

Example C: Scaling up ($5,000)

You've validated the strategy in paper mode and small live, now deploying serious capital.

MODE=live
PRINCIPAL=5000
SPLIT_NUMBER=20
PRINCIPAL_LEFTOVER=10
TAKE_PROFIT=0.7
STOP_LOSS=2
GRID_SPREAD=0.3
GRID_RESET_THRESHOLD=2
MAX_DAILY_LOSS=3
HAPPY_GAIN=20
SLIPPAGE_BPS=50

Breakdown:

With 18 active units and a deep 5.4% grid, you catch a wide range of dips. Realistic daily return: $10–30 (0.2–0.6% of principal). Profits auto-transfer every time $20 accumulates.


9. The Paper-to-Live Pipeline

Don't go straight from zero to live trading. Follow this pipeline:

Stage 1: Paper with aggressive settings (1–2 days)

MODE=paper
TAKE_PROFIT=0.3
GRID_SPREAD=0.2

Goal: generate lots of trades to understand how the grid behaves. Watch the dashboard. Look at:

Stage 2: Paper with your intended live settings (3–5 days)

MODE=paper
TAKE_PROFIT=0.7
GRID_SPREAD=0.5

Goal: validate that your actual configuration is profitable. Collect enough data for statistical significance (at least 20–30 completed trades). Check the History tab:

Stage 3: Live with small capital (1–2 weeks)

MODE=live
PRINCIPAL=50

Goal: verify real execution. Jupiter swaps, actual slippage, real gas costs. Compare live P&L to paper P&L. Live will typically be slightly worse due to real-world execution costs.

Stage 4: Scale up

Once live results match or are close to paper results over 1–2 weeks, gradually increase PRINCIPAL. Don't jump from $50 to $5,000. Go $50 → $200 → $500 → $1,000 → target.


10. Common Mistakes

Setting TAKE_PROFIT too low

If TAKE_PROFIT doesn't exceed your round-trip trading costs, every "winning" trade actually loses money. In live mode, keep it at 0.7% minimum unless you've verified lower values are profitable with your specific RPC/routing setup.

Example: you set TAKE_PROFIT=0.3 with a $50 unit. The bot buys at $149.25, sells at $149.70 — gross profit $0.15. But Jupiter fees + slippage cost $0.15 on the buy and $0.10 on the sell = $0.25 total fees. Net result: -$0.10 loss on a "winning" trade. Repeat 20 times a day and you lose $2/day while the dashboard shows positive trades.

Ignoring the stop-loss / take-profit ratio

A 0.5% take-profit with a 3% stop-loss sounds like 6:1, but costs make it far worse. After ~0.3% round-trip costs, your net win is ~0.2% while your actual loss is ~3.15% (stop-loss + sell fees). That's ~16 wins needed per loss. If your win rate drops below 94%, you bleed capital. Keep the ratio manageable — ideally STOP_LOSS should be no more than 3–4× your TAKE_PROFIT.

Example: with $50 units, TAKE_PROFIT=0.5 (net ~$0.10/win after fees) and STOP_LOSS=3 (loss = $1.58 with fees). You need 16 winning trades to recover from a single stop-loss. If you get 2 stop-losses in a row, that's 32 wins needed just to break even.

Grid spread too narrow with many units

GRID_SPREAD=0.1 with 20 units means all 20 units buy within a 2% range below market. If SOL drops 2% and then drops another 3%, you have 20 units all underwater together. Wider spread = better risk distribution.

Example: SOL at $150, GRID_SPREAD=0.1, 20 units. All units have buy targets between $149.85 and $147.00. SOL drops to $147 → all 20 units buy. SOL continues to $143 → all 20 units hit stop-loss simultaneously. Loss: 20 × $1.50 = $30.00 in one move. Had you used GRID_SPREAD=0.5 (targets spanning $149.25 to $140.50), only the top 6–8 units would have triggered, limiting the damage.

Chasing losses by widening daily loss limit

When MAX_DAILY_LOSS triggers, it's doing its job. Don't increase it just because you hit it once. A day where you hit the loss limit is a day where the market conditions were bad for grid trading. The bot stopped you from losing more. That's a win.

Example: your bot hits MAX_DAILY_LOSS=3 ($15 loss on $500 principal) during a SOL selloff. You think "the selloff is almost over" and increase to MAX_DAILY_LOSS=10. SOL keeps dropping — the bot buys into every dead-cat bounce and stops out each time. End of day: -$42 loss instead of -$15. The original limit would have saved you $27.

Running on a flaky RPC

Jupiter swaps involve multiple API calls (quote → swap → sign → send → confirm). If your RPC is slow or unreliable, transactions may timeout and retry, wasting gas and creating edge cases. Use a reliable RPC (Helius recommended) and enable FALLBACK_RPC with a second provider. During network congestion, if you see repeated TX_FAILED events in the logs, enable PRIORITY_FEE=Y with a higher PRIORITY_FEE_LAMPORTS value (e.g., 100000 for moderate congestion).

Never checking the dashboard

The dashboard exists for a reason. Check it daily. Look at:

Not exporting trade history

Use the Export CSV button in the History tab to download your trades. Open them in a spreadsheet and analyze: which times of day are most profitable? Do certain units perform better? Is there a pattern to stop-loss events? Data-driven tuning beats guesswork.


11. Advanced Tactics

Adjusting parameters by time of day

SOL volatility isn't uniform. US market hours (14:30–21:00 UTC) and Asian market hours (01:00–08:00 UTC) tend to be more volatile. You can:

  1. Run with tighter settings (lower TAKE_PROFIT, narrower GRID_SPREAD) during high-volatility hours
  2. Switch to wider settings during quiet hours

This requires manually updating config.env and restarting, but can be scripted with a cron job that swaps config files.

Example setup with two config files:

# config.env.active — for volatile hours (US/Asia market overlap)
TAKE_PROFIT=0.5
GRID_SPREAD=0.2

# config.env.quiet — for low-volatility hours (overnight US)
TAKE_PROFIT=0.7
GRID_SPREAD=0.5
# Cron jobs (add via `crontab -e`):
# Switch to active at 14:30 UTC (US market open)
30 14 * * 1-5 cp config.env.active config.env && pm2 restart magellan
# Switch to quiet at 21:00 UTC (US market close)
0 21 * * 1-5 cp config.env.quiet config.env && pm2 restart magellan

Using GRID_SPREAD=0 for conviction plays

Setting GRID_SPREAD=0 makes all units buy at the same price. This is essentially an all-in at a single level — useful if you're highly confident SOL will bounce from a specific support level.

GRID_SPREAD=0
TAKE_PROFIT=1.0

All 9 units buy together, and a 1% bounce gives you 9× the single-unit profit. But if the bounce doesn't come, 9 units all take the stop-loss together. High reward, high risk.

Stacking HAPPY_GAIN for compound growth

Set HAPPY_GAIN high enough that profits stay in the operator wallet and effectively increase your trading capital between transfers. The bot trades with PRINCIPAL, but if your wallet has more USDC than PRINCIPAL, the excess acts as additional buffer.

Example: PRINCIPAL=500, HAPPY_GAIN=50. The bot accumulates up to $50 in profit before transferring. That extra $50 in the wallet absorbs slippage and provides a larger cushion against stop-losses.

Monitoring with the dashboard CSV export

Export CSVs for multiple days and combine them in a spreadsheet. Create charts of:

This analysis tells you when your parameters need updating better than any single metric.

Fallback RPC for reliability

For serious live deployments, always enable fallback:

FALLBACK_ENABLED=Y
FALLBACK_RPC_URL=https://solana-mainnet.g.alchemy.com/v2/YOUR_KEY

If your primary RPC goes down, the bot automatically switches. Downtime = missed trades = missed profits.


12. Quick Reference Cheat Sheet

Parameter relationships

TAKE_PROFIT > round-trip cost              → profitability requirement
STOP_LOSS ≤ 3–4 × TAKE_PROFIT             → manageable loss ratio
GRID_SPREAD ≈ TAKE_PROFIT / 2              → balanced grid density
GRID_RESET_THRESHOLD ≈ grid max depth      → prevents stale grids
    (grid depth = GRID_SPREAD × active_units)
MAX_DAILY_LOSS = your pain threshold        → capital protection

Settings by market condition

ConditionTAKE_PROFITSTOP_LOSSGRID_SPREADGRID_RESET
Choppy / sideways0.5%2%0.2%2%
Volatile swings0.7%3%0.5%3%
Trending up0.7%2%0.3%1.5%
Trending down0.7%1.5%0.5%3%
Low volatility0.7%+3%0.5%3%

Minimum viable settings for live trading

MODE=live
TAKE_PROFIT=0.7          # covers live trading costs with margin
STOP_LOSS=2              # room to breathe, but not too much
GRID_SPREAD=0.3          # balanced density
GRID_RESET_THRESHOLD=2   # adapts to market movement
MAX_DAILY_LOSS=3         # conservative protection
PRINCIPAL_LEFTOVER=10    # safety buffer
HAPPY_GAIN=5             # regular profit extraction
SLIPPAGE_BPS=50          # standard Jupiter tolerance

The golden rules

  1. TAKE_PROFIT must beat your costs. In live mode, 0.7% is the safe minimum.
  2. Let MAX_DAILY_LOSS do its job. Don't increase it after a bad day.
  3. Paper test every parameter change before applying it to live capital.
  4. Check the dashboard daily. Win rate, P&L trend, and stop-loss frequency are your vital signs.
  5. Magellan loves oscillation. Sideways choppy markets are where it shines. Trending markets require patience and protection.
  6. Start small, scale gradually. The paper → small live → full live pipeline exists for a reason.
  7. Export and analyze your trades. Data beats intuition every time.

Appendix A: How to Change Structural Parameters

Structural parameters define the shape of Magellan's trading grid and cannot be hot-reloaded while the bot is running. Changing them requires a full stop, state reset, and restart.

Which parameters are structural?

ParameterWhat It ControlsDefault
PRINCIPALTotal USDC budget allocated to trading100
SPLIT_NUMBERNumber of trading units (grid slots)10
PRINCIPAL_LEFTOVER% of principal reserved as safety buffer10
MODEpaper (simulated) or live (real swaps)paper
RPC_URLSolana RPC endpoint
FALLBACK_RPC_URLBackup RPC endpoint
JUPITER_API_URLJupiter swap API base URLhttps://api.jup.ag/swap/v1
JUPITER_API_KEYJupiter API key
OPERATOR_WALLET_PRIVATE_KEYBase58 wallet private key
BENEFICIARY_WALLETPublic key for profit transfers
MIN_TRADE_USDCMinimum trade size in USDC1

These are everything except the 9 hot-reloadable parameters (TAKE_PROFIT, STOP_LOSS, GRID_SPREAD, GRID_RESET_THRESHOLD, MAX_DAILY_LOSS, SOL_GAS_AMOUNT, SLIPPAGE_BPS, HAPPY_GAIN, POLL_INTERVAL_MS), which can be changed via the dashboard Config Editor or by editing config.env while the bot is running — no restart needed.

Why can't they be hot-reloaded?

When Magellan starts, it creates a state.json file containing a units[] array with exactly SPLIT_NUMBER entries, each sized at PRINCIPAL / SPLIT_NUMBER USDC. The entire grid — unit count, unit sizing, buy targets — is derived from these structural values.

If you changed SPLIT_NUMBER from 10 to 15 while the bot was running, the state file would still have 10 units. The grid math would break: some units would be missing, buy targets would be wrong, and the buffer calculation would be off. The same applies to PRINCIPAL — existing units would have the old unit size baked in.

The only safe way to change structural parameters is: stop → edit → delete state → restart.

Scenario A: No open positions (all units IDLE)

This is the simple case. All units are IDLE, no SOL is held, no transactions are in flight.

Step 1 — Verify all units are IDLE

cd /var/www/magellan/current

cat state.json | python3 -c "
import json, sys
s = json.load(sys.stdin)
non_idle = [(i, u['status']) for i, u in enumerate(s.get('units', []), 1) if u['status'] != 'IDLE']
if non_idle:
    print('NOT SAFE -- these units are not IDLE:')
    for uid, status in non_idle:
        print(f'  Unit {uid}: {status}')
    print('\nUse Scenario B instead.')
else:
    print(f'All {len(s.get(\"units\", []))} units are IDLE -- safe to proceed')
"

Step 2 — Note your current lifetime stats (optional)

If you want to track continuity, save these before deleting state:

cat state.json | python3 -c "
import json, sys
s = json.load(sys.stdin)
print('=== Lifetime Stats (save these for your records) ===')
print(f'  Inception:         {s.get(\"inceptionDate\", \"?\")}')
print(f'  Lifetime P&L:      {s.get(\"lifetimePnlUsdc\", 0):.4f} USDC')
print(f'  Total trades:      {s.get(\"lifetimeTotalTrades\", 0)}')
print(f'  Winning trades:    {s.get(\"lifetimeWinningTrades\", 0)}')
print(f'  Losing trades:     {s.get(\"lifetimeLosingTrades\", 0)}')
print(f'  Cum. profit:       {s.get(\"cumulativeProfitUsdc\", 0):.4f} USDC')
print(f'  Transfers:         {s.get(\"lifetimeTransfersUsdc\", 0):.4f} USDC')
"

Deleting state.json resets all lifetime counters to zero. The dashboard will show a fresh inception date and zero P&L. Your historical performance is preserved in the JSONL log files under log/ — those are never deleted.

Step 3 — Stop, edit, delete state, restart

# Stop the bot
pm2 stop magellan

# Edit your parameters
nano config.env
# Change PRINCIPAL=xxx and/or SPLIT_NUMBER=yyy
# (change any other structural parameters as needed)

# Verify the new config is valid BEFORE deleting state
npm run build && node dist/tools/validateConfig.js
# If validateConfig reports errors, fix them before continuing

# Back up the old state (just in case) and delete it
cp state.json state.json.before-resize
rm state.json

# Restart -- bot creates fresh state with new parameters
pm2 restart magellan

# Verify the new grid
pm2 logs magellan --lines 20

Step 4 — Verify on dashboard

Open the dashboard and confirm:

Scenario B: Some units are HOLDING (open positions)

This is the case where the bot has bought SOL and is holding it, waiting to sell. You must not delete state.json until all positions are closed — otherwise the bot loses track of held SOL and those funds become stranded.

Step 1 — Halt new trading

cd /var/www/magellan/current

# Enable kill switch -- stops new buys, allows existing positions to sell
touch HALT

You can also toggle the kill switch from the dashboard.

Step 2 — Wait for all positions to close

Monitor the units until everything is IDLE:

# Check current unit status
cat state.json | python3 -c "
import json, sys
s = json.load(sys.stdin)
for i, u in enumerate(s.get('units', []), 1):
    status = u['status']
    extra = ''
    if status == 'HOLDING':
        entry = u.get('entryPrice', 0)
        sol = u.get('amountSol', 0)
        extra = f' (entry: \${entry:.4f}, {sol:.6f} SOL)'
    elif status == 'WAITING_TO_BUY':
        target = u.get('targetBuyPrice', 0)
        extra = f' (target: \${target:.4f})'
    print(f'  Unit {i}: {status}{extra}')

holding = sum(1 for u in s.get('units', []) if u['status'] == 'HOLDING')
waiting = sum(1 for u in s.get('units', []) if u['status'] == 'WAITING_TO_BUY')
print(f'\n  Summary: {holding} HOLDING, {waiting} WAITING_TO_BUY')
if holding > 0:
    print('  Wait for HOLDING units to hit take-profit or stop-loss')
else:
    print('  No HOLDING units -- safe to proceed to Step 3')
"

How long will this take?

If you can't wait and need to force-close positions:

Step 3 — Stop, edit, delete state, restart

Once all units are IDLE, follow the same steps as Scenario A:

# Verify all IDLE
cat state.json | python3 -c "
import json, sys
s = json.load(sys.stdin)
holding = sum(1 for u in s.get('units', []) if u['status'] != 'IDLE')
if holding > 0:
    print(f'STOP -- {holding} units are not IDLE. Wait longer.')
    sys.exit(1)
print('All units IDLE')
"

# Stop the bot
pm2 stop magellan

# Edit config
nano config.env

# Validate
npm run build && node dist/tools/validateConfig.js

# Back up and delete state
cp state.json state.json.before-resize
rm state.json

# Remove HALT file (clean start)
rm -f HALT

# Restart
pm2 restart magellan

Common resize scenarios

Scaling up: More capital, same unit count

# Before: PRINCIPAL=500, SPLIT_NUMBER=10 → $50/unit
# After:  PRINCIPAL=1000, SPLIT_NUMBER=10 → $100/unit

Each unit now has twice the buying power. Grid spread stays the same. More USDC per trade means more absolute profit per fill, but also more absolute loss per stop-loss. Make sure your wallet has enough USDC for the new principal.

Scaling up: Same capital, more units

# Before: PRINCIPAL=500, SPLIT_NUMBER=10 → $50/unit
# After:  PRINCIPAL=500, SPLIT_NUMBER=20 → $25/unit

More grid density — buys at more price levels, each smaller. This catches more oscillations but each fill profits less. Works well in choppy, range-bound markets. Check that the new unit size is above MIN_TRADE_USDC (default: $1) — validateConfig will catch this.

Scaling down: Less capital

# Before: PRINCIPAL=1000, SPLIT_NUMBER=20 → $50/unit
# After:  PRINCIPAL=500, SPLIT_NUMBER=10 → $50/unit

Keep the same unit size by reducing both proportionally. The grid becomes less dense but each position is the same size. Suitable when you want to reduce exposure.

Changing PRINCIPAL_LEFTOVER

# Before: PRINCIPAL=500, SPLIT_NUMBER=10, PRINCIPAL_LEFTOVER=10
#   → 9 active units, 1 buffer, $50/unit
# After:  PRINCIPAL=500, SPLIT_NUMBER=10, PRINCIPAL_LEFTOVER=20
#   → 8 active units, 2 buffer, $50/unit

More buffer means fewer active units but more capital held in reserve. This is conservative — fewer positions fill, but you have a larger cushion against drawdowns.

The minimum viable configuration

PRINCIPAL=50
SPLIT_NUMBER=5
PRINCIPAL_LEFTOVER=10
# → 4-5 active units, $10/unit

Unit size must be above MIN_TRADE_USDC. With the default MIN_TRADE_USDC=1, the smallest practical setup is around $5/unit. Below that, Jupiter quote rounding and transaction fees eat into returns significantly.

Switching MODE (Paper ↔ Live)

Switching MODE is also a structural change, but it has additional safety checks built in.

Paper → Live

The bot detects paper HOLDING units on startup in live mode and automatically resets them to IDLE with a warning. This prevents the bot from trying to sell SOL on-chain that was never actually purchased.

# You'll see this in the logs:
[WARN] Unit 3 is HOLDING with paper signature -- resetting to IDLE (paper positions are not real on-chain)

Recommended procedure: Still follow Scenario A or B above (close all positions first, delete state). The auto-reset is a safety net, not the intended workflow.

Live → Paper

If you switch from live to paper mode while HOLDING real SOL, the bot will "sell" those positions in paper mode — meaning it simulates the sell but your real SOL stays in your wallet. The bot's P&L accounting will be wrong because it thinks it sold, but no on-chain transaction happened.

Always close all live positions before switching to paper mode.

Pre-change checklist

Before starting:

During the change:

After restart:

Structural parameters FAQ

Q: Can I change PRINCIPAL without changing SPLIT_NUMBER?
Yes. Each unit will be resized to new_PRINCIPAL / SPLIT_NUMBER. Follow the same procedure.

Q: Can I change just PRINCIPAL_LEFTOVER without touching PRINCIPAL or SPLIT_NUMBER?
Yes, but it still requires the full stop-delete-restart procedure because it changes how many units are active vs buffer.

Q: What if I delete state.json while units are HOLDING in live mode?
The bot forgets about those positions. Your SOL is still in your wallet — it's not lost on-chain. But Magellan won't manage it anymore. You'd need to manually swap the SOL back to USDC via Jupiter UI, Phantom, or another wallet tool.

Q: Do I lose my log history?
No. Log files in log/YYYY/MM/ are never affected by state deletion. Your entire trade history is preserved there. The dashboard Logs tab and History tab will still show all past trades.

Q: Can I edit state.json manually instead of deleting it?
Technically possible but strongly discouraged. The state file has internal consistency requirements (unit IDs, array length matching SPLIT_NUMBER, amountUsdc matching unit size, grid center vs current price). A manual edit that gets any of these wrong can cause silent bugs. Deleting and letting the bot create a fresh state is always safer.

Q: What about settings_history.jsonl?
Keep it. The bot appends a new CONFIG_SNAPSHOT on startup with the new parameters. The Settings tab will show the change in the configuration history timeline.

← Back