From Research to Production: Complete Strategy Development Workflow
This document provides a complete end-to-end example showing how to go from a strategy idea through backtesting, optimization, and validation, all the way to live trading deployment, covering the entire lifecycle of a quantitative trading strategy.
Full-Process AI Assistance
Every stage of this workflow can be completed with the help of an AI coding assistant. After installing FinLab Skill, AI can assist with writing code, interpreting performance, and troubleshooting errors from strategy conception to live deployment.
Workflow Overview
graph TD
A[Strategy Idea] --> B[Data Exploration]
B --> C[Strategy Development]
C --> D[Strategy Optimization]
D --> E[In-Depth Analysis]
E --> F[Out-of-Sample Testing]
F --> G{Validation Passed?}
G -->|Yes| H[Pre-Deployment Prep]
G -->|No| C
H --> I[Live Trading]
I --> J[Performance Tracking]
J --> K{Adjustment Needed?}
K -->|Yes| C
K -->|No| I
Stage 1: Strategy Conception & Initial Research
Strategy Hypothesis
We will develop a "Revenue Growth + Technical Breakout" combination strategy for S&P 500 stocks:
- Fundamentals: Quarterly revenue is growing more than 10% year over year
- Technicals: Price breaks above moving averages, confirming momentum
- Risk Control: Exit when price drops below moving average, set 10% stop loss
Data Exploration
First, check whether the required data is complete:
from finlab import data
import pandas as pd
# Load required data for S&P 500 stocks
data.set_market('us')
with data.us_universe(index='SP500'):
close = data.get('price:adj_close')
rev = data.get('us_income_statement:revenue')
# Check data range (revenue is quarterly, indexed by labels such as 2024-US-Q3)
print(f"Close-price range: {close.index[0]} ~ {close.index[-1]}")
print(f"Revenue range: {rev.index[0]} ~ {rev.index[-1]}")
# Check missing value ratio
print(f"Close-price missing: {close.isna().sum().sum() / close.size * 100:.2f}%")
print(f"Revenue missing: {rev.isna().sum().sum() / rev.size * 100:.2f}%")
Stage 2: Strategy Development & Backtesting
Writing Strategy Logic
from finlab import data
from finlab.backtest import sim
# 1. Load data
data.set_market('us')
with data.us_universe(index='SP500'):
close = data.get('price:adj_close')
rev = data.get('us_income_statement:revenue')
# 2. Calculate technical indicators
ma20 = close.average(20)
ma60 = close.average(60)
# 3. Calculate revenue indicators (quarterly, aligned to each SEC filing date)
rev_yoy = rev.pct_change(4) # Year-over-year revenue growth
# 4. Define entry conditions
entry_tech = (close > ma20) & (
close > ma60
) # Technicals: breakout above moving averages
entry_fund = rev_yoy > 0.1 # Fundamentals: revenue growth above 10%
entry_signal = entry_tech & entry_fund
# 5. Define exit conditions
exit_signal = close < ma20 # Break below 20-day MA
# 6. Combine entry/exit signals (with stop loss)
position = entry_signal.hold_until(
exit=exit_signal,
stop_loss=0.1, # Stop loss 10%
)
# 7. Backtest
report = sim(
position,
resample='M', # Monthly rebalance
position_limit=0.1, # Single-holding cap 10%
upload=False,
name="Revenue Growth Breakout v1.0",
)
# 8. Display performance
report.display()
Preliminary Performance Evaluation
stats = report.get_stats()
print(f"Annualized return: {stats['cagr'] * 100:.2f}%")
print(f"Sharpe ratio: {stats['daily_sharpe']:.2f}")
print(f"Max drawdown: {stats['max_drawdown'] * 100:.2f}%")
print(f"Win rate: {stats['win_ratio'] * 100:.2f}%")
Preliminary Assessment
- Compare the annualized return and maximum drawdown with simply holding the S&P 500 over the same period
- A Sharpe ratio well below 1 means the return does not yet compensate for the volatility
- Next step: optimize the strategy to improve risk-adjusted returns
Stage 3: Strategy Optimization
3.1 Use sim_conditions() to Test Condition Combinations
We add more candidate conditions to find the best combination:
from finlab.optimize.combinations import sim_conditions
# Original conditions
c1 = (close > ma20) & (close > ma60) # MA bullish
c2 = rev_yoy > 0.1 # Revenue growth
# New conditions
with data.us_universe(index='SP500'):
eps = data.get('us_income_statement:eps_diluted')
raw_close = data.get('price:close') # unadjusted price for valuation
pe = raw_close / eps.rolling(4).sum() # P/E on trailing-12-month diluted EPS
c3 = (pe > 0) & (pe < 20) # Low P/E
c4 = close == close.rolling(20).max() # New 20-day high
c5 = rev / rev.shift(1) > 0.9 # Revenue QoQ change > -10%
# Conditions dictionary
conditions = {
'c1': c1, # MA bullish
'c2': c2, # Revenue growth
'c3': c3, # Low P/E
'c4': c4, # New high
'c5': c5, # Revenue QoQ
}
# Test all combinations (31 total)
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exit_signal, 'stop_loss': 0.1},
resample='M',
position_limit=0.1,
upload=False,
)
# Visualize comparison
report_collection.plot_stats('heatmap')
3.2 Analyze Optimization Results
# Get performance metrics table
stats_df = report_collection.get_stats()
# Find top 3 combinations
top3 = stats_df.T.sort_values('daily_sharpe', ascending=False).head(3)
print(top3[['daily_mean', 'daily_sharpe', 'max_drawdown', 'win_ratio']])
Optimization Results
The rest of this walkthrough adopts c1 & c2 & c3 (MA bullish + revenue growth + low P/E). Use whichever combination ranks first in your own run, and prefer combinations whose neighbors also rank well over a single outlier.
3.3 Adopt the Best Combination
# Redefine strategy using the best combination
best_entry = c1 & c2 & c3
position_optimized = best_entry.hold_until(exit=exit_signal, stop_loss=0.1)
report_opt = sim(
position_optimized,
resample='M',
position_limit=0.1,
upload=False,
name="Revenue Growth Breakout v2.0 (optimized)",
)
3.4 Optimize Stop Loss & Take Profit
Use MAE/MFE analysis to find optimal stop loss/take profit levels:
# Display volatility analysis
report_opt.display_mae_mfe_analysis()
# Get trade records
trades = report_opt.get_trades()
# Analyze MAE/MFE quantiles
mae_q75 = abs(trades['mae'].quantile(0.75))
gmfe_q75 = trades['gmfe'].quantile(0.75)
print(f"MAE Q75: {mae_q75 * 100:.2f}%")
print(f"GMFE Q75: {gmfe_q75 * 100:.2f}%")
# Adjust stop loss/take profit
position_final = best_entry.hold_until(
exit=exit_signal,
stop_loss=mae_q75 * 1.2, # Stop loss = MAE Q75 * 1.2
take_profit=gmfe_q75 * 0.8, # Take profit = GMFE Q75 * 0.8
)
report_final = sim(
position_final,
resample='M',
position_limit=0.1,
upload=False,
name="Revenue Growth Breakout v3.0 (final)",
)
Stage 4: In-Depth Analysis
4.1 Liquidity Risk Assessment
Check whether the strategy is suitable for large capital by requiring a minimum dollar volume, then rerun the backtest and compare:
with data.us_universe(index='SP500'):
volume = data.get('price:volume')
# 20-day average dollar volume; require at least $5M traded per day
dollar_volume = (close * volume).average(20)
liquid = dollar_volume > 5_000_000
position_liquid = (best_entry & liquid).hold_until(exit=exit_signal, stop_loss=0.1)
report_liquid = sim(position_liquid, resample='M', position_limit=0.1, upload=False)
Liquidity Review
- If performance collapses once illiquid stocks are excluded, the backtest relied on trades that large capital cannot execute
- For Taiwan stocks,
report.run_analysis('LiquidityAnalysis', required_volume=100000)also reports trades in alert, disposition and full-delivery stocks (Taiwan market only)
4.2 Annual Stability Analysis
Review strategy performance across different years:
report_final.run_analysis('PeriodStatsAnalysis')
# Output shows Sharpe ratio, return, volatility, etc. for each year
# Check if the strategy performs particularly poorly in certain years
4.3 Alpha/Beta Analysis
Measure the strategy's excess return:
report_final.run_analysis('AlphaBetaAnalysis')
# Displays three sections: overall summary, annual Alpha/Beta, recent Alpha/Beta
# Alpha: annualized excess return over the benchmark (S&P 500 for U.S. stocks)
# Beta: market sensitivity
To obtain raw values for further analysis:
result = report_final.run_analysis('AlphaBetaAnalysis', display=False)
print(f"Alpha: {result['overall']['alpha']:.2%}")
print(f"Beta: {result['overall']['beta']:.2f}")
Alpha/Beta Assessment
- A positive alpha indicates excess return that the market exposure does not explain
- A beta below 1 means the strategy moves less than the market
Stage 5: Out-of-Sample Testing
Split data into training and testing sets to verify the strategy is not overfit:
# Define split date
train_end = '2022-12-31'
test_start = '2023-01-01'
# Training set backtest (through 2022)
position_train = position_final[position_final.index <= train_end]
report_train = sim(
position_train,
resample='M',
position_limit=0.1,
upload=False,
name="Training set (through 2022)",
)
# Test set backtest (from 2023)
position_test = position_final[position_final.index >= test_start]
report_test = sim(
position_test,
resample='M',
position_limit=0.1,
upload=False,
name="Test set (from 2023)",
)
# Performance comparison
stats_train = report_train.get_stats()
stats_test = report_test.get_stats()
comparison = pd.DataFrame(
{
'Training': [
stats_train['cagr'],
stats_train['daily_sharpe'],
stats_train['max_drawdown'],
stats_train['win_ratio'],
],
'Testing': [
stats_test['cagr'],
stats_test['daily_sharpe'],
stats_test['max_drawdown'],
stats_test['win_ratio'],
],
},
index=['Annualized return', 'Sharpe ratio', 'Max drawdown', 'Win rate'],
)
print(comparison)
Out-of-Sample Evaluation Criteria
| Metric | Pass when training vs testing shows |
|---|---|
| Annualized Return | Reasonable degradation (less than 20%) |
| Sharpe Ratio | A slight decrease, still above 1 |
| Max Drawdown | A small difference |
| Win Rate | A stable value |
Out-of-Sample Test
If the test period passes these checks, the strategy is unlikely to be overfit and can proceed to deployment.
Stage 6: Pre-Deployment Preparation
6.1 Upload Strategy to Cloud
# Upload the final version to the FinLab platform
report_final.upload(name="Revenue Growth Breakout v3.0")
Then check the holdings the strategy targets:
# Get position list
position_info = report_final.position_info()
print(f"Next rebalance date: {position_info['next_trading_date']}")
# position_info keys are stock symbols (e.g., 'AAPL'), mixed with some metadata keys
# Filter out holdings (exclude metadata keys)
metadata_keys = {
'update_date',
'next_trading_date',
'trade_at',
'freq',
'market',
'stop_loss',
'take_profit',
}
stocks = {k: v for k, v in position_info.items() if k not in metadata_keys}
print(f"Current holdings count: {len(stocks)}")
print("Holdings list:")
for symbol, info in stocks.items():
print(f" {symbol}: weight={info['weight']:.2%}, status={info['status']}")
6.2 Prepare Live Capital Allocation
# Assuming $100,000 of live capital
capital = 100_000
# Calculate the amount to invest in each stock (reusing stocks and metadata_keys from 6.1)
for symbol, info in stocks.items():
info['amount'] = info['weight'] * capital
print("Capital allocation:")
for symbol, info in stocks.items():
print(f" {symbol}: weight={info['weight']:.2%}, amount=${info['amount']:,.0f}")
Stage 7: Live Trading
7.1 Set Up Broker Account (Charles Schwab Example)
from finlab.online.schwab_account import SchwabAccount
# Reads SCHWAB_API_KEY, SCHWAB_SECRET and SCHWAB_TOKEN_PATH from the environment
account = SchwabAccount()
Taiwan brokers (SinoPac, Fubon, E.SUN, Fugle, Masterlink) are supported as well; see the live trading tutorial.
Live Trading Precautions
- For first-time use, test with
view_only=Truefor simulated orders - Confirm broker API keys are correct
- Ensure sufficient funds and credit limits
- Start with small capital for testing
7.2 Calculate Target Positions
from finlab.online.order_executor import Position
# Calculate target share counts
capital = 100_000 # Live capital: $100,000
target_position = Position.from_report(report_final, capital)
print(target_position)
# A list of {'stock_id', 'quantity', 'order_condition'} entries, one per stock
7.3 Execute Orders
from finlab.online.order_executor import OrderExecutor
# Create order executor
order_executor = OrderExecutor(target_position, account=account)
# Order preview (view mode, no actual orders placed)
order_executor.create_orders(view_only=True)
# Execute orders (actual orders placed; test during market close for first-time use)
order_executor.create_orders()
# Update limit price (use last trade price as new limit price)
order_executor.update_order_price()
# Cancel all pending orders
order_executor.cancel_orders()
Stage 8: Performance Tracking
8.1 Retrieve Live Trading Records
# account: the SchwabAccount created in 7.1
# Get live positions
account_positions = account.get_position()
print(account_positions)
# Get account total value and cash
account_value = account.get_total_balance()
print(f"Account total value: {account_value:,.0f}")
8.2 Monitor Strategy Performance
# Periodically check account status (recommended after each market close)
# account: the SchwabAccount created in 7.1
import pandas as pd
# Record daily account value
daily_value = {
'date': pd.Timestamp.today(),
'account_value': account.get_total_balance(),
'positions': len(account.get_position()),
}
print(f"Date: {daily_value['date']}")
print(f"Account value: {daily_value['account_value']:,.0f}")
print(f"Holdings count: {daily_value['positions']}")
# Compare with backtest
backtest_return = report_final.get_stats()['cagr']
print(f"Backtest annualized return: {backtest_return * 100:.2f}%")
8.3 Periodic Review
Recommended monthly review items:
# account_positions: from the SchwabAccount in 8.1
# 1. Check if holdings follow the plan
current_stocks = set(account_positions['stock_id'])
metadata_keys = {
'update_date',
'next_trading_date',
'trade_at',
'freq',
'market',
'stop_loss',
'take_profit',
}
planned_stocks = {k for k in position_info if k not in metadata_keys}
print(f"Planned holdings: {planned_stocks}")
print(f"Actual holdings: {current_stocks}")
print(f"Mismatches: {planned_stocks.symmetric_difference(current_stocks)}")
# 2. Check if stop loss is triggered
for stock_id, pos in account_positions.items():
entry_price = pos['avg_price']
current_price = pos['current_price']
unrealized_pnl = current_price / entry_price - 1
if unrealized_pnl < -0.10: # Stop loss 10%
print(
f"Warning: {stock_id} stop loss triggered, unrealized P&L: {unrealized_pnl * 100:.2f}%"
)
Complete Code Summary
Below is the complete executable code:
# =============================================================================
# From Research to Production: Revenue Growth Breakout Strategy Complete Example
# =============================================================================
from finlab import data
from finlab.backtest import sim
from finlab.optimize.combinations import sim_conditions
# -----------------------------------------------------------------------------
# Stages 1 & 2: Data Loading & Strategy Development
# -----------------------------------------------------------------------------
# Load data for S&P 500 stocks
data.set_market('us')
with data.us_universe(index='SP500'):
close = data.get('price:adj_close')
raw_close = data.get('price:close') # unadjusted price for valuation
rev = data.get('us_income_statement:revenue')
eps = data.get('us_income_statement:eps_diluted')
# Calculate indicators
ma20 = close.average(20)
ma60 = close.average(60)
rev_yoy = rev.pct_change(4)
pe = raw_close / eps.rolling(4).sum()
# Define conditions
c1 = (close > ma20) & (close > ma60) # MA bullish
c2 = rev_yoy > 0.1 # Revenue growth
c3 = (pe > 0) & (pe < 20) # Low P/E
# Exit signal
exit_signal = close < ma20
# -----------------------------------------------------------------------------
# Stage 3: Strategy Optimization
# -----------------------------------------------------------------------------
# Test condition combinations
conditions = {'c1': c1, 'c2': c2, 'c3': c3}
report_collection = sim_conditions(
conditions=conditions,
hold_until={'exit': exit_signal, 'stop_loss': 0.1},
resample='M',
position_limit=0.1,
upload=False,
)
# Find the best combination
stats_df = report_collection.get_stats()
top1 = stats_df.T.sort_values('daily_sharpe', ascending=False).index[0]
print(f"Best combination: {top1}")
# Use the best combination
best_entry = c1 & c2 & c3
# Optimize stop loss/take profit
position_temp = best_entry.hold_until(exit=exit_signal, stop_loss=0.1)
report_temp = sim(position_temp, resample='M', position_limit=0.1, upload=False)
trades = report_temp.get_trades()
mae_q75 = abs(trades['mae'].quantile(0.75))
gmfe_q75 = trades['gmfe'].quantile(0.75)
# Final strategy
position_final = best_entry.hold_until(
exit=exit_signal, stop_loss=mae_q75 * 1.2, take_profit=gmfe_q75 * 0.8
)
report_final = sim(
position_final,
resample='M',
position_limit=0.1,
upload=False,
name="Revenue Growth Breakout v3.0",
)
# -----------------------------------------------------------------------------
# Stage 4: In-Depth Analysis
# -----------------------------------------------------------------------------
# Volatility analysis
report_final.display_mae_mfe_analysis()
# Period stability
report_final.run_analysis('PeriodStatsAnalysis')
# Alpha/Beta
report_final.run_analysis('AlphaBetaAnalysis')
# -----------------------------------------------------------------------------
# Stage 5: Out-of-Sample Testing
# -----------------------------------------------------------------------------
train_end = '2022-12-31'
test_start = '2023-01-01'
position_train = position_final[position_final.index <= train_end]
position_test = position_final[position_final.index >= test_start]
report_train = sim(position_train, resample='M', position_limit=0.1, upload=False)
report_test = sim(position_test, resample='M', position_limit=0.1, upload=False)
print("Training set Sharpe:", report_train.get_stats()['daily_sharpe'])
print("Test set Sharpe: ", report_test.get_stats()['daily_sharpe'])
# -----------------------------------------------------------------------------
# Stage 6: Pre-Deployment Preparation
# -----------------------------------------------------------------------------
# Get position list
position_info = report_final.position_info()
metadata_keys = {
'update_date',
'next_trading_date',
'trade_at',
'freq',
'market',
'stop_loss',
'take_profit',
}
stocks = {k: v for k, v in position_info.items() if k not in metadata_keys}
print("Next-period holdings:")
for symbol, info in stocks.items():
print(f" {symbol}: weight={info['weight']:.2%}")
To deploy, upload the final report with report_final.upload(name="Revenue Growth Breakout v3.0") (Stage 6.1), then connect a broker account and run Stages 7 and 8 above.
Key Takeaways
Development Stage Checklist
- [ ] Clear strategy hypothesis: Has a clear logical basis (technical/fundamental/institutional)
- [ ] Data integrity check: Confirm data range is sufficient and missing value ratio is reasonable
- [ ] Acceptable initial backtest performance: Annualized return > 10%, Sharpe ratio > 0.5
- [ ] Condition combination optimization: Use
sim_conditions()to find the best combination - [ ] Stop loss/take profit optimization: Use MAE/MFE analysis to set reasonable levels
Validation Stage Checklist
- [ ] Liquidity risk: Performance holds up after requiring enough dollar volume for your capital
- [ ] Annual stability: No consecutive years of extremely poor performance
- [ ] Alpha > 0: Strategy has excess returns
- [ ] Out-of-sample test passed: Test set performance degradation < 30%, Sharpe ratio still > 1
Deployment Stage Checklist
- [ ] Strategy uploaded to cloud: Can review position list anytime
- [ ] Broker account setup complete: API keys correct, permissions granted
- [ ] Tested with simulated orders first: Confirmed order logic is correct
- [ ] Small capital test: Tested with small amounts for 1-2 months
- [ ] Regular review mechanism: Set up monthly live performance review
Further Learning
After completing this workflow, you can explore:
- Machine Learning Strategy: ML Strategy Complete Workflow (Advanced)
- Multi-Strategy Portfolio: Multi-Strategy Portfolio Management (Advanced)
- Custom Analysis Modules: Strategy Analysis Module Guide
- Custom Market Backtesting: Custom Market Object Guide
Common Error Handling Checklist
When executing the complete workflow, make sure to perform error checks at each stage. Below are key checkpoints and solutions:
Stages 1-2: Data Loading & Backtesting
Common Errors:
- KeyError: 'price:adj_close' -- API token not set or incorrect data table name
- Data is empty or has too many missing values
- Backtest has no trade records (entry conditions too strict)
Validation Methods:
try:
# 1. Check data loading
data.set_market('us')
close = data.get('price:adj_close')
if close.empty:
raise ValueError("❌ Data is empty; check your API token")
missing_ratio = close.isna().sum().sum() / close.size
if missing_ratio > 0.1:
print(f"⚠️ Warning: missing value ratio {missing_ratio:.1%} > 10%")
# 2. Check entry signals
entry_count = entry_signal.sum(axis=1).mean()
if entry_count < 1:
print("⚠️ Warning: entry conditions too strict; average < 1 stock per day")
print("Suggestion: loosen conditions or use .is_largest(N) for a fixed count")
# 3. Check backtest results
report = sim(position, resample='M', position_limit=0.1)
trades = report.get_trades()
if len(trades) == 0:
raise ValueError("❌ Strategy produced no trades; check your entry conditions")
print(f"✅ Backtest succeeded: {len(trades)} trades")
except KeyError as e:
print(f"❌ Dataset not found: {e}")
print("Visit https://studio.finlab.finance/en/database to confirm the dataset name")
print("Or verify your API token is correctly set")
except ValueError as e:
print(f"❌ Data validation failed: {e}")
Detailed Error Handling: See Data Download Error Handling
Stage 3: Strategy Optimization
Common Errors:
- sim_conditions() takes too long due to too many combinations
- Condition definitions are incorrect (inconsistent date ranges)
Validation Methods:
# Limit the number of combinations (< 50)
conditions = {
'c1': c1,
'c2': c2,
'c3': c3,
# Maximum 5 conditions (2^5 = 32 combinations)
}
# Estimate execution time
num_combinations = 2 ** len(conditions) - 1
print(
f"Will test {num_combinations} combinations, estimated time: ~{num_combinations * 30 / 60:.1f} minutes"
)
if num_combinations > 100:
print("⚠️ Warning: too many combinations; test a subset of conditions first")
Detailed Error Handling: See Strategy Optimization Error Handling
Stage 5: Out-of-Sample Testing
Common Errors: - Training and test set date ranges overlap - Test set performance differs too much from training set (overfitting)
Validation Methods:
# Check date ranges
train_end = '2022-12-31'
test_start = '2023-01-01'
position_train = position_final[position_final.index <= train_end]
position_test = position_final[position_final.index >= test_start]
# Confirm no overlap
if position_train.index[-1] >= position_test.index[0]:
raise ValueError("❌ Training and test set dates overlap!")
# Check performance difference
sharpe_train = report_train.get_stats()['daily_sharpe']
sharpe_test = report_test.get_stats()['daily_sharpe']
degradation = (sharpe_train - sharpe_test) / sharpe_train
if degradation > 0.5:
print(
f"⚠️ Warning: Sharpe ratio degradation {degradation:.1%} > 50%; possible overfitting"
)
print("Suggestions:")
print("1. Simplify strategy conditions")
print("2. Extend the training data range")
print("3. Use cross-validation")
Detailed Error Handling: See Backtest Error Handling
Stage 7: Live Trading
Common Errors: - Broker account connection failure (wrong API key, expired certificate) - Insufficient funds or too many positions - Orders rejected (insufficient buying power, trading halts; in Taiwan also disposition stocks and limit-up/down locks)
Safety Checklist:
# Pre-trading safety checks
def pre_trading_checks(position, account):
"""Pre-order safety checks for live trading"""
print("=== Live Trading Safety Checks ===\n")
# 1. Check number of positions
if len(position) > 30:
raise ValueError(f"❌ Too many positions ({len(position)}); recommended < 30")
# 2. Check available funds
available = account.get_balance()
required = position.total_value # Assumes this method exists
if required > available * 0.9:
raise ValueError(
f"❌ Insufficient funds! Need {required:,.0f}, available {available:,.0f}"
)
# 3. Check account connection
try:
account.get_position()
except Exception as e:
raise ConnectionError(f"❌ Broker account connection failed: {e}")
print("✅ All checks passed\n")
# Execute checks
try:
pre_trading_checks(position_info, account)
# Preview first (no actual orders)
executor.create_orders(view_only=True)
# Execute after confirmation (uncomment)
# executor.create_orders()
except (ValueError, ConnectionError) as e:
print(f"\n{e}")
print("Fix the issues above before placing orders")
Detailed Error Handling: See Live Trading Error Handling
Final Reminder for Live Trading
Before placing orders, confirm the following:
- Strategy has passed out-of-sample testing
- Tested with a simulated account for at least 1 week
- Initial capital does not exceed 10-20% of total capital
- Stop loss mechanism is in place to avoid catastrophic losses
- Monitor positions and order status daily
Errors can lead to financial losses! Please proceed with caution!