Skip to content

Commit 94f7016

Browse files
committed
Indicators
1 parent a83820b commit 94f7016

15 files changed

Lines changed: 571 additions & 392 deletions

Core/Core/Conventions/Indicator.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Core.Models;
2+
using Core.Services;
23
using System.Collections.Generic;
34
using System.Threading.Tasks;
45

@@ -35,6 +36,11 @@ public class Indicator : IIndicator
3536
/// </summary>
3637
public virtual Price Response { get; protected set; } = new();
3738

39+
/// <summary>
40+
/// Average
41+
/// </summary>
42+
public virtual AverageService Average { get; protected set; } = new();
43+
3844
/// <summary>
3945
/// Calculate indicator values
4046
/// </summary>
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
using Core.Conventions;
2+
using Core.Models;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
8+
namespace Core.Indicators
9+
{
10+
public class AtrIndicator : Indicator
11+
{
12+
/// <summary>
13+
/// Number of bars to average
14+
/// </summary>
15+
public int Interval { get; set; }
16+
17+
/// <summary>
18+
/// Calculate single value
19+
/// </summary>
20+
/// <param name="collection"></param>
21+
public override Task<IIndicator> Update(IList<Price> collection)
22+
{
23+
var response = Task.FromResult<IIndicator>(this);
24+
var currentPoint = collection.ElementAtOrDefault(collection.Count - 1);
25+
var previousPoint = collection.ElementAtOrDefault(collection.Count - 2);
26+
27+
if (currentPoint is null || previousPoint is null)
28+
{
29+
return response;
30+
}
31+
32+
var interval = Math.Min(Interval, collection.Count);
33+
var value =
34+
Math.Max(currentPoint.Bar.High.Value, previousPoint.Bar.Close.Value) -
35+
Math.Min(currentPoint.Bar.Low.Value, previousPoint.Bar.Close.Value);
36+
37+
if (interval is not 0)
38+
{
39+
value = ((Response.Last ?? 1) * Math.Max(Interval - 1, 0) + value) / interval;
40+
}
41+
42+
Response = Response with { Last = value };
43+
44+
return response;
45+
}
46+
}
47+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using Core.Conventions;
2+
using Core.Models;
3+
using System.Collections.Generic;
4+
using System.Linq;
5+
using System.Threading.Tasks;
6+
7+
namespace Core.Indicators
8+
{
9+
public class ImbalanceIndicator : Indicator
10+
{
11+
/// <summary>
12+
/// Mode
13+
/// </summary>
14+
public virtual int Mode { get; set; } = 0;
15+
16+
/// <summary>
17+
/// Calculate indicator value
18+
/// </summary>
19+
/// <param name="collection"></param>
20+
/// <param name="side"></param>
21+
public override Task<IIndicator> Update(IList<Price> collection)
22+
{
23+
var response = Task.FromResult<IIndicator>(this);
24+
var currentPoint = collection.LastOrDefault();
25+
26+
if (currentPoint is null)
27+
{
28+
return response;
29+
}
30+
31+
var value = 0.0;
32+
33+
switch (Mode)
34+
{
35+
case 0: value = currentPoint.AskSize.Value - currentPoint.BidSize.Value; break;
36+
case 1: value = currentPoint.AskSize.Value; break;
37+
case -1: value = currentPoint.BidSize.Value; break;
38+
}
39+
40+
Response = Response with { Last = value };
41+
42+
return response;
43+
}
44+
}
45+
}
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Core.Conventions;
2+
using Core.Extensions;
3+
using Core.Models;
4+
using System;
5+
using System.Collections.Generic;
6+
using System.Linq;
7+
using System.Threading.Tasks;
8+
9+
namespace Core.Indicators
10+
{
11+
/// <summary>
12+
/// Calculation mode
13+
/// </summary>
14+
public enum AveragePriceEnum : byte
15+
{
16+
Bid = 1,
17+
Ask = 2,
18+
Close = 3
19+
}
20+
21+
public class MaIndicator : Indicator
22+
{
23+
/// <summary>
24+
/// Number of bars to average
25+
/// </summary>
26+
public int Interval { get; set; }
27+
28+
/// <summary>
29+
/// Calculation mode
30+
/// </summary>
31+
public AveragePriceEnum Mode { get; set; }
32+
33+
/// <summary>
34+
/// Calculate single value
35+
/// </summary>
36+
/// <param name="collection"></param>
37+
public override Task<IIndicator> Update(IList<Price> collection)
38+
{
39+
var response = Task.FromResult<IIndicator>(this);
40+
var currentPoint = collection.LastOrDefault();
41+
42+
if (currentPoint is null)
43+
{
44+
return response;
45+
}
46+
47+
var value = currentPoint.Last.Value;
48+
49+
switch (Mode)
50+
{
51+
case AveragePriceEnum.Bid: value = currentPoint.Bid.Value; break;
52+
case AveragePriceEnum.Ask: value = currentPoint.Ask.Value; break;
53+
}
54+
55+
var interval = Math.Min(Interval, collection.Count);
56+
var average = Average.LinearWeightAverage(collection.Select(o => o.Last.Value), collection.Count - 1, interval) as double?;
57+
58+
Response = Response with { Last = average.Is(0) ? value : average };
59+
60+
return response;
61+
}
62+
}
63+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
using Core.Conventions;
2+
using Core.Extensions;
3+
using Core.Models;
4+
using Newtonsoft.Json.Linq;
5+
using System;
6+
using System.Collections.Generic;
7+
using System.Linq;
8+
using System.Threading.Tasks;
9+
10+
namespace Core.Indicators
11+
{
12+
public class RsiIndicator : Indicator
13+
{
14+
/// <summary>
15+
/// Number of bars to average
16+
/// </summary>
17+
public int Interval { get; set; }
18+
19+
/// <summary>
20+
/// Calculate single value
21+
/// </summary>
22+
/// <param name="collection"></param>
23+
public override Task<IIndicator> Update(IList<Price> collection)
24+
{
25+
var response = Task.FromResult<IIndicator>(this);
26+
var currentPoint = collection.LastOrDefault();
27+
28+
if (currentPoint is null)
29+
{
30+
return response;
31+
}
32+
33+
var ups = new List<double>(Interval);
34+
var downs = new List<double>(Interval);
35+
36+
for (var i = 1; i <= Interval; i++)
37+
{
38+
var nextPrice = collection.ElementAtOrDefault(collection.Count - i);
39+
var previousPrice = collection.ElementAtOrDefault(collection.Count - i - 1);
40+
41+
if (nextPrice is not null && previousPrice is not null)
42+
{
43+
ups.Add(Math.Max(nextPrice.Last.Value - previousPrice.Last.Value, 0.0));
44+
downs.Add(Math.Max(previousPrice.Last.Value - nextPrice.Last.Value, 0.0));
45+
}
46+
}
47+
48+
var interval = Math.Min(Interval, collection.Count);
49+
var averageUp = Average.SimpleAverage(ups, ups.Count - 1, interval);
50+
var averageDown = Average.SimpleAverage(downs, downs.Count - 1, interval) as double?;
51+
var average = averageDown.Is(0) ? 1.0 : averageUp / averageDown;
52+
53+
Response = Response with { Last = 100.0 - 100.0 / (1.0 + average) };
54+
55+
return response;
56+
}
57+
}
58+
}

Core/Core/Indicators/ScaleIndicator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,11 +33,11 @@ public class ScaleIndicator : Indicator
3333
/// <summary>
3434
/// Calculate indicator value
3535
/// </summary>
36-
/// <param name="prices"></param>
37-
public override Task<IIndicator> Update(IList<Price> prices)
36+
/// <param name="collection"></param>
37+
public override Task<IIndicator> Update(IList<Price> collection)
3838
{
3939
var response = Task.FromResult<IIndicator>(this);
40-
var currentPoint = prices.LastOrDefault();
40+
var currentPoint = collection.LastOrDefault();
4141

4242
if (currentPoint is null)
4343
{
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Core.Conventions;
2+
using Core.Models;
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
8+
namespace Core.Indicators
9+
{
10+
public class VwapIndicator : Indicator
11+
{
12+
/// <summary>
13+
/// Calculate indicator value
14+
/// </summary>
15+
/// <param name="collection"></param>
16+
public override Task<IIndicator> Update(IList<Price> collection)
17+
{
18+
var response = Task.FromResult<IIndicator>(this);
19+
var currentPoint = collection.LastOrDefault();
20+
21+
if (currentPoint?.Bar is null)
22+
{
23+
return response;
24+
}
25+
26+
var cumPrice = 0.0;
27+
var cumVolume = 0.0;
28+
var items = new List<(double Price, double Volume)>();
29+
30+
Response = Response with { Bar = Response.Bar ?? new() };
31+
32+
foreach (var point in collection)
33+
{
34+
var price = (point.Bar.Low + point.Bar.High + point.Bar.Close).Value / 3.0;
35+
var volume = point.Volume ?? (point.BidSize ?? 0 + point.AskSize ?? 0);
36+
37+
cumPrice += price * volume;
38+
cumVolume += volume;
39+
40+
items.Add((price, volume));
41+
42+
var average = cumPrice / cumVolume;
43+
var variance = items
44+
.Select(o => o.Volume * (o.Price - average) * (o.Price - average))
45+
.Sum() / cumVolume;
46+
47+
var deviation = Math.Sqrt(variance);
48+
49+
Response = Response with
50+
{
51+
Last = average,
52+
Bar = Response.Bar with
53+
{
54+
High = average + 2.0 * deviation,
55+
Low = average - 2.0 * deviation
56+
}
57+
};
58+
}
59+
60+
return response;
61+
}
62+
}
63+
}

Dashboard/Dashboard/Pages/BasePage.cs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
using BitMart.Net.Enums;
12
using Canvas.Core.Models;
23
using Canvas.Core.Shapes;
34
using Core.Conventions;
@@ -97,14 +98,16 @@ protected virtual string GetDate(IList<IShape> items, int index)
9798
/// <summary>
9899
/// Open position
99100
/// </summary>
101+
/// <param name="adapter"></param>
100102
/// <param name="asset"></param>
101103
/// <param name="side"></param>
102-
protected virtual async Task OpenPosition(IGateway adapter, Instrument asset, OrderSideEnum side)
104+
/// <param name="amount"></param>
105+
protected virtual async Task OpenPosition(IGateway adapter, Instrument asset, OrderSideEnum side, double amount = 1)
103106
{
104107
var order = new Order
105108
{
106-
Amount = 1,
107109
Side = side,
110+
Amount = amount,
108111
Type = OrderTypeEnum.Market,
109112
Operation = new() { Instrument = asset }
110113
};
@@ -115,9 +118,11 @@ protected virtual async Task OpenPosition(IGateway adapter, Instrument asset, Or
115118
/// <summary>
116119
/// Close positions
117120
/// </summary>
121+
/// <param name="adapter"></param>
118122
/// <param name="condition"></param>
119-
protected virtual async Task ClosePosition(IGateway adapter, Func<Order, bool> condition = null)
123+
protected virtual async Task<List<Order>> ClosePosition(IGateway adapter, Func<Order, bool> condition = null)
120124
{
125+
var response = new List<Order>();
121126
var positions = await adapter.GetPositions(default);
122127

123128
foreach (var position in positions.Data)
@@ -136,8 +141,12 @@ protected virtual async Task ClosePosition(IGateway adapter, Func<Order, bool> c
136141
};
137142

138143
await adapter.SendOrder(order);
144+
145+
response.Add(order);
139146
}
140147
}
148+
149+
return response;
141150
}
142151
}
143152
}

0 commit comments

Comments
 (0)