C#で「ローソク足チャートを表示させたい」とお悩みではありませんか?
この記事では、C#エンジニア歴10年の筆者が、C#のChartコントロールを活用してローソク足チャートを作成する方法を徹底解説します。この技術を習得することで、金融データの可視化ツールやトレードアプリの開発に役立てることができます。
記事を読むことで得られる3つのポイント
- C#のChartコントロールの基本的な使い方
ローソク足チャートを表示するためのコントロール配置やプロパティ設定の基礎がわかります。 - データベース連携を通じたローソク足データの取得方法
Pan Active Marketライブラリを活用したデータの取得方法を具体的に学べます。 - ローソク足チャートに移動平均を追加する実践テクニック
FinancialFormulaメソッドを使用し、チャートをさらに見やすく、分析しやすい形に仕上げる方法を習得できます。
記事を読み終えるころには、ローソク足チャート作成の基礎から応用までを理解し、自分のアプリケーションに実装できるスキルが身についているはずです。
では、早速始めましょう!

動作確認環境
- Windows10
- Visual studio Community 2017
- ターゲットフレームワーク .NET Framework 4.6.1
コントロールの配置とプロパティ

コントロール | プロパティ | 値 |
---|---|---|
chart1 | Anchor | Top,Bottom,Left, right |
txtCode | Text | “1308” |
lblName | Text | “銘柄名” |
btnView | Text | “表示” |
numericMA | Maximum | 25 |
Minimum | 5 |
参照設定
Pan Active Market DataBaseの参照設定は下記を参考にして下さい。
PanActiveDBAc.cs:Pan Active MarketDataBaseのラッパー
using System;
using System.Data;
using System.Collections.Generic;
namespace PanActiveMarket
{
class Prices
{
private ActiveMarket.Prices prices = new ActiveMarket.Prices();
/// <summary>
/// Readメソッドで読み出すデータの日付位置の最小値
/// </summary>
public int ReadBegin
{
set
{
prices.ReadBegin = value;
}
get
{
return (prices.ReadBegin);
}
}
/// <summary>
/// Readメソッドで読み出すデータの日付位置の最大値
/// </summary>
public int ReadEnd
{
set
{
prices.ReadEnd = value;
}
get
{
return (prices.ReadEnd);
}
}
/// <summary>
/// 四本値と出来高をオブジェクト内に読み込む
/// </summary>
public void Read(string code)
{
try
{
prices.Read(code);
}
catch
{
}
}
/// <summary>
/// 日付位置の最大値を返す
/// </summary>
public int End()
{
return (prices.End());
}
/// <summary>
/// 日付位置の最小値を返す
/// </summary>
public int Begin()
{
return (prices.Begin());
}
/// <summary>
/// 指定した日付位置が休場日かどうかを返す
/// </summary>
public bool IsClosed(int DatePos)
{
// ヘルプでは、boolを返すのに、intで定義されている。
bool result = (prices.IsClosed(DatePos) == 0) ? false : true;
return result;
}
/// <summary>
/// 指定した日付位置の始値を返す
/// </summary>
public double Open(int DatePos)
{
return (prices.Open(DatePos));
}
/// <summary>
/// 指定した日付位置の高値を返す
/// </summary>
public double High(int DatePos)
{
return (prices.High(DatePos));
}
/// <summary>
/// 指定した日付位置の安値を返す
/// </summary>
public double Low(int DatePos)
{
return (prices.Low(DatePos));
}
/// <summary>
/// 指定した日付位置の終値を返す
/// </summary>
public double Close(int DatePos)
{
return (prices.Close(DatePos));
}
/// <summary>
/// 指定した日付位置の出来高を返す
/// </summary>
public double Volume(int DatePos)
{
return (prices.Volume(DatePos));
}
/// <summary>
/// 指定した日付位置の権利落ち乗数を返す
/// </summary>
public double ExRights(int DatePos)
{
return (prices.ExRights(DatePos));
}
/// <summary>
/// true を設定すると権利落ち修正した価格を読み込みます
/// </summary>
public bool AdjustExRights
{
set
{
// AdjustExRightsはヘルプでみるとboolなのに、なぜかintで定義されている
if (value == false)
{
prices.AdjustExRights = 0;
}
else
{
prices.AdjustExRights = 1;
}
}
}
/// <summary>
/// 読み込んでいるいる銘柄名を返す
/// </summary>
public string Name()
{
return (prices.Name());
}
/// <summary>
/// Panデータから4本値のDataTableを返す
/// </summary>
/// <param name="code">銘柄コード</param>
/// <param name="beginDay">最初の日付</param>
/// <param name="endDay">最終の日付</param>
/// <returns>DataTable型の4本値 </returns>
public DataTable GetDataTable(string code, DateTime beginDay, DateTime endDay, bool nan = false)
{
DataTable dtable = new DataTable();
Calendar calendar = new Calendar();
dtable.Columns.Add("date");
dtable.Columns.Add("high_v", Type.GetType("System.Double"));
dtable.Columns.Add("low_v", Type.GetType("System.Double"));
dtable.Columns.Add("open_v", Type.GetType("System.Double"));
dtable.Columns.Add("close_v", Type.GetType("System.Double"));
dtable.Columns.Add("volume_v", Type.GetType("System.Double"));
dtable.Columns.Add("exrights_v", Type.GetType("System.Double"));
int begin = 0;
int end = 0;
try
{
// 四本値と出来高を読み込む
prices.Read(code);
// 日付位置の最小値と最大値を読み込む
end = prices.End();
begin = prices.Begin();
}
catch (Exception a)
{
// 例外処理
//イミディエイトウィンドウにエラー表示
Console.WriteLine(a.Message);
dtable = null;
return dtable;
}
int dataPosBegin = calendar.DatePosition(beginDay, -1); // 最初の日付位置を取得
int dataPosEnd = calendar.DatePosition(endDay, -1); // 最終の日付位置を取得
// 最初の日付と最終日付を補正
if (dataPosBegin <= end && dataPosEnd >= begin)
{
if (dataPosBegin >= begin)
{
begin = dataPosBegin;
}
if(dataPosEnd < end)
{
end = dataPosEnd;
}
}
else
{
// 指定した期間にデータが存在しない
dtable = null;
return dtable;
}
for (int i = begin; i <= end; i++)
{
if (prices.IsClosed(i) == 0)
{
DataRow dr = dtable.NewRow();
try
{
dr["date"] = calendar.Date(i);
dr["high_v"] = prices.High(i);
dr["low_v"] = prices.Low(i);
dr["open_v"] = prices.Open(i);
dr["close_v"] = prices.Close(i);
dr["volume_v"] = prices.Volume(i);
}
catch (Exception a)
{
//イミディエイトウィンドウにエラー表示
Console.WriteLine(a.Message);
continue;
}
dtable.Rows.Add(dr);
}
else
{
Console.WriteLine("休場日");
if (nan == true) {
// 休場日には、非数を代入する。
DataRow dr = dtable.NewRow();
dr["date"] = calendar.Date(i);
dr["high_v"] = Double.NaN;
dr["low_v"] = Double.NaN;
dr["open_v"] = Double.NaN;
dr["close_v"] = Double.NaN;
dr["volume_v"] = Double.NaN;
dtable.Rows.Add(dr);
}
}
}
return dtable;
}
}
class Calendar
{
private ActiveMarket.Calendar calendar = new ActiveMarket.Calendar();
/// <summary>
/// DatePosで指定した日付位置に対応する日付を返す
/// </summary>
/// <param name="Datepos">日付位置</param>
/// <returns>日付</returns>
public DateTime Date(int Datepos)
{
return (calendar.Date(Datepos));
}
//
/// <summary>
/// dateで指定した日付に対応する日付位置を返す
/// </summary>
/// <param name="date">日付</param>
/// <param name="direction">検索方向 -1:過去へ、1:未来へ</param>
/// <returns>日付位置</returns>
public int DatePosition(DateTime date, int direction = 0)
{
return (calendar.DatePosition(date, direction));
}
}
class AllCodeNames
{
private ActiveMarket.Names names = new ActiveMarket.Names();
private Array codeAr;
private Array nameAr;
public Dictionary<string, string> dict = new Dictionary<string, string>();
// 銘柄コードと銘柄名の一覧を取得します
public void AllNames()
{
names.AllNames(ActiveMarket._KindFlag.AM_KINDFLAG_SPOTS, out codeAr, out nameAr);
}
// 銘柄コードと銘柄名の一覧を取得します
// dictへ格納
public AllCodeNames()
{
AllNames();
for (int i = 0; i < codeAr.Length; i++)
{
dict.Add(Convert.ToString(codeAr.GetValue(i + 1)), Convert.ToString(nameAr.GetValue(i + 1)));
}
}
}
}
Form1.cs
表示期間の設定
「cmbPeriod」コントロールを使用して、ローソク足チャートの表示期間を設定します。
サンプルでは、次のような表示期間を動的に追加しています。
- 1か月
- 2か月
- 6か月
- 1年
- 期間指定(日付を手動で設定)
期間指定を選択する場合は、「dateTPbegin」や「dateTPend」で日付を指定する必要があります。一方で、その他の選択肢を選んだ場合は、「期間(月数)」を自動計算して開始日が設定されます。
例えば、2か月を選択すると、現在の終了日(dateTPend.Value
)から2か月前を開始日(dateTPbegin.Value
)として設定する仕組みです。
この機能により、動的に異なる表示期間を簡単に切り替えることができ、柔軟なチャート表示が可能になります。
cmbPeriod.DataSource = dataPeriod;
cmbPeriod.DisplayMember = "表示期間";
cmbPeriod.ValueMember = "データ列";
移動平均
移動平均には、FinancialFormulaメソッドを使用し、数式の種別を設定すると自動的に計算されます。サンプルでは、移動平均FinancialFormula.MovingAverageを指定しています。
数式の種類はこちら
移動平均の計算と適用
chart1.DataManipulator.FinancialFormula(FinancialFormula.MovingAverage, numericMA.Value.ToString(), legend1 + ":Y4", legend2);
numericMA.Value
で移動平均の計算期間を指定する(例:5日、10日など)legend1
はローソク足のデータ系列名legend2
は移動平均のデータ系列名
移動平均線のプロパティ設定
chart1.Series[legend2].ChartType = SeriesChartType.Line;
chart1.Series[legend2].Legend = chart1.Series[legend1].Legend;
chart1.Series[legend2].LegendText = "単純移動平均";
chart1.Series[legend2].ToolTip = "日付:#VALX\n移動平均値:#VALY";
ChartType
で表示形式を指定(ラインチャートを選択)LegendText
で凡例に移動平均の説明を追加ToolTip
でデータポイントにカーソルを合わせた際の情報を設定
系列間のデータ整合性の補完
移動平均の計算結果がローソク足データのポイント数と一致しない場合、データを補完する必要があります。以下のコードでデータ系列間の整合性を保ちます。
for (int i = dataCount - chart1.Series[legend2].Points.Count - 1; i >= 0; i--)
{
DataPoint newDataPoint = new DataPoint();
newDataPoint.XValue = chart1.Series[legend1].Points[i].XValue;
newDataPoint.IsEmpty = true; // 空のデータとして補完
chart1.Series[legend2].Points.Insert(0, newDataPoint);
}
以下は、ローソク足と移動平均を表示するサンプルプログラムです。ローソク足は全て日足で、週足、月足の設定はできません。コードを入力し、表示期間と移動平均計算日数を設定して、「検索」ボタンを押下します。表示期間が「期間指定」の場合は、あらかじめ日付を設定しておきます。
プログラムコード
using System;
using System.Data;
using System.Windows.Forms;
using System.Drawing;
using System.Windows.Forms.DataVisualization.Charting;
using PanActiveMarket;
namespace CandleChart
{
public partial class Form1 : Form
{
private Prices prices = new Prices();
private Calendar calendar = new Calendar();
private AllCodeNames allCodeNames = new AllCodeNames();
private DataTable dataPeriod = new DataTable();
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
dataPeriod.Columns.Add("表示期間");
dataPeriod.Columns.Add("データ列");
DataRow row = dataPeriod.NewRow();
row["表示期間"] = "1か月";
row["データ列"] = 1;
dataPeriod.Rows.Add(row);
row = dataPeriod.NewRow();
row["表示期間"] = "2か月";
row["データ列"] = 2;
dataPeriod.Rows.Add(row);
row = dataPeriod.NewRow();
row["表示期間"] = "6か月";
row["データ列"] = 6;
dataPeriod.Rows.Add(row);
row = dataPeriod.NewRow();
row["表示期間"] = "1年";
row["データ列"] = 12;
dataPeriod.Rows.Add(row);
row = dataPeriod.NewRow();
row["表示期間"] = "期間指定";
row["データ列"] = -1;
dataPeriod.Rows.Add(row);
cmbPeriod.DataSource = dataPeriod;
cmbPeriod.DisplayMember = "表示期間";
cmbPeriod.ValueMember = "データ列";
int period = 2;
cmbPeriod.SelectedIndex = period;
cmbPeriod.DropDownStyle = ComboBoxStyle.DropDownList; // キーボードからの入力を禁止する
int value = Convert.ToInt32(cmbPeriod.SelectedValue);
dateTPbegin.Value = dateTPend.Value.AddMonths(0 - value); // 期間設定
}
private void btnView_Click(object sender, EventArgs e)
{
prices.AdjustExRights = true; // 権利落ち修正した価格を読み込む。
string legend1 = txtCode.Text; // ローソク足系列
string legend2 = "SMA"; // 移動平均系列
int value = Convert.ToInt32(cmbPeriod.SelectedValue);
DataTable PanStock;
if ( value < 0)
{
// 期間指定
PanStock = prices.GetDataTable(legend1, dateTPbegin.Value, dateTPend.Value);
}
else
{
// 月数指定
DateTime endDate = dateTPend.Value;
DateTime beginDate = endDate.AddMonths(0 - value);
PanStock = prices.GetDataTable(legend1, beginDate, endDate);
}
if (allCodeNames.dict.ContainsKey(legend1) == false)
{
// 存在しないコード
return;
}
if (PanStock is null)
{
// オブジェクトが存在しない。
return;
}
int dataCount = PanStock.Rows.Count;
if (dataCount == 0)
{
// データなし
return;
}
string strName = allCodeNames.dict[txtCode.Text]; // 銘柄名を取得する
chart1.Titles.Clear();
chart1.Titles.Add(strName);
chart1.ChartAreas["ChartArea1"].AxisX.ScaleView.Size = dataCount;
chart1.ChartAreas["ChartArea1"].AxisX.IsMarginVisible = true; // マージンあり
chart1.ChartAreas["ChartArea1"].AxisY.IsStartedFromZero = false; // 軸の最小値を0に設定しない
chart1.Series.Clear(); //グラフ初期化(系列を全て削除)
chart1.Series.Add(legend1); //データ系列追加
chart1.Series.Add(legend2); //データ系列追加
chart1.Series[legend1].ChartArea = "ChartArea1"; // ChartArea1と紐づけする
chart1.Series[legend1].ChartType = SeriesChartType.Candlestick;
chart1.Series[legend1].LegendText = "株価"; //凡例に表示するテキストを指定
chart1.Series[legend1].IsXValueIndexed = true;
chart1.Series[legend1]["PriceUpColor"] = Color.Red.Name;
chart1.Series[legend1]["PriceDownColor"] = Color.Blue.Name;
chart1.Series[legend1].ToolTip = "日付:#VALX\n高値:#VALY\n安値:#VALY2\n始値" +
":#VALY3\n終値:#VALY4";
object date;
double high;
double low;
double open;
double end;
// ローソク足の表示
for (int i = 0, j = 0 ; i < dataCount; i++, j++)
{
date = PanStock.Rows[j]["date"];
high = Convert.ToDouble(PanStock.Rows[j]["high_v"]);
low = Convert.ToDouble(PanStock.Rows[j]["low_v"]);
open = Convert.ToDouble(PanStock.Rows[j]["open_v"]);
end = Convert.ToDouble(PanStock.Rows[j]["close_v"]);
// データポイントの値の登録
chart1.Series[legend1].Points.AddXY(DateTime.Parse(date.ToString()), high);
chart1.Series[legend1].Points[i].YValues[1] = low;
chart1.Series[legend1].Points[i].YValues[2] = open;
chart1.Series[legend1].Points[i].YValues[3] = end;
}
value = chart1.Series[legend1].Points.Count;
chart1.DataManipulator.FinancialFormula(FinancialFormula.MovingAverage, numericMA.Value.ToString(), legend1 + ":Y4", legend2);
chart1.Series[legend2].ChartType = SeriesChartType.Line;
chart1.Series[legend2].ChartArea = "ChartArea1"; // ChartArea1と紐づけする
chart1.Series[legend2].Legend = chart1.Series[legend1].Legend;
chart1.Series[legend2].IsXValueIndexed = true;
chart1.Series[legend2].LegendText = "単純移動平均";
chart1.Series[legend2].ToolTip = "日付:#VALX\n移動平均値:#VALY";
// 系列間のデータ数を合わせるためにデータを補完する
for (int i = dataCount - chart1.Series[legend2].Points.Count - 1; i >= 0; i--)
{
DataPoint newDataPoint = new DataPoint();
newDataPoint.XValue = chart1.Series[legend1].Points[i].XValue;
newDataPoint.IsEmpty = true; // 空のデータを設定する
chart1.Series[legend2].Points.Insert(0, newDataPoint);
}
}
private void cmbPeriod_SelectionChangeCommitted(object sender, EventArgs e)
{
// 期間が変更された時は、dateTimePicker2を起点に再計算されて設定されます。
int value = Convert.ToInt32(cmbPeriod.SelectedValue);
if (value > 0)
{
dateTPbegin.Value = dateTPend.Value.AddMonths(0 - value); // 期間設定
}
}
}
}
実行結果

まとめ
「C#のChartコントロールを使ってローソク足チャートを表示させる」方法について解説してきました。今回の記事で学んだポイントを振り返りながら、要点をまとめます。この記事で学んだポイントをまとめ、習得した内容を振り返りましょう。
- Chartコントロールを使った基本的なローソク足チャートを作成できる
Chartコントロールのプロパティ設定やデータの紐づけ方法を理解することで、ローソク足チャートを簡単に描画できるようになります。 - Pan Active Marketを活用した四本値データの取得と加工ができる
Pan Active Marketライブラリを活用し、正確な四本値データを取得する方法を学びました。これにより、データの可視化が効率的に行えます。 - 移動平均を取り入れたチャートをカスタマイズできる
FinancialFormulaメソッドを使った移動平均線の追加によって、より実用的で見やすいチャートを作成するスキルを習得しました。
C#のChartコントロールは柔軟性が高く、多機能であるため、今回紹介した内容をベースにさらに高度なチャート作成に挑戦することもできます。
金融データの可視化やトレードツールの作成を考えているなら、ぜひこの記事を参考にして実践してみて下さい。