2009年1月8日木曜日

今日の戦績

Lv1.00(整数部がmini発注枚数の半分の切り上げ値)
---------------------------

---------------------------
トータル損益は+4992円です。

このうち手数料の支払いが1008円含まれます。

買い注文回数は10回, 売り注文回数は11回です。

未返済の建て玉1枚の見込み損益は手数料を含まずに+2452円です。


---------------------------
OK
---------------------------
夕場に変な参戦の仕方をしたので訂正。
夕場が、調子に乗って下げすぎだろう、という感じだったのだが、これ、引けにかけて今日の日経平均の終値付近まで戻す展開になれば売り建てして持ち越しするのも面白そうだな、と思い8880で指値しておいた。引けにかけて戻す展開を想定していたのでずっと場を見ていなかったのだが、ふと見たらとっくに約定している!で、オーバーシュートせずにドンピシャで8900で切り替えしていた。こえー。

さて、明日は下げるなら25MAに向けてどかんと下げる。上げるなら今日の終値付近からという展開になるというてっきとーな読みでこういう事をしたわけですが、目はどう出るでしょうか。

Lv1.00(整数部がmini発注枚数の半分の切り上げ値)
---------------------------

---------------------------
トータル損益は+5040円です。

このうち手数料の支払いが960円含まれます。

買い注文回数は10回, 売り注文回数は10回です。

未返済の建て玉0枚の見込み損益は手数料を含まずに±0円です。


---------------------------
OK
---------------------------

午前はともかく午後は一方的に下げるだけで値幅も大きかったのにこの程度の利益とは・・・がっくし。動きがなくなって余りの眠さに頭をはっきりさせようとスキップリストのライブラリに手をつけていたら一気に8900まで下げちゃうんだもんなあ。その後は逃がした魚は大きいという奴で欲が出て底値付近でショートして損きりになったり逆にリバウンドを狙ってロングしてやっぱり損きりになったりを繰り返してしまった。しかし引けにかけては一段の下げがあってどうにか体裁を保てたという所。まあ二日連勝は素直に喜ぼう。

以下午前中の愚痴

前場は、日経平均だけ見ると反落の後半値戻しという分かり易い展開なんだけど、先物は、完膚なきまでにランダムウォークなんだよなあ。やりにくい。夕場で先行して下げたというのもあるんだろうけれど。ちなみに、時間の平方根を取る表示なのでランダムウォークが種々の長さの直線運動の連なりとして視覚的に分かり易くなるのだ。

9005S-8975Cで3000円利益が出せたけど、その後小幅のリバウンドを待ってさらにショートしたら大きい戻しに捕まってしまった。損きりと言えないくらいの大きな損を出して手仕舞いした後改めて9040S。現在その含み損益は入れずに-5000円くらい。後場の終値が9000円を割らないという事はないと思うけれど、もうそうそう-3%とかの下げにもならないだろうから、どこかでうまくロングポジションを取らないと、この損を取り返すだけで終わってしまいそう。

スキップリストの.net実装を更新

スキップリストの.net上の実装を少し更新した。いつもと同様ファイル置き場に置いてある。とは言っても、ヘルプに記載しているサンプルコードに手を入れたのと、パブリックなメソッドの引数名を先頭大文字にした事だけ。この辺の標準的な名前付け規約はまだ良く分かっていないので時々変えるかも。

今回のサンプルコードの変更では、以前は

totalVolume[depth] = SkipList<MyItem, MyNode>
.GetEnumerableInOrder(start, true, end, true, depth - 1)
.Sum((n) => n.totalVolume[depth - 1]);
vwap[depth] = SkipList<MyItem, MyNode>
.GetEnumerableInOrder(start, true, end, true, depth - 1)
.Aggregate<MyNode, VolumeVwapPair>(new VolumeVwapPair() {
Volume = 0,
Vwap = 0
}, (pair, node) =>
VolumeVwapPair.Composite(pair, new VolumeVwapPair() {
Volume = node.TotalVolume[depth - 1],
Vwap = node.Vwap[depth - 1]
})).Vwap;

とごちゃごちゃしていた所を、

VolumeVwapPair ret = SkipList<MyItem, MyNode>
.GetEnumerableInOrder(start, true, end, true, depth - 1)
.Select((node) => new VolumeVwapPair
(node.TotalVolume[depth - 1], node.Vwap[depth - 1]))
.Aggregate(VolumeVwapPair.Composite);
totalVolume[depth] = ret.Volume;
vwap[depth] = ret.Vwap;

とすっきりした記述にした、等等、匿名メソッドを闇雲に使う代わりにSystem.LinqのSelect拡張メソッドを一段階挟んで、VolumeVwapPair.Compositeメソッドが可換半群の合成規則を与えている事が明瞭になるようにした。この変更は「身も蓋もない」のコードにも反映してある。もちろん以前のコードと等価な範囲の変更である。

サンプルコードの全体は以下のようになっている。可換半群の任意の区間に渡る総和計算をスキップリストのツリー状の構造を用いて高速に行う事が実際に簡単に実装できるようになっているのが分かると思う。

使用例

時刻、値、出来高を持つ株価の歩み値データを考えます。このデータを時刻によりソートし、任意の区間に対して出来高の総和と出来高を重みとした加重平均値(VWAP値)を高速に計算できるようにしたい場合には次のようにします。なお、以下のコードではEnumerableで定義される拡張メソッドが用いられているためコードの先頭でusing System.Linq;が記述されている必要があります。

まずMyItemクラスを次のように定義します。

C#
class MyItem
{
private DateTime time;
private double price;
private double volume;
public MyItem(DateTime time, double price, double volume)
{
this.time = time;
this.price = price;
this.volume = volume;
}
public DateTime Time { get { return time; } }
public double Price { get { return price; } }
public double Volume { get { return volume; } }
}

次に(重み,平均値)ペアがなす可換半群を表すVolumeVwapPairクラスを定義します。なお、重みが0である全てのペアは同一視されこの可換半群の単位元としての役割を果たします。

C#
class VolumeVwapPair
{
public double Volume;
public double Vwap;
public VolumeVwapPair(double Volume, double Vwap)
{
this.Volume = Volume;
this.Vwap = Vwap;
}
public static VolumeVwapPair Composite(VolumeVwapPair pair1,
VolumeVwapPair pair2)
{
double totalVolume = pair1.Volume + pair2.Volume;
if (totalVolume == 0) return new VolumeVwapPair(0, 0);
return new VolumeVwapPair(totalVolume,
(pair1.Volume * pair1.Vwap + pair2.Volume * pair2.Vwap)
/ totalVolume);
}
}
そしてMyNodeクラスを次のように定義します。
C#
class MyNode : SkipListNode<MyItem, MyNode>
{
private double[] totalVolume;
private double[] vwap;
protected override void OnHeightDecided()
{
base.OnHeightDecided();
InitializeParameters(ref totalVolume);
InitializeParameters(ref vwap);
}
protected override void OnMaintainance(int depth)
{
base.OnMaintainance(depth);
if (IsHeader || IsFooter) return;
if (depth == 0) {
totalVolume[0] = Item.Volume;
vwap[0] = Item.Price;
} else {
MyNode start = this;
MyNode end = this.Next[depth].Prev[depth - 1];
VolumeVwapPair ret = SkipList<MyItem, MyNode>
.GetEnumerableInOrder(start, true, end, true, depth - 1)
.Select((node) => new VolumeVwapPair
(node.TotalVolume[depth - 1], node.Vwap[depth - 1]))
.Aggregate(VolumeVwapPair.Composite);
totalVolume[depth] = ret.Volume;
vwap[depth] = ret.Vwap;
}
}
protected override int Compare(MyItem item1, MyItem item2)
{
return item1.Time.CompareTo(item2.Time);
}
public double[] TotalVolume { get { return totalVolume; } }
public double[] Vwap { get { return vwap; } }
}

最後にMySequenceクラスを次のように定義します。

C#
class MySequence : SkipList<MyItem, MyNode>
{
public double TotalVolume(MyNode former, MyNode latter)
{
return Sum(former, latter, (node, depth) => (node.TotalVolume[depth]));
}
public double Vwap(MyNode former, MyNode latter)
{
return Aggregate(former, latter,
VolumeVwapPair.Composite,
(node, depth) => new VolumeVwapPair
(node.TotalVolume[depth], node.Vwap[depth]),
new VolumeVwapPair(0, 0)).Vwap;
}
}

この例では重みの総和に関してはDouble型の総和を計算するSum(TNode, TNode, Func<TNode, Int32, Double>)メソッドを用いて計算し、全平均値に関しては通常の意味での加法によって合成されない可換半群の典型例である(重み,平均値)ペア(重みは通常の和で、平均値は加重平均によって合成される)に対して一般的な可換半群の総和を計算するAggregate<T>(TNode, TNode, Func<T, T, T>, Func<TNode, Int32, T>, T)メソッドを適用する事で計算しています。

ほとんどの場合、可換半群は通常の意味での加法群と等価である事に注意してください。実際、(重み,平均値)ペアと(重み,重み*平均値)ペアとは重みが0である場合を除いて一対一に対応します。そして重み及び重み*平均値はそれぞれ独立に通常の意味での加法によって合成されます。従ってMyNodeクラスのOnMaintainanceメソッド及びMySequenceクラスのVwapメソッドはそれぞれ

C#
protected override void OnMaintainance(int depth)
{
base.OnMaintainance(depth);
if (IsHeader || IsFooter) return;
if (depth == 0) {
totalVolume[0] = Item.Volume;
vwap[0] = Item.Price;
} else {
MyNode start = this;
MyNode end = this.Next[depth].Prev[depth - 1];
totalVolume[depth] = SkipList<MyItem, MyNode>
.GetEnumerableInOrder(start, true, end, true, depth - 1)
.Select((node) => node.TotalVolume[depth - 1])
.Sum();
vwap[depth] = SkipList<MyItem, MyNode>
.GetEnumerableInOrder(start, true, end, true, depth - 1)
.Select((node) => node.Vwap[depth - 1]
      * node.TotalVolume[depth - 1])
.Sum() / totalVolume[depth];
}
}
及び
C#
public double Vwap(MyNode former, MyNode latter)
{
double totalVolume = TotalVolume(former, latter);
if (totalVolume == 0) return 0;
return Sum(former, latter,
(node, depth) => node.Vwap[depth] * node.TotalVolume[depth])
       / totalVolume;
}
と書く事もできます。