Quick Answer
CS0029: Cannot implicitly convert type 'string' to 'double' means you put a value of one type into a variable, parameter or return value of another type, and C# has no conversion at all between the two that it can do for you. This is different from CS0266 (double to int), where a conversion exists and you only have to write it as a cast.
In a cBot the usual cases are a text value that should be a number (a parameter or a string from a file), a number used as a condition (if (count)), a bool stored in an int, and the result of an order call stored in the wrong type: ExecuteMarketOrder gives back a trade result, not a position. The fix depends on the case: parse the text, write a real comparison, or read the right member of the result.
From the author: I build cBots in cTrader Automate and convert strategies from Pine Script and MQL5 to C#. CS0029 appears after a hand conversion because Pine Script and MQL5 are looser about types: MQL5 lets an
intact as a condition, and a Pine variable can change type from line to line. C# does not, and this guide shows the cases and the safest fix for each.
Key Facts
| What you see | Usual cause | Fix |
|---|---|---|
| 'string' to 'double' | Text stored in a number | double.TryParse(text, ..., out value) |
| 'double' to 'bool' or 'int' to 'bool' | A number used as a condition | Write a comparison: if (count > 0) |
| 'bool' to 'int' | A condition stored in a number | int flag = isUp ? 1 : 0; |
| 'TradeResult' to 'Position' | The result of an order call stored as a position | Use result.Position |
| 'int' to 'TradeType' | A number used as buy or sell | Use TradeType.Buy or TradeType.Sell |
| 'double' to 'int' | A conversion exists | That is CS0266, a cast fixes it |
CS0029 or CS0266?
Both messages start with "cannot implicitly convert". The difference is the second half:
- CS0266 says "An explicit conversion exists (are you missing a cast?)". Write
(int)valueor useMath.Round. See cTrader C# error CS0266. - CS0029 has no such hint. No cast helps, and you have to change the code: parse, compare or read another member.
The Broken cBot
This cBot reads a few values and prints them. It does not place trades. Four lines have a CS0029 error:
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class BrokenBot : Robot
{
[Parameter("Risk % (text)", DefaultValue = "1.5")]
public string RiskText { get; set; }
protected override void OnBar()
{
double risk = RiskText; // CS0029: string to double
int openCount = Positions.Count;
if (openCount) // CS0029: int to bool
Print("There are open positions");
bool isUp = Bars.ClosePrices.Last(1) > Bars.OpenPrices.Last(1);
int flag = isUp; // CS0029: bool to int
Position position = ExecuteMarketOrder(TradeType.Buy, SymbolName, 1000); // CS0029: TradeResult to Position
Print("Risk {0}, flag {1}", risk, flag);
}
}
}
The last line also places a real order on every bar, so do not run it. It only shows the wrong type.
Cause 1: Text Where a Number Is Expected
A string is never converted to double by the compiler. Parse it, and handle the case where the text is not a number. Use the invariant culture, so that 1.5 is read the same way on every computer, also on a computer that uses a comma as the decimal separator:
using System.Globalization;
double risk;
if (!double.TryParse(RiskText, NumberStyles.Float, CultureInfo.InvariantCulture, out risk))
{
Print("Risk is not a number: {0}", RiskText);
return;
}
The better fix for a cBot parameter is to declare it as a double in the first place:
[Parameter("Risk %", DefaultValue = 1.5, MinValue = 0.1, MaxValue = 5.0)]
public double RiskPercent { get; set; }
A typed parameter shows a numeric field in the parameter panel and cannot hold text. This is explained for the number types in cTrader C# error CS0266.
Cause 2: A Number Used as a Condition
In MQL5 and C, if (count) is true when count is not 0. C# requires a real bool:
if (Positions.Count > 0)
Print("There are open positions");
Write the comparison you mean. > 0, == 0 and != 0 all read better than a bare number, and they also make the intent clear for the next person who reads the cBot.
Cause 3: A Condition Stored in a Number
If you want a number (for example to count signals), convert the condition yourself:
bool isUp = Bars.ClosePrices.Last(1) > Bars.OpenPrices.Last(1);
int flag = isUp ? 1 : 0;
The ? : operator is the usual way. Keep a bool as a bool whenever you can, because most conditions only need the true or false value.
Cause 4: The Result of an Order Call
ExecuteMarketOrder does not return a position. It returns a trade result, which tells you whether the order worked and carries the position when it did. Store it in the right type, check it, and then read the position from it:
TradeResult result = ExecuteMarketOrder(TradeType.Buy, SymbolName, volume, "Label");
if (result.IsSuccessful)
{
Position position = result.Position;
Print("Opened position {0}", position.Id);
}
else
{
Print("Order failed: {0}", result.Error);
}
The same pattern applies to the other trade methods that return a result. Checking the result is also the first rule of the pre-live checklist: a cBot that ignores a failed order does not know that it has no position. The order calls themselves are explained in Pine Script strategy.entry to cTrader C#.
Cause 5: A Number Instead of TradeType
A cBot that was converted from MQL5 can carry an int for the direction (0 for buy, 1 for sell). In C# the direction is an enum, and an int does not convert to it implicitly:
TradeType side = isUp ? TradeType.Buy : TradeType.Sell;
If you must keep a number, convert it in one place with an if or a switch, and keep the rest of the cBot on TradeType. A wrong argument type in a call gives CS1503 instead: cTrader C# error CS1503.
The Fixed cBot
using cAlgo.API;
namespace cAlgo.Robots
{
[Robot(TimeZone = TimeZones.UTC, AccessRights = AccessRights.None)]
public class FixedBot : Robot
{
[Parameter("Risk %", DefaultValue = 1.5, MinValue = 0.1, MaxValue = 5.0)]
public double RiskPercent { get; set; }
protected override void OnBar()
{
double risk = RiskPercent;
if (Positions.Count > 0)
Print("There are open positions");
bool isUp = Bars.ClosePrices.Last(1) > Bars.OpenPrices.Last(1);
int flag = isUp ? 1 : 0;
TradeType side = isUp ? TradeType.Buy : TradeType.Sell;
Print("Risk {0}, flag {1}, side {2}", risk, flag, side);
// To place an order, store the TradeResult and check it first:
// TradeResult result = ExecuteMarketOrder(side, SymbolName, volume, "Label");
// if (result.IsSuccessful) { Position position = result.Position; }
}
}
}
The bot only prints, so you can compare the values with the chart before you add an order. Test any order code on a demo account first.
Similar Errors
| Error | Message | What it means | |---|---|---| | CS0029 | Cannot implicitly convert type 'A' to 'B' | No automatic conversion exists | | CS0266 | Cannot implicitly convert type 'A' to 'B'. An explicit conversion exists | A cast fixes it | | CS1503 | Argument N: cannot convert from 'A' to 'B' | A call has an argument of the wrong type | | CS0103 | The name 'x' does not exist in the current context | A name is unknown | | CS1061 | 'X' does not contain a definition for 'Y' | The member after the dot does not exist |
Checklist: What to Check When You See CS0029
- Read both types in the message: the one you have and the one that is expected.
- Is it text? Parse it with
TryParse, or change the parameter type. - Is it a condition? Write a comparison, or keep the value as a
bool. - Is it an order call? Store the
TradeResult, checkIsSuccessful, then readPosition. - Is it a direction? Use
TradeType, not a number. - Does the message mention "explicit conversion exists"? Then it is CS0266 and a cast fixes it.
- Build again after each change, and print the values before you trade.
- Test on a demo account before the cBot trades with real money.
Common Mistakes
- Using a
stringparameter for a number. - Using a number as a condition, as in C or MQL5.
- Reading
ExecuteMarketOrderas a position. - Ignoring the trade result after you fix the type.
- Parsing text without a culture, which reads
1.5wrongly on some computers. - Adding a cast where no conversion exists.
FAQ
Related Guides
- cTrader C# error CS0266: when a cast fixes it.
- cTrader C# error CS1503: an argument of the wrong type in a call.
- cTrader C# errors CS7036 and CS1501: the wrong number of arguments.
- cTrader C# error CS1061: the object is found, the member is not.
- cTrader C# error CS0103: the name does not exist in the current context.
- Pine Script strategy.entry to cTrader C#: order calls and risk-based volume.
- What to check in a trading bot before going live: a pre-live checklist for stops, lot size, spread and trade results.
- cTrader cBot crash: index was out of range: a runtime error that appears after the cBot builds.
Fix Compile Errors Without Guessing
If a cBot or an Expert Advisor does not build, paste the code and the error message into the CodeFlowOS Error Fixer. It suggests a minimal fix, explains each error and shows what changed. A fix uses 1 credit, with up to 3 attempts included, and the compiler check is optional. Outputs are not guaranteed, and code that compiles can still have wrong trading logic, so always review the result. Before a bot goes live, the Code Review is free during the early-bird period. CodeFlowOS also converts strategies between Pine Script, MQL5 and cTrader C#: try the converter page.
This article is educational. It does not describe trading performance and is not financial advice. Always build the code in cTrader Automate and backtest it before using it on a live account.