2018-05-22 10:11:50 +02:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.IO ;
using System.Text ;
using Core.Main ;
using Core.Helper ;
using Core.Main.DataObjects.PTMagicData ;
using Newtonsoft.Json ;
using Core.ProfitTrailer ;
2019-02-26 23:49:07 +01:00
using System.Threading ;
using System.Threading.Tasks ;
2018-05-22 10:11:50 +02:00
2018-12-15 22:07:29 +01:00
namespace Core.MarketAnalyzer
{
public class Bittrex : BaseAnalyzer
{
public static double GetMainCurrencyPrice ( string mainMarket , PTMagicConfiguration systemConfiguration , LogHelper log )
{
2018-05-22 10:11:50 +02:00
double result = 0 ;
2018-12-15 22:07:29 +01:00
try
{
2018-05-22 10:11:50 +02:00
string baseUrl = "https://bittrex.com/api/v1.1/public/getmarketsummary?market=USDT-" + mainMarket ;
log . DoLogInfo ( "Bittrex - Getting main market price..." ) ;
2019-12-14 14:13:47 +01:00
Dictionary < string , dynamic > jsonObject = GetJsonFromURL ( baseUrl , log , null ) ;
2018-12-15 22:07:29 +01:00
if ( jsonObject . Count > 0 )
{
if ( jsonObject [ "success" ] )
{
2018-05-22 10:11:50 +02:00
log . DoLogInfo ( "Bittrex - Market data received for USDT-" + mainMarket ) ;
result = jsonObject [ "result" ] [ 0 ] [ "Last" ] ;
log . DoLogInfo ( "Bittrex - Current price for USDT-" + mainMarket + ": " + result . ToString ( "#,#0.00" ) + " USD" ) ;
}
}
2018-12-15 22:07:29 +01:00
}
catch ( Exception ex )
{
2018-05-22 10:11:50 +02:00
log . DoLogCritical ( ex . Message , ex ) ;
}
return result ;
}
2018-12-15 22:07:29 +01:00
public static List < string > GetMarketData ( string mainMarket , Dictionary < string , MarketInfo > marketInfos , PTMagicConfiguration systemConfiguration , LogHelper log )
{
2018-05-22 10:11:50 +02:00
List < string > result = new List < string > ( ) ;
string lastMarket = "" ;
Newtonsoft . Json . Linq . JObject lastTicker = null ;
2018-12-15 22:07:29 +01:00
try
{
2018-05-22 10:11:50 +02:00
string baseUrl = "https://bittrex.com/api/v2.0/pub/markets/GetMarketSummaries" ;
log . DoLogInfo ( "Bittrex - Getting market data..." ) ;
2019-12-14 14:13:47 +01:00
Dictionary < string , dynamic > jsonObject = GetJsonFromURL ( baseUrl , log , null ) ;
2018-12-15 22:07:29 +01:00
if ( jsonObject . Count > 0 )
{
if ( jsonObject [ "success" ] )
{
2018-05-22 10:11:50 +02:00
log . DoLogInfo ( "Bittrex - Market data received for " + jsonObject [ "result" ] . Count . ToString ( ) + " currencies" ) ;
double mainCurrencyPrice = 1 ;
2018-12-15 22:07:29 +01:00
if ( ! mainMarket . Equals ( "USDT" , StringComparison . InvariantCultureIgnoreCase ) )
{
2018-05-22 10:11:50 +02:00
mainCurrencyPrice = Bittrex . GetMainCurrencyPrice ( mainMarket , systemConfiguration , log ) ;
}
2018-12-15 22:07:29 +01:00
if ( mainCurrencyPrice > 0 )
{
2018-05-22 10:11:50 +02:00
Dictionary < string , Market > markets = new Dictionary < string , Market > ( ) ;
2018-12-15 22:07:29 +01:00
foreach ( Newtonsoft . Json . Linq . JObject currencyTicker in jsonObject [ "result" ] )
{
2018-05-22 10:11:50 +02:00
string marketName = currencyTicker [ "Summary" ] [ "MarketName" ] . ToString ( ) ;
2018-12-15 22:07:29 +01:00
if ( marketName . StartsWith ( mainMarket , StringComparison . InvariantCultureIgnoreCase ) )
{
2018-05-22 10:11:50 +02:00
// Set last values in case any error occurs
lastMarket = marketName ;
lastTicker = currencyTicker ;
Market market = new Market ( ) ;
market . Position = markets . Count + 1 ;
market . Name = marketName ;
market . Symbol = currencyTicker [ "Summary" ] [ "MarketName" ] . ToString ( ) ;
if ( currencyTicker [ "Summary" ] [ "Last" ] . Type = = Newtonsoft . Json . Linq . JTokenType . Float ) market . Price = ( double ) currencyTicker [ "Summary" ] [ "Last" ] ;
if ( currencyTicker [ "Summary" ] [ "BaseVolume" ] . Type = = Newtonsoft . Json . Linq . JTokenType . Float ) market . Volume24h = ( double ) currencyTicker [ "Summary" ] [ "BaseVolume" ] ;
market . MainCurrencyPriceUSD = mainCurrencyPrice ;
markets . Add ( market . Name , market ) ;
result . Add ( market . Name ) ;
// Save market info
MarketInfo marketInfo = null ;
2018-12-15 22:07:29 +01:00
if ( marketInfos . ContainsKey ( marketName ) )
{
2018-05-22 10:11:50 +02:00
marketInfo = marketInfos [ marketName ] ;
}
2018-12-15 22:07:29 +01:00
if ( marketInfo = = null )
{
2018-05-22 10:11:50 +02:00
marketInfo = new MarketInfo ( ) ;
marketInfo . Name = marketName ;
marketInfos . Add ( marketName , marketInfo ) ;
}
if ( currencyTicker [ "Summary" ] [ "Created" ] . Type = = Newtonsoft . Json . Linq . JTokenType . Date ) marketInfo . FirstSeen = ( DateTime ) currencyTicker [ "Summary" ] [ "Created" ] ;
2019-02-04 01:17:38 +01:00
marketInfo . LastSeen = DateTime . UtcNow ;
2018-05-22 10:11:50 +02:00
}
}
BaseAnalyzer . SaveMarketInfosToFile ( marketInfos , systemConfiguration , log ) ;
Bittrex . CheckForMarketDataRecreation ( mainMarket , markets , systemConfiguration , log ) ;
2019-03-09 09:38:46 +01:00
DateTime fileDateTime = DateTime . UtcNow ;
2018-05-22 10:11:50 +02:00
FileHelper . WriteTextToFile ( Directory . GetCurrentDirectory ( ) + Path . DirectorySeparatorChar + Constants . PTMagicPathData + Path . DirectorySeparatorChar + Constants . PTMagicPathExchange + Path . DirectorySeparatorChar , "MarketData_" + fileDateTime . ToString ( "yyyy-MM-dd_HH.mm" ) + ".json" , JsonConvert . SerializeObject ( markets ) , fileDateTime , fileDateTime ) ;
log . DoLogInfo ( "Bittrex - Market data saved for " + markets . Count . ToString ( ) + " markets with " + mainMarket + "." ) ;
FileHelper . CleanupFiles ( Directory . GetCurrentDirectory ( ) + Path . DirectorySeparatorChar + Constants . PTMagicPathData + Path . DirectorySeparatorChar + Constants . PTMagicPathExchange + Path . DirectorySeparatorChar , systemConfiguration . AnalyzerSettings . MarketAnalyzer . StoreDataMaxHours ) ;
log . DoLogInfo ( "Bittrex - Market data cleaned." ) ;
2018-12-15 22:07:29 +01:00
}
else
{
2018-05-22 10:11:50 +02:00
log . DoLogError ( "Bittrex - Failed to get main market price for " + mainMarket + "." ) ;
result = null ;
}
}
}
2018-12-15 22:07:29 +01:00
}
catch ( Exception ex )
{
2018-05-22 10:11:50 +02:00
log . DoLogCritical ( "Exception while getting data for '" + lastMarket + "': " + ex . Message , ex ) ;
result = null ;
}
return result ;
}
2018-12-15 22:07:29 +01:00
public static List < MarketTick > GetMarketTicks ( string marketName , PTMagicConfiguration systemConfiguration , LogHelper log )
{
2018-05-22 10:11:50 +02:00
List < MarketTick > result = new List < MarketTick > ( ) ;
2018-12-15 22:07:29 +01:00
try
{
2018-05-22 10:11:50 +02:00
string baseUrl = "https://bittrex.com/Api/v2.0/pub/market/GetTicks?tickInterval=oneMin&marketName=" + marketName ;
log . DoLogDebug ( "Bittrex - Getting ticks for '" + marketName + "'..." ) ;
2019-12-14 14:13:47 +01:00
Dictionary < string , dynamic > jsonObject = GetJsonFromURL ( baseUrl , log , null ) ;
2018-12-15 22:07:29 +01:00
if ( jsonObject . Count > 0 )
{
if ( jsonObject [ "success" ] )
{
if ( jsonObject [ "result" ] ! = null )
{
2018-05-22 10:11:50 +02:00
log . DoLogDebug ( "Bittrex - " + jsonObject [ "result" ] . Count . ToString ( ) + " ticks received." ) ;
2018-12-15 22:07:29 +01:00
foreach ( var marketTick in jsonObject [ "result" ] )
{
2018-05-22 10:11:50 +02:00
MarketTick tick = new MarketTick ( ) ;
tick . Price = ( double ) marketTick [ "C" ] ;
tick . Time = SystemHelper . TextToDateTime ( marketTick [ "T" ] . ToString ( ) , Constants . confMinDate ) ;
result . Add ( tick ) ;
}
2018-12-15 22:07:29 +01:00
}
else
{
2018-05-22 10:11:50 +02:00
log . DoLogDebug ( "Bittrex - No ticks received." ) ;
}
}
}
2018-12-15 22:07:29 +01:00
}
catch ( Exception ex )
{
2018-05-22 10:11:50 +02:00
log . DoLogCritical ( ex . Message , ex ) ;
}
return result ;
}
2018-12-15 22:07:29 +01:00
public static void CheckForMarketDataRecreation ( string mainMarket , Dictionary < string , Market > markets , PTMagicConfiguration systemConfiguration , LogHelper log )
{
2018-05-22 10:11:50 +02:00
string bittrexDataDirectoryPath = Directory . GetCurrentDirectory ( ) + Path . DirectorySeparatorChar + Constants . PTMagicPathData + Path . DirectorySeparatorChar + Constants . PTMagicPathExchange + Path . DirectorySeparatorChar ;
2018-12-15 22:07:29 +01:00
if ( ! Directory . Exists ( bittrexDataDirectoryPath ) )
{
2018-05-22 10:11:50 +02:00
Directory . CreateDirectory ( bittrexDataDirectoryPath ) ;
}
DirectoryInfo dataDirectory = new DirectoryInfo ( bittrexDataDirectoryPath ) ;
// Check for existing market files
DateTime latestMarketDataFileDateTime = Constants . confMinDate ;
List < FileInfo > marketFiles = dataDirectory . EnumerateFiles ( "MarketData*" ) . ToList ( ) ;
FileInfo latestMarketDataFile = null ;
2018-12-15 22:07:29 +01:00
if ( marketFiles . Count > 0 )
{
2018-05-22 10:11:50 +02:00
latestMarketDataFile = marketFiles . OrderByDescending ( mdf = > mdf . LastWriteTimeUtc ) . First ( ) ;
latestMarketDataFileDateTime = latestMarketDataFile . LastWriteTimeUtc ;
}
2019-02-04 01:17:38 +01:00
if ( latestMarketDataFileDateTime < DateTime . UtcNow . AddMinutes ( - 20 ) )
2018-12-15 22:07:29 +01:00
{
2019-02-04 01:17:38 +01:00
int lastMarketDataAgeInSeconds = ( int ) Math . Ceiling ( DateTime . UtcNow . Subtract ( latestMarketDataFileDateTime ) . TotalSeconds ) ;
2018-05-22 10:11:50 +02:00
// Go back in time and create market data
2019-02-04 01:17:38 +01:00
DateTime startDateTime = DateTime . UtcNow ;
DateTime endDateTime = DateTime . UtcNow . AddHours ( - systemConfiguration . AnalyzerSettings . MarketAnalyzer . StoreDataMaxHours ) ;
2018-12-15 22:07:29 +01:00
if ( latestMarketDataFileDateTime ! = Constants . confMinDate & & latestMarketDataFileDateTime > endDateTime )
{
2018-05-22 10:11:50 +02:00
// Existing market files too old => Recreate market data for configured timeframe
log . DoLogInfo ( "Bittrex - Recreating market data for " + markets . Count + " markets over " + SystemHelper . GetProperDurationTime ( lastMarketDataAgeInSeconds ) + ". This may take a while..." ) ;
endDateTime = latestMarketDataFileDateTime ;
2018-12-15 22:07:29 +01:00
}
else
{
2018-05-22 10:11:50 +02:00
// No existing market files found => Recreate market data for configured timeframe
log . DoLogInfo ( "Bittrex - Recreating market data for " + markets . Count + " markets over " + systemConfiguration . AnalyzerSettings . MarketAnalyzer . StoreDataMaxHours + " hours. This may take a while..." ) ;
}
// Get Ticks for main market
List < MarketTick > mainMarketTicks = new List < MarketTick > ( ) ;
2018-12-15 22:07:29 +01:00
if ( ! mainMarket . Equals ( "USDT" , StringComparison . InvariantCultureIgnoreCase ) )
{
2018-05-22 10:11:50 +02:00
mainMarketTicks = Bittrex . GetMarketTicks ( "USDT-" + mainMarket , systemConfiguration , log ) ;
}
// Get Ticks for all markets
log . DoLogDebug ( "Bittrex - Getting ticks for '" + markets . Count + "' markets" ) ;
Dictionary < string , List < MarketTick > > marketTicks = new Dictionary < string , List < MarketTick > > ( ) ;
2019-02-26 23:49:07 +01:00
2019-03-09 09:38:46 +01:00
Parallel . ForEach ( markets . Keys ,
2019-02-26 23:49:07 +01:00
new ParallelOptions { MaxDegreeOfParallelism = 5 } ,
( key ) = >
2018-12-15 22:07:29 +01:00
{
2019-03-09 09:38:46 +01:00
if ( ! marketTicks . TryAdd ( key , Bittrex . GetMarketTicks ( key , systemConfiguration , log ) ) )
{
// Failed to add ticks to dictionary
throw new Exception ( "Failed to add ticks for " + key + " to the memory dictionary, results may be incorrectly calculated!" ) ;
}
2018-05-22 10:11:50 +02:00
2018-12-15 22:07:29 +01:00
if ( ( marketTicks . Count % 10 ) = = 0 )
{
2018-05-22 10:11:50 +02:00
log . DoLogInfo ( "Bittrex - No worries, I am still alive... " + marketTicks . Count + "/" + markets . Count + " markets done..." ) ;
}
2019-02-26 23:49:07 +01:00
} ) ;
2018-05-22 10:11:50 +02:00
log . DoLogInfo ( "Bittrex - Ticks completed." ) ;
log . DoLogInfo ( "Bittrex - Creating initial market data ticks. This may take another while..." ) ;
int totalTicks = ( int ) Math . Ceiling ( startDateTime . Subtract ( endDateTime ) . TotalMinutes ) ;
int completedTicks = 0 ;
2018-12-15 22:07:29 +01:00
if ( marketTicks . Count > 0 )
{
for ( DateTime tickTime = startDateTime . ToUniversalTime ( ) ; tickTime > = endDateTime . ToUniversalTime ( ) ; tickTime = tickTime . AddMinutes ( - 1 ) )
{
2018-05-22 10:11:50 +02:00
completedTicks + + ;
double mainCurrencyPrice = 1 ;
2018-12-15 22:07:29 +01:00
if ( mainMarketTicks . Count > 0 )
{
2018-05-22 10:11:50 +02:00
List < MarketTick > mainCurrencyTickRange = mainMarketTicks . FindAll ( t = > t . Time < = tickTime ) ;
2018-12-15 22:07:29 +01:00
if ( mainCurrencyTickRange . Count > 0 )
{
2018-05-22 10:11:50 +02:00
MarketTick mainCurrencyTick = mainCurrencyTickRange . OrderByDescending ( t = > t . Time ) . First ( ) ;
mainCurrencyPrice = mainCurrencyTick . Price ;
}
}
Dictionary < string , Market > tickMarkets = new Dictionary < string , Market > ( ) ;
2018-12-15 22:07:29 +01:00
foreach ( string key in markets . Keys )
{
2018-05-22 10:11:50 +02:00
List < MarketTick > tickRange = marketTicks [ key ] . FindAll ( t = > t . Time < = tickTime ) ;
2018-12-15 22:07:29 +01:00
if ( tickRange . Count > 0 )
{
2018-05-22 10:11:50 +02:00
MarketTick marketTick = tickRange . OrderByDescending ( t = > t . Time ) . First ( ) ;
Market market = new Market ( ) ;
market . Position = markets . Count + 1 ;
market . Name = key ;
market . Symbol = key ;
market . Price = marketTick . Price ;
//market.Volume24h = marketTick.Volume24h;
market . MainCurrencyPriceUSD = mainCurrencyPrice ;
tickMarkets . Add ( market . Name , market ) ;
}
}
DateTime fileDateTime = new DateTime ( tickTime . ToLocalTime ( ) . Year , tickTime . ToLocalTime ( ) . Month , tickTime . ToLocalTime ( ) . Day , tickTime . ToLocalTime ( ) . Hour , tickTime . ToLocalTime ( ) . Minute , 0 ) . ToUniversalTime ( ) ;
FileHelper . WriteTextToFile ( Directory . GetCurrentDirectory ( ) + Path . DirectorySeparatorChar + Constants . PTMagicPathData + Path . DirectorySeparatorChar + Constants . PTMagicPathExchange + Path . DirectorySeparatorChar , "MarketData_" + fileDateTime . ToString ( "yyyy-MM-dd_HH.mm" ) + ".json" , JsonConvert . SerializeObject ( tickMarkets ) , fileDateTime , fileDateTime ) ;
log . DoLogDebug ( "Bittrex - Market data saved for tick " + fileDateTime . ToString ( ) + " - MainCurrencyPrice=" + mainCurrencyPrice . ToString ( "#,#0.00" ) + " USD." ) ;
2018-12-15 22:07:29 +01:00
if ( ( completedTicks % 100 ) = = 0 )
{
2018-05-22 10:11:50 +02:00
log . DoLogInfo ( "Bittrex - Our magicbots are still at work, hang on... " + completedTicks + "/" + totalTicks + " ticks done..." ) ;
}
}
}
log . DoLogInfo ( "Bittrex - Initial market data created. Ready to go!" ) ;
}
}
}
}